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

[ops ] guard shape for decimal and negative values #7014

Merged
merged 3 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tfjs-core/src/ops/broadcast_to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {Tensor} from '../tensor';
import {NamedTensorMap} from '../tensor_types';
import {convertToTensor} from '../tensor_util_env';
import {Rank, ShapeMap, TensorLike} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {clone} from './clone';
import {op} from './operation';
Expand Down Expand Up @@ -50,6 +51,8 @@ function broadcastTo_<R extends Rank>(
throw new Error(`broadcastTo(): Invalid broadcast shape [${shape}].`);
}

assertNonNegativeIntegerDimensions(shape);
Copy link
Member

Choose a reason for hiding this comment

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

We can avoid having to check this for values we get from convertToTensor if we add this check to every convertToTensor call. We already check dtype, so checking shape as well seems fine to me.

Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately, I don't think there's a similar fix for ops that make new tensors unless we make changes to the op function, and even then, it's probably not really possible since you don't know the order of the args.


if (shape.length < input.rank) {
throw new Error(`broadcastTo(): shape.length=${shape.length} < input.rank=${
input.rank}.`);
Expand Down
5 changes: 5 additions & 0 deletions tfjs-core/src/ops/broadcast_to_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,9 @@ describeWithFlags('broadcastTo', ALL_ENVS, () => {

expectArraysClose(await df(a).array(), await dh(a).array());
});

it('should throw error when shape is not integer', () => {
const a = tf.scalar(4.2);
expect(() => tf.broadcastTo(a, [2, 2.22, 3.33])).toThrow();
});
});
3 changes: 3 additions & 0 deletions tfjs-core/src/ops/fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {Fill, FillAttrs} from '../kernel_names';
import {NamedAttrMap} from '../kernel_registry';
import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

/**
* Creates a `tf.Tensor` filled with a scalar value.
Expand All @@ -37,6 +38,8 @@ import {DataType, Rank, ShapeMap} from '../types';
*/
function fill<R extends Rank>(
shape: ShapeMap[R], value: number|string, dtype?: DataType): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);

const attrs: FillAttrs = {shape, value, dtype};

return ENGINE.runKernel(Fill, {}, attrs as {} as NamedAttrMap);
Expand Down
4 changes: 4 additions & 0 deletions tfjs-core/src/ops/fill_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,8 @@ describeWithFlags('fill', ALL_ENVS, () => {
expect(a.shape).toEqual([2, 1, 2, 1, 2]);
expectArraysClose(await a.data(), [2, 2, 2, 2, 2, 2, 2, 2]);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.fill([2, 2.22, 3.33], 1)).toThrow();
});
});
3 changes: 2 additions & 1 deletion tfjs-core/src/ops/ones.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {ENGINE} from '../engine';
import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {makeOnesTypedArray, sizeFromShape} from '../util';

import {assertNonNegativeIntegerDimensions} from '../util_base';
import {complex} from './complex';
import {zeros} from './zeros';

Expand All @@ -38,6 +38,7 @@ import {zeros} from './zeros';
*/
export function ones<R extends Rank>(
shape: ShapeMap[R], dtype: DataType = 'float32'): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
if (dtype === 'complex64') {
const real = ones(shape, 'float32');
const imag = zeros(shape, 'float32');
Expand Down
4 changes: 4 additions & 0 deletions tfjs-core/src/ops/ones_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,8 @@ describeWithFlags('ones', ALL_ENVS, () => {
expect(a.shape).toEqual([3, 2, 1, 1]);
expectArraysEqual(await a.data(), [1, 1, 1, 1, 1, 1]);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.ones([2, 2.22, 3.33])).toThrow();
});
});
2 changes: 2 additions & 0 deletions tfjs-core/src/ops/rand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {ENGINE} from '../engine';
import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {sizeFromShape} from '../util';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {op} from './operation';

Expand All @@ -36,6 +37,7 @@ import {op} from './operation';
function rand_<R extends Rank>(
shape: ShapeMap[R], randFunction: () => number,
dtype?: DataType): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
const size = sizeFromShape(shape);
let values = null;
if (dtype == null || dtype === 'float32') {
Expand Down
5 changes: 5 additions & 0 deletions tfjs-core/src/ops/rand_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ describeWithFlags('rand', ALL_ENVS, () => {
expect(result.dtype).toBe('bool');
expectValuesInRange(await result.data(), 0, 1);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.rand([2, 2.22, 3.33], () => util.randUniform(0, 2)))
.toThrow();
});
});

