From 8d9b585f068f2e69e68972e92d8f48b3eb4dd23b Mon Sep 17 00:00:00 2001 From: Jason Shin Date: Thu, 29 Nov 2018 20:48:38 +1100 Subject: [PATCH] chore: moving the reshape method to ops/tensor_ops.ts; adding more exception tests --- src/lib/ops/tensor_ops.ts | 59 +++++++++++++++++++++++++++++++++++++ src/lib/utils/tensor.ts | 49 ------------------------------ test/ops/tensor_ops.test.ts | 39 ++++++++++++++++++++++++ test/utils/tensor.test.ts | 20 ------------- 4 files changed, 98 insertions(+), 69 deletions(-) delete mode 100644 src/lib/utils/tensor.ts delete mode 100644 test/utils/tensor.test.ts diff --git a/src/lib/ops/tensor_ops.ts b/src/lib/ops/tensor_ops.ts index e9147397..c962209d 100644 --- a/src/lib/ops/tensor_ops.ts +++ b/src/lib/ops/tensor_ops.ts @@ -1,4 +1,5 @@ import * as tf from '@tensorflow/tfjs'; +import { flattenDeep } from 'lodash'; import { Type1DMatrix, Type2DMatrix, TypeMatrix } from '../types'; /** @@ -84,3 +85,61 @@ export function validateMatrix2D(X: Type2DMatrix): number[] { } return shape; } + +/** + * Reshapes any size of array into a new shape. The code was copied from + * math.js, https://github.com/josdejong/mathjs/blob/5750a1845442946d236822505c607a522be23474/src/utils/array.js#L258 + * in order to use specific method from Math.js instead of install an entire library. + * + * @example + * reshape([1, 2, 3, 4, 5, 6], [2, 3]); // [[1, 2, 3], [4, 5, 6]] + * + * @param array - Target array + * @param sizes - New array shape to resize into + * @ignore + */ +export function reshape( + array: TypeMatrix, + sizes: number[] +): TypeMatrix { + // Initial validations + if (!Array.isArray(array)) { + throw new TypeError('The input array must be an array!'); + } + + if (!Array.isArray(sizes)) { + throw new TypeError('The sizes must be an array!'); + } + + const deepFlatArray = flattenDeep(array); + // If the reshaping is to single dimensional + if (sizes.length === 1 && deepFlatArray.length === sizes[0]) { + return deepFlatArray; + } else if (sizes.length === 1 && deepFlatArray.length !== sizes[0]) { + throw new TypeError( + `Target array shape [${ + deepFlatArray.length + }] cannot be reshaped into ${sizes}` + ); + } + + // testing if there are enough elements for the requested shape + let tmpArray = deepFlatArray; + let tmpArray2; + // for each dimensions starting by the last one and ignoring the first one + for (let sizeIndex = sizes.length - 1; sizeIndex > 0; sizeIndex--) { + const size = sizes[sizeIndex]; + + tmpArray2 = []; + + // aggregate the elements of the current tmpArray in elements of the requested size + const length = tmpArray.length / size; + for (let i = 0; i < length; i++) { + tmpArray2.push(tmpArray.slice(i * size, (i + 1) * size)); + } + // set it as the new tmpArray for the next loop turn or for return + tmpArray = tmpArray2; + } + + return tmpArray; +} diff --git a/src/lib/utils/tensor.ts b/src/lib/utils/tensor.ts deleted file mode 100644 index 5267c28c..00000000 --- a/src/lib/utils/tensor.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { flattenDeep } from 'lodash'; -import { TypeMatrix } from '../types/matrix.types'; - -/** - * Reshapes any size of array into a new shape. The code was copied from - * math.js, https://github.com/josdejong/mathjs/blob/5750a1845442946d236822505c607a522be23474/src/utils/array.js#L258 - * in order to use specific method from Math.js instead of install an entire library. - * - * @example - * reshape([1, 2, 3, 4, 5, 6], [2, 3]); // [[1, 2, 3], [4, 5, 6]] - * - * @param array - Target array - * @param sizes - New array shape to resize into - * @ignore - */ -export function reshape(array, sizes): TypeMatrix { - // If the reshaping is to single dimensional - if (Array.isArray(array) && sizes.length === 1) { - const deepFlat = flattenDeep(array); - if (deepFlat.length === sizes[0]) { - return deepFlat; - } else { - throw new TypeError( - `Target array shape [${ - deepFlat.length - }] cannot be reshaped into ${sizes}` - ); - } - } - - // testing if there are enough elements for the requested shape - let tmpArray = array; - let tmpArray2; - // for each dimensions starting by the last one and ignoring the first one - for (let sizeIndex = sizes.length - 1; sizeIndex > 0; sizeIndex--) { - const size = sizes[sizeIndex]; - tmpArray2 = []; - - // aggregate the elements of the current tmpArray in elements of the requested size - const length = tmpArray.length / size; - for (let i = 0; i < length; i++) { - tmpArray2.push(tmpArray.slice(i * size, (i + 1) * size)); - } - // set it as the new tmpArray for the next loop turn or for return - tmpArray = tmpArray2; - } - - return tmpArray; -} diff --git a/test/ops/tensor_ops.test.ts b/test/ops/tensor_ops.test.ts index c436424f..19436155 100644 --- a/test/ops/tensor_ops.test.ts +++ b/test/ops/tensor_ops.test.ts @@ -1,6 +1,7 @@ import { Iris } from '../../src/lib/datasets'; import { inferShape, + reshape, validateFitInputs, validateMatrix1D, validateMatrix2D @@ -157,3 +158,41 @@ describe('ops', () => { }); }); }); + +describe('utils.reshape', () => { + it('should reshape an array of shape [1] into [2, 3]', () => { + const result = reshape([1, 2, 3, 4, 5, 6], [2, 3]); + expect(result).toEqual([[1, 2, 3], [4, 5, 6]]); + }); + + it('should reshape an array of shape [2, 3] into [1]', () => { + // console.log(tf.tensor1d([1, 2, 3]).shape); + const result = reshape([[1, 2, 3], [4, 5, 6]], [6]); + expect(result).toEqual(result); + }); + + it('should reshape an array of shape [1] into [2, 3, 1]', () => { + const result = reshape([1, 2, 3, 4, 5, 6], [2, 3, 1]); + expect(result).toEqual([[[1], [2], [3]], [[4], [5], [6]]]); + }); + + it('should reshape an array of shape [2, 3] into [2, 3, 1]', () => { + const result = reshape([[1, 2, 3], [4, 5, 6]], [2, 3, 1]); + expect(result).toEqual([[[1], [2], [3]], [[4], [5], [6]]]); + }); + + it('should not reshape invalid inputs', () => { + expect(() => reshape(null, [1])).toThrow( + 'The input array must be an array!' + ); + expect(() => reshape([], [1])).toThrow( + 'Target array shape [0] cannot be reshaped into 1' + ); + expect(() => reshape([[1, 2, 3]], null)).toThrow( + 'The sizes must be an array!' + ); + expect(() => reshape([[1, 2, 3]], 1)).toThrow( + 'The sizes must be an array!' + ); + }); +}); diff --git a/test/utils/tensor.test.ts b/test/utils/tensor.test.ts deleted file mode 100644 index 1e84b124..00000000 --- a/test/utils/tensor.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as tf from '@tensorflow/tfjs'; -import { reshape } from '../../src/lib/utils/tensor'; - -describe('utils.reshape', () => { - it('should reshape an array of shape [1] into [2, 3]', () => { - const result = reshape([1, 2, 3, 4, 5, 6], [2, 3]); - expect(result).toEqual([[1, 2, 3], [4, 5, 6]]); - }); - - it('should reshape an array of shape [2, 3] into [1]', () => { - // console.log(tf.tensor1d([1, 2, 3]).shape); - const result = reshape([[1, 2, 3], [4, 5, 6]], [6]); - expect(result).toEqual(result); - }); - - it('should reshape an array of shape [1] into [2, 3, 1]', () => { - const result = reshape([1, 2, 3, 4, 5, 6], [2, 3, 1]); - expect(result).toEqual([[[1], [2], [3]], [[4], [5], [6]]]); - }); -});