"use strict";
/**
 * @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.
 * =============================================================================
 */
Object.defineProperty(exports, "__esModule", { value: true });
var device_util = require("./device_util");
var engine_1 = require("./engine");
var environment_util_1 = require("./environment_util");
var tensor_1 = require("./tensor");
var tensor_util_1 = require("./tensor_util");
exports.EPSILON_FLOAT16 = 1e-4;
var TEST_EPSILON_FLOAT16 = 1e-1;
exports.EPSILON_FLOAT32 = 1e-7;
var TEST_EPSILON_FLOAT32 = 1e-3;
var Environment = /** @class */ (function () {
    function Environment(features) {
        this.features = {};
        this.registry = {};
        if (features != null) {
            this.features = features;
        }
        if (this.get('DEBUG')) {
            console.warn('Debugging mode is ON. The output of every math call will ' +
                'be downloaded to CPU and checked for NaNs. ' +
                'This significantly impacts performance.');
        }
    }
    /**
     * Sets the backend (cpu, webgl, etc) responsible for creating tensors and
     * executing operations on those tensors.
     *
     * Note this disposes the current backend, if any, as well as any tensors
     * associated with it. A new backend is initialized, even if it is of the
     * same type as the previous one.
     *
     * @param backendName The name of the backend. Currently supports
     *     `'webgl'|'cpu'` in the browser, and `'tensorflow'` under node.js
     *     (requires tfjs-node).
     * @param safeMode Defaults to false. In safe mode, you are forced to
     *     construct tensors and call math operations inside a `tidy()` which
     *     will automatically clean up intermediate tensors.
     */
    /** @doc {heading: 'Environment'} */
    Environment.setBackend = function (backendName, safeMode) {
        if (safeMode === void 0) { safeMode = false; }
        if (!(backendName in exports.ENV.registry)) {
            throw new Error("Backend name '" + backendName + "' not found in registry");
        }
        exports.ENV.engine.backend = exports.ENV.findBackend(backendName);
        exports.ENV.backendName = backendName;
    };
    /**
     * Returns the current backend name (cpu, webgl, etc). The backend is
     * responsible for creating tensors and executing operations on those tensors.
     */
    /** @doc {heading: 'Environment'} */
    Environment.getBackend = function () {
        exports.ENV.initEngine();
        return exports.ENV.backendName;
    };
    /**
     * Dispose all variables kept in backend engine.
     */
    /** @doc {heading: 'Environment'} */
    Environment.disposeVariables = function () {
        exports.ENV.engine.disposeVariables();
    };
    /**
     * Returns memory info at the current time in the program. The result is an
     * object with the following properties:
     *
     * - `numBytes`: Number of bytes allocated (undisposed) at this time.
     * - `numTensors`: Number of unique tensors allocated.
     * - `numDataBuffers`: Number of unique data buffers allocated
     *   (undisposed) at this time, which is ≤ the number of tensors
     *   (e.g. `a.reshape(newShape)` makes a new Tensor that shares the same
     *   data buffer with `a`).
     * - `unreliable`: True if the memory usage is unreliable. See `reasons` when
     *    `unrealible` is true.
     * - `reasons`: `string[]`, reasons why the memory is unreliable, present if
     *    `unreliable` is true.
     */
    /** @doc {heading: 'Performance', subheading: 'Memory'} */
    Environment.memory = function () {
        return exports.ENV.engine.memory();
    };
    /**
     * Executes the provided function `f()` and returns a promise that resolves
     * with information about the function's memory use:
     * - `newBytes`: tne number of new bytes allocated
     * - `newTensors`: the number of new tensors created
     * - `peakBytes`: the peak number of bytes allocated
     * - `kernels`: an array of objects for each kernel involved that reports
     * their input and output shapes, number of bytes used, and number of new
     * tensors created.
     *
     * ```js
     * const profile = await tf.profile(() => {
     *   const x = tf.tensor1d([1, 2, 3]);
     *   let x2 = x.square();
     *   x2.dispose();
     *   x2 = x.square();
     *   x2.dispose();
     *   return x;
     * });
     *
     * console.log(`newBytes: ${profile.newBytes}`);
     * console.log(`newTensors: ${profile.newTensors}`);
     * console.log(`byte usage over all kernels: ${profile.kernels.map(k =>
     * k.totalBytesSnapshot)}`);
     * ```
     *
     */
    /** @doc {heading: 'Performance', subheading: 'Profile'} */
    Environment.profile = function (f) {
        return exports.ENV.engine.profile(f);
    };
    /**
     * Executes the provided function `fn` and after it is executed, cleans up all
     * intermediate tensors allocated by `fn` except those returned by `fn`.
     * `fn` must not return a Promise (async functions not allowed). The returned
     * result can be a complex object.
     *
     * Using this method helps avoid memory leaks. In general, wrap calls to
     * operations in `tf.tidy` for automatic memory cleanup.
     *
     * When in safe mode, you must enclose all `tf.Tensor` creation and ops
     * inside a `tf.tidy` to prevent memory leaks.
     *
     * ```js
     * // y = 2 ^ 2 + 1
     * const y = tf.tidy(() => {
     *   // a, b, and one will be cleaned up when the tidy ends.
     *   const one = tf.scalar(1);
     *   const a = tf.scalar(2);
     *   const b = a.square();
     *
     *   console.log('numTensors (in tidy): ' + tf.memory().numTensors);
     *
     *   // The value returned inside the tidy function will return
     *   // through the tidy, in this case to the variable y.
     *   return b.add(one);
     * });
     *
     * console.log('numTensors (outside tidy): ' + tf.memory().numTensors);
     * y.print();
     * ```
     *
     * @param nameOrFn The name of the closure, or the function to execute.
     *     If a name is provided, the 2nd argument should be the function.
     *     If debug mode is on, the timing and the memory usage of the function
     *     will be tracked and displayed on the console using the provided name.
     * @param fn The function to execute.
     */
    /** @doc {heading: 'Performance', subheading: 'Memory'} */
    Environment.tidy = function (nameOrFn, fn) {
        return exports.ENV.engine.tidy(nameOrFn, fn);
    };
    /**
     * Disposes any `tf.Tensor`s found within the provided object.
     *
     * @param container an object that may be a `tf.Tensor` or may directly
     *     contain `tf.Tensor`s, such as a `Tensor[]` or `{key: Tensor, ...}`. If
     *     the object is not a `tf.Tensor` or does not contain `Tensors`, nothing
     *     happens. In general it is safe to pass any object here, except that
     *     `Promise`s are not supported.
     */
    /** @doc {heading: 'Performance', subheading: 'Memory'} */
    Environment.dispose = function (container) {
        var tensors = tensor_util_1.getTensorsInContainer(container);
        tensors.forEach(function (tensor) { return tensor.dispose(); });
    };
    /**
     * Keeps a `tf.Tensor` generated inside a `tf.tidy` from being disposed
     * automatically.
     *
     * ```js
     * let b;
     * const y = tf.tidy(() => {
     *   const one = tf.scalar(1);
     *   const a = tf.scalar(2);
     *
     *   // b will not be cleaned up by the tidy. a and one will be cleaned up
     *   // when the tidy ends.
     *   b = tf.keep(a.square());
     *
     *   console.log('numTensors (in tidy): ' + tf.memory().numTensors);
     *
     *   // The value returned inside the tidy function will return
     *   // through the tidy, in this case to the variable y.
     *   return b.add(one);
     * });
     *
     * console.log('numTensors (outside tidy): ' + tf.memory().numTensors);
     * console.log('y:');
     * y.print();
     * console.log('b:');
     * b.print();
     * ```
     *
     * @param result The tensor to keep from being disposed.
     */
    /** @doc {heading: 'Performance', subheading: 'Memory'} */
    Environment.keep = function (result) {
        return exports.ENV.engine.keep(result);
    };
    /**
     * Executes `f()` and returns a promise that resolves with timing
     * information.
     *
     * The result is an object with the following properties:
     *
     * - `wallMs`: Wall execution time.
     * - `kernelMs`: Kernel execution time, ignoring data transfer.
     * - On `WebGL` The following additional properties exist:
     *   - `uploadWaitMs`: CPU blocking time on texture uploads.
     *   - `downloadWaitMs`: CPU blocking time on texture downloads (readPixels).
     *
     * ```js
     * const x = tf.randomNormal([20, 20]);
     * const time = await tf.time(() => x.matMul(x));
     *
     * console.log(`kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);
     * ```
     *
     * @param f The function to execute and time.
     */
    /** @doc {heading: 'Performance', subheading: 'Timing'} */
    Environment.time = function (f) {
        return exports.ENV.engine.time(f);
    };
    Environment.prototype.get = function (feature) {
        if (feature in this.features) {
            return this.features[feature];
        }
        this.features[feature] = this.evaluateFeature(feature);
        return this.features[feature];
    };
    Environment.prototype.getFeatures = function () {
        return this.features;
    };
    Environment.prototype.set = function (feature, value) {
        this.features[feature] = value;
    };
    Environment.prototype.getBestBackendName = function () {
        var _this = this;
        if (Object.keys(this.registry).length === 0) {
            throw new Error('No backend found in registry.');
        }
        var sortedBackends = Object.keys(this.registry)
            .map(function (name) {
            return { name: name, entry: _this.registry[name] };
        })
            .sort(function (a, b) {
            // Highest priority comes first.
            return b.entry.priority - a.entry.priority;
        });
        return sortedBackends[0].name;
    };
    Environment.prototype.evaluateFeature = function (feature) {
        if (feature === 'DEBUG') {
            return false;
        }
        else if (feature === 'IS_BROWSER') {
            return typeof window !== 'undefined';
        }
        else if (feature === 'IS_NODE') {
            return (typeof process !== 'undefined') &&
                (typeof process.versions !== 'undefined') &&
                (typeof process.versions.node !== 'undefined');
        }
        else if (feature === 'IS_CHROME') {
            return environment_util_1.isChrome();
        }
        else if (feature === 'WEBGL_CPU_FORWARD') {
            return true;
        }
        else if (feature === 'WEBGL_PACK') {
            return this.get('WEBGL_VERSION') === 0 ? false : true;
        }
        else if (feature === 'WEBGL_PACK_BATCHNORMALIZATION') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_PACK_CLIP') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_PACK_DEPTHWISECONV') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_PACK_BINARY_OPERATIONS') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_PACK_ARRAY_OPERATIONS') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_PACK_IMAGE_OPERATIONS') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_LAZILY_UNPACK') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_CONV_IM2COL') {
            return this.get('WEBGL_PACK');
        }
        else if (feature === 'WEBGL_NUM_MB_BEFORE_PAGING') {
            if (this.get('PROD') || !this.get('IS_BROWSER')) {
                return Number.POSITIVE_INFINITY;
            }
            return environment_util_1.getNumMBBeforePaging();
        }
        else if (feature === 'WEBGL_MAX_TEXTURE_SIZE') {
            return environment_util_1.getWebGLMaxTextureSize(this.get('WEBGL_VERSION'));
        }
        else if (feature === 'WEBGL_MAX_TEXTURES_IN_SHADER') {
            return environment_util_1.getMaxTexturesInShader(this.get('WEBGL_VERSION'));
        }
        else if (feature === 'IS_TEST') {
            return false;
        }
        else if (feature === 'BACKEND') {
            return this.getBestBackendName();
        }
        else if (feature === 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') {
            var webGLVersion = this.get('WEBGL_VERSION');
            if (webGLVersion === 0) {
                return 0;
            }
            return environment_util_1.getWebGLDisjointQueryTimerVersion(webGLVersion);
        }
        else if (feature === 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE') {
            return this.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0 &&
                !device_util.isMobile();
        }
        else if (feature === 'HAS_WEBGL') {
            return this.get('WEBGL_VERSION') > 0;
        }
        else if (feature === 'WEBGL_VERSION') {
            if (environment_util_1.isWebGLVersionEnabled(2)) {
                return 2;
            }
            else if (environment_util_1.isWebGLVersionEnabled(1)) {
                return 1;
            }
            return 0;
        }
        else if (feature === 'WEBGL_RENDER_FLOAT32_ENABLED') {
            return environment_util_1.isRenderToFloatTextureEnabled(this.get('WEBGL_VERSION'));
        }
        else if (feature === 'WEBGL_DOWNLOAD_FLOAT_ENABLED') {
            return environment_util_1.isDownloadFloatTextureEnabled(this.get('WEBGL_VERSION'));
        }
        else if (feature === 'WEBGL_FENCE_API_ENABLED') {
            return environment_util_1.isWebGLFenceEnabled(this.get('WEBGL_VERSION'));
        }
        else if (feature === 'WEBGL_SIZE_UPLOAD_UNIFORM') {
            // Use uniform uploads only when 32bit floats are supported. In 16bit
            // environments there are problems with comparing a 16bit texture value
            // with a 32bit uniform value.
            var useUniforms = this.get('WEBGL_RENDER_FLOAT32_ENABLED');
            return useUniforms ? 4 : 0;
        }
        else if (feature === 'TEST_EPSILON') {
            return this.backend.floatPrecision() === 32 ? TEST_EPSILON_FLOAT32 :
                TEST_EPSILON_FLOAT16;
        }
        else if (feature === 'EPSILON') {
            return this.backend.floatPrecision() === 32 ? exports.EPSILON_FLOAT32 :
                exports.EPSILON_FLOAT16;
        }
        else if (feature === 'PROD') {
            return false;
        }
        else if (feature === 'TENSORLIKE_CHECK_SHAPE_CONSISTENCY') {
            return !this.get('PROD');
        }
        else if (feature === 'DEPRECATION_WARNINGS_ENABLED') {
            return true;
        }
        throw new Error("Unknown feature " + feature + ".");
    };
    Environment.prototype.setFeatures = function (features) {
        this.features = Object.assign({}, features);
    };
    Environment.prototype.reset = function () {
        this.features = environment_util_1.getFeaturesFromURL();
        if (this.globalEngine != null) {
            this.globalEngine = null;
        }
    };
    Object.defineProperty(Environment.prototype, "backend", {
        get: function () {
            return this.engine.backend;
        },
        enumerable: true,
        configurable: true
    });
    Environment.prototype.findBackend = function (name) {
        if (!(name in this.registry)) {
            return null;
        }
        return this.registry[name].backend;
    };
    /**
     * Registers a global backend. The registration should happen when importing
     * a module file (e.g. when importing `backend_webgl.ts`), and is used for
     * modular builds (e.g. custom tfjs bundle with only webgl support).
     *
     * @param factory The backend factory function. When called, it should
     * return an instance of the backend.
     * @param priority The priority of the backend (higher = more important).
     *     In case multiple backends are registered, the priority is used to find
     *     the best backend. Defaults to 1.
     * @return False if the creation/registration failed. True otherwise.
     */
    Environment.prototype.registerBackend = function (name, factory, priority) {
        var _this = this;
        if (priority === void 0) { priority = 1; }
        if (name in this.registry) {
            console.warn(name + " backend was already registered. Reusing existing backend");
            return false;
        }
        try {
            var backend = factory();
            backend.setDataMover({ moveData: function (dataId) { return _this.engine.moveData(dataId); } });
            this.registry[name] = { backend: backend, priority: priority };
            return true;
        }
        catch (err) {
            console.warn("Registration of backend " + name + " failed");
            console.warn(err.stack || err.message);
            return false;
        }
    };
    Environment.prototype.removeBackend = function (name) {
        if (!(name in this.registry)) {
            throw new Error(name + " backend not found in registry");
        }
        this.registry[name].backend.dispose();
        delete this.registry[name];
    };
    Object.defineProperty(Environment.prototype, "engine", {
        get: function () {
            this.initEngine();
            return this.globalEngine;
        },
        enumerable: true,
        configurable: true
    });
    Environment.prototype.initEngine = function () {
        var _this = this;
        if (this.globalEngine == null) {
            this.backendName = this.get('BACKEND');
            var backend = this.findBackend(this.backendName);
            this.globalEngine =
                new engine_1.Engine(backend, false /* safeMode */, function () { return _this.get('DEBUG'); });
        }
    };
    Object.defineProperty(Environment.prototype, "global", {
        get: function () {
            return getGlobalNamespace();
        },
        enumerable: true,
        configurable: true
    });
    return Environment;
}());
exports.Environment = Environment;
var _global;
function getGlobalNamespace() {
    if (_global == null) {
        // tslint:disable-next-line:no-any
        var ns = void 0;
        if (typeof (window) !== 'undefined') {
            ns = window;
        }
        else if (typeof (global) !== 'undefined') {
            ns = global;
        }
        else if (typeof (process) !== 'undefined') {
            ns = process;
        }
        else {
            throw new Error('Could not find a global object');
        }
        _global = ns;
    }
    return _global;
}
function getOrMakeEnvironment() {
    var ns = getGlobalNamespace();
    if (ns.ENV == null) {
        ns.ENV = new Environment(environment_util_1.getFeaturesFromURL());
    }
    // Tell the current tensor interface that the global engine is responsible for
    // tracking.
    tensor_1.setTensorTracker(function () { return ns.ENV.engine; });
    return ns.ENV;
}
/**
 * Enables production mode which disables correctness checks in favor of
 * performance.
 */