function isFloat(n: number): boolean {
Expand Down
2 changes: 2 additions & 0 deletions tfjs-core/src/ops/random_gamma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import {Tensor} from '../tensor';
import {Rank, ShapeMap} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {buffer} from './buffer';
import {op} from './operation';
Expand All @@ -41,6 +42,7 @@ import {RandGamma} from './rand_util';
function randomGamma_<R extends Rank>(
shape: ShapeMap[R], alpha: number, beta = 1,
dtype: 'float32'|'int32' = 'float32', seed?: number): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
if (beta == null) {
beta = 1;
}
Expand Down
4 changes: 4 additions & 0 deletions tfjs-core/src/ops/random_gamma_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,8 @@ describeWithFlags('randomGamma', ALL_ENVS, () => {
expect(result.dtype).toBe('int32');
expectValuesInRange(await result.data(), GAMMA_MIN, GAMMA_MAX);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.randomGamma([2, 2.22, 3.33], 2, 2)).toThrow();
});
});
2 changes: 2 additions & 0 deletions tfjs-core/src/ops/random_normal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {buffer} from './buffer';
import {op} from './operation';
Expand All @@ -40,6 +41,7 @@ import {MPRandGauss} from './rand_util';
function randomNormal_<R extends Rank>(
shape: ShapeMap[R], mean = 0, stdDev = 1, dtype?: 'float32'|'int32',
seed?: number): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
if (dtype != null && (dtype as DataType) === 'bool') {
throw new Error(`Unsupported data type ${dtype}`);
}
Expand Down
5 changes: 5 additions & 0 deletions tfjs-core/src/ops/random_normal_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,9 @@ describeWithFlags('randomNormal', ALL_ENVS, () => {
jarqueBeraNormalityTest(await result.data());
expectArrayInMeanStdRange(await result.data(), 0, 2, EPSILON);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.randomNormal([2, 2.22, 3.33], 0, 2, 'int32', SEED))
.toThrow();
});
});
2 changes: 2 additions & 0 deletions tfjs-core/src/ops/random_uniform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {buffer} from './buffer';
import {op} from './operation';
Expand Down Expand Up @@ -45,6 +46,7 @@ import {UniformRandom} from './rand_util';
function randomUniform_<R extends Rank>(
shape: ShapeMap[R], minval = 0, maxval = 1, dtype: DataType = 'float32',
seed?: number|string): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
const res = buffer(shape, dtype);
const random = new UniformRandom(minval, maxval, null, seed);
for (let i = 0; i < res.values.length; i++) {
Expand Down
4 changes: 4 additions & 0 deletions tfjs-core/src/ops/random_uniform_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,8 @@ describeWithFlags('randomUniform', ALL_ENVS, () => {
expect(result.dtype).toBe('bool');
expectValuesInRange(await result.data(), 0, 1);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.randomUniform([2, 2.22, 3.33], 0, 1)).toThrow();
});
});
2 changes: 2 additions & 0 deletions tfjs-core/src/ops/scatter_nd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {Tensor} from '../tensor';
import {NamedTensorMap} from '../tensor_types';
import {convertToTensor} from '../tensor_util_env';
import {Rank, ShapeMap, TensorLike} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {op} from './operation';
import * as scatter_nd_util from './scatter_nd_util';
Expand All @@ -48,6 +49,7 @@ import * as scatter_nd_util from './scatter_nd_util';
function scatterND_<R extends Rank>(
indices: Tensor|TensorLike, updates: Tensor|TensorLike,
shape: ShapeMap[R]): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
const $indices = convertToTensor(indices, 'indices', 'scatterND', 'int32');
const $updates = convertToTensor(updates, 'updates', 'scatterND');
scatter_nd_util.validateInput($updates, $indices, shape);
Expand Down
7 changes: 7 additions & 0 deletions tfjs-core/src/ops/scatter_nd_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,11 @@ describeWithFlags('scatterND', ALL_ENVS, () => {
const shape = [5, 3];
expect(() => tf.scatterND(indices, updates, shape)).toThrow();
});

