Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bitwiseAnd() ops #7645

Merged
merged 16 commits into from
May 3, 2023
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"editor.tabSize": 2,
"editor.insertSpaces": true,
"[typescript]": {
"editor.formatOnSave": true
"editor.formatOnSave": true,
"editor.defaultFormatter": "xaver.clang-format"
},
"[javascript]": {
"editor.formatOnSave": true
Expand Down
32 changes: 32 additions & 0 deletions tfjs-backend-cpu/src/kernels/BitwiseAnd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @license
* Copyright 2023 Google LLC.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import {BitwiseAnd, KernelConfig} from '@tensorflow/tfjs-core';

import {createSimpleBinaryKernelImpl} from '../utils/binary_impl';
import {binaryKernelFunc} from '../utils/binary_utils';

export const bitwiseAndImpl =
createSimpleBinaryKernelImpl(((a: number, b: number) => a & b));

export const bitwiseAnd = binaryKernelFunc(BitwiseAnd, bitwiseAndImpl);

export const bitwiseAndConfig: KernelConfig = {
kernelName: BitwiseAnd,
backendName: 'cpu',
kernelFunc: bitwiseAnd
};
2 changes: 2 additions & 0 deletions tfjs-backend-cpu/src/register_all_kernels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {batchMatMulConfig} from './kernels/BatchMatMul';
import {batchNormConfig} from './kernels/BatchNorm';
import {batchToSpaceNDConfig} from './kernels/BatchToSpaceND';
import {bincountConfig} from './kernels/Bincount';
import {bitwiseAndConfig} from './kernels/BitwiseAnd';
import {broadcastArgsConfig} from './kernels/BroadcastArgs';
import {castConfig} from './kernels/Cast';
import {ceilConfig} from './kernels/Ceil';
Expand Down Expand Up @@ -215,6 +216,7 @@ const kernelConfigs: KernelConfig[] = [
batchNormConfig,
batchToSpaceNDConfig,
bincountConfig,
bitwiseAndConfig,
broadcastArgsConfig,
castConfig,
ceilConfig,
Expand Down
1 change: 1 addition & 0 deletions tfjs-backend-cpu/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
export {simpleAbsImpl} from './kernels/Abs';
export {addImpl} from './kernels/Add';
export {bincountImpl, bincountReduceImpl} from './kernels/Bincount_impl';
export {bitwiseAndImpl} from './kernels/BitwiseAnd';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. Adding it here means other backend could use this kernel implementation. We could discuss this later when implementing this kernel for other backends.

export {castImpl} from './kernels/Cast';
export {ceilImpl} from './kernels/Ceil';
export {concatImpl} from './kernels/Concat_impl';
Expand Down
6 changes: 6 additions & 0 deletions tfjs-backend-wasm/src/setup_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ const TEST_FILTERS: TestFilter[] = [
{startsWith: 'logicalNot '},
{startsWith: 'logicalOr '},
{startsWith: 'logicalXor '},
{
startsWith: 'bitwiseAnd',
excludes: [
'bitwiseAnd',
]
},
{
startsWith: 'tile ',
excludes: [
Expand Down
6 changes: 4 additions & 2 deletions tfjs-backend-webgl/src/setup_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import './index';
import '@tensorflow/tfjs-core/dist/public/chained_ops/register_all_chained_ops';
// tslint:disable-next-line: no-imports-from-dist
import '@tensorflow/tfjs-core/dist/register_all_gradients';
import {registerTestEnvs} from './backend_webgl_test_registry';

// tslint:disable-next-line: no-imports-from-dist
import {parseTestEnvFromKarmaFlags, setTestEnvs, setupTestFilters, TEST_ENVS, TestFilter} from '@tensorflow/tfjs-core/dist/jasmine_util';

import {registerTestEnvs} from './backend_webgl_test_registry';

registerTestEnvs();

const TEST_FILTERS: TestFilter[] = [];
Expand All @@ -34,7 +36,7 @@ const customInclude = (testName: string) => {
'isBrowser: false', 'dilation gradient',
'throws when index is out of bound',
// otsu tests for threshold op is failing on windows
'method otsu'
'method otsu', 'bitwiseAnd'
];
for (const subStr of toExclude) {
if (testName.includes(subStr)) {
Expand Down
6 changes: 6 additions & 0 deletions tfjs-backend-webgpu/src/setup_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ const TEST_FILTERS: TestFilter[] = [
'calling .data() twice works',
],
},
{
startsWith: 'bitwiseAnd',
excludes: [
'bitwiseAnd',
],
},

// exclude unsupported kernels and to be fixed cases
{
Expand Down
1 change: 1 addition & 0 deletions tfjs-converter/docs/supported_ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@

|Tensorflow Op Name|Tensorflow.js Op Name|
|---|---|
|BitwiseAnd|bitwiseAnd|
|Equal|equal|
|Greater|greater|
|GreaterEqual|greaterEqual|
Expand Down
18 changes: 17 additions & 1 deletion tfjs-converter/python/tensorflowjs/op_list/logical.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,5 +267,21 @@
"notSupported": true
}
]
},
{
"tfOpName": "BitwiseAnd",
"category": "logical",
"inputs": [
{
"start": 0,
"name": "x",
"type": "tensor"
},
{
"start": 1,
"name": "y",
"type": "tensor"
}
]
}
]
]
9 changes: 7 additions & 2 deletions tfjs-converter/src/operations/executors/logical_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import {InternalOpExecutor, Node} from '../types';
import {getParamValue} from './utils';

export const executeOp: InternalOpExecutor =
(node: Node, tensorMap: NamedTensorsMap,
context: ExecutionContext, ops = tfOps): Tensor[] => {
(node: Node, tensorMap: NamedTensorsMap, context: ExecutionContext,
ops = tfOps): Tensor[] => {
switch (node.op) {
case 'Equal': {
return [ops.equal(
Expand Down Expand Up @@ -80,6 +80,11 @@ export const executeOp: InternalOpExecutor =
getParamValue('a', node, tensorMap, context) as Tensor,
getParamValue('b', node, tensorMap, context) as Tensor)];
}
case 'BitwiseAnd': {
return [ops.bitwiseAnd(
getParamValue('a', node, tensorMap, context) as Tensor,
getParamValue('b', node, tensorMap, context) as Tensor)];
}
default:
throw TypeError(`Node type ${node.op} is not implemented`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('logical', () => {

([
'Equal', 'NotEqual', 'Greater', 'GreaterEqual', 'Less', 'LessEqual',
'LogicalAnd', 'LogicalOr'
'LogicalAnd', 'LogicalOr', 'BitwiseAnd'
] as const )
.forEach(op => {
it('should call tfOps.' + op, () => {
Expand Down
3 changes: 3 additions & 0 deletions tfjs-core/src/kernel_names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ export interface BincountAttrs {
size: number;
}

export const BitwiseAnd = 'BitwiseAnd';
export type BitwiseAndInputs = BinaryInputs;

export const BroadcastTo = 'BroadcastTo';
export type BroadcastToInputs = Pick<NamedTensorInfoMap, 'x'>;
export interface BroadCastToAttrs {
Expand Down
65 changes: 65 additions & 0 deletions tfjs-core/src/ops/bitwise_and.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* @license
* Copyright 2023 Google LLC.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import {ENGINE} from '../engine';
import {BitwiseAnd, BitwiseAndInputs} from '../kernel_names';
import {Tensor} from '../tensor';
import {NamedTensorMap} from '../tensor_types';
import {convertToTensor} from '../tensor_util_env';
import {Rank} from '../types';
import {arraysEqual} from '../util_base';

import {op} from './operation';

/**
* Bitwise `AND` operation for input tensors.
*
* Given two input tensors, returns a new tensor
* with the `AND` calculated values.
*
* The method supports int32 values
*
*
* ```js
* const x = tf.tensor1d([0, 5, 3, 14], 'int32');
* const y = tf.tensor1d([5, 0, 7, 11], 'int32');
* tf.bitwiseAnd(x, y).print();
* ```
*
* @param x The input tensor to be calculated.
* @param y The input tensor to be calculated.
*
* @doc {heading: 'Operations', subheading: 'Logical'}
*/
function bitwiseAnd_<R extends Rank>(x: Tensor, y: Tensor): Tensor<R> {
const $x = convertToTensor(x, 'x', 'bitwiseAnd');
const $y = convertToTensor(y, 'y', 'bitwiseAnd');

if (!arraysEqual($x.shape, $y.shape)) {
throw new Error(`BitwiseAnd: Tensors must have the same shape. x: ${
$x.shape}, y: ${$y.shape}`);
}
if ($x.dtype !== 'int32' || $y.dtype !== 'int32') {
throw new Error(
`BitwiseAnd: Only supports 'int32' values in tensor, found type of x: ${
$x.dtype} and type of y: ${$y.dtype}`);
}

const inputs: BitwiseAndInputs = {a: $x, b: $y};
return ENGINE.runKernel(BitwiseAnd, inputs as unknown as NamedTensorMap);
}
export const bitwiseAnd = /* @__PURE__ */ op({bitwiseAnd_});
46 changes: 46 additions & 0 deletions tfjs-core/src/ops/bitwise_and_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license
* Copyright 2023 Google LLC.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import * as tf from '../index';
import {ALL_ENVS, describeWithFlags} from '../jasmine_util';
import {expectArraysClose} from '../test_util';
import {bitwiseAnd} from './bitwise_and';

describeWithFlags('bitwiseAnd', ALL_ENVS, () => {
it('a bitwiseAnd b', async () => {
const a = tf.tensor1d([0, 5, 3, 14], 'int32');
const b = tf.tensor1d([5, 0, 7, 11], 'int32');

const res = bitwiseAnd(a, b);
expectArraysClose(await res.data(), [0, 0, 3, 10]);
});

it('different shape', () => {
const a = tf.tensor1d([0, 5, 3, 14]);
const b = tf.tensor1d([5, 0, 7]);

expect(() => bitwiseAnd(a, b))
.toThrowError(/BitwiseAnd: Tensors must have the same shape/);
});

it('wrong type', () => {
const a = tf.tensor1d([0, 1, 3, 14], 'float32');
const b = tf.tensor1d([5, 0, 7, 12], 'float32');

expect(() => bitwiseAnd(a, b))
.toThrowError(/BitwiseAnd: Only supports 'int32' values in tensor/);
});
});
1 change: 1 addition & 0 deletions tfjs-core/src/ops/ops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export {batchNorm2d} from './batchnorm2d';
export {batchNorm3d} from './batchnorm3d';
export {batchNorm4d} from './batchnorm4d';
export {bincount} from './bincount';
export {bitwiseAnd} from './bitwise_and';
export {broadcastArgs} from './broadcast_args';
export {broadcastTo} from './broadcast_to';
export {buffer} from './buffer';
Expand Down
1 change: 1 addition & 0 deletions tfjs-node/src/run_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jasmine_util.setTestEnvs([{

const IGNORE_LIST: string[] = [
// Always ignore version tests:
'bitwiseAnd',
'version version',
'unreliable is true due to both auto gc and string tensors',
'unreliable is true due to auto gc',
Expand Down