/** @doc {heading: 'Environment'} */
function enableProdMode() {
    exports.ENV.set('PROD', true);
}
exports.enableProdMode = enableProdMode;
/**
 * Enables debug mode which will log information about all executed kernels:
 * the ellapsed time of the kernel execution, as well as the rank, shape, and
 * size of the output tensor.
 *
 * Debug mode will significantly slow down your application as it will
 * download the result of every operation to the CPU. This should not be used in
 * production. Debug mode does not affect the timing information of the kernel
 * execution as we do not measure download time in the kernel execution time.
 *
 * See also: `tf.profile`, `tf.memory`.
 */
/** @doc {heading: 'Environment'} */
function enableDebugMode() {
    exports.ENV.set('DEBUG', true);
}
exports.enableDebugMode = enableDebugMode;
/** Globally disables deprecation warnings */
function disableDeprecationWarnings() {
    exports.ENV.set('DEPRECATION_WARNINGS_ENABLED', false);
    console.warn("TensorFlow.js deprecation warnings have been disabled.");
}
exports.disableDeprecationWarnings = disableDeprecationWarnings;
/** Warn users about deprecated functionality. */
function deprecationWarn(msg) {
    if (exports.ENV.get('DEPRECATION_WARNINGS_ENABLED')) {
        console.warn(msg + ' You can disable deprecation warnings with ' +
            'tf.disableDeprecationWarnings().');
    }
}
exports.deprecationWarn = deprecationWarn;
tensor_1.setDeprecationWarningFn(deprecationWarn);
exports.ENV = getOrMakeEnvironment();
//# sourceMappingURL=environment.js.map