it('should throw error when shape is not integer', () => {
const indices = [0, 4, 2];
const updates = [100, 101, 102];
const shape = [5.55, 2.22];
expect(() => tf.scatterND(indices, updates, shape)).toThrow();
});
});
3 changes: 3 additions & 0 deletions tfjs-core/src/ops/sparse_to_dense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {Scalar, Tensor} from '../tensor';
import {NamedTensorMap} from '../tensor_types';
import {convertToTensor} from '../tensor_util_env';
import {Rank, ScalarLike, ShapeMap, TensorLike} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {op} from './operation';

Expand Down Expand Up @@ -67,6 +68,8 @@ import {op} from './operation';
function sparseToDense_<R extends Rank>(
sparseIndices: Tensor|TensorLike, sparseValues: Tensor|TensorLike,
outputShape: ShapeMap[R], defaultValue: Scalar|ScalarLike = 0): Tensor<R> {
assertNonNegativeIntegerDimensions(outputShape);

const $sparseIndices =
convertToTensor(sparseIndices, 'sparseIndices', 'sparseToDense', 'int32');
const $sparseValues = convertToTensor(
Expand Down
8 changes: 8 additions & 0 deletions tfjs-core/src/ops/sparse_to_dense_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,12 @@ describeWithFlags('sparseToDense', ALL_ENVS, () => {
expect(() => tf.sparseToDense(indices, values, shape, defaultValue))
.toThrow();
});

it('should throw error when shape is not integer', () => {
const indices = [[0, 1], [1, 1]];
const values = [5, 6];
const shape = [2.22, 2.22];
expect(() => tf.sparseToDense(indices, values, shape, tf.scalar(1)))
.toThrow();
});
});
2 changes: 2 additions & 0 deletions tfjs-core/src/ops/truncated_normal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {assertNonNegativeIntegerDimensions} from '../util_base';

import {buffer} from './buffer';
import {op} from './operation';
Expand Down Expand Up @@ -45,6 +46,7 @@ import {MPRandGauss} from './rand_util';
function truncatedNormal_<R extends Rank>(
shape: ShapeMap[R], mean = 0, stdDev = 1, dtype?: 'float32'|'int32',
seed?: number): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
if (dtype != null && (dtype as DataType) === 'bool') {
throw new Error(`Unsupported data type $ { dtype }`);
}
Expand Down
5 changes: 5 additions & 0 deletions tfjs-core/src/ops/truncated_normal_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,9 @@ describeWithFlags('truncatedNormal', ALL_ENVS, () => {
assertTruncatedValues(await result.data(), 0, 5);
expectArrayInMeanStdRange(await result.data(), 0, 5, EPSILON);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.truncatedNormal([5.55, 3.33, 2.22], 0, 5, 'int32', SEED))
.toThrow();
});
});
3 changes: 2 additions & 1 deletion tfjs-core/src/ops/zeros.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import {ENGINE} from '../engine';
import {Tensor} from '../tensor';
import {DataType, Rank, ShapeMap} from '../types';
import {makeZerosTypedArray, sizeFromShape} from '../util';
import {assertNonNegativeIntegerDimensions, makeZerosTypedArray, sizeFromShape} from '../util';

import {complex} from './complex';

Expand All @@ -37,6 +37,7 @@ import {complex} from './complex';
*/
export function zeros<R extends Rank>(
shape: ShapeMap[R], dtype: DataType = 'float32'): Tensor<R> {
assertNonNegativeIntegerDimensions(shape);
if (dtype === 'complex64') {
const real = zeros(shape, 'float32');
const imag = zeros(shape, 'float32');
Expand Down
4 changes: 4 additions & 0 deletions tfjs-core/src/ops/zeros_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,8 @@ describeWithFlags('zeros', ALL_ENVS, () => {
expect(a.shape).toEqual([3, 2, 1, 1]);
expectArraysEqual(await a.data(), [0, 0, 0, 0, 0, 0]);
});

it('should throw error when shape is not integer', () => {
expect(() => tf.zeros([2, 2.22, 3.33])).toThrow();
});
});