From 4238b2b24cff68151732f0a021b55eb093e9d8bf Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 10:53:49 -0700 Subject: [PATCH 01/13] Further modularize unit tests. --- src/jasmine_util.ts | 37 +++++++----------- .../cpu/backend_cpu_test_harness.ts} | 7 ++-- .../webgl/backend_webgl_test_harness.ts | 38 +++++++++++++++++++ src/test_node.ts | 2 +- 4 files changed, 56 insertions(+), 28 deletions(-) rename src/{test_env.ts => kernels/cpu/backend_cpu_test_harness.ts} (80%) create mode 100644 src/kernels/webgl/backend_webgl_test_harness.ts diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index bf7e7aae82..3a7d38bff7 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -14,7 +14,6 @@ * limitations under the License. * ============================================================================= */ - import {ENGINE} from './engine'; import {ENV, Environment, Flags} from './environment'; import {DataMover, KernelBackend} from './kernels/backend'; @@ -129,30 +128,10 @@ export function describeWithFlags( export interface TestEnv { name: string; backendName: string; - flags: Flags; + flags?: Flags; } -export let TEST_ENVS: TestEnv[] = [ - { - name: 'webgl1', - backendName: 'webgl', - flags: { - 'WEBGL_VERSION': 1, - 'WEBGL_CPU_FORWARD': false, - 'WEBGL_SIZE_UPLOAD_UNIFORM': 0 - } - }, - { - name: 'webgl2', - backendName: 'webgl', - flags: { - 'WEBGL_VERSION': 2, - 'WEBGL_CPU_FORWARD': false, - 'WEBGL_SIZE_UPLOAD_UNIFORM': 0 - } - }, - {name: 'cpu', backendName: 'cpu', flags: {'HAS_WEBGL': false}} -]; +export let TEST_ENVS: TestEnv[] = []; if (typeof __karma__ !== 'undefined') { const testEnv = parseKarmaFlags(__karma__.config.args); @@ -165,12 +144,22 @@ export function setTestEnvs(testEnvs: TestEnv[]) { TEST_ENVS = testEnvs; } +export function registerTestEnv(testEnv: TestEnv) { + // When overriding via command line, turn off registration of test + // environments. + if (typeof __karma__ !== 'undefined') { + TEST_ENVS.push(testEnv); + } +} + function executeTests( testName: string, tests: (env: TestEnv) => void, testEnv: TestEnv) { describe(testName, () => { beforeAll(() => { ENGINE.reset(); - ENV.setFlags(testEnv.flags); + if (testEnv.flags != null) { + ENV.setFlags(testEnv.flags); + } ENV.set('IS_TEST', true); ENGINE.setBackend(testEnv.backendName); }); diff --git a/src/test_env.ts b/src/kernels/cpu/backend_cpu_test_harness.ts similarity index 80% rename from src/test_env.ts rename to src/kernels/cpu/backend_cpu_test_harness.ts index 2a5944b74c..1cfd751878 100644 --- a/src/test_env.ts +++ b/src/kernels/cpu/backend_cpu_test_harness.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC. All Rights Reserved. * 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 @@ -15,5 +15,6 @@ * ============================================================================= */ -import './kernels/cpu/backend_cpu'; -import './kernels/webgl/backend_webgl'; +import {registerTestEnv} from '../../jasmine_util'; + +registerTestEnv({name: 'cpu', backendName: 'cpu'}); diff --git a/src/kernels/webgl/backend_webgl_test_harness.ts b/src/kernels/webgl/backend_webgl_test_harness.ts new file mode 100644 index 0000000000..2f9296d049 --- /dev/null +++ b/src/kernels/webgl/backend_webgl_test_harness.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright 2019 Google LLC. All Rights Reserved. + * 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 {registerTestEnv} from '../../jasmine_util'; + +registerTestEnv({ + name: 'webgl1', + backendName: 'webgl', + flags: { + 'WEBGL_VERSION': 1, + 'WEBGL_CPU_FORWARD': false, + 'WEBGL_SIZE_UPLOAD_UNIFORM': 0 + } +}); + +registerTestEnv({ + name: 'webgl2', + backendName: 'webgl', + flags: { + 'WEBGL_VERSION': 2, + 'WEBGL_CPU_FORWARD': false, + 'WEBGL_SIZE_UPLOAD_UNIFORM': 0 + } +}); diff --git a/src/test_node.ts b/src/test_node.ts index dcadf6c5a2..5edab75a15 100644 --- a/src/test_node.ts +++ b/src/test_node.ts @@ -24,7 +24,7 @@ process.on('unhandledRejection', e => { throw e; }); -setTestEnvs([{name: 'node', backendName: 'cpu', flags: {}}]); +setTestEnvs([{name: 'node', backendName: 'cpu'}]); const runner = new jasmine(); runner.loadConfig({spec_files: ['dist/**/**_test.js'], random: false}); From e6137a264155cc99dd05ed26fdb7804c48cb1b51 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 11:58:06 -0700 Subject: [PATCH 02/13] save --- src/{kernels => backends}/backend.ts | 0 src/{kernels => backends}/backend_test.ts | 0 src/{kernels => backends}/backend_util.ts | 0 src/{kernels => backends}/complex_util.ts | 0 .../complex_util_test.ts | 0 src/{kernels => backends}/cpu/backend_cpu.ts | 0 src/backends/cpu/backend_cpu_test.ts | 108 ++++++++++++++++++ .../cpu/backend_cpu_test_harness.ts | 9 +- src/backends/cpu/memory_test.ts | 56 +++++++++ .../non_max_suppression_impl.ts | 0 src/{kernels => backends}/packing_util.ts | 0 src/{kernels => backends}/split_shared.ts | 0 src/{kernels => backends}/topk_impl.ts | 0 .../webgl/argminmax_gpu.ts | 0 .../webgl/argminmax_packed_gpu.ts | 0 .../webgl/avg_pool_backprop_gpu.ts | 0 .../webgl/backend_webgl.ts | 0 .../webgl/backend_webgl_test.ts | 0 .../webgl/backend_webgl_test_harness.ts | 6 +- .../webgl/batchnorm_gpu.ts | 0 .../webgl/batchnorm_packed_gpu.ts | 0 .../webgl/binaryop_complex_gpu.ts | 0 .../webgl/binaryop_gpu.ts | 0 .../webgl/binaryop_packed_gpu.ts | 0 .../webgl/canvas_util.ts | 0 .../webgl/canvas_util_test.ts | 0 src/{kernels => backends}/webgl/clip_gpu.ts | 0 .../webgl/clip_packed_gpu.ts | 0 .../webgl/complex_abs_gpu.ts | 0 src/{kernels => backends}/webgl/concat_gpu.ts | 0 .../webgl/concat_packed_gpu.ts | 0 .../webgl/conv_backprop_gpu.ts | 0 .../webgl/conv_backprop_gpu_depthwise.ts | 0 src/{kernels => backends}/webgl/conv_gpu.ts | 0 .../webgl/conv_gpu_depthwise.ts | 0 .../webgl/conv_packed_gpu_depthwise.ts | 0 .../webgl/crop_and_resize_gpu.ts | 0 src/{kernels => backends}/webgl/cumsum_gpu.ts | 0 .../webgl/depth_to_space_gpu.ts | 0 .../webgl/encode_float_gpu.ts | 0 src/{kernels => backends}/webgl/fft_gpu.ts | 0 src/{kernels => backends}/webgl/fill_gpu.ts | 0 .../webgl/flags_webgl.ts | 0 .../webgl/flags_webgl_test.ts | 0 .../webgl/from_pixels_gpu.ts | 0 src/{kernels => backends}/webgl/gather_gpu.ts | 0 .../webgl/gather_nd_gpu.ts | 0 .../webgl/glsl_version.ts | 0 .../webgl/gpgpu_context.ts | 0 .../webgl/gpgpu_context_test.ts | 0 src/{kernels => backends}/webgl/gpgpu_math.ts | 0 src/{kernels => backends}/webgl/gpgpu_util.ts | 0 .../webgl/gpgpu_util_test.ts | 0 .../webgl/im2col_packed_gpu.ts | 0 src/{kernels => backends}/webgl/lrn_gpu.ts | 0 .../webgl/lrn_grad_gpu.ts | 0 .../webgl/max_pool_backprop_gpu.ts | 0 .../webgl/mulmat_packed_gpu.ts | 0 .../webgl/multinomial_gpu.ts | 0 src/{kernels => backends}/webgl/onehot_gpu.ts | 0 src/{kernels => backends}/webgl/pack_gpu.ts | 0 src/{kernels => backends}/webgl/pad_gpu.ts | 0 .../webgl/pad_packed_gpu.ts | 0 src/{kernels => backends}/webgl/pool_gpu.ts | 0 src/{kernels => backends}/webgl/reduce_gpu.ts | 0 .../webgl/reshape_packed_gpu.ts | 0 .../webgl/reshape_packed_test.ts | 0 .../webgl/resize_bilinear_backprop_gpu.ts | 0 .../webgl/resize_bilinear_gpu.ts | 0 .../webgl/resize_bilinear_packed_gpu.ts | 0 .../resize_nearest_neighbor_backprop_gpu.ts | 0 .../webgl/resize_nearest_neighbor_gpu.ts | 0 .../webgl/reverse_gpu.ts | 0 .../webgl/reverse_packed_gpu.ts | 0 .../webgl/scatter_gpu.ts | 0 .../webgl/segment_gpu.ts | 0 src/{kernels => backends}/webgl/select_gpu.ts | 0 .../webgl/shader_compiler.ts | 0 .../webgl/shader_compiler_util.ts | 0 .../webgl/shader_compiler_util_test.ts | 0 src/{kernels => backends}/webgl/slice_gpu.ts | 0 .../webgl/slice_packed_gpu.ts | 0 .../webgl/strided_slice_gpu.ts | 0 src/{kernels => backends}/webgl/tex_util.ts | 0 .../webgl/tex_util_test.ts | 0 .../webgl/texture_manager.ts | 0 src/{kernels => backends}/webgl/tile_gpu.ts | 0 .../webgl/transpose_gpu.ts | 0 .../webgl/transpose_packed_gpu.ts | 0 .../webgl/unaryop_gpu.ts | 0 .../webgl/unaryop_packed_gpu.ts | 0 src/{kernels => backends}/webgl/unpack_gpu.ts | 0 .../webgl/webgl_custom_op_test.ts | 0 .../webgl/webgl_types.ts | 0 src/{kernels => backends}/webgl/webgl_util.ts | 0 .../webgl/webgl_util_test.ts | 0 src/{kernels => backends}/where_impl.ts | 0 src/engine_test.ts | 44 +------ src/jasmine_util.ts | 11 +- src/kernels/cpu/backend_cpu_test.ts | 54 --------- src/ops/array_ops_test.ts | 15 +-- src/ops/gather_nd_test.ts | 10 +- src/ops/scatter_nd_test.ts | 20 +--- src/ops/sparse_to_dense_test.ts | 12 +- 104 files changed, 186 insertions(+), 159 deletions(-) rename src/{kernels => backends}/backend.ts (100%) rename src/{kernels => backends}/backend_test.ts (100%) rename src/{kernels => backends}/backend_util.ts (100%) rename src/{kernels => backends}/complex_util.ts (100%) rename src/{kernels => backends}/complex_util_test.ts (100%) rename src/{kernels => backends}/cpu/backend_cpu.ts (100%) create mode 100644 src/backends/cpu/backend_cpu_test.ts rename src/{kernels => backends}/cpu/backend_cpu_test_harness.ts (79%) create mode 100644 src/backends/cpu/memory_test.ts rename src/{kernels => backends}/non_max_suppression_impl.ts (100%) rename src/{kernels => backends}/packing_util.ts (100%) rename src/{kernels => backends}/split_shared.ts (100%) rename src/{kernels => backends}/topk_impl.ts (100%) rename src/{kernels => backends}/webgl/argminmax_gpu.ts (100%) rename src/{kernels => backends}/webgl/argminmax_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/avg_pool_backprop_gpu.ts (100%) rename src/{kernels => backends}/webgl/backend_webgl.ts (100%) rename src/{kernels => backends}/webgl/backend_webgl_test.ts (100%) rename src/{kernels => backends}/webgl/backend_webgl_test_harness.ts (88%) rename src/{kernels => backends}/webgl/batchnorm_gpu.ts (100%) rename src/{kernels => backends}/webgl/batchnorm_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/binaryop_complex_gpu.ts (100%) rename src/{kernels => backends}/webgl/binaryop_gpu.ts (100%) rename src/{kernels => backends}/webgl/binaryop_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/canvas_util.ts (100%) rename src/{kernels => backends}/webgl/canvas_util_test.ts (100%) rename src/{kernels => backends}/webgl/clip_gpu.ts (100%) rename src/{kernels => backends}/webgl/clip_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/complex_abs_gpu.ts (100%) rename src/{kernels => backends}/webgl/concat_gpu.ts (100%) rename src/{kernels => backends}/webgl/concat_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/conv_backprop_gpu.ts (100%) rename src/{kernels => backends}/webgl/conv_backprop_gpu_depthwise.ts (100%) rename src/{kernels => backends}/webgl/conv_gpu.ts (100%) rename src/{kernels => backends}/webgl/conv_gpu_depthwise.ts (100%) rename src/{kernels => backends}/webgl/conv_packed_gpu_depthwise.ts (100%) rename src/{kernels => backends}/webgl/crop_and_resize_gpu.ts (100%) rename src/{kernels => backends}/webgl/cumsum_gpu.ts (100%) rename src/{kernels => backends}/webgl/depth_to_space_gpu.ts (100%) rename src/{kernels => backends}/webgl/encode_float_gpu.ts (100%) rename src/{kernels => backends}/webgl/fft_gpu.ts (100%) rename src/{kernels => backends}/webgl/fill_gpu.ts (100%) rename src/{kernels => backends}/webgl/flags_webgl.ts (100%) rename src/{kernels => backends}/webgl/flags_webgl_test.ts (100%) rename src/{kernels => backends}/webgl/from_pixels_gpu.ts (100%) rename src/{kernels => backends}/webgl/gather_gpu.ts (100%) rename src/{kernels => backends}/webgl/gather_nd_gpu.ts (100%) rename src/{kernels => backends}/webgl/glsl_version.ts (100%) rename src/{kernels => backends}/webgl/gpgpu_context.ts (100%) rename src/{kernels => backends}/webgl/gpgpu_context_test.ts (100%) rename src/{kernels => backends}/webgl/gpgpu_math.ts (100%) rename src/{kernels => backends}/webgl/gpgpu_util.ts (100%) rename src/{kernels => backends}/webgl/gpgpu_util_test.ts (100%) rename src/{kernels => backends}/webgl/im2col_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/lrn_gpu.ts (100%) rename src/{kernels => backends}/webgl/lrn_grad_gpu.ts (100%) rename src/{kernels => backends}/webgl/max_pool_backprop_gpu.ts (100%) rename src/{kernels => backends}/webgl/mulmat_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/multinomial_gpu.ts (100%) rename src/{kernels => backends}/webgl/onehot_gpu.ts (100%) rename src/{kernels => backends}/webgl/pack_gpu.ts (100%) rename src/{kernels => backends}/webgl/pad_gpu.ts (100%) rename src/{kernels => backends}/webgl/pad_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/pool_gpu.ts (100%) rename src/{kernels => backends}/webgl/reduce_gpu.ts (100%) rename src/{kernels => backends}/webgl/reshape_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/reshape_packed_test.ts (100%) rename src/{kernels => backends}/webgl/resize_bilinear_backprop_gpu.ts (100%) rename src/{kernels => backends}/webgl/resize_bilinear_gpu.ts (100%) rename src/{kernels => backends}/webgl/resize_bilinear_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/resize_nearest_neighbor_backprop_gpu.ts (100%) rename src/{kernels => backends}/webgl/resize_nearest_neighbor_gpu.ts (100%) rename src/{kernels => backends}/webgl/reverse_gpu.ts (100%) rename src/{kernels => backends}/webgl/reverse_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/scatter_gpu.ts (100%) rename src/{kernels => backends}/webgl/segment_gpu.ts (100%) rename src/{kernels => backends}/webgl/select_gpu.ts (100%) rename src/{kernels => backends}/webgl/shader_compiler.ts (100%) rename src/{kernels => backends}/webgl/shader_compiler_util.ts (100%) rename src/{kernels => backends}/webgl/shader_compiler_util_test.ts (100%) rename src/{kernels => backends}/webgl/slice_gpu.ts (100%) rename src/{kernels => backends}/webgl/slice_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/strided_slice_gpu.ts (100%) rename src/{kernels => backends}/webgl/tex_util.ts (100%) rename src/{kernels => backends}/webgl/tex_util_test.ts (100%) rename src/{kernels => backends}/webgl/texture_manager.ts (100%) rename src/{kernels => backends}/webgl/tile_gpu.ts (100%) rename src/{kernels => backends}/webgl/transpose_gpu.ts (100%) rename src/{kernels => backends}/webgl/transpose_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/unaryop_gpu.ts (100%) rename src/{kernels => backends}/webgl/unaryop_packed_gpu.ts (100%) rename src/{kernels => backends}/webgl/unpack_gpu.ts (100%) rename src/{kernels => backends}/webgl/webgl_custom_op_test.ts (100%) rename src/{kernels => backends}/webgl/webgl_types.ts (100%) rename src/{kernels => backends}/webgl/webgl_util.ts (100%) rename src/{kernels => backends}/webgl/webgl_util_test.ts (100%) rename src/{kernels => backends}/where_impl.ts (100%) delete mode 100644 src/kernels/cpu/backend_cpu_test.ts diff --git a/src/kernels/backend.ts b/src/backends/backend.ts similarity index 100% rename from src/kernels/backend.ts rename to src/backends/backend.ts diff --git a/src/kernels/backend_test.ts b/src/backends/backend_test.ts similarity index 100% rename from src/kernels/backend_test.ts rename to src/backends/backend_test.ts diff --git a/src/kernels/backend_util.ts b/src/backends/backend_util.ts similarity index 100% rename from src/kernels/backend_util.ts rename to src/backends/backend_util.ts diff --git a/src/kernels/complex_util.ts b/src/backends/complex_util.ts similarity index 100% rename from src/kernels/complex_util.ts rename to src/backends/complex_util.ts diff --git a/src/kernels/complex_util_test.ts b/src/backends/complex_util_test.ts similarity index 100% rename from src/kernels/complex_util_test.ts rename to src/backends/complex_util_test.ts diff --git a/src/kernels/cpu/backend_cpu.ts b/src/backends/cpu/backend_cpu.ts similarity index 100% rename from src/kernels/cpu/backend_cpu.ts rename to src/backends/cpu/backend_cpu.ts diff --git a/src/backends/cpu/backend_cpu_test.ts b/src/backends/cpu/backend_cpu_test.ts new file mode 100644 index 0000000000..4e0338dd15 --- /dev/null +++ b/src/backends/cpu/backend_cpu_test.ts @@ -0,0 +1,108 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 {describeWithFlags} from '../../jasmine_util'; +import {tensor2d} from '../../ops/ops'; +import {expectArraysEqual} from '../../test_util'; + +import {MathBackendCPU} from './backend_cpu'; +import {CPU_ENVS} from './backend_cpu_test_harness'; + +describeWithFlags('backendCPU', CPU_ENVS, () => { + let backend: MathBackendCPU; + beforeEach(() => { + backend = tf.backend() as MathBackendCPU; + }); + + it('register empty string tensor', () => { + const t = tf.Tensor.make([3], {}, 'string'); + expect(backend.readSync(t.dataId) == null).toBe(true); + }); + + it('register empty string tensor and write', () => { + const t = tf.Tensor.make([3], {}, 'string'); + backend.write(t.dataId, ['c', 'a', 'b']); + expectArraysEqual(backend.readSync(t.dataId), ['c', 'a', 'b']); + }); + + it('register string tensor with values', () => { + const t = tf.Tensor.make([3], {values: ['a', 'b', 'c']}, 'string'); + expectArraysEqual(backend.readSync(t.dataId), ['a', 'b', 'c']); + }); + + it('register string tensor with values and overwrite', () => { + const t = tf.Tensor.make([3], {values: ['a', 'b', 'c']}, 'string'); + backend.write(t.dataId, ['c', 'a', 'b']); + expectArraysEqual(backend.readSync(t.dataId), ['c', 'a', 'b']); + }); + + it('register string tensor with values and mismatched shape', () => { + expect(() => tf.tensor(['a', 'b', 'c'], [4], 'string')).toThrowError(); + }); +}); + +describeWithFlags('depthToSpace', CPU_ENVS, () => { + it('throws when CPU backend used with data format NCHW', () => { + const t = tf.tensor4d([1, 2, 3, 4], [1, 4, 1, 1]); + const blockSize = 2; + const dataFormat = 'NCHW'; + + expect(() => tf.depthToSpace(t, blockSize, dataFormat)) + .toThrowError( + `Only NHWC dataFormat supported on CPU for depthToSpace. Got ${ + dataFormat}`); + }); +}); + +describeWithFlags('gatherND CPU', CPU_ENVS, () => { + it('should throw error when index out of range', () => { + const indices = tensor2d([0, 2, 99], [3, 1], 'int32'); + const input = tensor2d( + [100, 101, 102, 777, 778, 779, 10000, 10001, 10002], [3, 3], 'float32'); + expect(() => tf.gatherND(input, indices)).toThrow(); + }); +}); + +describeWithFlags('scatterND CPU', CPU_ENVS, () => { + it('should throw error when index out of range', () => { + const indices = tf.tensor2d([0, 4, 99], [3, 1], 'int32'); + const updates = tf.tensor2d( + [100, 101, 102, 777, 778, 779, 10000, 10001, 10002], [3, 3], 'float32'); + const shape = [5, 3]; + expect(() => tf.scatterND(indices, updates, shape)).toThrow(); + }); + + it('should throw error when indices has wrong dimension', () => { + const indices = tf.tensor2d([0, 4, 99], [3, 1], 'int32'); + const updates = tf.tensor2d( + [100, 101, 102, 777, 778, 779, 10000, 10001, 10002], [3, 3], 'float32'); + const shape = [2, 3]; + expect(() => tf.scatterND(indices, updates, shape)).toThrow(); + }); +}); + +describeWithFlags('sparseToDense CPU', CPU_ENVS, () => { + it('should throw error when index out of range', () => { + const defaultValue = 2; + const indices = tf.tensor1d([0, 2, 6], 'int32'); + const values = tf.tensor1d([100, 101, 102], 'int32'); + const shape = [6]; + expect(() => tf.sparseToDense(indices, values, shape, defaultValue)) + .toThrow(); + }); +}); diff --git a/src/kernels/cpu/backend_cpu_test_harness.ts b/src/backends/cpu/backend_cpu_test_harness.ts similarity index 79% rename from src/kernels/cpu/backend_cpu_test_harness.ts rename to src/backends/cpu/backend_cpu_test_harness.ts index 1cfd751878..312f6450ce 100644 --- a/src/kernels/cpu/backend_cpu_test_harness.ts +++ b/src/backends/cpu/backend_cpu_test_harness.ts @@ -15,6 +15,13 @@ * ============================================================================= */ -import {registerTestEnv} from '../../jasmine_util'; +import {Constraints, registerTestEnv} from '../../jasmine_util'; + +export const CPU_ENVS: Constraints = { + backends: 'cpu' +}; +export const PACKED_ENVS: Constraints = { + flags: {'WEBGL_PACK': true} +}; registerTestEnv({name: 'cpu', backendName: 'cpu'}); diff --git a/src/backends/cpu/memory_test.ts b/src/backends/cpu/memory_test.ts new file mode 100644 index 0000000000..3569943fd0 --- /dev/null +++ b/src/backends/cpu/memory_test.ts @@ -0,0 +1,56 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 {describeWithFlags} from '../../jasmine_util'; +import {CPU_ENVS} from './backend_cpu_test_harness'; + +describeWithFlags('memory cpu', CPU_ENVS, () => { + it('unreliable is true due to auto gc', () => { + tf.tensor(1); + const mem = tf.memory(); + expect(mem.numTensors).toBe(1); + expect(mem.numDataBuffers).toBe(1); + expect(mem.numBytes).toBe(4); + expect(mem.unreliable).toBe(true); + + const expectedReason = + 'The reported memory is an upper bound. Due to automatic garbage ' + + 'collection, the true allocated memory may be less.'; + expect(mem.reasons.indexOf(expectedReason) >= 0).toBe(true); + }); + + it('unreliable is true due to both auto gc and string tensors', () => { + tf.tensor(1); + tf.tensor('a'); + + const mem = tf.memory(); + expect(mem.numTensors).toBe(2); + expect(mem.numDataBuffers).toBe(2); + expect(mem.numBytes).toBe(6); + expect(mem.unreliable).toBe(true); + + const expectedReasonGC = + 'The reported memory is an upper bound. Due to automatic garbage ' + + 'collection, the true allocated memory may be less.'; + expect(mem.reasons.indexOf(expectedReasonGC) >= 0).toBe(true); + const expectedReasonString = + 'Memory usage by string tensors is approximate ' + + '(2 bytes per character)'; + expect(mem.reasons.indexOf(expectedReasonString) >= 0).toBe(true); + }); +}); diff --git a/src/kernels/non_max_suppression_impl.ts b/src/backends/non_max_suppression_impl.ts similarity index 100% rename from src/kernels/non_max_suppression_impl.ts rename to src/backends/non_max_suppression_impl.ts diff --git a/src/kernels/packing_util.ts b/src/backends/packing_util.ts similarity index 100% rename from src/kernels/packing_util.ts rename to src/backends/packing_util.ts diff --git a/src/kernels/split_shared.ts b/src/backends/split_shared.ts similarity index 100% rename from src/kernels/split_shared.ts rename to src/backends/split_shared.ts diff --git a/src/kernels/topk_impl.ts b/src/backends/topk_impl.ts similarity index 100% rename from src/kernels/topk_impl.ts rename to src/backends/topk_impl.ts diff --git a/src/kernels/webgl/argminmax_gpu.ts b/src/backends/webgl/argminmax_gpu.ts similarity index 100% rename from src/kernels/webgl/argminmax_gpu.ts rename to src/backends/webgl/argminmax_gpu.ts diff --git a/src/kernels/webgl/argminmax_packed_gpu.ts b/src/backends/webgl/argminmax_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/argminmax_packed_gpu.ts rename to src/backends/webgl/argminmax_packed_gpu.ts diff --git a/src/kernels/webgl/avg_pool_backprop_gpu.ts b/src/backends/webgl/avg_pool_backprop_gpu.ts similarity index 100% rename from src/kernels/webgl/avg_pool_backprop_gpu.ts rename to src/backends/webgl/avg_pool_backprop_gpu.ts diff --git a/src/kernels/webgl/backend_webgl.ts b/src/backends/webgl/backend_webgl.ts similarity index 100% rename from src/kernels/webgl/backend_webgl.ts rename to src/backends/webgl/backend_webgl.ts diff --git a/src/kernels/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts similarity index 100% rename from src/kernels/webgl/backend_webgl_test.ts rename to src/backends/webgl/backend_webgl_test.ts diff --git a/src/kernels/webgl/backend_webgl_test_harness.ts b/src/backends/webgl/backend_webgl_test_harness.ts similarity index 88% rename from src/kernels/webgl/backend_webgl_test_harness.ts rename to src/backends/webgl/backend_webgl_test_harness.ts index 2f9296d049..4e07e832d0 100644 --- a/src/kernels/webgl/backend_webgl_test_harness.ts +++ b/src/backends/webgl/backend_webgl_test_harness.ts @@ -15,7 +15,11 @@ * ============================================================================= */ -import {registerTestEnv} from '../../jasmine_util'; +import {Constraints, registerTestEnv} from '../../jasmine_util'; + +export const WEBGL_ENVS: Constraints = { + backends: 'webgl' +}; registerTestEnv({ name: 'webgl1', diff --git a/src/kernels/webgl/batchnorm_gpu.ts b/src/backends/webgl/batchnorm_gpu.ts similarity index 100% rename from src/kernels/webgl/batchnorm_gpu.ts rename to src/backends/webgl/batchnorm_gpu.ts diff --git a/src/kernels/webgl/batchnorm_packed_gpu.ts b/src/backends/webgl/batchnorm_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/batchnorm_packed_gpu.ts rename to src/backends/webgl/batchnorm_packed_gpu.ts diff --git a/src/kernels/webgl/binaryop_complex_gpu.ts b/src/backends/webgl/binaryop_complex_gpu.ts similarity index 100% rename from src/kernels/webgl/binaryop_complex_gpu.ts rename to src/backends/webgl/binaryop_complex_gpu.ts diff --git a/src/kernels/webgl/binaryop_gpu.ts b/src/backends/webgl/binaryop_gpu.ts similarity index 100% rename from src/kernels/webgl/binaryop_gpu.ts rename to src/backends/webgl/binaryop_gpu.ts diff --git a/src/kernels/webgl/binaryop_packed_gpu.ts b/src/backends/webgl/binaryop_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/binaryop_packed_gpu.ts rename to src/backends/webgl/binaryop_packed_gpu.ts diff --git a/src/kernels/webgl/canvas_util.ts b/src/backends/webgl/canvas_util.ts similarity index 100% rename from src/kernels/webgl/canvas_util.ts rename to src/backends/webgl/canvas_util.ts diff --git a/src/kernels/webgl/canvas_util_test.ts b/src/backends/webgl/canvas_util_test.ts similarity index 100% rename from src/kernels/webgl/canvas_util_test.ts rename to src/backends/webgl/canvas_util_test.ts diff --git a/src/kernels/webgl/clip_gpu.ts b/src/backends/webgl/clip_gpu.ts similarity index 100% rename from src/kernels/webgl/clip_gpu.ts rename to src/backends/webgl/clip_gpu.ts diff --git a/src/kernels/webgl/clip_packed_gpu.ts b/src/backends/webgl/clip_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/clip_packed_gpu.ts rename to src/backends/webgl/clip_packed_gpu.ts diff --git a/src/kernels/webgl/complex_abs_gpu.ts b/src/backends/webgl/complex_abs_gpu.ts similarity index 100% rename from src/kernels/webgl/complex_abs_gpu.ts rename to src/backends/webgl/complex_abs_gpu.ts diff --git a/src/kernels/webgl/concat_gpu.ts b/src/backends/webgl/concat_gpu.ts similarity index 100% rename from src/kernels/webgl/concat_gpu.ts rename to src/backends/webgl/concat_gpu.ts diff --git a/src/kernels/webgl/concat_packed_gpu.ts b/src/backends/webgl/concat_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/concat_packed_gpu.ts rename to src/backends/webgl/concat_packed_gpu.ts diff --git a/src/kernels/webgl/conv_backprop_gpu.ts b/src/backends/webgl/conv_backprop_gpu.ts similarity index 100% rename from src/kernels/webgl/conv_backprop_gpu.ts rename to src/backends/webgl/conv_backprop_gpu.ts diff --git a/src/kernels/webgl/conv_backprop_gpu_depthwise.ts b/src/backends/webgl/conv_backprop_gpu_depthwise.ts similarity index 100% rename from src/kernels/webgl/conv_backprop_gpu_depthwise.ts rename to src/backends/webgl/conv_backprop_gpu_depthwise.ts diff --git a/src/kernels/webgl/conv_gpu.ts b/src/backends/webgl/conv_gpu.ts similarity index 100% rename from src/kernels/webgl/conv_gpu.ts rename to src/backends/webgl/conv_gpu.ts diff --git a/src/kernels/webgl/conv_gpu_depthwise.ts b/src/backends/webgl/conv_gpu_depthwise.ts similarity index 100% rename from src/kernels/webgl/conv_gpu_depthwise.ts rename to src/backends/webgl/conv_gpu_depthwise.ts diff --git a/src/kernels/webgl/conv_packed_gpu_depthwise.ts b/src/backends/webgl/conv_packed_gpu_depthwise.ts similarity index 100% rename from src/kernels/webgl/conv_packed_gpu_depthwise.ts rename to src/backends/webgl/conv_packed_gpu_depthwise.ts diff --git a/src/kernels/webgl/crop_and_resize_gpu.ts b/src/backends/webgl/crop_and_resize_gpu.ts similarity index 100% rename from src/kernels/webgl/crop_and_resize_gpu.ts rename to src/backends/webgl/crop_and_resize_gpu.ts diff --git a/src/kernels/webgl/cumsum_gpu.ts b/src/backends/webgl/cumsum_gpu.ts similarity index 100% rename from src/kernels/webgl/cumsum_gpu.ts rename to src/backends/webgl/cumsum_gpu.ts diff --git a/src/kernels/webgl/depth_to_space_gpu.ts b/src/backends/webgl/depth_to_space_gpu.ts similarity index 100% rename from src/kernels/webgl/depth_to_space_gpu.ts rename to src/backends/webgl/depth_to_space_gpu.ts diff --git a/src/kernels/webgl/encode_float_gpu.ts b/src/backends/webgl/encode_float_gpu.ts similarity index 100% rename from src/kernels/webgl/encode_float_gpu.ts rename to src/backends/webgl/encode_float_gpu.ts diff --git a/src/kernels/webgl/fft_gpu.ts b/src/backends/webgl/fft_gpu.ts similarity index 100% rename from src/kernels/webgl/fft_gpu.ts rename to src/backends/webgl/fft_gpu.ts diff --git a/src/kernels/webgl/fill_gpu.ts b/src/backends/webgl/fill_gpu.ts similarity index 100% rename from src/kernels/webgl/fill_gpu.ts rename to src/backends/webgl/fill_gpu.ts diff --git a/src/kernels/webgl/flags_webgl.ts b/src/backends/webgl/flags_webgl.ts similarity index 100% rename from src/kernels/webgl/flags_webgl.ts rename to src/backends/webgl/flags_webgl.ts diff --git a/src/kernels/webgl/flags_webgl_test.ts b/src/backends/webgl/flags_webgl_test.ts similarity index 100% rename from src/kernels/webgl/flags_webgl_test.ts rename to src/backends/webgl/flags_webgl_test.ts diff --git a/src/kernels/webgl/from_pixels_gpu.ts b/src/backends/webgl/from_pixels_gpu.ts similarity index 100% rename from src/kernels/webgl/from_pixels_gpu.ts rename to src/backends/webgl/from_pixels_gpu.ts diff --git a/src/kernels/webgl/gather_gpu.ts b/src/backends/webgl/gather_gpu.ts similarity index 100% rename from src/kernels/webgl/gather_gpu.ts rename to src/backends/webgl/gather_gpu.ts diff --git a/src/kernels/webgl/gather_nd_gpu.ts b/src/backends/webgl/gather_nd_gpu.ts similarity index 100% rename from src/kernels/webgl/gather_nd_gpu.ts rename to src/backends/webgl/gather_nd_gpu.ts diff --git a/src/kernels/webgl/glsl_version.ts b/src/backends/webgl/glsl_version.ts similarity index 100% rename from src/kernels/webgl/glsl_version.ts rename to src/backends/webgl/glsl_version.ts diff --git a/src/kernels/webgl/gpgpu_context.ts b/src/backends/webgl/gpgpu_context.ts similarity index 100% rename from src/kernels/webgl/gpgpu_context.ts rename to src/backends/webgl/gpgpu_context.ts diff --git a/src/kernels/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts similarity index 100% rename from src/kernels/webgl/gpgpu_context_test.ts rename to src/backends/webgl/gpgpu_context_test.ts diff --git a/src/kernels/webgl/gpgpu_math.ts b/src/backends/webgl/gpgpu_math.ts similarity index 100% rename from src/kernels/webgl/gpgpu_math.ts rename to src/backends/webgl/gpgpu_math.ts diff --git a/src/kernels/webgl/gpgpu_util.ts b/src/backends/webgl/gpgpu_util.ts similarity index 100% rename from src/kernels/webgl/gpgpu_util.ts rename to src/backends/webgl/gpgpu_util.ts diff --git a/src/kernels/webgl/gpgpu_util_test.ts b/src/backends/webgl/gpgpu_util_test.ts similarity index 100% rename from src/kernels/webgl/gpgpu_util_test.ts rename to src/backends/webgl/gpgpu_util_test.ts diff --git a/src/kernels/webgl/im2col_packed_gpu.ts b/src/backends/webgl/im2col_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/im2col_packed_gpu.ts rename to src/backends/webgl/im2col_packed_gpu.ts diff --git a/src/kernels/webgl/lrn_gpu.ts b/src/backends/webgl/lrn_gpu.ts similarity index 100% rename from src/kernels/webgl/lrn_gpu.ts rename to src/backends/webgl/lrn_gpu.ts diff --git a/src/kernels/webgl/lrn_grad_gpu.ts b/src/backends/webgl/lrn_grad_gpu.ts similarity index 100% rename from src/kernels/webgl/lrn_grad_gpu.ts rename to src/backends/webgl/lrn_grad_gpu.ts diff --git a/src/kernels/webgl/max_pool_backprop_gpu.ts b/src/backends/webgl/max_pool_backprop_gpu.ts similarity index 100% rename from src/kernels/webgl/max_pool_backprop_gpu.ts rename to src/backends/webgl/max_pool_backprop_gpu.ts diff --git a/src/kernels/webgl/mulmat_packed_gpu.ts b/src/backends/webgl/mulmat_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/mulmat_packed_gpu.ts rename to src/backends/webgl/mulmat_packed_gpu.ts diff --git a/src/kernels/webgl/multinomial_gpu.ts b/src/backends/webgl/multinomial_gpu.ts similarity index 100% rename from src/kernels/webgl/multinomial_gpu.ts rename to src/backends/webgl/multinomial_gpu.ts diff --git a/src/kernels/webgl/onehot_gpu.ts b/src/backends/webgl/onehot_gpu.ts similarity index 100% rename from src/kernels/webgl/onehot_gpu.ts rename to src/backends/webgl/onehot_gpu.ts diff --git a/src/kernels/webgl/pack_gpu.ts b/src/backends/webgl/pack_gpu.ts similarity index 100% rename from src/kernels/webgl/pack_gpu.ts rename to src/backends/webgl/pack_gpu.ts diff --git a/src/kernels/webgl/pad_gpu.ts b/src/backends/webgl/pad_gpu.ts similarity index 100% rename from src/kernels/webgl/pad_gpu.ts rename to src/backends/webgl/pad_gpu.ts diff --git a/src/kernels/webgl/pad_packed_gpu.ts b/src/backends/webgl/pad_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/pad_packed_gpu.ts rename to src/backends/webgl/pad_packed_gpu.ts diff --git a/src/kernels/webgl/pool_gpu.ts b/src/backends/webgl/pool_gpu.ts similarity index 100% rename from src/kernels/webgl/pool_gpu.ts rename to src/backends/webgl/pool_gpu.ts diff --git a/src/kernels/webgl/reduce_gpu.ts b/src/backends/webgl/reduce_gpu.ts similarity index 100% rename from src/kernels/webgl/reduce_gpu.ts rename to src/backends/webgl/reduce_gpu.ts diff --git a/src/kernels/webgl/reshape_packed_gpu.ts b/src/backends/webgl/reshape_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/reshape_packed_gpu.ts rename to src/backends/webgl/reshape_packed_gpu.ts diff --git a/src/kernels/webgl/reshape_packed_test.ts b/src/backends/webgl/reshape_packed_test.ts similarity index 100% rename from src/kernels/webgl/reshape_packed_test.ts rename to src/backends/webgl/reshape_packed_test.ts diff --git a/src/kernels/webgl/resize_bilinear_backprop_gpu.ts b/src/backends/webgl/resize_bilinear_backprop_gpu.ts similarity index 100% rename from src/kernels/webgl/resize_bilinear_backprop_gpu.ts rename to src/backends/webgl/resize_bilinear_backprop_gpu.ts diff --git a/src/kernels/webgl/resize_bilinear_gpu.ts b/src/backends/webgl/resize_bilinear_gpu.ts similarity index 100% rename from src/kernels/webgl/resize_bilinear_gpu.ts rename to src/backends/webgl/resize_bilinear_gpu.ts diff --git a/src/kernels/webgl/resize_bilinear_packed_gpu.ts b/src/backends/webgl/resize_bilinear_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/resize_bilinear_packed_gpu.ts rename to src/backends/webgl/resize_bilinear_packed_gpu.ts diff --git a/src/kernels/webgl/resize_nearest_neighbor_backprop_gpu.ts b/src/backends/webgl/resize_nearest_neighbor_backprop_gpu.ts similarity index 100% rename from src/kernels/webgl/resize_nearest_neighbor_backprop_gpu.ts rename to src/backends/webgl/resize_nearest_neighbor_backprop_gpu.ts diff --git a/src/kernels/webgl/resize_nearest_neighbor_gpu.ts b/src/backends/webgl/resize_nearest_neighbor_gpu.ts similarity index 100% rename from src/kernels/webgl/resize_nearest_neighbor_gpu.ts rename to src/backends/webgl/resize_nearest_neighbor_gpu.ts diff --git a/src/kernels/webgl/reverse_gpu.ts b/src/backends/webgl/reverse_gpu.ts similarity index 100% rename from src/kernels/webgl/reverse_gpu.ts rename to src/backends/webgl/reverse_gpu.ts diff --git a/src/kernels/webgl/reverse_packed_gpu.ts b/src/backends/webgl/reverse_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/reverse_packed_gpu.ts rename to src/backends/webgl/reverse_packed_gpu.ts diff --git a/src/kernels/webgl/scatter_gpu.ts b/src/backends/webgl/scatter_gpu.ts similarity index 100% rename from src/kernels/webgl/scatter_gpu.ts rename to src/backends/webgl/scatter_gpu.ts diff --git a/src/kernels/webgl/segment_gpu.ts b/src/backends/webgl/segment_gpu.ts similarity index 100% rename from src/kernels/webgl/segment_gpu.ts rename to src/backends/webgl/segment_gpu.ts diff --git a/src/kernels/webgl/select_gpu.ts b/src/backends/webgl/select_gpu.ts similarity index 100% rename from src/kernels/webgl/select_gpu.ts rename to src/backends/webgl/select_gpu.ts diff --git a/src/kernels/webgl/shader_compiler.ts b/src/backends/webgl/shader_compiler.ts similarity index 100% rename from src/kernels/webgl/shader_compiler.ts rename to src/backends/webgl/shader_compiler.ts diff --git a/src/kernels/webgl/shader_compiler_util.ts b/src/backends/webgl/shader_compiler_util.ts similarity index 100% rename from src/kernels/webgl/shader_compiler_util.ts rename to src/backends/webgl/shader_compiler_util.ts diff --git a/src/kernels/webgl/shader_compiler_util_test.ts b/src/backends/webgl/shader_compiler_util_test.ts similarity index 100% rename from src/kernels/webgl/shader_compiler_util_test.ts rename to src/backends/webgl/shader_compiler_util_test.ts diff --git a/src/kernels/webgl/slice_gpu.ts b/src/backends/webgl/slice_gpu.ts similarity index 100% rename from src/kernels/webgl/slice_gpu.ts rename to src/backends/webgl/slice_gpu.ts diff --git a/src/kernels/webgl/slice_packed_gpu.ts b/src/backends/webgl/slice_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/slice_packed_gpu.ts rename to src/backends/webgl/slice_packed_gpu.ts diff --git a/src/kernels/webgl/strided_slice_gpu.ts b/src/backends/webgl/strided_slice_gpu.ts similarity index 100% rename from src/kernels/webgl/strided_slice_gpu.ts rename to src/backends/webgl/strided_slice_gpu.ts diff --git a/src/kernels/webgl/tex_util.ts b/src/backends/webgl/tex_util.ts similarity index 100% rename from src/kernels/webgl/tex_util.ts rename to src/backends/webgl/tex_util.ts diff --git a/src/kernels/webgl/tex_util_test.ts b/src/backends/webgl/tex_util_test.ts similarity index 100% rename from src/kernels/webgl/tex_util_test.ts rename to src/backends/webgl/tex_util_test.ts diff --git a/src/kernels/webgl/texture_manager.ts b/src/backends/webgl/texture_manager.ts similarity index 100% rename from src/kernels/webgl/texture_manager.ts rename to src/backends/webgl/texture_manager.ts diff --git a/src/kernels/webgl/tile_gpu.ts b/src/backends/webgl/tile_gpu.ts similarity index 100% rename from src/kernels/webgl/tile_gpu.ts rename to src/backends/webgl/tile_gpu.ts diff --git a/src/kernels/webgl/transpose_gpu.ts b/src/backends/webgl/transpose_gpu.ts similarity index 100% rename from src/kernels/webgl/transpose_gpu.ts rename to src/backends/webgl/transpose_gpu.ts diff --git a/src/kernels/webgl/transpose_packed_gpu.ts b/src/backends/webgl/transpose_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/transpose_packed_gpu.ts rename to src/backends/webgl/transpose_packed_gpu.ts diff --git a/src/kernels/webgl/unaryop_gpu.ts b/src/backends/webgl/unaryop_gpu.ts similarity index 100% rename from src/kernels/webgl/unaryop_gpu.ts rename to src/backends/webgl/unaryop_gpu.ts diff --git a/src/kernels/webgl/unaryop_packed_gpu.ts b/src/backends/webgl/unaryop_packed_gpu.ts similarity index 100% rename from src/kernels/webgl/unaryop_packed_gpu.ts rename to src/backends/webgl/unaryop_packed_gpu.ts diff --git a/src/kernels/webgl/unpack_gpu.ts b/src/backends/webgl/unpack_gpu.ts similarity index 100% rename from src/kernels/webgl/unpack_gpu.ts rename to src/backends/webgl/unpack_gpu.ts diff --git a/src/kernels/webgl/webgl_custom_op_test.ts b/src/backends/webgl/webgl_custom_op_test.ts similarity index 100% rename from src/kernels/webgl/webgl_custom_op_test.ts rename to src/backends/webgl/webgl_custom_op_test.ts diff --git a/src/kernels/webgl/webgl_types.ts b/src/backends/webgl/webgl_types.ts similarity index 100% rename from src/kernels/webgl/webgl_types.ts rename to src/backends/webgl/webgl_types.ts diff --git a/src/kernels/webgl/webgl_util.ts b/src/backends/webgl/webgl_util.ts similarity index 100% rename from src/kernels/webgl/webgl_util.ts rename to src/backends/webgl/webgl_util.ts diff --git a/src/kernels/webgl/webgl_util_test.ts b/src/backends/webgl/webgl_util_test.ts similarity index 100% rename from src/kernels/webgl/webgl_util_test.ts rename to src/backends/webgl/webgl_util_test.ts diff --git a/src/kernels/where_impl.ts b/src/backends/where_impl.ts similarity index 100% rename from src/kernels/where_impl.ts rename to src/backends/where_impl.ts diff --git a/src/engine_test.ts b/src/engine_test.ts index 0fec8b4a14..5c98237e37 100644 --- a/src/engine_test.ts +++ b/src/engine_test.ts @@ -15,12 +15,12 @@ * ============================================================================= */ +import {KernelBackend} from './backends/backend'; +import {MathBackendCPU} from './backends/cpu/backend_cpu'; +import {MathBackendWebGL} from './backends/webgl/backend_webgl'; import {ENGINE} from './engine'; import * as tf from './index'; -import {ALL_ENVS, CPU_ENVS, describeWithFlags, TestKernelBackend, WEBGL_ENVS} from './jasmine_util'; -import {KernelBackend} from './kernels/backend'; -import {MathBackendCPU} from './kernels/cpu/backend_cpu'; -import {MathBackendWebGL} from './kernels/webgl/backend_webgl'; +import {ALL_ENVS, describeWithFlags, TestKernelBackend, WEBGL_ENVS} from './jasmine_util'; import {Tensor} from './tensor'; import {expectArraysClose, expectArraysEqual} from './test_util'; @@ -251,42 +251,6 @@ describeWithFlags('memory webgl', WEBGL_ENVS, () => { }); }); -describeWithFlags('memory cpu', CPU_ENVS, () => { - it('unreliable is true due to auto gc', () => { - tf.tensor(1); - const mem = tf.memory(); - expect(mem.numTensors).toBe(1); - expect(mem.numDataBuffers).toBe(1); - expect(mem.numBytes).toBe(4); - expect(mem.unreliable).toBe(true); - - const expectedReason = - 'The reported memory is an upper bound. Due to automatic garbage ' + - 'collection, the true allocated memory may be less.'; - expect(mem.reasons.indexOf(expectedReason) >= 0).toBe(true); - }); - - it('unreliable is true due to both auto gc and string tensors', () => { - tf.tensor(1); - tf.tensor('a'); - - const mem = tf.memory(); - expect(mem.numTensors).toBe(2); - expect(mem.numDataBuffers).toBe(2); - expect(mem.numBytes).toBe(6); - expect(mem.unreliable).toBe(true); - - const expectedReasonGC = - 'The reported memory is an upper bound. Due to automatic garbage ' + - 'collection, the true allocated memory may be less.'; - expect(mem.reasons.indexOf(expectedReasonGC) >= 0).toBe(true); - const expectedReasonString = - 'Memory usage by string tensors is approximate ' + - '(2 bytes per character)'; - expect(mem.reasons.indexOf(expectedReasonString) >= 0).toBe(true); - }); -}); - describeWithFlags('profile', ALL_ENVS, () => { it('squaring', async () => { const profile = await tf.profile(() => { diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index adf622ed7f..cd5160a177 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -14,9 +14,9 @@ * limitations under the License. * ============================================================================= */ +import {KernelBackend} from './backends/backend'; import {ENGINE} from './engine'; import {ENV, Environment, Flags} from './environment'; -import {KernelBackend} from './kernels/backend'; Error.stackTraceLimit = Infinity; @@ -25,15 +25,6 @@ export type Constraints = { backends?: string | string[]; }; -export const WEBGL_ENVS: Constraints = { - backends: 'webgl' -}; -export const CPU_ENVS: Constraints = { - backends: 'cpu' -}; -export const PACKED_ENVS: Constraints = { - flags: {'WEBGL_PACK': true} -}; export const NODE_ENVS: Constraints = { flags: {'IS_NODE': true} }; diff --git a/src/kernels/cpu/backend_cpu_test.ts b/src/kernels/cpu/backend_cpu_test.ts deleted file mode 100644 index ca833d7153..0000000000 --- a/src/kernels/cpu/backend_cpu_test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license - * Copyright 2017 Google Inc. All Rights Reserved. - * 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 {CPU_ENVS, describeWithFlags} from '../../jasmine_util'; -import {expectArraysEqual} from '../../test_util'; -import {MathBackendCPU} from './backend_cpu'; - -describeWithFlags('backendCPU', CPU_ENVS, () => { - let backend: MathBackendCPU; - beforeEach(() => { - backend = tf.backend() as MathBackendCPU; - }); - - it('register empty string tensor', () => { - const t = tf.Tensor.make([3], {}, 'string'); - expect(backend.readSync(t.dataId) == null).toBe(true); - }); - - it('register empty string tensor and write', () => { - const t = tf.Tensor.make([3], {}, 'string'); - backend.write(t.dataId, ['c', 'a', 'b']); - expectArraysEqual(backend.readSync(t.dataId), ['c', 'a', 'b']); - }); - - it('register string tensor with values', () => { - const t = tf.Tensor.make([3], {values: ['a', 'b', 'c']}, 'string'); - expectArraysEqual(backend.readSync(t.dataId), ['a', 'b', 'c']); - }); - - it('register string tensor with values and overwrite', () => { - const t = tf.Tensor.make([3], {values: ['a', 'b', 'c']}, 'string'); - backend.write(t.dataId, ['c', 'a', 'b']); - expectArraysEqual(backend.readSync(t.dataId), ['c', 'a', 'b']); - }); - - it('register string tensor with values and mismatched shape', () => { - expect(() => tf.tensor(['a', 'b', 'c'], [4], 'string')).toThrowError(); - }); -}); diff --git a/src/ops/array_ops_test.ts b/src/ops/array_ops_test.ts index 48908b499b..97f0486d71 100644 --- a/src/ops/array_ops_test.ts +++ b/src/ops/array_ops_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {describeWithFlags, ALL_ENVS, BROWSER_ENVS, CPU_ENVS, NODE_ENVS, WEBGL_ENVS} from '../jasmine_util'; +import {describeWithFlags, ALL_ENVS, BROWSER_ENVS, NODE_ENVS, WEBGL_ENVS} from '../jasmine_util'; import { expectArraysClose, expectArraysEqual, expectPromiseToFail, expectValuesInRange} from '../test_util'; import * as util from '../util'; import {expectArrayInMeanStdRange, jarqueBeraNormalityTest} from './rand_util'; @@ -4197,19 +4197,6 @@ describeWithFlags('depthToSpace', BROWSER_ENVS, () => { }); }); -describeWithFlags('depthToSpace', CPU_ENVS, () => { - it('throws when CPU backend used with data format NCHW', () => { - const t = tf.tensor4d([1, 2, 3, 4], [1, 4, 1, 1]); - const blockSize = 2; - const dataFormat = 'NCHW'; - - expect(() => tf.depthToSpace(t, blockSize, dataFormat)) - .toThrowError( - `Only NHWC dataFormat supported on CPU for depthToSpace. Got ${ - dataFormat}`); - }); -}); - describeWithFlags('depthToSpace', WEBGL_ENVS, () => { it('tensor4d, input shape=[1, 4, 1, 1], blockSize=2, format=NCHW', () => { const t = tf.tensor4d([1, 2, 3, 4], [1, 4, 1, 1]); diff --git a/src/ops/gather_nd_test.ts b/src/ops/gather_nd_test.ts index d73516378f..261b1cea73 100644 --- a/src/ops/gather_nd_test.ts +++ b/src/ops/gather_nd_test.ts @@ -15,7 +15,7 @@ * ============================================================================= */ -import {ALL_ENVS, CPU_ENVS, describeWithFlags} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; import {gatherND} from './gather_nd'; @@ -140,11 +140,3 @@ describeWithFlags('gatherND', ALL_ENVS, () => { expect(() => gatherND(input, indices)).toThrow(); }); }); -describeWithFlags('gatherND CPU', CPU_ENVS, () => { - it('should throw error when index out of range', () => { - const indices = tensor2d([0, 2, 99], [3, 1], 'int32'); - const input = tensor2d( - [100, 101, 102, 777, 778, 779, 10000, 10001, 10002], [3, 3], 'float32'); - expect(() => gatherND(input, indices)).toThrow(); - }); -}); diff --git a/src/ops/scatter_nd_test.ts b/src/ops/scatter_nd_test.ts index 3bb8833ddb..d324eec147 100644 --- a/src/ops/scatter_nd_test.ts +++ b/src/ops/scatter_nd_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, CPU_ENVS, describeWithFlags} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; describeWithFlags('scatterND', ALL_ENVS, () => { @@ -150,21 +150,3 @@ describeWithFlags('scatterND', ALL_ENVS, () => { expect(() => tf.scatterND(indices, updates, shape)).toThrow(); }); }); - -describeWithFlags('scatterND CPU', CPU_ENVS, () => { - it('should throw error when index out of range', () => { - const indices = tf.tensor2d([0, 4, 99], [3, 1], 'int32'); - const updates = tf.tensor2d( - [100, 101, 102, 777, 778, 779, 10000, 10001, 10002], [3, 3], 'float32'); - const shape = [5, 3]; - expect(() => tf.scatterND(indices, updates, shape)).toThrow(); - }); - - it('should throw error when indices has wrong dimension', () => { - const indices = tf.tensor2d([0, 4, 99], [3, 1], 'int32'); - const updates = tf.tensor2d( - [100, 101, 102, 777, 778, 779, 10000, 10001, 10002], [3, 3], 'float32'); - const shape = [2, 3]; - expect(() => tf.scatterND(indices, updates, shape)).toThrow(); - }); -}); diff --git a/src/ops/sparse_to_dense_test.ts b/src/ops/sparse_to_dense_test.ts index fa8d474e25..6b05dd79f1 100644 --- a/src/ops/sparse_to_dense_test.ts +++ b/src/ops/sparse_to_dense_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, CPU_ENVS, describeWithFlags} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; let defaultValue: tf.Scalar; @@ -140,13 +140,3 @@ describeWithFlags('sparseToDense', ALL_ENVS, () => { .toThrow(); }); }); - -describeWithFlags('sparseToDense CPU', CPU_ENVS, () => { - it('should throw error when index out of range', () => { - const indices = tf.tensor1d([0, 2, 6], 'int32'); - const values = tf.tensor1d([100, 101, 102], 'int32'); - const shape = [6]; - expect(() => tf.sparseToDense(indices, values, shape, defaultValue)) - .toThrow(); - }); -}); From f7b4234203943ffb3f98238cd578abb9f828bfb5 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 13:35:40 -0700 Subject: [PATCH 03/13] save --- src/backends/cpu/backend_cpu_test.ts | 40 +- ...arness.ts => backend_cpu_test_registry.ts} | 3 - src/backends/cpu/memory_test.ts | 56 -- src/backends/webgl/backend_webgl_test.ts | 129 ++- ...ness.ts => backend_webgl_test_registry.ts} | 3 + src/backends/webgl/webgl_batchnorm_test.ts | 80 ++ src/backends/webgl/webgl_ops_test.ts | 802 ++++++++++++++++++ src/debug_mode_test.ts | 32 +- src/engine_test.ts | 103 +-- src/globals_test.ts | 45 +- src/ops/array_ops_test.ts | 228 +---- src/ops/batchnorm_test.ts | 61 +- src/ops/binary_ops_test.ts | 45 +- src/ops/conv2d_test.ts | 106 +-- src/ops/linalg_ops_test.ts | 14 +- src/ops/matmul_test.ts | 180 +--- src/ops/reduction_ops_test.ts | 65 +- src/ops/slice_test.ts | 60 +- src/ops/spectral_ops_test.ts | 4 +- src/ops/unary_ops_test.ts | 75 +- src/profiler_test.ts | 2 +- src/webgl.ts | 24 - 22 files changed, 1083 insertions(+), 1074 deletions(-) rename src/backends/cpu/{backend_cpu_test_harness.ts => backend_cpu_test_registry.ts} (92%) delete mode 100644 src/backends/cpu/memory_test.ts rename src/backends/webgl/{backend_webgl_test_harness.ts => backend_webgl_test_registry.ts} (94%) create mode 100644 src/backends/webgl/webgl_batchnorm_test.ts create mode 100644 src/backends/webgl/webgl_ops_test.ts delete mode 100644 src/webgl.ts diff --git a/src/backends/cpu/backend_cpu_test.ts b/src/backends/cpu/backend_cpu_test.ts index 4e0338dd15..34ff61cc3f 100644 --- a/src/backends/cpu/backend_cpu_test.ts +++ b/src/backends/cpu/backend_cpu_test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC. All Rights Reserved. * 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 @@ -21,7 +21,7 @@ import {tensor2d} from '../../ops/ops'; import {expectArraysEqual} from '../../test_util'; import {MathBackendCPU} from './backend_cpu'; -import {CPU_ENVS} from './backend_cpu_test_harness'; +import {CPU_ENVS} from './backend_cpu_test_registry'; describeWithFlags('backendCPU', CPU_ENVS, () => { let backend: MathBackendCPU; @@ -106,3 +106,39 @@ describeWithFlags('sparseToDense CPU', CPU_ENVS, () => { .toThrow(); }); }); + +describeWithFlags('memory cpu', CPU_ENVS, () => { + it('unreliable is true due to auto gc', () => { + tf.tensor(1); + const mem = tf.memory(); + expect(mem.numTensors).toBe(1); + expect(mem.numDataBuffers).toBe(1); + expect(mem.numBytes).toBe(4); + expect(mem.unreliable).toBe(true); + + const expectedReason = + 'The reported memory is an upper bound. Due to automatic garbage ' + + 'collection, the true allocated memory may be less.'; + expect(mem.reasons.indexOf(expectedReason) >= 0).toBe(true); + }); + + it('unreliable is true due to both auto gc and string tensors', () => { + tf.tensor(1); + tf.tensor('a'); + + const mem = tf.memory(); + expect(mem.numTensors).toBe(2); + expect(mem.numDataBuffers).toBe(2); + expect(mem.numBytes).toBe(6); + expect(mem.unreliable).toBe(true); + + const expectedReasonGC = + 'The reported memory is an upper bound. Due to automatic garbage ' + + 'collection, the true allocated memory may be less.'; + expect(mem.reasons.indexOf(expectedReasonGC) >= 0).toBe(true); + const expectedReasonString = + 'Memory usage by string tensors is approximate ' + + '(2 bytes per character)'; + expect(mem.reasons.indexOf(expectedReasonString) >= 0).toBe(true); + }); +}); diff --git a/src/backends/cpu/backend_cpu_test_harness.ts b/src/backends/cpu/backend_cpu_test_registry.ts similarity index 92% rename from src/backends/cpu/backend_cpu_test_harness.ts rename to src/backends/cpu/backend_cpu_test_registry.ts index 312f6450ce..08be2aac9f 100644 --- a/src/backends/cpu/backend_cpu_test_harness.ts +++ b/src/backends/cpu/backend_cpu_test_registry.ts @@ -20,8 +20,5 @@ import {Constraints, registerTestEnv} from '../../jasmine_util'; export const CPU_ENVS: Constraints = { backends: 'cpu' }; -export const PACKED_ENVS: Constraints = { - flags: {'WEBGL_PACK': true} -}; registerTestEnv({name: 'cpu', backendName: 'cpu'}); diff --git a/src/backends/cpu/memory_test.ts b/src/backends/cpu/memory_test.ts deleted file mode 100644 index 3569943fd0..0000000000 --- a/src/backends/cpu/memory_test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @license - * Copyright 2017 Google Inc. All Rights Reserved. - * 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 {describeWithFlags} from '../../jasmine_util'; -import {CPU_ENVS} from './backend_cpu_test_harness'; - -describeWithFlags('memory cpu', CPU_ENVS, () => { - it('unreliable is true due to auto gc', () => { - tf.tensor(1); - const mem = tf.memory(); - expect(mem.numTensors).toBe(1); - expect(mem.numDataBuffers).toBe(1); - expect(mem.numBytes).toBe(4); - expect(mem.unreliable).toBe(true); - - const expectedReason = - 'The reported memory is an upper bound. Due to automatic garbage ' + - 'collection, the true allocated memory may be less.'; - expect(mem.reasons.indexOf(expectedReason) >= 0).toBe(true); - }); - - it('unreliable is true due to both auto gc and string tensors', () => { - tf.tensor(1); - tf.tensor('a'); - - const mem = tf.memory(); - expect(mem.numTensors).toBe(2); - expect(mem.numDataBuffers).toBe(2); - expect(mem.numBytes).toBe(6); - expect(mem.unreliable).toBe(true); - - const expectedReasonGC = - 'The reported memory is an upper bound. Due to automatic garbage ' + - 'collection, the true allocated memory may be less.'; - expect(mem.reasons.indexOf(expectedReasonGC) >= 0).toBe(true); - const expectedReasonString = - 'Memory usage by string tensors is approximate ' + - '(2 bytes per character)'; - expect(mem.reasons.indexOf(expectedReasonString) >= 0).toBe(true); - }); -}); diff --git a/src/backends/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts index a96cb8c801..999e2eeffb 100644 --- a/src/backends/webgl/backend_webgl_test.ts +++ b/src/backends/webgl/backend_webgl_test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC. All Rights Reserved. * 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 @@ -16,9 +16,10 @@ */ import * as tf from '../../index'; -import {describeWithFlags, WEBGL_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; import {expectArraysClose, expectArraysEqual} from '../../test_util'; import {MathBackendWebGL, WebGLMemoryInfo} from './backend_webgl'; +import {WEBGL_ENVS} from './backend_webgl_test_registry'; describeWithFlags('lazy packing and unpacking', WEBGL_ENVS, () => { let webglLazilyUnpackFlagSaved: boolean; @@ -307,3 +308,127 @@ describeWithFlags('upload tensors as uniforms', FLOAT32_WEBGL_ENVS, () => { expectArraysClose(res, expected); }); }); + +describeWithFlags('debug on webgl', WEBGL_ENVS, () => { + beforeAll(() => { + tf.ENV.set('DEBUG', true); + }); + + afterAll(() => { + tf.ENV.set('DEBUG', false); + }); + + it('debug mode errors when overflow in tensor construction', () => { + const savedRenderFloat32Flag = + tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); + tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', false); + const a = () => tf.tensor1d([2, Math.pow(2, 17)], 'float32'); + expect(a).toThrowError(); + tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', savedRenderFloat32Flag); + }); + + it('debug mode errors when underflow in tensor construction', () => { + const savedRenderFloat32Flag = + tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); + tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', false); + const a = () => tf.tensor1d([2, 1e-8], 'float32'); + expect(a).toThrowError(); + tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', savedRenderFloat32Flag); + }); +}); + +describeWithFlags('memory webgl', WEBGL_ENVS, () => { + it('unreliable is falsy/not present when all tensors are numeric', () => { + tf.tensor(1); + const mem = tf.memory(); + expect(mem.numTensors).toBe(1); + expect(mem.numDataBuffers).toBe(1); + expect(mem.numBytes).toBe(4); + expect(mem.unreliable).toBeFalsy(); + }); +}); + +// We do not yet fully support half float backends. These tests are a starting +// point. +describeWithFlags('backend without render float32 support', WEBGL_ENVS, () => { + const savedRenderFloat32Flag = tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); + + beforeAll(() => { + tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', false); + }); + + beforeEach(() => { + tf.registerBackend('half-float-webgl', () => new MathBackendWebGL(null)); + }); + + afterEach(() => { + tf.removeBackend('half-float-webgl'); + }); + + afterAll(() => { + tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', savedRenderFloat32Flag); + }); + + it('basic usage', () => { + tf.setBackend('half-float-webgl'); + + const a = tf.tensor2d([1, 2], [1, 2]); + const b = tf.tensor2d([1, 2], [1, 2]); + const c = tf.add(a, b); + expectArraysClose(c, [2, 4]); + }); + + it('disposing tensors should not cause errors', () => { + tf.setBackend('half-float-webgl'); + expect(() => tf.tidy(() => { + const a = tf.tensor2d([1, 2], [1, 2]); + const b = tf.tensor2d([1, 2], [1, 2]); + const c = tf.add(a, b); + c.dataSync(); + return c.add(tf.tensor2d([2, 4], [1, 2])); + })).not.toThrowError(); + }); +}); + +describeWithFlags('time webgl', WEBGL_ENVS, () => { + it('upload + compute', async () => { + const a = tf.zeros([10, 10]); + const time = await tf.time(() => a.square()) as tf.webgl.WebGLTimingInfo; + expect(time.uploadWaitMs > 0); + expect(time.downloadWaitMs === 0); + expect(time.kernelMs > 0); + expect(time.wallMs >= time.kernelMs); + }); + + it('upload + compute + dataSync', async () => { + const a = tf.zeros([10, 10]); + const time = + await tf.time(() => a.square().dataSync()) as tf.webgl.WebGLTimingInfo; + expect(time.uploadWaitMs > 0); + expect(time.downloadWaitMs > 0); + expect(time.kernelMs > 0); + expect(time.wallMs >= time.kernelMs); + }); + + it('upload + compute + data', async () => { + const a = tf.zeros([10, 10]); + const time = await tf.time(async () => await a.square().data()) as + tf.webgl.WebGLTimingInfo; + expect(time.uploadWaitMs > 0); + expect(time.downloadWaitMs > 0); + expect(time.kernelMs > 0); + expect(time.wallMs >= time.kernelMs); + }); + + it('preupload (not included) + compute + data', async () => { + const a = tf.zeros([10, 10]); + // Pre-upload a on gpu. + a.square(); + const time = await tf.time(() => a.sqrt()) as tf.webgl.WebGLTimingInfo; + // The tensor was already on gpu. + expect(time.uploadWaitMs === 0); + expect(time.downloadWaitMs === 0); + expect(time.kernelMs > 0); + expect(time.wallMs >= time.kernelMs); + }); +}); diff --git a/src/backends/webgl/backend_webgl_test_harness.ts b/src/backends/webgl/backend_webgl_test_registry.ts similarity index 94% rename from src/backends/webgl/backend_webgl_test_harness.ts rename to src/backends/webgl/backend_webgl_test_registry.ts index 4e07e832d0..af1f5ae6e0 100644 --- a/src/backends/webgl/backend_webgl_test_harness.ts +++ b/src/backends/webgl/backend_webgl_test_registry.ts @@ -20,6 +20,9 @@ import {Constraints, registerTestEnv} from '../../jasmine_util'; export const WEBGL_ENVS: Constraints = { backends: 'webgl' }; +export const PACKED_ENVS: Constraints = { + flags: {'WEBGL_PACK': true} +}; registerTestEnv({ name: 'webgl1', diff --git a/src/backends/webgl/webgl_batchnorm_test.ts b/src/backends/webgl/webgl_batchnorm_test.ts new file mode 100644 index 0000000000..9257538890 --- /dev/null +++ b/src/backends/webgl/webgl_batchnorm_test.ts @@ -0,0 +1,80 @@ +/** + * @license + * Copyright 2019 Google LLC. All Rights Reserved. + * 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 {describeWithFlags} from '../../jasmine_util'; +import {expectArraysClose} from '../../test_util'; +import {PACKED_ENVS, WEBGL_ENVS} from './backend_webgl_test_registry'; + +describeWithFlags('batchNorm', WEBGL_ENVS, () => { + it('should work for broadcasted inputs', () => { + const x = tf.tensor4d([2, 4, 9, 23], [2, 1, 1, 2]); + const mean = tf.tensor4d([1], [1, 1, 1, 1]); + const variance = tf.tensor4d([1], [1, 1, 1, 1]); + + const result = tf.batchNorm4d(x, mean, variance); + expectArraysClose(result, [0.9995003, 2.9985011, 7.9960027, 21.9890079]); + }); + + it('should work when squarification results in zero padding', () => { + const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); + + const x = tf.tensor3d( + [ + 0.49955603, 0.04158615, -1.09440524, 2.03854165, -0.61578344, + 2.87533573, 1.18105987, 0.807462, 1.87888837, 2.26563962, -0.37040935, + 1.35848753, -0.75347094, 0.15683117, 0.91925946, 0.34121279, + 0.92717143, 1.89683965 + ], + [2, 3, 3]); + const mean = tf.tensor1d([0.39745062, -0.48062894, 0.4847822]); + const variance = tf.tensor1d([0.32375343, 0.67117643, 1.08334653]); + const offset = tf.tensor1d([0.69398749, -1.29056387, 0.9429723]); + const scale = tf.tensor1d([-0.5607271, 0.9878457, 0.25181573]); + const varianceEpsilon = .001; + + const result = + tf.batchNorm3d(x, mean, variance, offset, scale, varianceEpsilon); + + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); + + expectArraysClose(result, [ + 0.59352049, -0.66135202, 0.5610874, -0.92077015, -1.45341019, 1.52106473, + -0.07704776, 0.26144429, 1.28010017, -1.14422404, -1.15776136, 1.15425493, + 1.82644104, -0.52249442, 1.04803919, 0.74932291, 0.40568101, 1.2844412 + ]); + }); +}); + +describeWithFlags('batchnorm packed', PACKED_ENVS, () => { + it('should not leak memory', () => { + const x = tf.tensor4d([2, 4, 9, 23], [2, 1, 1, 2]); + const mean = tf.tensor1d([1, 2]); + const variance = tf.tensor1d([2, 3]); + const varianceEpsilon = .001; + + const startNumBytes = tf.memory().numBytes; + const startNumTensors = tf.memory().numTensors; + tf.batchNorm4d(x, mean, variance, undefined, undefined, varianceEpsilon); + const endNumBytes = tf.memory().numBytes; + const endNumTensors = tf.memory().numTensors; + + expect(endNumBytes - startNumBytes).toEqual(16); + expect(endNumTensors - startNumTensors).toEqual(1); + }); +}); diff --git a/src/backends/webgl/webgl_ops_test.ts b/src/backends/webgl/webgl_ops_test.ts new file mode 100644 index 0000000000..e3ae23d025 --- /dev/null +++ b/src/backends/webgl/webgl_ops_test.ts @@ -0,0 +1,802 @@ +/** + * @license + * Copyright 2017 Google Inc. All Rights Reserved. + * 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 {describeWithFlags} from '../../jasmine_util'; +import {Tensor2D} from '../../tensor'; +import {expectArraysClose, expectArraysEqual} from '../../test_util'; +import {Rank} from '../../types'; + +import {WebGLMemoryInfo} from './backend_webgl'; +import {PACKED_ENVS, WEBGL_ENVS} from './backend_webgl_test_registry'; + +describeWithFlags('fromPixels + regular math op', WEBGL_ENVS, () => { + it('debug mode does not error when no nans', () => { + const pixels = new ImageData(2, 2); + for (let i = 0; i < 8; i++) { + pixels.data[i] = 100; + } + for (let i = 8; i < 16; i++) { + pixels.data[i] = 250; + } + + const a = tf.browser.fromPixels(pixels, 4); + const b = tf.scalar(20, 'int32'); + + const res = tf.add(a, b); + + expectArraysEqual(res, [ + 120, 120, 120, 120, 120, 120, 120, 120, 270, 270, 270, 270, 270, 270, 270, + 270 + ]); + }); +}); + +describeWithFlags('toPixels', WEBGL_ENVS, () => { + it('draws a rank-2 float32 tensor, canvas', done => { + const x = tf.tensor2d([.15, .2], [2, 1], 'float32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = new Uint8ClampedArray([ + Math.round(.15 * 255), Math.round(.15 * 255), Math.round(.15 * 255), + 255, Math.round(.2 * 255), Math.round(.2 * 255), Math.round(.2 * 255), + 255 + ]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-2 int32 tensor, canvas', done => { + const x = tf.tensor2d([10, 20], [2, 1], 'int32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = + new Uint8ClampedArray([10, 10, 10, 255, 20, 20, 20, 255]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-3 float32 tensor, 1 channel, canvas', done => { + const x = tf.tensor3d([.15, .2], [2, 1, 1], 'float32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = new Uint8ClampedArray([ + Math.round(.15 * 255), Math.round(.15 * 255), Math.round(.15 * 255), + 255, Math.round(.2 * 255), Math.round(.2 * 255), Math.round(.2 * 255), + 255 + ]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-3 int32 tensor, 1 channel, canvas', done => { + const x = tf.tensor3d([10, 20], [2, 1, 1], 'int32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = + new Uint8ClampedArray([10, 10, 10, 255, 20, 20, 20, 255]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-3 float32 tensor, 3 channel, canvas', done => { + const x = + tf.tensor3d([.05, .1001, .15, .20, .25, .3001], [2, 1, 3], 'float32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = new Uint8ClampedArray([ + Math.round(.05 * 255), Math.round(.1001 * 255), Math.round(.15 * 255), + 255, Math.round(.2 * 255), Math.round(.25 * 255), + Math.round(.3001 * 255), 255 + ]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-3 int32 tensor, 3 channel, canvas', done => { + const x = tf.tensor3d([10, 20, 30, 40, 50, 60], [2, 1, 3], 'int32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = + new Uint8ClampedArray([10, 20, 30, 255, 40, 50, 60, 255]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-3 float32 tensor, 4 channel, canvas', done => { + // ImageData roundtrips are lossy because of pre-multiplied alphas, so we + // use an alpha = 1 to avoid losing precision on r, g, b channels in these + // tests https://www.w3.org/TR/2dcontext/ + const x = tf.tensor3d( + [.05, .1001, .15, 1, .20, .25, .3001, 1], [2, 1, 4], 'float32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = new Uint8ClampedArray([ + Math.round(.05 * 255), Math.round(.1001 * 255), Math.round(.15 * 255), + 255, Math.round(.20 * 255), Math.round(.25 * 255), + Math.round(.3001 * 255), 255 + ]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('draws a rank-3 int32 tensor, 4 channel, canvas', done => { + // ImageData roundtrips are lossy because of pre-multiplied alphas, so we + // use an alpha = 1 to avoid losing precision on r, g, b channels in these + // tests https://www.w3.org/TR/2dcontext/ + const x = + tf.tensor3d([10, 20, 30, 255, 50, 60, 70, 255], [2, 1, 4], 'int32'); + const canvas = document.createElement('canvas'); + + tf.browser.toPixels(x, canvas).then(data => { + const expected = + new Uint8ClampedArray([10, 20, 30, 255, 50, 60, 70, 255]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + done(); + }); + }); + + it('accepts a tensor-like object', async () => { + const x = [[127], [100]]; // 2x1; + const canvas = document.createElement('canvas'); + + const data = await tf.browser.toPixels(x, canvas); + const expected = + new Uint8ClampedArray([127, 127, 127, 255, 100, 100, 100, 255]); + expect(data).toEqual(expected); + + const ctx = canvas.getContext('2d'); + const imgData = ctx.getImageData(0, 0, 1, 2); + + expect(imgData.data).toEqual(expected); + }); +}); + +describeWithFlags('depthToSpace', WEBGL_ENVS, () => { + it('tensor4d, input shape=[1, 4, 1, 1], blockSize=2, format=NCHW', () => { + const t = tf.tensor4d([1, 2, 3, 4], [1, 4, 1, 1]); + const blockSize = 2; + const dataFormat = 'NCHW'; + + const res = tf.depthToSpace(t, blockSize, dataFormat); + expect(res.shape).toEqual([1, 1, 2, 2]); + expectArraysClose(res, [1, 2, 3, 4]); + }); + + it('tensor4d, input shape=[1, 12, 1, 1], blockSize=2, format=NCHW', () => { + const t = + tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [1, 12, 1, 1]); + const blockSize = 2; + const dataFormat = 'NCHW'; + + const res = tf.depthToSpace(t, blockSize, dataFormat); + expect(res.shape).toEqual([1, 3, 2, 2]); + expectArraysClose(res, [1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12]); + }); + + it('tensor4d, input shape=[1, 4, 2, 2], blockSize=2, format=NCHW', () => { + const t = tf.tensor4d( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [1, 4, 2, 2]); + const blockSize = 2; + const dataFormat = 'NCHW'; + + const res = tf.depthToSpace(t, blockSize, dataFormat); + expect(res.shape).toEqual([1, 1, 4, 4]); + expectArraysClose( + res, [1, 5, 2, 6, 9, 13, 10, 14, 3, 7, 4, 8, 11, 15, 12, 16]); + }); + + it('tensor4d, input shape=[1, 8, 2, 2], blockSize=2, format=NCHW', () => { + const t = tf.tensor4d( + [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + ], + [1, 8, 2, 2]); + const blockSize = 2; + const dataFormat = 'NCHW'; + + const res = tf.depthToSpace(t, blockSize, dataFormat); + expect(res.shape).toEqual([1, 2, 4, 4]); + expectArraysClose(res, [ + 1, 9, 2, 10, 17, 25, 18, 26, 3, 11, 4, 12, 19, 27, 20, 28, + 5, 13, 6, 14, 21, 29, 22, 30, 7, 15, 8, 16, 23, 31, 24, 32 + ]); + }); +}); + +describeWithFlags('maximum', WEBGL_ENVS, () => { + it('works with squarification for large dimension', () => { + const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); + const a = + tf.tensor2d([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [2, 7]); + const b = + tf.tensor2d([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [2, 7]); + + const result = tf.maximum(a, b); + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); + expectArraysClose(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + }); +}); + +describeWithFlags('div', PACKED_ENVS, () => { + it('works when unused channels are divided', () => { + // Tests that the 0's in unused channels for input textures do not corrupt + // the result when swizzled with 3 / 3. + const a = tf.tensor2d([1], [1, 1]); + const b = tf.tensor2d([1], [1, 1]); + + const c = tf.add(a, b).div(a); + const d = tf.add(a, b).div(a); + + const result = c.matMul(d); + expectArraysClose(result, [4]); + }); + + it('works when unused channels in tensors with size > 1 are divided', () => { + const a = tf.tensor2d([1, 2, 3], [3, 1]); + const b = tf.tensor2d([1, 2, 3], [3, 1]); + const c = a.div(b); + + const d = tf.tensor1d([1, 2, 3]); + const e = tf.tensor1d([1, 2, 3]); + const f = d.div(e).reshape([1, 3]); + + const result = c.matMul(f); + expectArraysClose(result, [1, 1, 1, 1, 1, 1, 1, 1, 1]); + }); +}); + +describeWithFlags('conv2d webgl', WEBGL_ENVS, () => { + it('packed input x=[2,1,2] f=[1,1,2,2] s=1 d=1 p=0', () => { + const inputShape: [number, number, number] = [2, 1, 2]; + const fSize = 1; + const pad = 0; + const stride = 1; + + const x = tf.tensor3d([1, 2, 3, 4], inputShape); + const w = tf.tensor4d([1, 2, 3, 4], [fSize, fSize, 2, 2]); + + const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); + tf.ENV.set('WEBGL_LAZILY_UNPACK', true); + const webglPackBinaryOperationsFlagSaved = + tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); + + // First conv2D tests conv2D with non-packed input |x|, and the second uses + // packed input |result|. + const result = tf.conv2d(x, w, stride, pad); + const result1 = tf.conv2d(result, w, stride, pad); + + tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); + tf.ENV.set( + 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); + + expectArraysClose(result, [7, 10, 15, 22]); + expectArraysClose(result1, [37, 54, 81, 118]); + }); + + it('tf.memory() packed input x=[1,1,1,2] f=[1,1,2,2] s=1 d=1 p=0', () => { + const inputShape: [number, number, number, number] = [1, 1, 1, 2]; + const fSize = 1; + const pad = 0; + const stride = 1; + + const xInit = tf.tensor4d([0, 1], inputShape); + const w = tf.tensor4d([1, 2, 3, 4], [fSize, fSize, 2, 2]); + + const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); + tf.ENV.set('WEBGL_LAZILY_UNPACK', true); + const webglPackBinaryOperationsFlagSaved = + tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); + + const x = xInit.add(1); + const result = tf.conv2d(x, w, stride, pad); + + tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); + tf.ENV.set( + 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); + + expectArraysClose(result, [7, 10]); + result.dispose(); + x.dispose(); + xInit.dispose(); + w.dispose(); + expect((tf.memory() as tf.webgl.WebGLMemoryInfo).numBytesInGPU).toBe(0); + expect(tf.memory().numBytes).toBe(0); + }); +}); + + +describeWithFlags('conv to matmul', PACKED_ENVS, () => { + it('im2col should not leak memory', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 2; + const pad = 0; + const stride = 1; + const dataFormat = 'NHWC'; + const dilation = 1; + + const x = tf.tensor3d([1, 2, 3, 4], inputShape); + const w = + tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); + + const startNumBytes = tf.memory().numBytes; + tf.conv2d(x, w, stride, pad, dataFormat, dilation); + const endNumBytes = tf.memory().numBytes; + + expect(endNumBytes - startNumBytes).toEqual(4); + }); + + it('pointwise conv should work when matmul is unpacked', () => { + const inputDepth = + 1001; // this number must be greater than MATMUL_SHARED_DIM_THRESHOLD + // for matmul to be unpacked + const inputShape: [number, number, number] = [3, 3, inputDepth]; + const outputDepth = 1; + const fSize = 1; + const pad = 'same'; + const stride: [number, number] = [1, 1]; + + let x = tf.randomNormal(inputShape) as tf.Tensor3D; + x = x.add(1); // this packs x so we can test the case where we mistakenly + // want to avoid expensive reshape in pointwise conv2d even + // though matmul is unpacked + const w = + tf.randomNormal([fSize, fSize, inputDepth, outputDepth]) as tf.Tensor4D; + + expect(() => tf.conv2d(x, w, stride, pad)).not.toThrow(); + }); +}); + +// For operations on non-trivial matrix sizes, we skip the CPU-only ENV and use +// only WebGL ENVs. +describeWithFlags('gramSchmidt-non-tiny', WEBGL_ENVS, () => { + it('16x128', () => { + // Part of this test's point is that operation on a matrix of this size + // can complete in the timeout limit of the unit test. + const xs = tf.randomUniform([16, 128]) as Tensor2D; + const y = tf.linalg.gramSchmidt(xs) as Tensor2D; + expectArraysClose(y.matMul(y.transpose()), tf.eye(16)); + }); +}); + +describeWithFlags('matmul webgl-only', WEBGL_ENVS, () => { + it('Matrix times vector, large matrix', () => { + const maxTexSize = 16000; + const sharedDim = maxTexSize + 4; + const matrix = tf.buffer([2, sharedDim], 'float32'); + matrix.set(1, 0, sharedDim - 3); + matrix.set(1, 0, sharedDim - 2); + + const v = tf.buffer([sharedDim], 'float32'); + v.set(1, sharedDim - 3); + v.set(1, sharedDim - 2); + + const result = tf.dot(matrix.toTensor(), v.toTensor()); + const expected = [2, 0]; + expectArraysClose(result, expected); + }); +}); + +describeWithFlags('matmul', PACKED_ENVS, () => { + it('should not leak memory', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]); + const b = tf.tensor2d( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [3, 5]); + + const startNumBytes = tf.memory().numBytes; + tf.matMul(a, b); + const endNumBytes = tf.memory().numBytes; + + expect(endNumBytes - startNumBytes).toEqual(60); + }); + + it('should work when input matrix dimensions are not divisible by 2', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]); + const b = tf.tensor2d( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [3, 5]); + + const c = tf.matMul(a, b); + + expect(c.shape).toEqual([3, 5]); + expectArraysClose( + c, + [46, 52, 58, 64, 70, 100, 115, 130, 145, 160, 154, 178, 202, 226, 250]); + }); + + it('should work when output texture shape != physical shape', () => { + const sharedDim = 16000; + const a = tf.buffer([2, sharedDim], 'float32'); + const b = tf.buffer([sharedDim, 2], 'float32'); + + a.set(1, 0, sharedDim - 1); + a.set(1, 0, sharedDim - 2); + a.set(1, 1, sharedDim - 1); + b.set(1, sharedDim - 1, 0); + b.set(1, sharedDim - 2, 0); + + const c = tf.matMul(a.toTensor(), b.toTensor()); + const expected = [2, 0, 1, 0]; + expectArraysClose(c, expected); + }); + + it('should work when input texture shapes != physical shape', () => { + const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); + const a = tf.tensor2d([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [1, 12]); + const b = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [12, 1]); + + const c = tf.matMul(a, b); + + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); + + expectArraysClose(c, [572]); + }); + + it('should work when squarification results in zero padding', () => { + const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 3); + const a = tf.tensor2d([1, 2], [1, 2]); + const b = tf.tensor2d( + [[0, 1, 2, 3, 4, 5, 6, 7, 8], [9, 10, 11, 12, 13, 14, 15, 16, 17]]); + + const c = tf.matMul(a, b); + + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); + + expectArraysClose(c, [18, 21, 24, 27, 30, 33, 36, 39, 42]); + }); + + it('A x B', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); + const b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]); + + const c = tf.matMul(a, b); + + expect(c.shape).toEqual([2, 2]); + expectArraysClose(c, [0, 8, -3, 20]); + }); + + it('A x B^t', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); + const b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]); + + const transposeA = false; + const transposeB = true; + const c = tf.matMul(a, b, transposeA, transposeB); + + const expected = [7, 10, 16, 31]; + expectArraysClose(c, expected); + }); + + it('A^t x B', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); + const b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]); + + const transposeA = true; + const transposeB = false; + const c = tf.matMul(a, b, transposeA, transposeB); + + const expected = [17, 12, 2, 22, 15, 4, 27, 18, 6]; + expectArraysClose(c, expected); + }); + + it('A^t x B^t', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [3, 2]); + const b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]); + + const transposeA = true; + const transposeB = true; + const c = tf.matMul(a, b, transposeA, transposeB); + + const expected = [11, 13, 14, 20]; + expectArraysClose(c, expected); + }); + + it('works when followed by an op that requires unpacked inputs', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); + const b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]); + + const c = tf.matMul(a, b); + + const webglPackBinarySaved = tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', false); + const d = tf.add(c, 1); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', webglPackBinarySaved); + + expectArraysClose(d, [1, 9, -2, 21]); + }); + + // tslint:disable-next-line:max-line-length + it('works when followed by a packed reshape that changes texture layout, and then an unpacked op', + () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9], [9, 1]); + const b = tf.tensor2d([1], [1, 1]); + const c = tf.matMul(a, b); + + const d = tf.reshape(c, [1, 3, 3, 1]); + + const webglPackBinarySaved = + tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', false); + const e = tf.add(d, 1); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', webglPackBinarySaved); + + expectArraysClose(e, [2, 3, 4, 5, 6, 7, 8, 9, 10]); + }); + + it('works when preceded by an op that requires packed inputs', () => { + const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); + const b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]); + + const c = tf.add(a, 1); + const d = tf.matMul(b, c); + + expectArraysClose(d, [5, 6, 7, 4, 3, 2, 9, 12, 15]); + }); +}); + +describeWithFlags('Reduction: webgl packed input', WEBGL_ENVS, () => { + it('argmax 3D, odd number of rows, axis = -1', () => { + const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); + tf.ENV.set('WEBGL_LAZILY_UNPACK', true); + const webglPackBinaryOperationsFlagSaved = + tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); + + const a = tf.tensor3d([3, 2, 5, 100, -7, 2], [2, 1, 3]).add(1); + const r = tf.argMax(a, -1); + tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); + tf.ENV.set( + 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); + + expect(r.dtype).toBe('int32'); + expectArraysEqual(r, [2, 0]); + }); + + it('argmin 4D, odd number of rows, axis = -1', () => { + const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); + tf.ENV.set('WEBGL_LAZILY_UNPACK', true); + const webglPackBinaryOperationsFlagSaved = + tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); + + const a = + tf.tensor4d( + [3, 2, 5, 100, -7, 2, 8, 7, -5, 101, 7, -2, 100, -7, 2, 8, 7, -5], + [1, 2, 3, 3]) + .add(1); + const r = tf.argMin(a, -1); + tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); + tf.ENV.set( + 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); + + expect(r.dtype).toBe('int32'); + expectArraysEqual(r, [1, 1, 2, 2, 1, 2]); + }); + + it('should not leak memory when called after unpacked op', () => { + const webglPackBinaryOperationsFlagSaved = + tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); + tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', false); + + const a = + tf.tensor5d( + [3, 2, 5, 100, -7, 2, 8, 7, -5, 101, 7, -2, 100, -7, 2, 8, 7, -5], + [1, 2, 3, 1, 3]) + .add(1); + const startNumBytes = tf.memory().numBytes; + const startNumTensors = tf.memory().numTensors; + const r = tf.argMin(a, -1); + tf.ENV.set( + 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); + const endNumBytes = tf.memory().numBytes; + const endNumTensors = tf.memory().numTensors; + expect(endNumBytes - startNumBytes).toEqual(24); + expect(endNumTensors - startNumTensors).toEqual(1); + expect(r.dtype).toBe('int32'); + expectArraysEqual(r, [1, 1, 2, 2, 1, 2]); + }); +}); + +describeWithFlags('slice and memory usage', WEBGL_ENVS, () => { + beforeAll(() => { + tf.ENV.set('WEBGL_CPU_FORWARD', false); + tf.ENV.set('WEBGL_SIZE_UPLOAD_UNIFORM', 0); + }); + + it('slice a tensor, read it and check memory', async () => { + const getMem = () => tf.memory() as WebGLMemoryInfo; + expect(getMem().numBytesInGPU).toBe(0); + + // Lazy upload won't increase gpu memory. + const a = tf.tensor([2, 3]); + expect(getMem().numBytesInGPU).toBe(0); + + // Upload a to the GPU by running an op. + a.square().dispose(); + expect(getMem().numBytesInGPU).toBe(8); + + // Slicing does not allocate new memory. + const b = a.slice(0); + expect(getMem().numBytesInGPU).toBe(8); + + // Download a to the CPU but the texture remains on GPU + // since b points to it. + await a.data(); + expect(getMem().numBytesInGPU).toBe(8); + + // Dispose a, but the texture should still remain on the GPU + // since b points to it. + a.dispose(); + expect(getMem().numBytesInGPU).toBe(8); + + // Dispose b and expect 0 memory on GPU. + b.dispose(); + expect(getMem().numBytesInGPU).toBe(0); + }); +}); + +describeWithFlags('slice a packed texture', WEBGL_ENVS, () => { + beforeAll(() => { + tf.ENV.set('WEBGL_PACK', true); + }); + + it('slice after a matmul', () => { + const a = [[1, 2], [3, 4]]; + const b = [[5, 6], [7, 8]]; + // Matmul gives a packed tensor in webgl. + // [19, 22] + // [43, 50] + const c = tf.matMul(a, b); + expectArraysClose(c.slice([0, 0]), [19, 22, 43, 50]); + expectArraysClose(c.slice([0, 1]), [22, 50]); + expectArraysClose(c.slice([1, 0]), [43, 50]); + expectArraysClose(c.slice([1, 1]), [50]); + }); +}); + +describeWithFlags('relu', WEBGL_ENVS, () => { + it('works with squarification for prime number length vector', () => { + const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); + const a = tf.tensor1d([1, -2, 5, -3, -1, 4, 7]); + const result = tf.relu(a); + + tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); + expectArraysClose(result, [1, 0, 5, 0, 0, 4, 7]); + }); +}); + +describeWithFlags('packed clip', PACKED_ENVS, () => { + it('should not leak memory', () => { + const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); + const min = -1; + const max = 50; + + const startNumBytes = tf.memory().numBytes; + const startNumTensors = tf.memory().numTensors; + tf.clipByValue(a, min, max); + const endNumBytes = tf.memory().numBytes; + const endNumTensors = tf.memory().numTensors; + + expect(endNumBytes - startNumBytes).toEqual(24); + expect(endNumTensors - startNumTensors).toEqual(1); + }); + + it('basic', () => { + const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); + const min = -1; + const max = 50; + + const result = tf.clipByValue(a, min, max); + + expectArraysClose(result, [3, -1, 0, 50, -1, 2]); + }); + + it('using extreme values', () => { + const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); + let result = + tf.clipByValue(a, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY); + expectArraysClose(result, [3, -1, 0, 100, -7, 2]); + + result = tf.clipByValue(a, Number.MIN_VALUE, Number.MAX_VALUE); + expectArraysClose( + result, + [3, Number.MIN_VALUE, Number.MIN_VALUE, 100, Number.MIN_VALUE, 2]); + }); + + it('should work for scalars', () => { + const a = tf.scalar(-4); + const min = -1; + const max = 50; + + const result = tf.clipByValue(a, min, max); + + expectArraysClose(result, [min]); + }); + + it('derivative: 1D tensor with max or min value', () => { + const min = -1; + const max = 2; + const x = tf.tensor1d([-1, 1, 2, 3]); + const dy = tf.tensor1d([1, 10, 100, 1000]); + const gradients = tf.grad(x => x.clipByValue(min, max))(x, dy); + + expect(gradients.shape).toEqual(x.shape); + expect(gradients.dtype).toEqual('float32'); + expectArraysClose(gradients, [1, 10, 100, 0]); + }); +}); diff --git a/src/debug_mode_test.ts b/src/debug_mode_test.ts index 930fe71ac7..1764a846ca 100644 --- a/src/debug_mode_test.ts +++ b/src/debug_mode_test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2018 Google Inc. All Rights Reserved. + * Copyright 2019 Google LLC. All Rights Reserved. * 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 @@ -16,7 +16,7 @@ */ import * as tf from './index'; -import {ALL_ENVS, describeWithFlags, WEBGL_ENVS} from './jasmine_util'; +import {ALL_ENVS, describeWithFlags} from './jasmine_util'; import {convertToTensor} from './tensor_util_env'; import {expectArraysClose} from './test_util'; @@ -95,34 +95,6 @@ describeWithFlags('debug on', ALL_ENVS, () => { }); }); -describeWithFlags('debug on webgl', WEBGL_ENVS, () => { - beforeAll(() => { - tf.ENV.set('DEBUG', true); - }); - - afterAll(() => { - tf.ENV.set('DEBUG', false); - }); - - it('debug mode errors when overflow in tensor construction', () => { - const savedRenderFloat32Flag = - tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); - tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', false); - const a = () => tf.tensor1d([2, Math.pow(2, 17)], 'float32'); - expect(a).toThrowError(); - tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', savedRenderFloat32Flag); - }); - - it('debug mode errors when underflow in tensor construction', () => { - const savedRenderFloat32Flag = - tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); - tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', false); - const a = () => tf.tensor1d([2, 1e-8], 'float32'); - expect(a).toThrowError(); - tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', savedRenderFloat32Flag); - }); -}); - describeWithFlags('debug off', ALL_ENVS, () => { beforeAll(() => { tf.ENV.set('DEBUG', false); diff --git a/src/engine_test.ts b/src/engine_test.ts index 5c98237e37..39337b38f7 100644 --- a/src/engine_test.ts +++ b/src/engine_test.ts @@ -16,13 +16,11 @@ */ import {KernelBackend} from './backends/backend'; -import {MathBackendCPU} from './backends/cpu/backend_cpu'; -import {MathBackendWebGL} from './backends/webgl/backend_webgl'; import {ENGINE} from './engine'; import * as tf from './index'; -import {ALL_ENVS, describeWithFlags, TestKernelBackend, WEBGL_ENVS} from './jasmine_util'; +import {ALL_ENVS, describeWithFlags, TestKernelBackend} from './jasmine_util'; import {Tensor} from './tensor'; -import {expectArraysClose, expectArraysEqual} from './test_util'; +import {expectArraysClose} from './test_util'; describe('Backend registration', () => { beforeAll(() => { @@ -155,28 +153,6 @@ describe('Backend registration', () => { }); }); -describeWithFlags('fromPixels + regular math op', WEBGL_ENVS, () => { - it('debug mode does not error when no nans', () => { - const pixels = new ImageData(2, 2); - for (let i = 0; i < 8; i++) { - pixels.data[i] = 100; - } - for (let i = 8; i < 16; i++) { - pixels.data[i] = 250; - } - - const a = tf.browser.fromPixels(pixels, 4); - const b = tf.scalar(20, 'int32'); - - const res = tf.add(a, b); - - expectArraysEqual(res, [ - 120, 120, 120, 120, 120, 120, 120, 120, 270, 270, 270, 270, 270, 270, 270, - 270 - ]); - }); -}); - describeWithFlags('memory', ALL_ENVS, () => { it('Sum(float)', () => { expect(tf.memory().numTensors).toBe(0); @@ -240,17 +216,6 @@ describeWithFlags('memory', ALL_ENVS, () => { }); }); -describeWithFlags('memory webgl', WEBGL_ENVS, () => { - it('unreliable is falsy/not present when all tensors are numeric', () => { - tf.tensor(1); - const mem = tf.memory(); - expect(mem.numTensors).toBe(1); - expect(mem.numDataBuffers).toBe(1); - expect(mem.numBytes).toBe(4); - expect(mem.unreliable).toBeFalsy(); - }); -}); - describeWithFlags('profile', ALL_ENVS, () => { it('squaring', async () => { const profile = await tf.profile(() => { @@ -328,10 +293,18 @@ describeWithFlags('disposeVariables', ALL_ENVS, () => { }); }); -describe('Switching cpu backends', () => { +/** + * The following unit tests are a special integration-style test that assume + * things about backends being registered. These tests don't live in the + * backend directories because it is testing engine rather than backend-specific + * details but needs a real backend to exist. These test will fail if the + * backends are not registered. This is intentional, we should have coverage for + * when these backends are enabled and ensuring they work with the engine. + */ +describeWithFlags('Switching cpu backends', {backends: 'cpu'}, () => { beforeEach(() => { - tf.registerBackend('cpu1', () => new MathBackendCPU()); - tf.registerBackend('cpu2', () => new MathBackendCPU()); + tf.registerBackend('cpu1', tf.findBackendFactory('cpu')); + tf.registerBackend('cpu2', tf.findBackendFactory('cpu')); }); afterEach(() => { @@ -396,53 +369,11 @@ describe('Switching cpu backends', () => { }); }); -// We do not yet fully support half float backends. These tests are a starting -// point. -describeWithFlags('backend without render float32 support', WEBGL_ENVS, () => { - const savedRenderFloat32Flag = tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED'); - - beforeAll(() => { - tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', false); - }); - - beforeEach(() => { - tf.registerBackend('half-float-webgl', () => new MathBackendWebGL(null)); - }); - - afterEach(() => { - tf.removeBackend('half-float-webgl'); - }); - - afterAll(() => { - tf.ENV.set('WEBGL_RENDER_FLOAT32_ENABLED', savedRenderFloat32Flag); - }); - - it('basic usage', () => { - tf.setBackend('half-float-webgl'); - - const a = tf.tensor2d([1, 2], [1, 2]); - const b = tf.tensor2d([1, 2], [1, 2]); - const c = tf.add(a, b); - expectArraysClose(c, [2, 4]); - }); - - it('disposing tensors should not cause errors', () => { - tf.setBackend('half-float-webgl'); - expect(() => tf.tidy(() => { - const a = tf.tensor2d([1, 2], [1, 2]); - const b = tf.tensor2d([1, 2], [1, 2]); - const c = tf.add(a, b); - c.dataSync(); - return c.add(tf.tensor2d([2, 4], [1, 2])); - })).not.toThrowError(); - }); -}); - -describeWithFlags('Switching WebGL + CPU backends', WEBGL_ENVS, () => { +describeWithFlags('Switching WebGL + CPU backends', {backends: 'webgl'}, () => { beforeEach(() => { - tf.registerBackend('webgl1', () => new MathBackendWebGL()); - tf.registerBackend('webgl2', () => new MathBackendWebGL()); - tf.registerBackend('cpu1', () => new MathBackendCPU()); + tf.registerBackend('webgl1', tf.findBackendFactory('webgl')); + tf.registerBackend('webgl2', tf.findBackendFactory('webgl')); + tf.registerBackend('cpu1', tf.findBackendFactory('cpu')); }); afterEach(() => { diff --git a/src/globals_test.ts b/src/globals_test.ts index 7356db2726..ffbe662d9e 100644 --- a/src/globals_test.ts +++ b/src/globals_test.ts @@ -16,7 +16,7 @@ */ import {ENV} from './environment'; import * as tf from './index'; -import {ALL_ENVS, describeWithFlags, NODE_ENVS, WEBGL_ENVS} from './jasmine_util'; +import {ALL_ENVS, describeWithFlags, NODE_ENVS} from './jasmine_util'; import {expectArraysClose, expectArraysEqual} from './test_util'; describe('deprecation warnings', () => { @@ -67,49 +67,6 @@ describe('Flag flipping methods', () => { }); }); -describeWithFlags('time webgl', WEBGL_ENVS, () => { - it('upload + compute', async () => { - const a = tf.zeros([10, 10]); - const time = await tf.time(() => a.square()) as tf.webgl.WebGLTimingInfo; - expect(time.uploadWaitMs > 0); - expect(time.downloadWaitMs === 0); - expect(time.kernelMs > 0); - expect(time.wallMs >= time.kernelMs); - }); - - it('upload + compute + dataSync', async () => { - const a = tf.zeros([10, 10]); - const time = - await tf.time(() => a.square().dataSync()) as tf.webgl.WebGLTimingInfo; - expect(time.uploadWaitMs > 0); - expect(time.downloadWaitMs > 0); - expect(time.kernelMs > 0); - expect(time.wallMs >= time.kernelMs); - }); - - it('upload + compute + data', async () => { - const a = tf.zeros([10, 10]); - const time = await tf.time(async () => await a.square().data()) as - tf.webgl.WebGLTimingInfo; - expect(time.uploadWaitMs > 0); - expect(time.downloadWaitMs > 0); - expect(time.kernelMs > 0); - expect(time.wallMs >= time.kernelMs); - }); - - it('preupload (not included) + compute + data', async () => { - const a = tf.zeros([10, 10]); - // Pre-upload a on gpu. - a.square(); - const time = await tf.time(() => a.sqrt()) as tf.webgl.WebGLTimingInfo; - // The tensor was already on gpu. - expect(time.uploadWaitMs === 0); - expect(time.downloadWaitMs === 0); - expect(time.kernelMs > 0); - expect(time.wallMs >= time.kernelMs); - }); -}); - describeWithFlags('time cpu', NODE_ENVS, () => { it('simple upload', async () => { const a = tf.zeros([10, 10]); diff --git a/src/ops/array_ops_test.ts b/src/ops/array_ops_test.ts index 97f0486d71..6be7f23f13 100644 --- a/src/ops/array_ops_test.ts +++ b/src/ops/array_ops_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {describeWithFlags, ALL_ENVS, BROWSER_ENVS, NODE_ENVS, WEBGL_ENVS} from '../jasmine_util'; +import {describeWithFlags, ALL_ENVS, BROWSER_ENVS, NODE_ENVS} from '../jasmine_util'; import { expectArraysClose, expectArraysEqual, expectPromiseToFail, expectValuesInRange} from '../test_util'; import * as util from '../util'; import {expectArrayInMeanStdRange, jarqueBeraNormalityTest} from './rand_util'; @@ -1586,179 +1586,6 @@ describeWithFlags('toPixels no canvas', ALL_ENVS, () => { }); }); -describeWithFlags('toPixels', WEBGL_ENVS, () => { - it('draws a rank-2 float32 tensor, canvas', done => { - const x = tf.tensor2d([.15, .2], [2, 1], 'float32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = new Uint8ClampedArray([ - Math.round(.15 * 255), Math.round(.15 * 255), Math.round(.15 * 255), - 255, Math.round(.2 * 255), Math.round(.2 * 255), Math.round(.2 * 255), - 255 - ]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-2 int32 tensor, canvas', done => { - const x = tf.tensor2d([10, 20], [2, 1], 'int32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = - new Uint8ClampedArray([10, 10, 10, 255, 20, 20, 20, 255]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-3 float32 tensor, 1 channel, canvas', done => { - const x = tf.tensor3d([.15, .2], [2, 1, 1], 'float32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = new Uint8ClampedArray([ - Math.round(.15 * 255), Math.round(.15 * 255), Math.round(.15 * 255), - 255, Math.round(.2 * 255), Math.round(.2 * 255), Math.round(.2 * 255), - 255 - ]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-3 int32 tensor, 1 channel, canvas', done => { - const x = tf.tensor3d([10, 20], [2, 1, 1], 'int32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = - new Uint8ClampedArray([10, 10, 10, 255, 20, 20, 20, 255]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-3 float32 tensor, 3 channel, canvas', done => { - const x = - tf.tensor3d([.05, .1001, .15, .20, .25, .3001], [2, 1, 3], 'float32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = new Uint8ClampedArray([ - Math.round(.05 * 255), Math.round(.1001 * 255), Math.round(.15 * 255), - 255, Math.round(.2 * 255), Math.round(.25 * 255), - Math.round(.3001 * 255), 255 - ]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-3 int32 tensor, 3 channel, canvas', done => { - const x = tf.tensor3d([10, 20, 30, 40, 50, 60], [2, 1, 3], 'int32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = - new Uint8ClampedArray([10, 20, 30, 255, 40, 50, 60, 255]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-3 float32 tensor, 4 channel, canvas', done => { - // ImageData roundtrips are lossy because of pre-multiplied alphas, so we - // use an alpha = 1 to avoid losing precision on r, g, b channels in these - // tests https://www.w3.org/TR/2dcontext/ - const x = tf.tensor3d( - [.05, .1001, .15, 1, .20, .25, .3001, 1], [2, 1, 4], 'float32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = new Uint8ClampedArray([ - Math.round(.05 * 255), Math.round(.1001 * 255), Math.round(.15 * 255), - 255, Math.round(.20 * 255), Math.round(.25 * 255), - Math.round(.3001 * 255), 255 - ]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('draws a rank-3 int32 tensor, 4 channel, canvas', done => { - // ImageData roundtrips are lossy because of pre-multiplied alphas, so we - // use an alpha = 1 to avoid losing precision on r, g, b channels in these - // tests https://www.w3.org/TR/2dcontext/ - const x = - tf.tensor3d([10, 20, 30, 255, 50, 60, 70, 255], [2, 1, 4], 'int32'); - const canvas = document.createElement('canvas'); - - tf.browser.toPixels(x, canvas).then(data => { - const expected = - new Uint8ClampedArray([10, 20, 30, 255, 50, 60, 70, 255]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - done(); - }); - }); - - it('accepts a tensor-like object', async () => { - const x = [[127], [100]]; // 2x1; - const canvas = document.createElement('canvas'); - - const data = await tf.browser.toPixels(x, canvas); - const expected = - new Uint8ClampedArray([127, 127, 127, 255, 100, 100, 100, 255]); - expect(data).toEqual(expected); - - const ctx = canvas.getContext('2d'); - const imgData = ctx.getImageData(0, 0, 1, 2); - - expect(imgData.data).toEqual(expected); - }); -}); - describeWithFlags('clone', ALL_ENVS, () => { it('1D default dtype', () => { const a = tf.tensor1d([1, 2, 3]); @@ -4197,59 +4024,6 @@ describeWithFlags('depthToSpace', BROWSER_ENVS, () => { }); }); -describeWithFlags('depthToSpace', WEBGL_ENVS, () => { - it('tensor4d, input shape=[1, 4, 1, 1], blockSize=2, format=NCHW', () => { - const t = tf.tensor4d([1, 2, 3, 4], [1, 4, 1, 1]); - const blockSize = 2; - const dataFormat = 'NCHW'; - - const res = tf.depthToSpace(t, blockSize, dataFormat); - expect(res.shape).toEqual([1, 1, 2, 2]); - expectArraysClose(res, [1, 2, 3, 4]); - }); - - it('tensor4d, input shape=[1, 12, 1, 1], blockSize=2, format=NCHW', () => { - const t = - tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [1, 12, 1, 1]); - const blockSize = 2; - const dataFormat = 'NCHW'; - - const res = tf.depthToSpace(t, blockSize, dataFormat); - expect(res.shape).toEqual([1, 3, 2, 2]); - expectArraysClose(res, [1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12]); - }); - - it('tensor4d, input shape=[1, 4, 2, 2], blockSize=2, format=NCHW', () => { - const t = tf.tensor4d( - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [1, 4, 2, 2]); - const blockSize = 2; - const dataFormat = 'NCHW'; - - const res = tf.depthToSpace(t, blockSize, dataFormat); - expect(res.shape).toEqual([1, 1, 4, 4]); - expectArraysClose( - res, [1, 5, 2, 6, 9, 13, 10, 14, 3, 7, 4, 8, 11, 15, 12, 16]); - }); - - it('tensor4d, input shape=[1, 8, 2, 2], blockSize=2, format=NCHW', () => { - const t = tf.tensor4d( - [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 - ], - [1, 8, 2, 2]); - const blockSize = 2; - const dataFormat = 'NCHW'; - - const res = tf.depthToSpace(t, blockSize, dataFormat); - expect(res.shape).toEqual([1, 2, 4, 4]); - expectArraysClose(res, [ - 1, 9, 2, 10, 17, 25, 18, 26, 3, 11, 4, 12, 19, 27, 20, 28, - 5, 13, 6, 14, 21, 29, 22, 30, 7, 15, 8, 16, 23, 31, 24, 32 - ]); - }); -}); - describeWithFlags('setdiff1dAsync', ALL_ENVS, () => { it('1d int32 tensor', async () => { const x = tf.tensor1d([1, 2, 3, 4], 'int32'); diff --git a/src/ops/batchnorm_test.ts b/src/ops/batchnorm_test.ts index dee324a1e1..9c1f6ef6d0 100644 --- a/src/ops/batchnorm_test.ts +++ b/src/ops/batchnorm_test.ts @@ -16,68 +16,9 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, PACKED_ENVS, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; -describeWithFlags('batchnorm packed', PACKED_ENVS, () => { - it('should not leak memory', () => { - const x = tf.tensor4d([2, 4, 9, 23], [2, 1, 1, 2]); - const mean = tf.tensor1d([1, 2]); - const variance = tf.tensor1d([2, 3]); - const varianceEpsilon = .001; - - const startNumBytes = tf.memory().numBytes; - const startNumTensors = tf.memory().numTensors; - tf.batchNorm4d(x, mean, variance, undefined, undefined, varianceEpsilon); - const endNumBytes = tf.memory().numBytes; - const endNumTensors = tf.memory().numTensors; - - expect(endNumBytes - startNumBytes).toEqual(16); - expect(endNumTensors - startNumTensors).toEqual(1); - }); -}); - -describeWithFlags('batchNorm', WEBGL_ENVS, () => { - it('should work for broadcasted inputs', () => { - const x = tf.tensor4d([2, 4, 9, 23], [2, 1, 1, 2]); - const mean = tf.tensor4d([1], [1, 1, 1, 1]); - const variance = tf.tensor4d([1], [1, 1, 1, 1]); - - const result = tf.batchNorm4d(x, mean, variance); - expectArraysClose(result, [0.9995003, 2.9985011, 7.9960027, 21.9890079]); - }); - - it('should work when squarification results in zero padding', () => { - const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); - - const x = tf.tensor3d( - [ - 0.49955603, 0.04158615, -1.09440524, 2.03854165, -0.61578344, - 2.87533573, 1.18105987, 0.807462, 1.87888837, 2.26563962, -0.37040935, - 1.35848753, -0.75347094, 0.15683117, 0.91925946, 0.34121279, - 0.92717143, 1.89683965 - ], - [2, 3, 3]); - const mean = tf.tensor1d([0.39745062, -0.48062894, 0.4847822]); - const variance = tf.tensor1d([0.32375343, 0.67117643, 1.08334653]); - const offset = tf.tensor1d([0.69398749, -1.29056387, 0.9429723]); - const scale = tf.tensor1d([-0.5607271, 0.9878457, 0.25181573]); - const varianceEpsilon = .001; - - const result = - tf.batchNorm3d(x, mean, variance, offset, scale, varianceEpsilon); - - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); - - expectArraysClose(result, [ - 0.59352049, -0.66135202, 0.5610874, -0.92077015, -1.45341019, 1.52106473, - -0.07704776, 0.26144429, 1.28010017, -1.14422404, -1.15776136, 1.15425493, - 1.82644104, -0.52249442, 1.04803919, 0.74932291, 0.40568101, 1.2844412 - ]); - }); -}); - describeWithFlags('batchNorm4D', ALL_ENVS, () => { it('simple batchnorm4D, no offset or scale, 2x1x1x2', async () => { const xT = tf.tensor4d([2, 4, 9, 23], [2, 1, 1, 2]); diff --git a/src/ops/binary_ops_test.ts b/src/ops/binary_ops_test.ts index 0ba3e8420a..9f06f7c4a5 100644 --- a/src/ops/binary_ops_test.ts +++ b/src/ops/binary_ops_test.ts @@ -16,37 +16,9 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, PACKED_ENVS, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose, expectArraysEqual} from '../test_util'; -describeWithFlags('div', PACKED_ENVS, () => { - it('works when unused channels are divided', () => { - // Tests that the 0's in unused channels for input textures do not corrupt - // the result when swizzled with 3 / 3. - const a = tf.tensor2d([1], [1, 1]); - const b = tf.tensor2d([1], [1, 1]); - - const c = tf.add(a, b).div(a); - const d = tf.add(a, b).div(a); - - const result = c.matMul(d); - expectArraysClose(result, [4]); - }); - - it('works when unused channels in tensors with size > 1 are divided', () => { - const a = tf.tensor2d([1, 2, 3], [3, 1]); - const b = tf.tensor2d([1, 2, 3], [3, 1]); - const c = a.div(b); - - const d = tf.tensor1d([1, 2, 3]); - const e = tf.tensor1d([1, 2, 3]); - const f = d.div(e).reshape([1, 3]); - - const result = c.matMul(f); - expectArraysClose(result, [1, 1, 1, 1, 1, 1, 1, 1, 1]); - }); -}); - describeWithFlags('prelu', ALL_ENVS, () => { it('basic', () => { const x = tf.tensor1d([0, 1, -2, -4]); @@ -321,21 +293,6 @@ describeWithFlags('maximum', ALL_ENVS, () => { }); }); -describeWithFlags('maximum', WEBGL_ENVS, () => { - it('works with squarification for large dimension', () => { - const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); - const a = - tf.tensor2d([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [2, 7]); - const b = - tf.tensor2d([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [2, 7]); - - const result = tf.maximum(a, b); - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); - expectArraysClose(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); - }); -}); - describeWithFlags('squaredDifference', ALL_ENVS, () => { it('float32 and float32', () => { const a = tf.tensor1d([0.5, 3, -0.1, -4]); diff --git a/src/ops/conv2d_test.ts b/src/ops/conv2d_test.ts index ce6a77dae4..56cc60bbf0 100644 --- a/src/ops/conv2d_test.ts +++ b/src/ops/conv2d_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, PACKED_ENVS, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; import {Rank} from '../types'; @@ -34,49 +34,6 @@ function generateCaseInputs(totalSizeTensor: number, totalSizeFilter: number) { return {input: inp, filter: filt}; } -describeWithFlags('conv to matmul', PACKED_ENVS, () => { - it('im2col should not leak memory', () => { - const inputDepth = 1; - const inputShape: [number, number, number] = [2, 2, inputDepth]; - const outputDepth = 1; - const fSize = 2; - const pad = 0; - const stride = 1; - const dataFormat = 'NHWC'; - const dilation = 1; - - const x = tf.tensor3d([1, 2, 3, 4], inputShape); - const w = - tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); - - const startNumBytes = tf.memory().numBytes; - tf.conv2d(x, w, stride, pad, dataFormat, dilation); - const endNumBytes = tf.memory().numBytes; - - expect(endNumBytes - startNumBytes).toEqual(4); - }); - - it('pointwise conv should work when matmul is unpacked', () => { - const inputDepth = - 1001; // this number must be greater than MATMUL_SHARED_DIM_THRESHOLD - // for matmul to be unpacked - const inputShape: [number, number, number] = [3, 3, inputDepth]; - const outputDepth = 1; - const fSize = 1; - const pad = 'same'; - const stride: [number, number] = [1, 1]; - - let x = tf.randomNormal(inputShape) as tf.Tensor3D; - x = x.add(1); // this packs x so we can test the case where we mistakenly - // want to avoid expensive reshape in pointwise conv2d even - // though matmul is unpacked - const w = - tf.randomNormal([fSize, fSize, inputDepth, outputDepth]) as tf.Tensor4D; - - expect(() => tf.conv2d(x, w, stride, pad)).not.toThrow(); - }); -}); - describeWithFlags('conv2d', ALL_ENVS, () => { it('x=[1,4,4,1] f=[1,1,1,3] s=2 d=1 p=same', () => { const inputDepth = 1; @@ -369,64 +326,3 @@ describeWithFlags('conv2d', ALL_ENVS, () => { expectArraysClose(result, [2, 4, 6, 8]); }); }); - -describeWithFlags('conv2d webgl', WEBGL_ENVS, () => { - it('packed input x=[2,1,2] f=[1,1,2,2] s=1 d=1 p=0', () => { - const inputShape: [number, number, number] = [2, 1, 2]; - const fSize = 1; - const pad = 0; - const stride = 1; - - const x = tf.tensor3d([1, 2, 3, 4], inputShape); - const w = tf.tensor4d([1, 2, 3, 4], [fSize, fSize, 2, 2]); - - const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); - tf.ENV.set('WEBGL_LAZILY_UNPACK', true); - const webglPackBinaryOperationsFlagSaved = - tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); - - // First conv2D tests conv2D with non-packed input |x|, and the second uses - // packed input |result|. - const result = tf.conv2d(x, w, stride, pad); - const result1 = tf.conv2d(result, w, stride, pad); - - tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); - tf.ENV.set( - 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); - - expectArraysClose(result, [7, 10, 15, 22]); - expectArraysClose(result1, [37, 54, 81, 118]); - }); - - it('tf.memory() packed input x=[1,1,1,2] f=[1,1,2,2] s=1 d=1 p=0', () => { - const inputShape: [number, number, number, number] = [1, 1, 1, 2]; - const fSize = 1; - const pad = 0; - const stride = 1; - - const xInit = tf.tensor4d([0, 1], inputShape); - const w = tf.tensor4d([1, 2, 3, 4], [fSize, fSize, 2, 2]); - - const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); - tf.ENV.set('WEBGL_LAZILY_UNPACK', true); - const webglPackBinaryOperationsFlagSaved = - tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); - - const x = xInit.add(1); - const result = tf.conv2d(x, w, stride, pad); - - tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); - tf.ENV.set( - 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); - - expectArraysClose(result, [7, 10]); - result.dispose(); - x.dispose(); - xInit.dispose(); - w.dispose(); - expect((tf.memory() as tf.webgl.WebGLMemoryInfo).numBytesInGPU).toBe(0); - expect(tf.memory().numBytes).toBe(0); - }); -}); diff --git a/src/ops/linalg_ops_test.ts b/src/ops/linalg_ops_test.ts index 23b45a2c90..bdd74732e1 100644 --- a/src/ops/linalg_ops_test.ts +++ b/src/ops/linalg_ops_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {Tensor1D, Tensor2D} from '../tensor'; import {expectArraysClose} from '../test_util'; @@ -82,18 +82,6 @@ describeWithFlags('gramSchmidt-tiny', ALL_ENVS, () => { }); }); -// For operations on non-trivial matrix sizes, we skip the CPU-only ENV and use -// only WebGL ENVs. -describeWithFlags('gramSchmidt-non-tiny', WEBGL_ENVS, () => { - it('16x128', () => { - // Part of this test's point is that operation on a matrix of this size - // can complete in the timeout limit of the unit test. - const xs = tf.randomUniform([16, 128]) as Tensor2D; - const y = tf.linalg.gramSchmidt(xs) as Tensor2D; - expectArraysClose(y.matMul(y.transpose()), tf.eye(16)); - }); -}); - describeWithFlags('qr', ALL_ENVS, () => { it('1x1', () => { const x = tensor2d([[10]], [1, 1]); diff --git a/src/ops/matmul_test.ts b/src/ops/matmul_test.ts index 32c6a9c225..e593a02596 100644 --- a/src/ops/matmul_test.ts +++ b/src/ops/matmul_test.ts @@ -15,170 +15,12 @@ * ============================================================================= */ +import {MATMUL_SHARED_DIM_THRESHOLD} from '../backends/webgl/backend_webgl'; import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, PACKED_ENVS, WEBGL_ENVS} from '../jasmine_util'; -import {MATMUL_SHARED_DIM_THRESHOLD} from '../kernels/webgl/backend_webgl'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose, expectArraysEqual} from '../test_util'; import {Rank} from '../types'; -describeWithFlags('matmul', PACKED_ENVS, () => { - it('should not leak memory', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]); - const b = tf.tensor2d( - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [3, 5]); - - const startNumBytes = tf.memory().numBytes; - tf.matMul(a, b); - const endNumBytes = tf.memory().numBytes; - - expect(endNumBytes - startNumBytes).toEqual(60); - }); - - it('should work when input matrix dimensions are not divisible by 2', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]); - const b = tf.tensor2d( - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [3, 5]); - - const c = tf.matMul(a, b); - - expect(c.shape).toEqual([3, 5]); - expectArraysClose( - c, - [46, 52, 58, 64, 70, 100, 115, 130, 145, 160, 154, 178, 202, 226, 250]); - }); - - it('should work when output texture shape != physical shape', () => { - const sharedDim = 16000; - const a = tf.buffer([2, sharedDim], 'float32'); - const b = tf.buffer([sharedDim, 2], 'float32'); - - a.set(1, 0, sharedDim - 1); - a.set(1, 0, sharedDim - 2); - a.set(1, 1, sharedDim - 1); - b.set(1, sharedDim - 1, 0); - b.set(1, sharedDim - 2, 0); - - const c = tf.matMul(a.toTensor(), b.toTensor()); - const expected = [2, 0, 1, 0]; - expectArraysClose(c, expected); - }); - - it('should work when input texture shapes != physical shape', () => { - const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); - const a = tf.tensor2d([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [1, 12]); - const b = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [12, 1]); - - const c = tf.matMul(a, b); - - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); - - expectArraysClose(c, [572]); - }); - - it('should work when squarification results in zero padding', () => { - const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 3); - const a = tf.tensor2d([1, 2], [1, 2]); - const b = tf.tensor2d( - [[0, 1, 2, 3, 4, 5, 6, 7, 8], [9, 10, 11, 12, 13, 14, 15, 16, 17]]); - - const c = tf.matMul(a, b); - - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); - - expectArraysClose(c, [18, 21, 24, 27, 30, 33, 36, 39, 42]); - }); - - it('A x B', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); - const b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]); - - const c = tf.matMul(a, b); - - expect(c.shape).toEqual([2, 2]); - expectArraysClose(c, [0, 8, -3, 20]); - }); - - it('A x B^t', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); - const b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]); - - const transposeA = false; - const transposeB = true; - const c = tf.matMul(a, b, transposeA, transposeB); - - const expected = [7, 10, 16, 31]; - expectArraysClose(c, expected); - }); - - it('A^t x B', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); - const b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]); - - const transposeA = true; - const transposeB = false; - const c = tf.matMul(a, b, transposeA, transposeB); - - const expected = [17, 12, 2, 22, 15, 4, 27, 18, 6]; - expectArraysClose(c, expected); - }); - - it('A^t x B^t', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [3, 2]); - const b = tf.tensor2d([1, 0, 2, 4, 3, 0], [2, 3]); - - const transposeA = true; - const transposeB = true; - const c = tf.matMul(a, b, transposeA, transposeB); - - const expected = [11, 13, 14, 20]; - expectArraysClose(c, expected); - }); - - it('works when followed by an op that requires unpacked inputs', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); - const b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]); - - const c = tf.matMul(a, b); - - const webglPackBinarySaved = tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', false); - const d = tf.add(c, 1); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', webglPackBinarySaved); - - expectArraysClose(d, [1, 9, -2, 21]); - }); - - // tslint:disable-next-line:max-line-length - it('works when followed by a packed reshape that changes texture layout, and then an unpacked op', - () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6, 7, 8, 9], [9, 1]); - const b = tf.tensor2d([1], [1, 1]); - const c = tf.matMul(a, b); - - const d = tf.reshape(c, [1, 3, 3, 1]); - - const webglPackBinarySaved = - tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', false); - const e = tf.add(d, 1); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', webglPackBinarySaved); - - expectArraysClose(e, [2, 3, 4, 5, 6, 7, 8, 9, 10]); - }); - - it('works when preceded by an op that requires packed inputs', () => { - const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); - const b = tf.tensor2d([0, 1, -3, 2, 2, 1], [3, 2]); - - const c = tf.add(a, 1); - const d = tf.matMul(b, c); - - expectArraysClose(d, [5, 6, 7, 4, 3, 2, 9, 12, 15]); - }); -}); - describeWithFlags('matmul', ALL_ENVS, () => { it('A x B', () => { const a = tf.tensor2d([1, 2, 3, 4, 5, 6], [2, 3]); @@ -1248,24 +1090,6 @@ describeWithFlags('matmulBatch', ALL_ENVS, () => { }); }); -describeWithFlags('matmul webgl-only', WEBGL_ENVS, () => { - it('Matrix times vector, large matrix', () => { - const maxTexSize = 16000; - const sharedDim = maxTexSize + 4; - const matrix = tf.buffer([2, sharedDim], 'float32'); - matrix.set(1, 0, sharedDim - 3); - matrix.set(1, 0, sharedDim - 2); - - const v = tf.buffer([sharedDim], 'float32'); - v.set(1, sharedDim - 3); - v.set(1, sharedDim - 2); - - const result = tf.dot(matrix.toTensor(), v.toTensor()); - const expected = [2, 0]; - expectArraysClose(result, expected); - }); -}); - describeWithFlags('dot', ALL_ENVS, () => { let a: tf.Tensor1D; let b: tf.Tensor2D; diff --git a/src/ops/reduction_ops_test.ts b/src/ops/reduction_ops_test.ts index 1a6af13b65..7ca5e8f50f 100644 --- a/src/ops/reduction_ops_test.ts +++ b/src/ops/reduction_ops_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose, expectArraysEqual} from '../test_util'; import * as reduce_util from './reduce_util'; @@ -592,69 +592,6 @@ describeWithFlags('Reduction: argmax', ALL_ENVS, () => { }); }); -describeWithFlags('Reduction: webgl packed input', WEBGL_ENVS, () => { - it('argmax 3D, odd number of rows, axis = -1', () => { - const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); - tf.ENV.set('WEBGL_LAZILY_UNPACK', true); - const webglPackBinaryOperationsFlagSaved = - tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); - - const a = tf.tensor3d([3, 2, 5, 100, -7, 2], [2, 1, 3]).add(1); - const r = tf.argMax(a, -1); - tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); - tf.ENV.set( - 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); - - expect(r.dtype).toBe('int32'); - expectArraysEqual(r, [2, 0]); - }); - - it('argmin 4D, odd number of rows, axis = -1', () => { - const webglLazilyUnpackFlagSaved = tf.ENV.getBool('WEBGL_LAZILY_UNPACK'); - tf.ENV.set('WEBGL_LAZILY_UNPACK', true); - const webglPackBinaryOperationsFlagSaved = - tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', true); - - const a = - tf.tensor4d( - [3, 2, 5, 100, -7, 2, 8, 7, -5, 101, 7, -2, 100, -7, 2, 8, 7, -5], - [1, 2, 3, 3]) - .add(1); - const r = tf.argMin(a, -1); - tf.ENV.set('WEBGL_LAZILY_UNPACK', webglLazilyUnpackFlagSaved); - tf.ENV.set( - 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); - - expect(r.dtype).toBe('int32'); - expectArraysEqual(r, [1, 1, 2, 2, 1, 2]); - }); - - it('should not leak memory when called after unpacked op', () => { - const webglPackBinaryOperationsFlagSaved = - tf.ENV.getBool('WEBGL_PACK_BINARY_OPERATIONS'); - tf.ENV.set('WEBGL_PACK_BINARY_OPERATIONS', false); - - const a = - tf.tensor5d( - [3, 2, 5, 100, -7, 2, 8, 7, -5, 101, 7, -2, 100, -7, 2, 8, 7, -5], - [1, 2, 3, 1, 3]) - .add(1); - const startNumBytes = tf.memory().numBytes; - const startNumTensors = tf.memory().numTensors; - const r = tf.argMin(a, -1); - tf.ENV.set( - 'WEBGL_PACK_BINARY_OPERATIONS', webglPackBinaryOperationsFlagSaved); - const endNumBytes = tf.memory().numBytes; - const endNumTensors = tf.memory().numTensors; - expect(endNumBytes - startNumBytes).toEqual(24); - expect(endNumTensors - startNumTensors).toEqual(1); - expect(r.dtype).toBe('int32'); - expectArraysEqual(r, [1, 1, 2, 2, 1, 2]); - }); -}); - describeWithFlags('Reduction: argmin', ALL_ENVS, () => { it('Tensor1D', () => { const a = tf.tensor1d([1, 0, 3, 2]); diff --git a/src/ops/slice_test.ts b/src/ops/slice_test.ts index c8522f29bc..36f427b7e9 100644 --- a/src/ops/slice_test.ts +++ b/src/ops/slice_test.ts @@ -16,8 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, WEBGL_ENVS} from '../jasmine_util'; -import {WebGLMemoryInfo} from '../kernels/webgl/backend_webgl'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; import {Rank} from '../types'; @@ -261,63 +260,6 @@ describeWithFlags('slice2d', ALL_ENVS, () => { }); }); -describeWithFlags('slice and memory usage', WEBGL_ENVS, () => { - beforeAll(() => { - tf.ENV.set('WEBGL_CPU_FORWARD', false); - tf.ENV.set('WEBGL_SIZE_UPLOAD_UNIFORM', 0); - }); - - it('slice a tensor, read it and check memory', async () => { - const getMem = () => tf.memory() as WebGLMemoryInfo; - expect(getMem().numBytesInGPU).toBe(0); - - // Lazy upload won't increase gpu memory. - const a = tf.tensor([2, 3]); - expect(getMem().numBytesInGPU).toBe(0); - - // Upload a to the GPU by running an op. - a.square().dispose(); - expect(getMem().numBytesInGPU).toBe(8); - - // Slicing does not allocate new memory. - const b = a.slice(0); - expect(getMem().numBytesInGPU).toBe(8); - - // Download a to the CPU but the texture remains on GPU - // since b points to it. - await a.data(); - expect(getMem().numBytesInGPU).toBe(8); - - // Dispose a, but the texture should still remain on the GPU - // since b points to it. - a.dispose(); - expect(getMem().numBytesInGPU).toBe(8); - - // Dispose b and expect 0 memory on GPU. - b.dispose(); - expect(getMem().numBytesInGPU).toBe(0); - }); -}); - -describeWithFlags('slice a packed texture', WEBGL_ENVS, () => { - beforeAll(() => { - tf.ENV.set('WEBGL_PACK', true); - }); - - it('slice after a matmul', () => { - const a = [[1, 2], [3, 4]]; - const b = [[5, 6], [7, 8]]; - // Matmul gives a packed tensor in webgl. - // [19, 22] - // [43, 50] - const c = tf.matMul(a, b); - expectArraysClose(c.slice([0, 0]), [19, 22, 43, 50]); - expectArraysClose(c.slice([0, 1]), [22, 50]); - expectArraysClose(c.slice([1, 0]), [43, 50]); - expectArraysClose(c.slice([1, 1]), [50]); - }); -}); - describeWithFlags('slice3d', ALL_ENVS, () => { it('slices 1x1x1 into shape 1x1x1 (effectively a copy)', () => { const a = tf.tensor3d([[[5]]], [1, 1, 1]); diff --git a/src/ops/spectral_ops_test.ts b/src/ops/spectral_ops_test.ts index def07a3f76..37e653175b 100644 --- a/src/ops/spectral_ops_test.ts +++ b/src/ops/spectral_ops_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; describeWithFlags('1D FFT', ALL_ENVS, () => { @@ -205,7 +205,7 @@ describeWithFlags('1D RFFT', ALL_ENVS, () => { }); }); -describeWithFlags('2D RFFT', WEBGL_ENVS, () => { +describeWithFlags('2D RFFT', ALL_ENVS, () => { it('should return the same value with TensorFlow (2x2 elements)', () => { const t1Real = tf.tensor2d([1, 2, 3, 4], [2, 2]); expectArraysClose(tf.spectral.rfft(t1Real), [3, 0, -1, 0, 7, 0, -1, 0]); diff --git a/src/ops/unary_ops_test.ts b/src/ops/unary_ops_test.ts index 9d78aee64a..95257207c5 100644 --- a/src/ops/unary_ops_test.ts +++ b/src/ops/unary_ops_test.ts @@ -16,7 +16,7 @@ */ import * as tf from '../index'; -import {ALL_ENVS, describeWithFlags, PACKED_ENVS, WEBGL_ENVS} from '../jasmine_util'; +import {ALL_ENVS, describeWithFlags} from '../jasmine_util'; import {expectArraysClose, TEST_EPSILON_FLOAT16} from '../test_util'; import * as util from '../util'; @@ -130,18 +130,6 @@ describeWithFlags('relu', ALL_ENVS, () => { }); }); -describeWithFlags('relu', WEBGL_ENVS, () => { - it('works with squarification for prime number length vector', () => { - const maxTextureSize = tf.ENV.getNumber('WEBGL_MAX_TEXTURE_SIZE'); - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', 5); - const a = tf.tensor1d([1, -2, 5, -3, -1, 4, 7]); - const result = tf.relu(a); - - tf.ENV.set('WEBGL_MAX_TEXTURE_SIZE', maxTextureSize); - expectArraysClose(result, [1, 0, 5, 0, 0, 4, 7]); - }); -}); - describeWithFlags('abs', ALL_ENVS, () => { it('basic', () => { const a = tf.tensor1d([1, -2, 0, 3, -0.1]); @@ -3062,67 +3050,6 @@ describeWithFlags('selu', ALL_ENVS, () => { }); }); -describeWithFlags('packed clip', PACKED_ENVS, () => { - it('should not leak memory', () => { - const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); - const min = -1; - const max = 50; - - const startNumBytes = tf.memory().numBytes; - const startNumTensors = tf.memory().numTensors; - tf.clipByValue(a, min, max); - const endNumBytes = tf.memory().numBytes; - const endNumTensors = tf.memory().numTensors; - - expect(endNumBytes - startNumBytes).toEqual(24); - expect(endNumTensors - startNumTensors).toEqual(1); - }); - - it('basic', () => { - const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); - const min = -1; - const max = 50; - - const result = tf.clipByValue(a, min, max); - - expectArraysClose(result, [3, -1, 0, 50, -1, 2]); - }); - - it('using extreme values', () => { - const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); - let result = - tf.clipByValue(a, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY); - expectArraysClose(result, [3, -1, 0, 100, -7, 2]); - - result = tf.clipByValue(a, Number.MIN_VALUE, Number.MAX_VALUE); - expectArraysClose( - result, - [3, Number.MIN_VALUE, Number.MIN_VALUE, 100, Number.MIN_VALUE, 2]); - }); - - it('should work for scalars', () => { - const a = tf.scalar(-4); - const min = -1; - const max = 50; - - const result = tf.clipByValue(a, min, max); - - expectArraysClose(result, [min]); - }); - - it('derivative: 1D tensor with max or min value', () => { - const min = -1; - const max = 2; - const x = tf.tensor1d([-1, 1, 2, 3]); - const dy = tf.tensor1d([1, 10, 100, 1000]); - const gradients = tf.grad(x => x.clipByValue(min, max))(x, dy); - - expect(gradients.shape).toEqual(x.shape); - expect(gradients.dtype).toEqual('float32'); - expectArraysClose(gradients, [1, 10, 100, 0]); - }); -}); - describeWithFlags('clip', ALL_ENVS, () => { it('basic', () => { const a = tf.tensor1d([3, -1, 0, 100, -7, 2]); diff --git a/src/profiler_test.ts b/src/profiler_test.ts index ef31ca3feb..c61a756070 100644 --- a/src/profiler_test.ts +++ b/src/profiler_test.ts @@ -15,8 +15,8 @@ * ============================================================================= */ +import {BackendTimer, BackendTimingInfo} from './backends/backend'; import * as tf from './index'; -import {BackendTimer, BackendTimingInfo} from './kernels/backend'; import {Logger, Profiler} from './profiler'; import {Tensor} from './tensor'; import {TypedArray} from './types'; diff --git a/src/webgl.ts b/src/webgl.ts deleted file mode 100644 index 7c59f3b9f7..0000000000 --- a/src/webgl.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @license - * Copyright 2017 Google Inc. All Rights Reserved. - * 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 gpgpu_util from './kernels/webgl/gpgpu_util'; -import * as webgl_util from './kernels/webgl/webgl_util'; -export {MathBackendWebGL, WebGLMemoryInfo, WebGLTimingInfo} from './kernels/webgl/backend_webgl'; -export {GPGPUContext} from './kernels/webgl/gpgpu_context'; -export {GPGPUProgram} from './kernels/webgl/gpgpu_math'; -// WebGL specific utils. -export {gpgpu_util, webgl_util}; From f17ee4858733a08d88fe86da25d2d4090422a12e Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 13:46:32 -0700 Subject: [PATCH 04/13] save --- CONTRIBUTING.md | 2 +- src/backends/webgl/gpgpu_util_test.ts | 3 +- src/backends/webgl/reshape_packed_test.ts | 3 +- .../webgl/shader_compiler_util_test.ts | 3 +- src/backends/webgl/tex_util_test.ts | 4 +-- src/backends/webgl/webgl_custom_op_test.ts | 3 +- src/backends/webgl/webgl_ops_test.ts | 26 +++++++++++++++++ src/backends/webgl/webgl_util_test.ts | 3 +- src/engine.ts | 2 +- src/globals.ts | 2 +- src/index.ts | 6 ++-- src/ops/conv2d_depthwise_test.ts | 28 +------------------ src/ops/image_ops.ts | 3 +- src/ops/logical_ops.ts | 2 +- src/profiler.ts | 2 +- src/webgl.ts | 24 ++++++++++++++++ 16 files changed, 73 insertions(+), 43 deletions(-) create mode 100644 src/webgl.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fba299ddd..ac9a5b22ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ add new backends. ### Adding an op When adding ops to the library and deciding whether to write a kernel -implementation in [backend.ts](/~https://github.com/tensorflow/tfjs-core/blob/master/src/kernels/backend.ts), +implementation in [backend.ts](/~https://github.com/tensorflow/tfjs-core/blob/master/src/backends/backend.ts), be sure to check out the TensorFlow ops list [here](/~https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/ops/ops.pbtxt). This list shows the kernels available for the TensorFlow C API. To ensure that we can bind to this with node.js, we should ensure that our backend.ts diff --git a/src/backends/webgl/gpgpu_util_test.ts b/src/backends/webgl/gpgpu_util_test.ts index 475a9dc142..a95bdf09ad 100644 --- a/src/backends/webgl/gpgpu_util_test.ts +++ b/src/backends/webgl/gpgpu_util_test.ts @@ -16,8 +16,9 @@ */ import * as tf from '../../index'; -import {describeWithFlags, WEBGL_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; import {expectArraysClose} from '../../test_util'; +import {WEBGL_ENVS} from './backend_webgl_test_registry'; import {GPGPUContext} from './gpgpu_context'; import * as gpgpu_util from './gpgpu_util'; diff --git a/src/backends/webgl/reshape_packed_test.ts b/src/backends/webgl/reshape_packed_test.ts index bd307cb3c2..4bb2703fd1 100644 --- a/src/backends/webgl/reshape_packed_test.ts +++ b/src/backends/webgl/reshape_packed_test.ts @@ -16,8 +16,9 @@ */ import * as tf from '../../index'; -import {describeWithFlags, PACKED_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; import {expectArraysClose} from '../../test_util'; +import {PACKED_ENVS} from './backend_webgl_test_registry'; describeWithFlags('expensive reshape', PACKED_ENVS, () => { const cValues = diff --git a/src/backends/webgl/shader_compiler_util_test.ts b/src/backends/webgl/shader_compiler_util_test.ts index 90b2a58b5f..dec3aa224f 100644 --- a/src/backends/webgl/shader_compiler_util_test.ts +++ b/src/backends/webgl/shader_compiler_util_test.ts @@ -15,7 +15,8 @@ * ============================================================================= */ -import {describeWithFlags, WEBGL_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; +import {WEBGL_ENVS} from './backend_webgl_test_registry'; import {dotify, getLogicalCoordinatesFromFlatIndex} from './shader_compiler_util'; describeWithFlags('shader compiler', WEBGL_ENVS, () => { diff --git a/src/backends/webgl/tex_util_test.ts b/src/backends/webgl/tex_util_test.ts index 9b74304826..7d5c8ec49a 100644 --- a/src/backends/webgl/tex_util_test.ts +++ b/src/backends/webgl/tex_util_test.ts @@ -15,9 +15,9 @@ * ============================================================================= */ -import {describeWithFlags, WEBGL_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; import {expectArraysClose} from '../../test_util'; - +import {WEBGL_ENVS} from './backend_webgl_test_registry'; import * as tex_util from './tex_util'; describe('tex_util getUnpackedMatrixTextureShapeWidthHeight', () => { diff --git a/src/backends/webgl/webgl_custom_op_test.ts b/src/backends/webgl/webgl_custom_op_test.ts index d62302d143..5df4aaa595 100644 --- a/src/backends/webgl/webgl_custom_op_test.ts +++ b/src/backends/webgl/webgl_custom_op_test.ts @@ -16,9 +16,10 @@ */ import * as tf from '../../index'; -import {describeWithFlags, WEBGL_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; import {Tensor} from '../../tensor'; import {expectArraysClose} from '../../test_util'; +import {WEBGL_ENVS} from './backend_webgl_test_registry'; describeWithFlags('custom-op webgl', WEBGL_ENVS, () => { class SquareAndAddKernel implements tf.webgl.GPGPUProgram { diff --git a/src/backends/webgl/webgl_ops_test.ts b/src/backends/webgl/webgl_ops_test.ts index e3ae23d025..86684f7cf0 100644 --- a/src/backends/webgl/webgl_ops_test.ts +++ b/src/backends/webgl/webgl_ops_test.ts @@ -800,3 +800,29 @@ describeWithFlags('packed clip', PACKED_ENVS, () => { expectArraysClose(gradients, [1, 10, 100, 0]); }); }); + +describeWithFlags('depthwiseConv2d packed', PACKED_ENVS, () => { + it('should not leak memory', () => { + const x = tf.tensor4d( + [ + 0.230664, 0.987388, 0.0685208, 0.419224, 0.887861, 0.731641, + 0.0741907, 0.409265, 0.351377 + ], + [1, 3, 3, 1]); + const w = tf.tensor4d( + [0.303873, 0.229223, 0.144333, 0.803373], + [2, 2, 1, 1], + ); + + const startNumBytes = tf.memory().numBytes; + const startNumTensors = tf.memory().numTensors; + + tf.depthwiseConv2d(x, w, 1, 'valid'); + + const endNumBytes = tf.memory().numBytes; + const endNumTensors = tf.memory().numTensors; + + expect(endNumBytes - startNumBytes).toEqual(16); + expect(endNumTensors - startNumTensors).toEqual(1); + }); +}); diff --git a/src/backends/webgl/webgl_util_test.ts b/src/backends/webgl/webgl_util_test.ts index 17b7073796..2e039e26ba 100644 --- a/src/backends/webgl/webgl_util_test.ts +++ b/src/backends/webgl/webgl_util_test.ts @@ -16,8 +16,9 @@ */ import * as tf from '../../index'; -import {describeWithFlags, WEBGL_ENVS} from '../../jasmine_util'; +import {describeWithFlags} from '../../jasmine_util'; import * as util from '../../util'; +import {WEBGL_ENVS} from './backend_webgl_test_registry'; import * as webgl_util from './webgl_util'; describeWithFlags('getTextureShapeFromLogicalShape', WEBGL_ENVS, () => { diff --git a/src/engine.ts b/src/engine.ts index 3d9eae6a3b..e2f1618532 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -15,8 +15,8 @@ * ============================================================================= */ +import {BackendTimingInfo, DataMover, KernelBackend} from './backends/backend'; import {Environment, setEnvironmentGlobal} from './environment'; -import {BackendTimingInfo, DataMover, KernelBackend} from './kernels/backend'; import {Profiler} from './profiler'; import {backpropagateGradients, getFilteredNodesXToY, NamedGradientMap, TapeNode} from './tape'; import {DataId, setTensorTracker, Tensor, Tensor3D, TensorTracker, Variable} from './tensor'; diff --git a/src/globals.ts b/src/globals.ts index 4532d9aabe..180e4c485c 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -15,9 +15,9 @@ * ============================================================================= */ +import {KernelBackend} from './backends/backend'; import {ENGINE, MemoryInfo, ProfileInfo, ScopeFn, TimingInfo} from './engine'; import {ENV} from './environment'; -import {KernelBackend} from './kernels/backend'; import {setDeprecationWarningFn, Tensor} from './tensor'; import {TensorContainer} from './tensor_types'; import {getTensorsInContainer} from './tensor_util'; diff --git a/src/index.ts b/src/index.ts index dca9cffa42..3b164526ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,8 +23,8 @@ import './flags'; // backend_cpu.ts and backend_webgl.ts are standalone files and should be // explicitly included here. -import './kernels/webgl/backend_webgl'; -import './kernels/cpu/backend_cpu'; +import './backends/webgl/backend_webgl'; +import './backends/cpu/backend_cpu'; import * as environment from './environment'; // Serialization. @@ -83,7 +83,7 @@ export { }; // Backend specific. -export {KernelBackend, BackendTimingInfo, DataMover, DataStorage} from './kernels/backend'; +export {KernelBackend, BackendTimingInfo, DataMover, DataStorage} from './backends/backend'; import * as ops from './ops/ops'; setOpHandler(ops); diff --git a/src/ops/conv2d_depthwise_test.ts b/src/ops/conv2d_depthwise_test.ts index a64a78790c..f01a4e9e1f 100644 --- a/src/ops/conv2d_depthwise_test.ts +++ b/src/ops/conv2d_depthwise_test.ts @@ -16,36 +16,10 @@ */ import * as tf from '../index'; -import {describeWithFlags, PACKED_ENVS, ALL_ENVS} from '../jasmine_util'; +import {describeWithFlags, ALL_ENVS} from '../jasmine_util'; import {expectArraysClose} from '../test_util'; import {Rank} from '../types'; -describeWithFlags('depthwiseConv2d packed', PACKED_ENVS, () => { - it('should not leak memory', () => { - const x = tf.tensor4d( - [ - 0.230664, 0.987388, 0.0685208, 0.419224, 0.887861, 0.731641, - 0.0741907, 0.409265, 0.351377 - ], - [1, 3, 3, 1]); - const w = tf.tensor4d( - [0.303873, 0.229223, 0.144333, 0.803373], - [2, 2, 1, 1], - ); - - const startNumBytes = tf.memory().numBytes; - const startNumTensors = tf.memory().numTensors; - - tf.depthwiseConv2d(x, w, 1, 'valid'); - - const endNumBytes = tf.memory().numBytes; - const endNumTensors = tf.memory().numTensors; - - expect(endNumBytes - startNumBytes).toEqual(16); - expect(endNumTensors - startNumTensors).toEqual(1); - }); -}); - describeWithFlags('depthwiseConv2D', ALL_ENVS, () => { it('input=1x3x3x1,f=2,s=1,d=1,p=valid,chMul=1', () => { const fSize = 2; diff --git a/src/ops/image_ops.ts b/src/ops/image_ops.ts index de66330159..0b3ef69259 100644 --- a/src/ops/image_ops.ts +++ b/src/ops/image_ops.ts @@ -15,12 +15,13 @@ * ============================================================================= */ +import {nonMaxSuppressionImpl} from '../backends/non_max_suppression_impl'; import {ENGINE, ForwardFunc} from '../engine'; -import {nonMaxSuppressionImpl} from '../kernels/non_max_suppression_impl'; import {Tensor, Tensor1D, Tensor2D, Tensor3D, Tensor4D} from '../tensor'; import {convertToTensor} from '../tensor_util_env'; import {TensorLike} from '../types'; import * as util from '../util'; + import {op} from './operation'; /** diff --git a/src/ops/logical_ops.ts b/src/ops/logical_ops.ts index e48584290c..6f4902b824 100644 --- a/src/ops/logical_ops.ts +++ b/src/ops/logical_ops.ts @@ -15,8 +15,8 @@ * ============================================================================= */ +import {whereImpl} from '../backends/where_impl'; import {ENGINE} from '../engine'; -import {whereImpl} from '../kernels/where_impl'; import {Tensor, Tensor2D} from '../tensor'; import {convertToTensor} from '../tensor_util_env'; import {TensorLike} from '../types'; diff --git a/src/profiler.ts b/src/profiler.ts index 25c7604eb9..cdafb61c81 100644 --- a/src/profiler.ts +++ b/src/profiler.ts @@ -15,7 +15,7 @@ * ============================================================================= */ -import {BackendTimer} from './kernels/backend'; +import {BackendTimer} from './backends/backend'; import {Tensor} from './tensor'; import {TypedArray} from './types'; import * as util from './util'; diff --git a/src/webgl.ts b/src/webgl.ts new file mode 100644 index 0000000000..c25bff2d4c --- /dev/null +++ b/src/webgl.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2019 Google LLC. All Rights Reserved. + * 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 gpgpu_util from './backends/webgl/gpgpu_util'; +import * as webgl_util from './backends/webgl/webgl_util'; +export {MathBackendWebGL, WebGLMemoryInfo, WebGLTimingInfo} from './backends/webgl/backend_webgl'; +export {GPGPUContext} from './backends/webgl/gpgpu_context'; +export {GPGPUProgram} from './backends/webgl/gpgpu_math'; +// WebGL specific utils. +export {gpgpu_util, webgl_util}; From 79d2ff7eab1286811177eb80064e4b433f27e5b6 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 13:55:21 -0700 Subject: [PATCH 05/13] save --- src/backends/webgl/webgl_ops_test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backends/webgl/webgl_ops_test.ts b/src/backends/webgl/webgl_ops_test.ts index 86684f7cf0..24a83280b7 100644 --- a/src/backends/webgl/webgl_ops_test.ts +++ b/src/backends/webgl/webgl_ops_test.ts @@ -376,7 +376,6 @@ describeWithFlags('conv2d webgl', WEBGL_ENVS, () => { }); }); - describeWithFlags('conv to matmul', PACKED_ENVS, () => { it('im2col should not leak memory', () => { const inputDepth = 1; From b84cec81edb71358ed0dc2e6609a92309588d05e Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 14:10:45 -0700 Subject: [PATCH 06/13] save --- src/jasmine_util.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index cd5160a177..3ba5e3ac25 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -137,10 +137,11 @@ export function setTestEnvs(testEnvs: TestEnv[]) { export function registerTestEnv(testEnv: TestEnv) { // When overriding via command line, turn off registration of test - // environments. + // environments because the command line will set all the test environments. if (typeof __karma__ !== 'undefined') { - TEST_ENVS.push(testEnv); + return; } + TEST_ENVS.push(testEnv); } function executeTests( From 193d20dd821a39ab48b9fb2653b89ccf9b42214d Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 14:21:22 -0700 Subject: [PATCH 07/13] save: --- src/backends/cpu/backend_cpu_test_registry.ts | 1 + src/jasmine_util.ts | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/backends/cpu/backend_cpu_test_registry.ts b/src/backends/cpu/backend_cpu_test_registry.ts index 08be2aac9f..a9a2155031 100644 --- a/src/backends/cpu/backend_cpu_test_registry.ts +++ b/src/backends/cpu/backend_cpu_test_registry.ts @@ -21,4 +21,5 @@ export const CPU_ENVS: Constraints = { backends: 'cpu' }; +console.log('registering CPU...........................;'); registerTestEnv({name: 'cpu', backendName: 'cpu'}); diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index 3ba5e3ac25..bb7b40094f 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -123,11 +123,11 @@ export interface TestEnv { } export let TEST_ENVS: TestEnv[] = []; - +let commandLineTestEnv: TestEnv; if (typeof __karma__ !== 'undefined') { - const testEnv = parseKarmaFlags(__karma__.config.args); - if (testEnv) { - setTestEnvs([testEnv]); + commandLineTestEnv = parseKarmaFlags(__karma__.config.args); + if (commandLineTestEnv != null) { + setTestEnvs([commandLineTestEnv]); } } @@ -138,7 +138,7 @@ export function setTestEnvs(testEnvs: TestEnv[]) { export function registerTestEnv(testEnv: TestEnv) { // When overriding via command line, turn off registration of test // environments because the command line will set all the test environments. - if (typeof __karma__ !== 'undefined') { + if (commandLineTestEnv != null) { return; } TEST_ENVS.push(testEnv); From 02e3d4bb7c6ab71be124931276f8c31989fcf32c Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 14:28:04 -0700 Subject: [PATCH 08/13] save --- src/backends/cpu/backend_cpu_test_registry.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backends/cpu/backend_cpu_test_registry.ts b/src/backends/cpu/backend_cpu_test_registry.ts index a9a2155031..08be2aac9f 100644 --- a/src/backends/cpu/backend_cpu_test_registry.ts +++ b/src/backends/cpu/backend_cpu_test_registry.ts @@ -21,5 +21,4 @@ export const CPU_ENVS: Constraints = { backends: 'cpu' }; -console.log('registering CPU...........................;'); registerTestEnv({name: 'cpu', backendName: 'cpu'}); From 5057686e35a6c4b9239c1706ef6654203ad24124 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 14:41:50 -0700 Subject: [PATCH 09/13] save --- src/backends/webgl/backend_webgl_test.ts | 10 ++++------ src/jasmine_util.ts | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/backends/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts index 999e2eeffb..f0366bd600 100644 --- a/src/backends/webgl/backend_webgl_test.ts +++ b/src/backends/webgl/backend_webgl_test.ts @@ -263,12 +263,10 @@ describeWithFlags('Custom window size', WEBGL_ENVS, () => { const SIZE_UPLOAD_UNIFORM = 4; // Run only for environments that have 32bit floating point support. const FLOAT32_WEBGL_ENVS = { - flags: Object.assign( - { - 'WEBGL_RENDER_FLOAT32_ENABLED': true, - 'WEBGL_SIZE_UPLOAD_UNIFORM': SIZE_UPLOAD_UNIFORM - }, - WEBGL_ENVS.flags), + flags: { + 'WEBGL_RENDER_FLOAT32_ENABLED': true, + 'WEBGL_SIZE_UPLOAD_UNIFORM': SIZE_UPLOAD_UNIFORM + }, backends: WEBGL_ENVS.backends }; describeWithFlags('upload tensors as uniforms', FLOAT32_WEBGL_ENVS, () => { diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index bb7b40094f..e5b1245668 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -41,7 +41,7 @@ export const ALL_ENVS: Constraints = {}; export function envSatisfiesConstraints( env: Environment, currentBackendName: string, constraints: Constraints): boolean { - if (constraints.flags != null) { + if (constraints != null && constraints.flags != null) { for (const flagName in constraints.flags) { const flagValue = constraints.flags[flagName]; if (env.get(flagName) !== flagValue) { @@ -49,7 +49,7 @@ export function envSatisfiesConstraints( } } } - if (constraints.backends != null) { + if (constraints != null && constraints.backends != null) { let anyBackendMatches = false; if (Array.isArray(constraints.backends)) { constraints.backends.forEach(constraintBackendName => { @@ -123,22 +123,27 @@ export interface TestEnv { } export let TEST_ENVS: TestEnv[] = []; -let commandLineTestEnv: TestEnv; + if (typeof __karma__ !== 'undefined') { - commandLineTestEnv = parseKarmaFlags(__karma__.config.args); - if (commandLineTestEnv != null) { - setTestEnvs([commandLineTestEnv]); + const testEnv = parseKarmaFlags(__karma__.config.args); + if (testEnv != null) { + setTestEnvs([testEnv]); } } +// Whether a call to setTestEnvs has been called so we turn off registration. +// This allows comamnd line overriding or programmatic overriding of the +// default registrations. +let testEnvSet = false; export function setTestEnvs(testEnvs: TestEnv[]) { + testEnvSet = true; TEST_ENVS = testEnvs; } export function registerTestEnv(testEnv: TestEnv) { - // When overriding via command line, turn off registration of test - // environments because the command line will set all the test environments. - if (commandLineTestEnv != null) { + // When using an explicit call to setTestEnvs, turn off registration of test + // environments because the explicit call will set the test environments. + if (testEnvSet) { return; } TEST_ENVS.push(testEnv); From 085ea9668d4796c3238aa70bae539b30ac167631 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Fri, 5 Apr 2019 14:56:02 -0700 Subject: [PATCH 10/13] save --- src/jasmine_util.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index e5b1245668..41e37f771c 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -124,13 +124,6 @@ export interface TestEnv { export let TEST_ENVS: TestEnv[] = []; -if (typeof __karma__ !== 'undefined') { - const testEnv = parseKarmaFlags(__karma__.config.args); - if (testEnv != null) { - setTestEnvs([testEnv]); - } -} - // Whether a call to setTestEnvs has been called so we turn off registration. // This allows comamnd line overriding or programmatic overriding of the // default registrations. @@ -149,6 +142,13 @@ export function registerTestEnv(testEnv: TestEnv) { TEST_ENVS.push(testEnv); } +if (typeof __karma__ !== 'undefined') { + const testEnv = parseKarmaFlags(__karma__.config.args); + if (testEnv != null) { + setTestEnvs([testEnv]); + } +} + function executeTests( testName: string, tests: (env: TestEnv) => void, testEnv: TestEnv) { describe(testName, () => { From fbe92a31b984a50339624b6e7a3c5f41c82c2f37 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Wed, 10 Apr 2019 16:18:52 -0400 Subject: [PATCH 11/13] save --- src/engine_test.ts | 18 ++++++++++++------ src/jasmine_util.ts | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/engine_test.ts b/src/engine_test.ts index 39337b38f7..5946f4951a 100644 --- a/src/engine_test.ts +++ b/src/engine_test.ts @@ -294,12 +294,9 @@ describeWithFlags('disposeVariables', ALL_ENVS, () => { }); /** - * The following unit tests are a special integration-style test that assume - * things about backends being registered. These tests don't live in the - * backend directories because it is testing engine rather than backend-specific - * details but needs a real backend to exist. These test will fail if the - * backends are not registered. This is intentional, we should have coverage for - * when these backends are enabled and ensuring they work with the engine. + * The following test constraints to the CPU environment because it needs a + * concrete backend to exist. This test will work for any backend, but currently + * this is the simplest backend to test against. */ describeWithFlags('Switching cpu backends', {backends: 'cpu'}, () => { beforeEach(() => { @@ -369,6 +366,15 @@ describeWithFlags('Switching cpu backends', {backends: 'cpu'}, () => { }); }); +/** + * The following unit test is a special integration-style test that assumes + * things about CPU & WebGL backends being registered. This tests doesn't live + * in the backend directory because it is testing engine rather than + * backend-specific details but needs a real backend to exist. This test will + * fail if the CPU backends is not registered. This is intentional, we should + * have coverage for when these backends are enabled and ensure they work with + * the engine. + */ describeWithFlags('Switching WebGL + CPU backends', {backends: 'webgl'}, () => { beforeEach(() => { tf.registerBackend('webgl1', tf.findBackendFactory('webgl')); diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index 41e37f771c..50f9c70a42 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -125,7 +125,7 @@ export interface TestEnv { export let TEST_ENVS: TestEnv[] = []; // Whether a call to setTestEnvs has been called so we turn off registration. -// This allows comamnd line overriding or programmatic overriding of the +// This allows command line overriding or programmatic overriding of the // default registrations. let testEnvSet = false; export function setTestEnvs(testEnvs: TestEnv[]) { From 1416ce81f3d2d29eeee7a3c751cc4f1c02a555c4 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Thu, 11 Apr 2019 00:13:21 -0400 Subject: [PATCH 12/13] save --- src/backends/cpu/backend_cpu_test_registry.ts | 2 +- src/backends/webgl/backend_webgl_test.ts | 2 +- .../webgl/backend_webgl_test_registry.ts | 2 +- src/engine_test.ts | 147 +++++++++--------- src/jasmine_util.ts | 41 +++-- 5 files changed, 96 insertions(+), 98 deletions(-) diff --git a/src/backends/cpu/backend_cpu_test_registry.ts b/src/backends/cpu/backend_cpu_test_registry.ts index 08be2aac9f..33e496bc6c 100644 --- a/src/backends/cpu/backend_cpu_test_registry.ts +++ b/src/backends/cpu/backend_cpu_test_registry.ts @@ -18,7 +18,7 @@ import {Constraints, registerTestEnv} from '../../jasmine_util'; export const CPU_ENVS: Constraints = { - backends: 'cpu' + activeBackend: 'cpu' }; registerTestEnv({name: 'cpu', backendName: 'cpu'}); diff --git a/src/backends/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts index f0366bd600..9c84364e95 100644 --- a/src/backends/webgl/backend_webgl_test.ts +++ b/src/backends/webgl/backend_webgl_test.ts @@ -267,7 +267,7 @@ const FLOAT32_WEBGL_ENVS = { 'WEBGL_RENDER_FLOAT32_ENABLED': true, 'WEBGL_SIZE_UPLOAD_UNIFORM': SIZE_UPLOAD_UNIFORM }, - backends: WEBGL_ENVS.backends + backends: WEBGL_ENVS.activeBackend }; describeWithFlags('upload tensors as uniforms', FLOAT32_WEBGL_ENVS, () => { it('small tensor gets uploaded as scalar', () => { diff --git a/src/backends/webgl/backend_webgl_test_registry.ts b/src/backends/webgl/backend_webgl_test_registry.ts index af1f5ae6e0..6f698a2b29 100644 --- a/src/backends/webgl/backend_webgl_test_registry.ts +++ b/src/backends/webgl/backend_webgl_test_registry.ts @@ -18,7 +18,7 @@ import {Constraints, registerTestEnv} from '../../jasmine_util'; export const WEBGL_ENVS: Constraints = { - backends: 'webgl' + activeBackend: 'webgl' }; export const PACKED_ENVS: Constraints = { flags: {'WEBGL_PACK': true} diff --git a/src/engine_test.ts b/src/engine_test.ts index 5946f4951a..00ecf030d3 100644 --- a/src/engine_test.ts +++ b/src/engine_test.ts @@ -298,7 +298,7 @@ describeWithFlags('disposeVariables', ALL_ENVS, () => { * concrete backend to exist. This test will work for any backend, but currently * this is the simplest backend to test against. */ -describeWithFlags('Switching cpu backends', {backends: 'cpu'}, () => { +describeWithFlags('Switching cpu backends', {activeBackend: 'cpu'}, () => { beforeEach(() => { tf.registerBackend('cpu1', tf.findBackendFactory('cpu')); tf.registerBackend('cpu2', tf.findBackendFactory('cpu')); @@ -375,80 +375,81 @@ describeWithFlags('Switching cpu backends', {backends: 'cpu'}, () => { * have coverage for when these backends are enabled and ensure they work with * the engine. */ -describeWithFlags('Switching WebGL + CPU backends', {backends: 'webgl'}, () => { - beforeEach(() => { - tf.registerBackend('webgl1', tf.findBackendFactory('webgl')); - tf.registerBackend('webgl2', tf.findBackendFactory('webgl')); - tf.registerBackend('cpu1', tf.findBackendFactory('cpu')); - }); - - afterEach(() => { - tf.removeBackend('webgl1'); - tf.removeBackend('webgl2'); - tf.removeBackend('cpu1'); - }); - - it('can execute op with data from mixed backends', () => { - tf.setBackend('webgl1'); - const a = tf.scalar(5); - - tf.setBackend('webgl2'); - const b = tf.scalar(3); - - tf.setBackend('cpu1'); - const c = tf.scalar(2); - - // Verify that ops can execute with mixed backend data. - tf.tidy(() => { - tf.setBackend('webgl1'); - expectArraysClose(tf.addN([a, b, c]), [10]); - - tf.setBackend('webgl2'); - expectArraysClose(tf.addN([a, b, c]), [10]); - - tf.setBackend('cpu1'); - expectArraysClose(tf.addN([a, b, c]), [10]); +describeWithFlags( + 'Switching WebGL + CPU backends', {activeBackend: 'webgl'}, () => { + beforeEach(() => { + tf.registerBackend('webgl1', tf.findBackendFactory('webgl')); + tf.registerBackend('webgl2', tf.findBackendFactory('webgl')); + tf.registerBackend('cpu1', tf.findBackendFactory('cpu')); + }); + + afterEach(() => { + tf.removeBackend('webgl1'); + tf.removeBackend('webgl2'); + tf.removeBackend('cpu1'); + }); + + it('can execute op with data from mixed backends', () => { + tf.setBackend('webgl1'); + const a = tf.scalar(5); + + tf.setBackend('webgl2'); + const b = tf.scalar(3); + + tf.setBackend('cpu1'); + const c = tf.scalar(2); + + // Verify that ops can execute with mixed backend data. + tf.tidy(() => { + tf.setBackend('webgl1'); + expectArraysClose(tf.addN([a, b, c]), [10]); + + tf.setBackend('webgl2'); + expectArraysClose(tf.addN([a, b, c]), [10]); + + tf.setBackend('cpu1'); + expectArraysClose(tf.addN([a, b, c]), [10]); + }); + + expect(tf.memory().numTensors).toBe(3); + expect(tf.memory().numDataBuffers).toBe(3); + + tf.dispose([a, b, c]); + + expect(tf.memory().numTensors).toBe(0); + expect(tf.memory().numDataBuffers).toBe(0); + }); + + it('fromPixels with mixed backends works', () => { + tf.setBackend('webgl1'); + const a = tf.browser.fromPixels( + new ImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1)); + + tf.setBackend('webgl2'); + const b = tf.browser.fromPixels( + new ImageData(new Uint8ClampedArray([5, 6, 7, 8]), 1, 1)); + + expectArraysClose(tf.add(a, b), [6, 8, 10]); + }); + + it('single tidy multiple backends', () => { + expect(tf.memory().numTensors).toBe(0); + + tf.tidy(() => { + tf.setBackend('webgl1'); + const a = tf.scalar(1); + a.square(); // Uploads to GPU. + + tf.setBackend('webgl2'); + const b = tf.scalar(1); + b.square(); // Uploads to GPU. + + expect(tf.memory().numTensors).toBe(4); + }); + expect(tf.memory().numTensors).toBe(0); + }); }); - expect(tf.memory().numTensors).toBe(3); - expect(tf.memory().numDataBuffers).toBe(3); - - tf.dispose([a, b, c]); - - expect(tf.memory().numTensors).toBe(0); - expect(tf.memory().numDataBuffers).toBe(0); - }); - - it('fromPixels with mixed backends works', () => { - tf.setBackend('webgl1'); - const a = tf.browser.fromPixels( - new ImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1)); - - tf.setBackend('webgl2'); - const b = tf.browser.fromPixels( - new ImageData(new Uint8ClampedArray([5, 6, 7, 8]), 1, 1)); - - expectArraysClose(tf.add(a, b), [6, 8, 10]); - }); - - it('single tidy multiple backends', () => { - expect(tf.memory().numTensors).toBe(0); - - tf.tidy(() => { - tf.setBackend('webgl1'); - const a = tf.scalar(1); - a.square(); // Uploads to GPU. - - tf.setBackend('webgl2'); - const b = tf.scalar(1); - b.square(); // Uploads to GPU. - - expect(tf.memory().numTensors).toBe(4); - }); - expect(tf.memory().numTensors).toBe(0); - }); -}); - // NOTE: This describe is purposefully not a describeWithFlags so that we test // tensor allocation where no scopes have been created. The backend here must // be set to CPU because we cannot allocate GPU tensors outside a diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index 50f9c70a42..04ad286255 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -22,7 +22,8 @@ Error.stackTraceLimit = Infinity; export type Constraints = { flags?: Flags; - backends?: string | string[]; + activeBackend?: string; + registeredBackends?: string[]; }; export const NODE_ENVS: Constraints = { @@ -39,9 +40,13 @@ export const ALL_ENVS: Constraints = {}; // Tests whether the current environment satisfies the set of constraints. export function envSatisfiesConstraints( - env: Environment, currentBackendName: string, + env: Environment, currentBackendName: string, registeredBackends: string[], constraints: Constraints): boolean { - if (constraints != null && constraints.flags != null) { + if (constraints == null) { + return true; + } + + if (constraints.flags != null) { for (const flagName in constraints.flags) { const flagValue = constraints.flags[flagName]; if (env.get(flagName) !== flagValue) { @@ -49,21 +54,12 @@ export function envSatisfiesConstraints( } } } - if (constraints != null && constraints.backends != null) { - let anyBackendMatches = false; - if (Array.isArray(constraints.backends)) { - constraints.backends.forEach(constraintBackendName => { - if (constraintBackendName === currentBackendName) { - anyBackendMatches = true; - } - }); - if (!anyBackendMatches) { - return false; - } - } else { - return currentBackendName === constraints.backends; - } + if (constraints.activeBackend != null) { + return currentBackendName === constraints.activeBackend; + } + if (constraints.registeredBackends != null) { } + return true; } @@ -124,9 +120,9 @@ export interface TestEnv { export let TEST_ENVS: TestEnv[] = []; -// Whether a call to setTestEnvs has been called so we turn off registration. -// This allows command line overriding or programmatic overriding of the -// default registrations. +// Whether a call to setTestEnvs has been called so we turn off +// registration. This allows command line overriding or programmatic +// overriding of the default registrations. let testEnvSet = false; export function setTestEnvs(testEnvs: TestEnv[]) { testEnvSet = true; @@ -134,8 +130,9 @@ export function setTestEnvs(testEnvs: TestEnv[]) { } export function registerTestEnv(testEnv: TestEnv) { - // When using an explicit call to setTestEnvs, turn off registration of test - // environments because the explicit call will set the test environments. + // When using an explicit call to setTestEnvs, turn off registration of + // test environments because the explicit call will set the test + // environments. if (testEnvSet) { return; } From c4a11a2e255ae838492c41229fa5c976ad716a12 Mon Sep 17 00:00:00 2001 From: Nikhil Thorat Date: Thu, 11 Apr 2019 13:52:46 -0400 Subject: [PATCH 13/13] save --- src/backends/webgl/backend_webgl_test.ts | 2 +- src/backends/webgl/gpgpu_context_test.ts | 2 +- src/engine_test.ts | 3 +- src/jasmine_util.ts | 14 ++++- src/jasmine_util_test.ts | 71 ++++++++++++++++++------ 5 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/backends/webgl/backend_webgl_test.ts b/src/backends/webgl/backend_webgl_test.ts index 9c84364e95..87c21ad2d3 100644 --- a/src/backends/webgl/backend_webgl_test.ts +++ b/src/backends/webgl/backend_webgl_test.ts @@ -267,7 +267,7 @@ const FLOAT32_WEBGL_ENVS = { 'WEBGL_RENDER_FLOAT32_ENABLED': true, 'WEBGL_SIZE_UPLOAD_UNIFORM': SIZE_UPLOAD_UNIFORM }, - backends: WEBGL_ENVS.activeBackend + activeBackend: WEBGL_ENVS.activeBackend }; describeWithFlags('upload tensors as uniforms', FLOAT32_WEBGL_ENVS, () => { it('small tensor gets uploaded as scalar', () => { diff --git a/src/backends/webgl/gpgpu_context_test.ts b/src/backends/webgl/gpgpu_context_test.ts index e5761931e3..30e6678648 100644 --- a/src/backends/webgl/gpgpu_context_test.ts +++ b/src/backends/webgl/gpgpu_context_test.ts @@ -24,7 +24,7 @@ import * as tex_util from './tex_util'; const DOWNLOAD_FLOAT_ENVS = { flags: {'WEBGL_DOWNLOAD_FLOAT_ENABLED': true}, - backends: 'webgl' + activeBackend: 'webgl' }; describeWithFlags( diff --git a/src/engine_test.ts b/src/engine_test.ts index 00ecf030d3..0559c9f075 100644 --- a/src/engine_test.ts +++ b/src/engine_test.ts @@ -376,7 +376,8 @@ describeWithFlags('Switching cpu backends', {activeBackend: 'cpu'}, () => { * the engine. */ describeWithFlags( - 'Switching WebGL + CPU backends', {activeBackend: 'webgl'}, () => { + 'Switching WebGL + CPU backends', + {activeBackend: 'webgl', registeredBackends: ['webgl', 'cpu']}, () => { beforeEach(() => { tf.registerBackend('webgl1', tf.findBackendFactory('webgl')); tf.registerBackend('webgl2', tf.findBackendFactory('webgl')); diff --git a/src/jasmine_util.ts b/src/jasmine_util.ts index 04ad286255..f12ec92886 100644 --- a/src/jasmine_util.ts +++ b/src/jasmine_util.ts @@ -23,6 +23,7 @@ Error.stackTraceLimit = Infinity; export type Constraints = { flags?: Flags; activeBackend?: string; + // If defined, all backends in this array must be registered. registeredBackends?: string[]; }; @@ -40,7 +41,7 @@ export const ALL_ENVS: Constraints = {}; // Tests whether the current environment satisfies the set of constraints. export function envSatisfiesConstraints( - env: Environment, currentBackendName: string, registeredBackends: string[], + env: Environment, activeBackend: string, registeredBackends: string[], constraints: Constraints): boolean { if (constraints == null) { return true; @@ -55,9 +56,15 @@ export function envSatisfiesConstraints( } } if (constraints.activeBackend != null) { - return currentBackendName === constraints.activeBackend; + return activeBackend === constraints.activeBackend; } if (constraints.registeredBackends != null) { + for (let i = 0; i < constraints.registeredBackends.length; i++) { + const registeredBackendConstraint = constraints.registeredBackends[i]; + if (registeredBackends.indexOf(registeredBackendConstraint) === -1) { + return false; + } + } } return true; @@ -104,7 +111,8 @@ export function describeWithFlags( name: string, constraints: Constraints, tests: (env: TestEnv) => void) { TEST_ENVS.forEach(testEnv => { ENV.setFlags(testEnv.flags); - if (envSatisfiesConstraints(ENV, testEnv.backendName, constraints)) { + if (envSatisfiesConstraints( + ENV, testEnv.backendName, ENGINE.backendNames(), constraints)) { const testName = name + ' ' + testEnv.name + ' ' + JSON.stringify(testEnv.flags); executeTests(testName, tests, testEnv); diff --git a/src/jasmine_util_test.ts b/src/jasmine_util_test.ts index dbce557f14..2147c19afe 100644 --- a/src/jasmine_util_test.ts +++ b/src/jasmine_util_test.ts @@ -24,74 +24,113 @@ describe('jasmine_util.envSatisfiesConstraints', () => { const backendName = 'test-backend'; const env = new Environment({}); env.setFlags({}); + const registeredBackends = ['test-backend1', 'test-backend2']; const constraints = {}; - expect(envSatisfiesConstraints(env, backendName, constraints)).toBe(true); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(true); }); it('ENV satisfies matching flag constraints, no backend constraint', () => { const backendName = 'test-backend'; const env = new Environment({}); env.setFlags({'TEST-FLAG': true}); + const registeredBackends = ['test-backend1', 'test-backend2']; const constraints = {flags: {'TEST-FLAG': true}}; - expect(envSatisfiesConstraints(env, backendName, constraints)).toBe(true); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(true); }); it('ENV satisfies matching flag and one backend constraint', () => { const backendName = 'test-backend'; const env = new Environment({}); env.setFlags({'TEST-FLAG': true}); + const registeredBackends = ['test-backend1', 'test-backend2']; const constraints = {flags: {'TEST-FLAG': true}, backends: backendName}; - expect(envSatisfiesConstraints(env, backendName, constraints)).toBe(true); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(true); }); it('ENV satisfies matching flag and multiple backend constraints', () => { const backendName = 'test-backend'; const env = new Environment({}); env.setFlags({'TEST-FLAG': true}); + const registeredBackends = ['test-backend1', 'test-backend2']; const constraints = { flags: {'TEST-FLAG': true}, backends: [backendName, 'other-backend'] }; - expect(envSatisfiesConstraints(env, backendName, constraints)).toBe(true); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(true); }); it('ENV does not satisfy mismatching flags constraints', () => { const backendName = 'test-backend'; const env = new Environment({}); env.setFlags({'TEST-FLAG': false}); + const registeredBackends = ['test-backend1', 'test-backend2']; const constraints = {flags: {'TEST-FLAG': true}}; - expect(envSatisfiesConstraints(env, backendName, constraints)).toBe(false); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(false); }); - it('ENV satisfies no flag constraint but does not satisfy backend constraint', - () => { - const backendName = 'test-backend'; - const env = new Environment({}); + it('ENV satisfies no flag constraint but not satisfy activebackend', () => { + const backendName = 'test-backend'; + const env = new Environment({}); + const registeredBackends = ['test-backend1', 'test-backend2']; - const constraints = {backends: 'test-backend2'}; + const constraints = {activeBackend: 'test-backend2'}; - expect(envSatisfiesConstraints(env, backendName, constraints)) - .toBe(false); - }); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(false); + }); + + it('ENV satisfies flags but does not satisfy active backend', () => { + const backendName = 'test-backend'; + const env = new Environment({}); + env.setFlags({'TEST-FLAG': true}); + const registeredBackends = ['test-backend1', 'test-backend2']; - it('ENV satisfies flags but does not satisfy backend constraint', () => { + const constraints = { + flags: {'TEST-FLAG': true}, + activeBackend: 'test-backend2' + }; + + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(false); + }); + + it('ENV satisfies flags active backend, but not registered backends', () => { const backendName = 'test-backend'; const env = new Environment({}); env.setFlags({'TEST-FLAG': true}); + const registeredBackends = ['test-backend1']; - const constraints = {flags: {'TEST-FLAG': true}, backends: 'test-backend2'}; + const constraints = { + flags: {'TEST-FLAG': true}, + activeBackend: 'test-backend1', + registeredBackends: ['test-backend1', 'test-backend2'] + }; - expect(envSatisfiesConstraints(env, backendName, constraints)).toBe(false); + expect(envSatisfiesConstraints( + env, backendName, registeredBackends, constraints)) + .toBe(false); }); });