<commit_before>/**
 *
 * Application or Website name
 *
 * Copyright 2015, Author Name
 * Some information on the license.
 *
 * Tests > Functional > Sample
 *
**/

var urls = {
	home: 'http://cousins.vm1.dev/'
};

module.exports = {
	before: function(browser){
		console.log('Starting functional tests...');
	},
	after: function(browser){
		console.log('Ending functional tests...');
	},
	beforeEach: function(browser){

	},
	afterEach: function(browser){

	},
	'Header element exists and has banner role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.page-header')
			.verify.attributeEquals('.page-header', 'role', 'banner')
	},
	'Navigation element exists and has navigation role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.navigation')
			.verify.attributeEquals('.navigation', 'role', 'navigation')
	},
	'Main element exists and has main role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.main')
			.verify.attributeEquals('.main', 'role', 'main')
	},
	'Footer element exists and has contentinfo role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.page-footer')
			.verify.attributeEquals('.page-footer', 'role', 'contentinfo')
			.end();
	}
};
<commit_msg>Fixed<commit_after>/**
 *
 * Application or Website name
 *
 * Copyright 2015, Author Name
 * Some information on the license.
 *
 * Tests > Functional > Sample
 *
**/

var urls = {
	home: 'http://google.com/'
};

module.exports = {
	before: function(browser){
		console.log('Starting functional tests...');
	},
	after: function(browser){
		console.log('Ending functional tests...');
	},
	beforeEach: function(browser){

	},
	afterEach: function(browser){

	},
	'Header element exists and has banner role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.page-header')
			.verify.attributeEquals('.page-header', 'role', 'banner')
	},
	'Navigation element exists and has navigation role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.navigation')
			.verify.attributeEquals('.navigation', 'role', 'navigation')
	},
	'Main element exists and has main role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.main')
			.verify.attributeEquals('.main', 'role', 'main')
	},
	'Footer element exists and has contentinfo role': function(browser){
		browser
			.url(urls.home)
			.waitForElementVisible('body', 1000)
      		.pause(1000)
			.assert.elementPresent('.page-footer')
			.verify.attributeEquals('.page-footer', 'role', 'contentinfo')
			.end();
	}
};
<|endoftext|>


<commit_before>var MongoClient = require('../../').MongoClient,
  expect = require('chai').expect;

function connectToDb(url, db, options, callback) {
  if (typeof options === 'function') {
    callback = options;
    options = {};
  }

  MongoClient.connect(url, options || {}, function(err, client) {
    if (err) return callback(err);
    callback(null, client.db(db), client);
  });
}

function setupDatabase(configuration, dbsToClean) {
  dbsToClean = Array.isArray(dbsToClean) ? dbsToClean : [];
  var configDbName = configuration.db;
  var client = configuration.newClient(configuration.writeConcernMax(), {
    poolSize: 1
  });

  dbsToClean.push(configDbName);
  return client.connect().then(function() {
    var cleanPromises = [];
    dbsToClean.forEach(function(dbName) {
      var cleanPromise = client
        .db(dbName)
        .command({
          dropAllUsersFromDatabase: 1,
          writeConcern: { w: 1 }
        })
        .then(function() {
          return client.db(dbName).dropDatabase();
        });

      cleanPromises.push(cleanPromise);
    });

    return Promise.all(cleanPromises);
  });
}

var assert = {
  equal: function(a, b) {
    expect(a).to.equal(b);
  },

  deepEqual: function(a, b) {
    expect(a).to.eql(b);
  },

  strictEqual: function(a, b) {
    expect(a).to.eql(b);
  },

  notEqual: function(a, b) {
    expect(a).to.not.equal(b);
  },

  ok: function(a) {
    expect(a).to.be.ok;
  },

  throws: function(func) {
    expect(func).to.throw;
  }
};

module.exports = {
  connectToDb: connectToDb,
  setupDatabase: setupDatabase,
  assert: assert
};
<commit_msg>refactor(tests): move `delay` into shared module for tests<commit_after>var MongoClient = require('../../').MongoClient,
  expect = require('chai').expect;

function connectToDb(url, db, options, callback) {
  if (typeof options === 'function') {
    callback = options;
    options = {};
  }

  MongoClient.connect(url, options || {}, function(err, client) {
    if (err) return callback(err);
    callback(null, client.db(db), client);
  });
}

function setupDatabase(configuration, dbsToClean) {
  dbsToClean = Array.isArray(dbsToClean) ? dbsToClean : [];
  var configDbName = configuration.db;
  var client = configuration.newClient(configuration.writeConcernMax(), {
    poolSize: 1
  });

  dbsToClean.push(configDbName);
  return client.connect().then(function() {
    var cleanPromises = [];
    dbsToClean.forEach(function(dbName) {
      var cleanPromise = client
        .db(dbName)
        .command({
          dropAllUsersFromDatabase: 1,
          writeConcern: { w: 1 }
        })
        .then(function() {
          return client.db(dbName).dropDatabase();
        });

      cleanPromises.push(cleanPromise);
    });

    return Promise.all(cleanPromises);
  });
}

var assert = {
  equal: function(a, b) {
    expect(a).to.equal(b);
  },

  deepEqual: function(a, b) {
    expect(a).to.eql(b);
  },

  strictEqual: function(a, b) {
    expect(a).to.eql(b);
  },

  notEqual: function(a, b) {
    expect(a).to.not.equal(b);
  },

  ok: function(a) {
    expect(a).to.be.ok;
  },

  throws: function(func) {
    expect(func).to.throw;
  }
};

var delay = function(timeout) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve();
    }, timeout);
  });
};

module.exports = {
  connectToDb: connectToDb,
  setupDatabase: setupDatabase,
  assert: assert,
  delay: delay
};
<|endoftext|>


<commit_before>/*
 * Functional tests
 */
require('should');
var spawn = require('child_process').spawn;
var Browser = require('zombie');
var serverHost = '127.0.0.1';
var serverPort = '9091';
var serverURL = 'http://' + serverHost + ':' + serverPort;

describe('the wassup server', function() {
  var server;
  var browser;

  beforeEach(function() {
    server = spawn('wassup', ['-H', serverHost, '-p', serverPort]);
    browser = new Browser();
    browser.site = serverURL;
  });

  afterEach(function() {
    server.kill();
  });

  it('should run', function(done) {
    browser.visit('/', function() {
      browser.success.should.be.ok;
      done();
    });
  });

  describe('home page', function() {
    beforeEach(function(done) {
      browser.visit('/').then(done, done);
    });

    it('should have a cool title', function() {
      browser.text('title').should.match(/Wassup/);
    });

    it('should let users add and remove a URL', function() {
      browser.
        // The user enters a URL and presses "Add URL"
        fill('new_url', 'http://google.com').
        pressButton('Add URL', function() {
          // They see they're on the same page
          browser.location.pathname.should.equal('/');

          // And see the new URL as the first item on the list
          var urlList = browser.document.querySelectorAll('#urls li');
          urlList.should.have.length(1, 'List should have 1 URL');
          urlList[0].textContent.should.match(/http\:\/\/google\.com/);

          // The user clicks on the item's "Delete" button
          browser.clickLink('#urls li:first-child a.delete', function() {
            // They see the item is removed
            urlList = browser.document.querySelectorAll('#urls li');
            urlList.should.have.length(0, 'List should have no URLs');
          });
        });
    });
  });
});
<commit_msg>Be a little nicer to the poor server<commit_after>/*
 * Functional tests
 */
require('should');
var spawn = require('child_process').spawn;
var Browser = require('zombie');
var serverHost = '127.0.0.1';
var serverPort = '9091';
var serverURL = 'http://' + serverHost + ':' + serverPort;

describe('the wassup server', function() {
  var server;
  var browser;

  before(function() {
    server = spawn('wassup', ['-H', serverHost, '-p', serverPort]);
    browser = new Browser();
    browser.site = serverURL;
  });

  after(function() {
    server.kill();
  });

  it('should run', function(done) {
    // Wait for some output to avoid race conditions
    server.stdout.on('data', function() {
      // Visit the home page
      browser.visit('/', function() {
        browser.success.should.be.ok;
        done();
      });
    });
  });

  describe('home page', function() {
    beforeEach(function(done) {
      browser.visit('/').then(done, done);
    });

    it('should have a cool title', function() {
      browser.text('title').should.match(/Wassup/);
    });

    it('should let users add and remove a URL', function() {
      // The user enters a URL and presses "Add URL"
      browser.
        fill('new_url', 'http://google.com').
        pressButton('Add URL', function() {

          // They see they're on the same page
          browser.location.pathname.should.equal('/');

          // And see the new URL as the first item on the list
          var urlList = browser.document.querySelectorAll('#urls li');
          urlList.should.have.length(1, 'List should have 1 URL');
          urlList[0].textContent.should.match(/http\:\/\/google\.com/);

          // The user clicks on the item's "Delete" button
          browser.clickLink('#urls li:first-child a.delete', function() {

            // They see the item is removed
            urlList = browser.document.querySelectorAll('#urls li');
            urlList.should.have.length(0, 'List should have no URLs');
          });
        });
    });
  });
});
<|endoftext|>


<commit_before>var expect = require('chai').expect;
var GhostReaper = require('../src/ghost_reaper');

describe("GhostReaper::reap", function() {
  it("turns off the machine", function() {

  });
  it("snapshots the machine on digitalocean", function() {

  })
  it("deletes the previous snapshot from digitalocean", function() {

  });
  it("replaces the previous snapshot id", function() {

  });
})
<commit_msg>add ghostreaper test<commit_after>var expect = require('chai').expect;
var GhostReaper = require('../src/ghost_reaper');

var ghost = null;

beforeEach(function() {
  ghost = {};
});

describe("GhostReaper::reap", function() {
  it("turns off the machine", function() {

    GhostReaper.reap(ghost)

  });
  it("snapshots the machine on digitalocean", function() {

  })
  it("deletes the previous snapshot from digitalocean", function() {

  });
  it("replaces the previous snapshot id", function() {

  });
})
<|endoftext|>


<commit_before>/**
 * @copyright Copyright 2016 Kevin Locke <kevin@kevinlocke.name>
 * @license MIT
 */

'use strict';

var gitBranchIsCmd = require('../bin/git-branch-is');

var BBPromise = require('bluebird').Promise;
// eslint-disable-next-line no-undef
var PPromise = typeof Promise === 'function' ? Promise : BBPromise;
var assert = require('assert');
var constants = require('../test-lib/constants');
var execFile = require('child_process').execFile;
var path = require('path');

/** Initial command arguments. */
var ARGS = [process.argv[0], 'git-branch-is'];

// Local copy of shared constants
var SUBDIR_NAME = constants.SUBDIR_NAME;
var TEST_REPO_PATH = constants.TEST_REPO_PATH;

describe('git-branch-is', function() {
  it('exit code 0 silently for same branch name', function(done) {
    gitBranchIsCmd(ARGS.concat('master'), function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('exit code 1 with warning for different branch name', function(done) {
    gitBranchIsCmd(ARGS.concat('invalid'), function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 1);
      assert(!result.stdout);
      assert(/\binvalid\b/.test(result.stderr));
      assert(/\bmaster\b/.test(result.stderr));
      done();
    });
  });

  it('exit code 1 silently with quiet option', function(done) {
    var args = ARGS.concat('-q', 'invalid');
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 1);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('exit code 0 with message if verbose', function(done) {
    var args = ARGS.concat('-v', 'master');
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(/\bmaster\b/.test(result.stdout));
      assert(!result.stderr);
      done();
    });
  });

  // Note:  This is one of the few errors that doesn't call process.exit
  it('callback Error for multiple args', function(done) {
    gitBranchIsCmd(ARGS.concat('master', 'foo'), function(err, result) {
      assert(err instanceof Error);
      assert(/\bargument/i.test(err.message));
      assert(/\busage/i.test(err.message));
      done();
    });
  });

  it('can specify an additional git argument', function(done) {
    var args = ARGS.concat(
        '-C',
        SUBDIR_NAME,
        '--git-arg=--git-dir=../.git',
        'master'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('can specify multiple additional git arguments', function(done) {
    var args = ARGS.concat(
        '-C',
        '..',
        '--git-arg=-C',
        '--git-arg=' + TEST_REPO_PATH,
        'master'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('can specify an additional git arguments separately', function(done) {
    var args = ARGS.concat(
        '--git-arg',
        '-C',
        '--git-arg',
        TEST_REPO_PATH,
        '-C',
        '..',
        'master'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('gitArgs takes precedence over gitDir', function(done) {
    var args = ARGS.concat(
        '--git-arg',
        // Note:  Also tests that Commander interprets this as option argument
        '--git-dir=.git',
        '--git-dir=invalid',
        'master'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('can specify git executable and args', function(done) {
    var gitArg = path.join('..', '..', 'test-bin', 'echo-surprise.js');
    var args = ARGS.concat(
        '-C',
        SUBDIR_NAME,
        '--git-arg=' + gitArg,
        '--git-path=' + process.execPath,
        'surprise'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  // Just like git -C and --git-dir
  it('gitDir is relative to cwd', function(done) {
    var args = ARGS.concat(
        '-C',
        SUBDIR_NAME,
        '--git-dir=' + path.join('..', '.git'),
        'master'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  describe('with global Promise', function() {
    var hadPromise, oldPromise;

    before('ensure global Promise', function() {
      if (global.Promise !== PPromise) {
        hadPromise = hasOwnProperty.call(global, 'Promise');
        oldPromise = global.Promise;
        global.Promise = PPromise;
      }
    });

    after('restore global Promise', function() {
      if (hadPromise === true) {
        global.Promise = oldPromise;
      } else if (hadPromise === false) {
        delete global.Promise;
      }
    });

    it('returns a Promise with the result', function() {
      var promise = gitBranchIsCmd(ARGS.concat('master'));
      assert(promise instanceof global.Promise);
      return promise.then(function(result) {
        assert.strictEqual(result.code, 0);
        assert(!result.stdout);
        assert(!result.stderr);
      });
    });

    it('rejects the Promise with an Error', function() {
      var promise = gitBranchIsCmd(ARGS.concat('-C', 'invalid', 'master'));
      assert(promise instanceof global.Promise);
      return promise.then(
        function(result) { throw new Error('expecting Error'); },
        function(err) { assert(err instanceof Error); }
      );
    });
  });

  describe('without global Promise', function() {
    var hadPromise, oldPromise;

    before('remove global Promise', function() {
      if (global.Promise) {
        hadPromise = hasOwnProperty.call(global, 'Promise');
        oldPromise = global.Promise;
        // Note:  Deleting triggers Mocha's global leak detection.
        // Also wouldn't work if global scope had a prototype chain.
        global.Promise = undefined;
      }
    });

    after('restore global Promise', function() {
      if (oldPromise) {
        if (hadPromise) {
          global.Promise = oldPromise;
        } else {
          delete global.Promise;
        }
      }
    });

    it('throws without a callback', function() {
      assert.throws(
          function() {
            gitBranchIsCmd(ARGS.concat('master'));
          },
          function(err) {
            return err instanceof TypeError &&
                /\bcallback\b/.test(err.message);
          }
      );
    });
  });

  it('exit code 0 works when executed', function(done) {
    execFile(
      process.execPath,
      ['../bin/git-branch-is', 'master'],
      function(err, result) {
        assert.ifError(err);
        assert(!result.stdout);
        assert(!result.stderr);
        done();
      }
    );
  });

  it('exit code 1 works when executed', function(done) {
    execFile(
      process.execPath,
      ['../bin/git-branch-is', 'invalid'],
      function(err, result) {
        assert(err instanceof Error);
        assert.strictEqual(err.code, 1);
        assert(/\binvalid\b/.test(err.message));
        assert(/\bmaster\b/.test(err.message));
        done();
      }
    );
  });
});
<commit_msg>Replace 'master' with BRANCH_CURRENT<commit_after>/**
 * @copyright Copyright 2016 Kevin Locke <kevin@kevinlocke.name>
 * @license MIT
 */

'use strict';

var gitBranchIsCmd = require('../bin/git-branch-is');

var BBPromise = require('bluebird').Promise;
// eslint-disable-next-line no-undef
var PPromise = typeof Promise === 'function' ? Promise : BBPromise;
var assert = require('assert');
var constants = require('../test-lib/constants');
var execFile = require('child_process').execFile;
var path = require('path');

/** Initial command arguments. */
var ARGS = [process.argv[0], 'git-branch-is'];

// Local copy of shared constants
var BRANCH_CURRENT = constants.BRANCH_CURRENT;
var SUBDIR_NAME = constants.SUBDIR_NAME;
var TEST_REPO_PATH = constants.TEST_REPO_PATH;

var BRANCH_CURRENT_RE = new RegExp('\\b' + constants.BRANCH_CURRENT + '\\b');

describe('git-branch-is', function() {
  it('exit code 0 silently for same branch name', function(done) {
    gitBranchIsCmd(ARGS.concat(BRANCH_CURRENT), function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('exit code 1 with warning for different branch name', function(done) {
    gitBranchIsCmd(ARGS.concat('invalid'), function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 1);
      assert(!result.stdout);
      assert(/\binvalid\b/.test(result.stderr));
      assert(BRANCH_CURRENT_RE.test(result.stderr));
      done();
    });
  });

  it('exit code 1 silently with quiet option', function(done) {
    var args = ARGS.concat('-q', 'invalid');
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 1);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('exit code 0 with message if verbose', function(done) {
    var args = ARGS.concat('-v', BRANCH_CURRENT);
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(BRANCH_CURRENT_RE.test(result.stdout));
      assert(!result.stderr);
      done();
    });
  });

  // Note:  This is one of the few errors that doesn't call process.exit
  it('callback Error for multiple args', function(done) {
    gitBranchIsCmd(ARGS.concat(BRANCH_CURRENT, 'foo'), function(err, result) {
      assert(err instanceof Error);
      assert(/\bargument/i.test(err.message));
      assert(/\busage/i.test(err.message));
      done();
    });
  });

  it('can specify an additional git argument', function(done) {
    var args = ARGS.concat(
        '-C',
        SUBDIR_NAME,
        '--git-arg=--git-dir=../.git',
        BRANCH_CURRENT
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('can specify multiple additional git arguments', function(done) {
    var args = ARGS.concat(
        '-C',
        '..',
        '--git-arg=-C',
        '--git-arg=' + TEST_REPO_PATH,
        BRANCH_CURRENT
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('can specify an additional git arguments separately', function(done) {
    var args = ARGS.concat(
        '--git-arg',
        '-C',
        '--git-arg',
        TEST_REPO_PATH,
        '-C',
        '..',
        BRANCH_CURRENT
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('gitArgs takes precedence over gitDir', function(done) {
    var args = ARGS.concat(
        '--git-arg',
        // Note:  Also tests that Commander interprets this as option argument
        '--git-dir=.git',
        '--git-dir=invalid',
        BRANCH_CURRENT
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  it('can specify git executable and args', function(done) {
    var gitArg = path.join('..', '..', 'test-bin', 'echo-surprise.js');
    var args = ARGS.concat(
        '-C',
        SUBDIR_NAME,
        '--git-arg=' + gitArg,
        '--git-path=' + process.execPath,
        'surprise'
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  // Just like git -C and --git-dir
  it('gitDir is relative to cwd', function(done) {
    var args = ARGS.concat(
        '-C',
        SUBDIR_NAME,
        '--git-dir=' + path.join('..', '.git'),
        BRANCH_CURRENT
    );
    gitBranchIsCmd(args, function(err, result) {
      assert.ifError(err);
      assert.strictEqual(result.code, 0);
      assert(!result.stdout);
      assert(!result.stderr);
      done();
    });
  });

  describe('with global Promise', function() {
    var hadPromise, oldPromise;

    before('ensure global Promise', function() {
      if (global.Promise !== PPromise) {
        hadPromise = hasOwnProperty.call(global, 'Promise');
        oldPromise = global.Promise;
        global.Promise = PPromise;
      }
    });

    after('restore global Promise', function() {
      if (hadPromise === true) {
        global.Promise = oldPromise;
      } else if (hadPromise === false) {
        delete global.Promise;
      }
    });

    it('returns a Promise with the result', function() {
      var promise = gitBranchIsCmd(ARGS.concat(BRANCH_CURRENT));
      assert(promise instanceof global.Promise);
      return promise.then(function(result) {
        assert.strictEqual(result.code, 0);
        assert(!result.stdout);
        assert(!result.stderr);
      });
    });

    it('rejects the Promise with an Error', function() {
      var promise = gitBranchIsCmd(ARGS.concat(
        '-C',
        'invalid',
        BRANCH_CURRENT
      ));
      assert(promise instanceof global.Promise);
      return promise.then(
        function(result) { throw new Error('expecting Error'); },
        function(err) { assert(err instanceof Error); }
      );
    });
  });

  describe('without global Promise', function() {
    var hadPromise, oldPromise;

    before('remove global Promise', function() {
      if (global.Promise) {
        hadPromise = hasOwnProperty.call(global, 'Promise');
        oldPromise = global.Promise;
        // Note:  Deleting triggers Mocha's global leak detection.
        // Also wouldn't work if global scope had a prototype chain.
        global.Promise = undefined;
      }
    });

    after('restore global Promise', function() {
      if (oldPromise) {
        if (hadPromise) {
          global.Promise = oldPromise;
        } else {
          delete global.Promise;
        }
      }
    });

    it('throws without a callback', function() {
      assert.throws(
          function() {
            gitBranchIsCmd(ARGS.concat(BRANCH_CURRENT));
          },
          function(err) {
            return err instanceof TypeError &&
                /\bcallback\b/.test(err.message);
          }
      );
    });
  });

  it('exit code 0 works when executed', function(done) {
    execFile(
      process.execPath,
      ['../bin/git-branch-is', BRANCH_CURRENT],
      function(err, result) {
        assert.ifError(err);
        assert(!result.stdout);
        assert(!result.stderr);
        done();
      }
    );
  });

  it('exit code 1 works when executed', function(done) {
    execFile(
      process.execPath,
      ['../bin/git-branch-is', 'invalid'],
      function(err, result) {
        assert(err instanceof Error);
        assert.strictEqual(err.code, 1);
        assert(/\binvalid\b/.test(err.message));
        assert(BRANCH_CURRENT_RE.test(err.message));
        done();
      }
    );
  });
});
<|endoftext|>


<commit_before>"use strict";

global.assert = require("assert");
var Mocha = require("mocha"),
    mocha = new Mocha({ui: "bdd", ignoreLeaks: false, globals: "test"}),
    path = require("path"),
    fs = require("fs");

require("../loader.js"); /*global loadGpfAndTests*/

loadGpfAndTests({
    parameters: process.argv.slice(2),
    gpfPath: path.resolve(__dirname, "../../.."),
    pathSeparator: path.sep,
    log: console.log.bind(console),
    exit: process.exit,
    require: require,
    read: function (filePath) {
        return fs.readFileSync(filePath).toString();
    },
    useBDD: false,
    loadTest: function (filePath) {
        mocha.addFile(filePath);
    },
    done: function () {
        // Now, you can run the tests.
        mocha.run(function (failures) {
            process.on("exit", function () {
                process.exit(failures);
            });
        });
    }
});
<commit_msg>Adapt to new configuration<commit_after>"use strict";

global.assert = require("assert");
var Mocha = require("mocha"),
    mocha = new Mocha({ui: "bdd", ignoreLeaks: false, globals: "test"}),
    path = require("path"),
    fs = require("fs");

require("../loader.js"); /*global loadGpfAndTests*/

loadGpfAndTests({
    parameters: process.argv.slice(2),
    gpfPath: path.resolve(__dirname, "../../.."),
    pathSeparator: path.sep,
    log: console.log.bind(console),
    exit: process.exit,
    require: require,
    read: function (filePath) {
        return fs.readFileSync(filePath).toString();
    },
    loadTest: function (filePath) {
        mocha.addFile(filePath);
    },
    run: function () {
        // Now, you can run the tests.
        mocha.run(function (failures) {
            process.on("exit", function () {
                process.exit(failures);
            });
        });
    }
});
<|endoftext|>


<commit_before>export { ScaleArraySet };

import * as FloatValue from "../../Unpacker/FloatValue.js";
//import * as ConvBiasActivation from "./ConvBiasActivation.js";

/**
 * Several scale arrays for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * 1. Analysis
 *
 * Suppose
 *   - The previous convolution-bias-activation (i.e. pointwise or depthwise)
 *     - output channel count is q.
 *     - activation function is f()
 *     - activation escaping is ( scale = a, translate = b )
 *     - per output channel original is X  = ( x1 , x2 , ..., xq  )
 *     - per output channel modified is X' = ( x1', x2', ..., xq' ) = a * X + b
 *     - f(X') = f( a * X + b ) is guaranteed still kept linear although activation function f() is non-linear.
 *
 *   - This pointwise (or depthwise)
 *     - This pointwise input channel count is s. (Or, this depthwise filter size is s.)
 *     - output channel count is u.
 *     - per output channel filter weights are S = ( S1, S2, ..., Ss ) and will be modified to S' = ( S1', S2', ..., Ss' ).
 *     - per output channel bias weights are T = ( T1, T2, ..., Tu ) and will be modified to T' = ( T1', T2', ..., Tu' ).
 *     - activation function is g()
 *     - activation escaping is ( scale = c, translate = d )
 *     - per output channel original is Y  = ( y1 , y2 , ..., yu  ) = S  * X  + T
 *     - per output channel modified is Y' = ( y1', y2', ..., yu' ) = S' * X' + T' = c * Y + d
 *     - g(Y') = g( c * Y + d ) is guaranteed still kept linear although activation function g() is non-linear.
 *
 * Find out S' and T'.
 *
 * On one hand:
 *

 *   Y' = S' * X' + T'
 *      = S' * ( a * X + b ) + T'
 *      = ( a * S' ) * X + ( b * S' + T' )
 *

 *
 * On the other hand:
 *

 *   Y' = c * Y + d
 *      = c * ( S * X + T ) + d
 *      = c * S * X + c * T + d
 *      = ( c * S ) * X + ( c * T + d )
 *

 *
 * Implied:
 *

 *   a * S' = c * S
 *   b * S' + T' = c * T + d
 *

 *
 * Got:
 *

 *   S' = ( c / a ) * S
 *   T' = ( c * T ) - ( b * S' ) + d
 *      = ( c * T ) - b * ( ( c / a ) * S ) + d
 *      = ( c * T ) - ( b * ( c / a ) * S ) + d
 *

 *
 * Verification:
 *

 *   Y' = S' * X' + T'
 *      = ( ( c / a ) * S ) * ( a * X + b ) + ( ( c * T ) - ( b * ( c / a ) * S ) + d )
 *      = ( c * S * X ) + ( b * ( c / a ) * S ) + ( c * T ) - ( b * ( c / a ) * S ) + d
 *      = ( c * S * X ) + ( c * T ) + d
 *      = c * ( S * X + T ) + d
 *      = c * Y + d
 *

 *
 *
 * 2. Problem:
 *   - Every element (not only every channel) needs have itself T' because T' depends on S.
 *     - This is difficult to be implemented.
 *
 *   - However, if the activation escaping could have only scale and without translate (i.e. translate = b = d = 0 ), this issue
 *       could be reduced.
 *
 *   - This implies the output range of the activation function should include both negative and positive near the origin point.
 *     - So that only scale is enough to escape their non-linear part of these activation function.
 *
 *   - For example, sin(), tanh(), erf() are feasible.
 *     - But cos(), relu(), relu6(), sigmoid() are not feasible because their output always non-negative near the origin point.
 *         These functions always need bias (i.e. non-zero translate) to escape their non-linear part.
 *
 *

//!!! ...unfinished... (2022/01/06)

 *
 *
 * 3. What if this pointwise (or depthwise) does not have filter weights ( S1, S2, ... Ss )? (e.g avg/max pooling)
 *
 *
 *
 * 4. What if this pointwise (or depthwise) does not have bias weights T?
 *
 *
 *
 * @member {FloatValue.ScaleArray} do
 *   The scale for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.BoundsArraySet.afterBias_beforeActivationEscaping escape from activation function's non-linear domain
 * into linear domain (i.e. generate ConvBiasActivation.BoundsArraySet.afterActivationEscaping_beforeActivation).
 *
 * @member {FloatValue.ScaleArray} undo
 *   If apply this.undo, it will have the effect of undoing the this.do.
 */
class ScaleArraySet {

  constructor( arrayLength ) {
    this.do = new FloatValue.ScaleArray( arrayLength );
    this.undo = new FloatValue.ScaleArray( arrayLength );
  }

  /**
   * @return {ScaleArraySet} Return a newly created ScaleArraySet which is a copy of this ScaleArraySet.
   */
  clone() {
    let result = new ScaleArraySet();
    result.set_byScaleSet( this );
    return result;
  }

  /**
   * @param {number} N  Set all scales[] by ( N ). Default are ( N = 1 ) (i.e. no scale).
   *
   * @return {ScaleArraySet} Return this (modified) object.
   */
  set_all_byN( N = 1 ) {
    this.do.set_all_byN( N );
    this.undo.set_all_byN( N );
    return this;
  }

  /**
   * @param {ScaleArraySet} aScaleArraySet  The ScaleArraySet to be copied.
   *
   * @return {ScaleArraySet} Return this (modified) object.
   */
  set_all_byScaleArraySet( aScaleArraySet ) {
    this.do.set_all_byScaleArray( aScaleArraySet.do );
    this.undo.set_all_byScaleArray( aScaleArraySet.undo );
    return this;
  }

//!!! ...unfinished... (2021/12/26)
/**
 * - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
 *   (can not scale-translate). Unless, pad can be non-zero?
 *
 *   - Fortunately, this is not a problem for pass-through channels. Because the pass-through filter value is also 0 in the padded
 *       position, the result is not affected. (But its a big problem for non-pass-through channels.)
 *
 *   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
 *
 *     - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
 *
 *     - In our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
 *         already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
 *
 *     - However, this is not so good for pass-through channels. Because the pass-through filter value is 0 at the right-bottom
 *         corner, the image value will be destroyed.
 *
 * - If previous convolution does not have activation escaping .do (i.e. is a normal convolution without pass-through),
 *     this convolution's .do should be different.
 *
 * - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
 *     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
 *
 * - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
 *     and their bounds. (The .do and .undo should also affect the value bounds.)
 *
 * - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
 *     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
 *
 */

}

<commit_msg>Update ActivationEscaping.js<commit_after>export { ScaleArraySet };

import * as FloatValue from "../../Unpacker/FloatValue.js";

/**
 * Several scale arrays for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * 1. Analysis
 *
 * Suppose
 *   - The previous convolution-bias-activation (i.e. pointwise or depthwise)
 *     - output channel count is q.
 *     - activation function is f()
 *     - activation escaping is ( scale = a, translate = b )
 *     - per output channel original is X  = ( x1 , x2 , ..., xq  )
 *     - per output channel modified is X' = ( x1', x2', ..., xq' ) = a * X + b
 *     - f(X') = f( a * X + b ) is guaranteed still kept linear although activation function f() is non-linear.
 *
 *   - This pointwise (or depthwise)
 *     - This pointwise input channel count is s. (Or, this depthwise filter size is s.)
 *     - output channel count is u.
 *     - per output channel filter weights are S = ( S1, S2, ..., Ss ) and will be modified to S' = ( S1', S2', ..., Ss' ).
 *     - per output channel bias weights are T = ( T1, T2, ..., Tu ) and will be modified to T' = ( T1', T2', ..., Tu' ).
 *     - activation function is g()
 *     - activation escaping is ( scale = c, translate = d )
 *     - per output channel original is Y  = ( y1 , y2 , ..., yu  ) = S  * X  + T
 *     - per output channel modified is Y' = ( y1', y2', ..., yu' ) = S' * X' + T' = c * Y + d
 *     - g(Y') = g( c * Y + d ) is guaranteed still kept linear although activation function g() is non-linear.
 *
 * Find out S' and T'.
 *
 * On one hand:
 *

 *   Y' = S' * X' + T'
 *      = S' * ( a * X + b ) + T'
 *      = ( a * S' ) * X + ( b * S' + T' )
 *

 *
 * On the other hand:
 *

 *   Y' = c * Y + d
 *      = c * ( S * X + T ) + d
 *      = c * S * X + c * T + d
 *      = ( c * S ) * X + ( c * T + d )
 *

 *
 * Implied:
 *

 *   a * S' = c * S
 *   b * S' + T' = c * T + d
 *

 *
 * Got:
 *

 *   S' = ( c / a ) * S
 *   T' = ( c * T ) - ( b * S' ) + d
 *      = ( c * T ) - b * ( ( c / a ) * S ) + d
 *      = ( c * T ) - ( b * ( c / a ) * S ) + d
 *

 *
 * Verification:
 *

 *   Y' = S' * X' + T'
 *      = ( ( c / a ) * S ) * ( a * X + b ) + ( ( c * T ) - ( b * ( c / a ) * S ) + d )
 *      = ( c * S * X ) + ( b * ( c / a ) * S ) + ( c * T ) - ( b * ( c / a ) * S ) + d
 *      = ( c * S * X ) + ( c * T ) + d
 *      = c * ( S * X + T ) + d
 *      = c * Y + d
 *

 *
 *
 * 2. Problem:
 *   - Every element (not only every channel) needs have itself T' because T' depends on S.
 *     - This is difficult to be implemented.
 *
 *   - However, if the activation escaping could have only scale and without translate (i.e. translate = b = d = 0 ), this issue
 *       could be reduced.
 *
 *   - This implies the output range of the activation function should include both negative and positive near the origin point.
 *     - So that only scale is enough to escape their non-linear part of these activation function.
 *
 *   - For example, sin(), tanh(), erf() are feasible.
 *     - But cos(), relu(), relu6(), sigmoid() are not feasible because their output always non-negative near the origin point.
 *         These functions always need bias (i.e. non-zero translate) to escape their non-linear part.
 *
 *

//!!! ...unfinished... (2022/01/06)

 *
 *
 * 3. What if this pointwise (or depthwise) does not have filter weights ( S1, S2, ... Ss )? (e.g avg/max pooling)
 *
 *
 *
 * 4. What if this pointwise (or depthwise) does not have bias weights T?
 *
 *
 *
 * @member {FloatValue.ScaleArray} do
 *   The scale for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.BoundsArraySet.afterBias_beforeActivationEscaping escape from activation function's non-linear domain
 * into linear domain (i.e. generate ConvBiasActivation.BoundsArraySet.afterActivationEscaping_beforeActivation).
 *
 * @member {FloatValue.ScaleArray} undo
 *   If apply this.undo, it will have the effect of undoing the this.do.
 */
class ScaleArraySet {

  constructor( arrayLength ) {
    this.do = new FloatValue.ScaleArray( arrayLength );
    this.undo = new FloatValue.ScaleArray( arrayLength );
  }

  /**
   * @return {ScaleArraySet} Return a newly created ScaleArraySet which is a copy of this ScaleArraySet.
   */
  clone() {
    let result = new ScaleArraySet();
    result.set_byScaleSet( this );
    return result;
  }

  /**
   * @param {number} N  Set all scales[] by ( N ). Default are ( N = 1 ) (i.e. no scale).
   *
   * @return {ScaleArraySet} Return this (modified) object.
   */
  set_all_byN( N = 1 ) {
    this.do.set_all_byN( N );
    this.undo.set_all_byN( N );
    return this;
  }

  /**
   * @param {ScaleArraySet} aScaleArraySet  The ScaleArraySet to be copied.
   *
   * @return {ScaleArraySet} Return this (modified) object.
   */
  set_all_byScaleArraySet( aScaleArraySet ) {
    this.do.set_all_byScaleArray( aScaleArraySet.do );
    this.undo.set_all_byScaleArray( aScaleArraySet.undo );
    return this;
  }

  /**
   * Swap two information.
   *
   * @param {number} index1  The 1st array index whose information will be swapped.
   * @param {number} index2  The 2nd array index whose information will be swapped.
   *
   * @return {ScaleArraySet} Return this (modified) object.
   */
  swap_two( index1, index2 ) {
    this.do.swap_two( index1, index2 );
    this.undo.swap_two( index1, index2 );
    return this;
  }

//!!! ...unfinished... (2021/12/26)
/**
 * - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
 *   (can not scale-translate). Unless, pad can be non-zero?
 *
 *   - Fortunately, this is not a problem for pass-through channels. Because the pass-through filter value is also 0 in the padded
 *       position, the result is not affected. (But its a big problem for non-pass-through channels.)
 *
 *   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
 *
 *     - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
 *
 *     - In our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
 *         already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
 *
 *     - However, this is not so good for pass-through channels. Because the pass-through filter value is 0 at the right-bottom
 *         corner, the image value will be destroyed.
 *
 * - If previous convolution does not have activation escaping .do (i.e. is a normal convolution without pass-through),
 *     this convolution's .do should be different.
 *
 * - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
 *     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
 *
 * - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
 *     and their bounds. (The .do and .undo should also affect the value bounds.)
 *
 * - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
 *     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
 *
 */

}

<|endoftext|>


<commit_before>export { ScaleTranslateArraySet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ConvBiasActivation from "./ConvBiasActivation.js";

/**
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleTranslateArray} doWithoutPreviousUndo
 *   The scale-translate for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.BoundsArraySet.beforeActivation escape from activation function's non-linear domain into linear domain.
 *
 * @member {FloatValue.ScaleTranslateArray} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleTranslateArray} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleTranslateArraySet {

  constructor() {
    this.doWithoutPreviousUndo = new FloatValue.ScaleTranslateArray();
    this.do = new FloatValue.ScaleTranslateArray();
    this.undo = new FloatValue.ScaleTranslateArray();
  }

  clone() {
    let result = new ScaleTranslateArraySet();
    result.set_byScaleTranslateSet( this );
    return result;
  }

  /**
   * @param {ScaleTranslateArraySet} aScaleTranslateSet
   *   The ScaleTranslateSet to be copied.
   */
  set_byScaleTranslateArraySet( aScaleTranslateArraySet ) {
    this.doWithoutPreviousUndo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.doWithoutPreviousUndo );
    this.do.set_all_byScaleTranslateArray( aScaleTranslateArraySet.do );
    this.undo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.undo );
  }

  /** Reset all scale-translate values. Default is ( scale = 1, translate = 0 ) (i.e. no scale and no translate). */
  reset_by_scale_translate( scale = 1, translate = 0 ) {
    this.doWithoutPreviousUndo.set_all_by_scale_translate( scale , translate );
    this.do.set_all_by_scale_translate( scale , translate );
    this.undo.set_all_by_scale_translate( scale , translate );
  }

//!!! ...unfinished... (2021/12/26)
/**
 * - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
 *   (can not scale-translate). Unless, pad can be non-zero?
 *
 *   - Fortunately, this is not a problem for pass-through channels. Because the pass-through filter value is also 0 in the padded
 *       position, the result is not affected. (But its a big problem for non-pass-through channels.)
 *
 *   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
 *
 *     - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
 *
 *     - In our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
 *         already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
 *
 *     - However, this is not so good for pass-through channels. Because the pass-through filter value is 0 at the right-bottom
 *         corner, the image value will be destroyed.
 *
 * - If previous convolution does not have activation escaping do (i.e. is a normal convolution without pass-through)，this
 *     this convolution's .do should be different.
 *
 * - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
 *     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
 *
 * - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
 *     and their bounds. (The .do and .undo should also affect the value bounds.)
 *
 * - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
 *     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
 *
 */

  /**
   * Set the following properties:
   *   - this.doWithoutPreviousUndo
   *   - this.do
   *   - this.undo
   *
   * @param {ConvBiasActivation.BoundsArraySet} current_ConvBiasActivation_BoundsArraySet
   *   The BoundsArraySet of current convolution-bias-activation for calculating this.doWithoutPreviousUndo.
   *
   * @param {ScaleTranslateArraySet} previous_ActivationEscaping_ScaleTranslateArraySet
   *   The ActivationEscaping.ScaleTranslateArraySet of previous convolution-bias-activation for calculating this.do.
   */
  set_by_currentBoundsArraySet_previousActivationEscaping(
    current_ConvBiasActivation_BoundsArraySet, previous_ActivationEscaping_ScaleTranslateArraySet ) {

    // Calculate the scale-translate for escaping from activation function's non-linear domain into linear domain.
    //
    // Note: This does not work for avg/max pooling.
    this.doWithoutPreviousUndo.set_all_by_fromBoundsArray_ToBoundsArray(
      current_ConvBiasActivation_BoundsArraySet.beforeActivation, current_ConvBiasActivation_BoundsArraySet.output );

    // Combine undoing previous activation escaping scale-translate and doing current activation escaping scale-translate.
    this.do.set_all_byScaleTranslateArray( previous_ActivationEscaping_ScaleTranslateArraySet.undo );
    this.do.scaleTranslate_all_byScaleTranslateArray( this.doWithoutPreviousUndo );

    // Prepare the undoing scale-translate for the next convolution-bias-activation.
    this.undo.set_all_byUndo_ScaleTranslateArray( this.do );
  }

  /**
   *
   * @param {number} fromLower  The source bounds [ fromLower, fromUpper ]
   * @param {number} fromUpper  The source bounds [ fromLower, fromUpper ]
   * @param {number} toLower    The destination bounds [ toLower, toUpper ]
   * @param {number} toUpper    The destination bounds [ toLower, toUpper ]
   *
   * @return {number}
   *   Return a scale value which could let source bounds [ fromLower, fromUpper ] completely insides destination bounds [ toLower, toUpper ].
   * Return Number.NaN, if it is impossible to do that.
   */
  static calc_scale_by_fromLowerUpper_toLowerUpper( fromLower, fromUpper, toLower, toUpper ) {
    let srcLower = Math.min( fromLower, fromUpper ); // Confirm ( lower <= upper ).
    let srcUpper = Math.max( fromLower, fromUpper );
    let dstLower = Math.min( toLower, toUpper );
    let dstUpper = Math.max( toLower, toUpper );

    let scale;

    // 1. Try lower bound.
    //
    // Note:
    //  (  0 / x ) = 0
    //  ( -1 / 0 ) = -Infinity
    //  ( +1 / 0 ) = +Infinity
    //  (  0 / 0 ) = NaN
    //
    scale = dstLower / srcLower;

//!!! (2022/01/06 Remarked)
//     if ( ( srcLower == 0 ) || ( dstLower == 0 ) ) {
//       scale = 0; // Avoid -Infinity, +Infinity, NaN.
//     } else {
//       scale = dstLower / srcLower;
//     }

    // 1.2 Verification.
    //   - If scale is zero or -Infinity or +Infinity or NaN, it is always failed.
    //   - If scaled source upper bound is out of destination range, it is also failed.
    let adjustedUpper = srcUpper * scale;
    if ( ( scale == 0 ) || ( !Number.isFinite( scale ) ) || ( adjustedUpper < dstLower ) || ( adjustedUpper > dstUpper ) ) {

      // 2. Try upperer bound, since it is failed to fit [ srcLower, srcUpper ] into [ dstLower, dstUpper ] by scale according to lower bound.
      scale = dstUpper / srcUpper;

//!!! (2022/01/06 Remarked)
//       if ( ( srcUpper == 0 ) || ( dstUpper == 0 ) ) {
//         scale = 0; // Avoid -Infinity, +Infinity, NaN.
//       } else {
//         scale = dstUpper / srcUpper;
//       }

      // 2.2 Verification. If scale is zero, it is always failed. Otherwise, check it by lower side.
      //   - If scale is zero or -Infinity or +Infinity or NaN, it is always failed.
      //   - If scaled source lower bound is out of destination range, it is also failed.
      let adjustedLower = srcLower * scale;
      if ( ( scale == 0 ) || ( !Number.isFinite( scale ) ) || ( adjustedLower < dstLower ) || ( adjustedLower > dstUpper ) ) {
        // 3. It is impossible to fit [ srcLower, srcUpper ] into [ dstLower, dstUpper ] only by scale because all cases are tried and failed.
        scale = Number.NaN;
      }
    }

    return scale;
  }

}

/**
 *
 * 1.
 *
 * Suppose
 *   - The previous convolution-bias-activation (i.e. pointwise or depthwise)
 *     - output channel count is q.
 *     - activation function is f()
 *     - activation escaping is ( scale = a, translate = b )
 *     - per output channel original is X  = ( x1 , x2 , ..., xq  )
 *     - per output channel modified is X' = ( x1', x2', ..., xq' ) = a * X + b
 *     - f(X') = f( a * X + b ) is guaranteed still kept linear although activation function f() is non-linear.
 *
 *   - This pointwise (or depthwise)
 *     - This pointwise input channel count is s. (Or, this depthwise filter size is s.)
 *     - output channel count is u.
 *     - per output channel filter weights are S = ( S1, S2, ..., Ss ) and will be modified to S' = ( S1', S2', ..., Ss' ).
 *     - per output channel bias weights are T = ( T1, T2, ..., Tu ) and will be modified to T' = ( T1', T2', ..., Tu' ).
 *     - activation function is g()
 *     - activation escaping is ( scale = c, translate = d )
 *     - per output channel original is Y  = ( y1 , y2 , ..., yu  ) = S  * X  + T
 *     - per output channel modified is Y' = ( y1', y2', ..., yu' ) = S' * X' + T' = c * Y + d
 *     - g(Y') = g( c * Y + d ) is guaranteed still kept linear although activation function g() is non-linear.
 *
 * Find out S' and T'.
 *
 * On one hand:
 *   Y' = S' * X' + T'
 *      = S' * ( a * X + b ) + T'
 *      = ( a * S' ) * X + ( b * S' + T' )
 *
 * On the other hand:
 *   Y' = c * Y + d
 *      = c * ( S * X + T ) + d
 *      = c * S * X + c * T + d
 *      = ( c * S ) * X + ( c * T + d )
 *
 * Implied:
 *   a * S' = c * S
 *   b * S' + T' = c * T + d
 *
 * Got:
 *   S' = ( c / a ) * S
 *   T' = ( c * T ) - ( b * S' ) + d
 *      = ( c * T ) - b * ( ( c / a ) * S ) + d
 *      = ( c * T ) - ( b * ( c / a ) * S ) + d
 *
 * Verification:
 *   Y' = S' * X' + T'
 *      = ( ( c / a ) * S ) * ( a * X + b ) + ( ( c * T ) - ( b * ( c / a ) * S ) + d )
 *      = ( c * S * X ) + ( b * ( c / a ) * S ) + ( c * T ) - ( b * ( c / a ) * S ) + d
 *      = ( c * S * X ) + ( c * T ) + d
 *      = c * ( S * X + T ) + d
 *      = c * Y + d
 *
 * Problem:
 *   - Every element (not only every channel) needs have itself T' because T' depends on S.
 *     - This is difficult to be implemented.
 *
 *   - However, if the activation escaping could have only scale and withou translate (i.e. translate = 0 ), this issue could be
 *       reduced.
 *
 *   - This implies the output range of the activation function should include both negative and positive near the origin point.
 *     - So that only scale is enough to escape their non-linear part of these activation function.
 *
 *   - For example, sin(), tanh(), erf() are feasible.
 *     - But cos(), relu(), relu6(), sigmoid() are not feasible because their output always non-negative near the origin point.
 *         These functions always need bias (i.e. non-zero translate) to escape their non-linear part.
 *
 *

//!!! ...unfinished... (2022/01/06)

 *
 *
 * 2. What if this pointwise (or depthwise) does not have filter weights ( S1, S2, ... Ss )? (e.g avg/max pooling)
 *
 *
 *
 *
 *
 * 3. What if this pointwise (or depthwise) does not have bias weights T?
 *
 *
 *
 *
 */
<commit_msg>Update ActivationEscaping.js<commit_after>export { ScaleArraySet };

//!!! (2022/01/07 Remarked) deprecated.
//export { ScaleTranslateArraySet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ConvBiasActivation from "./ConvBiasActivation.js";

//!!! ...unfinished... (2022/01/07)

/**
 *
 * 1.
 *
 * Suppose
 *   - The previous convolution-bias-activation (i.e. pointwise or depthwise)
 *     - output channel count is q.
 *     - activation function is f()
 *     - activation escaping is ( scale = a, translate = b )
 *     - per output channel original is X  = ( x1 , x2 , ..., xq  )
 *     - per output channel modified is X' = ( x1', x2', ..., xq' ) = a * X + b
 *     - f(X') = f( a * X + b ) is guaranteed still kept linear although activation function f() is non-linear.
 *
 *   - This pointwise (or depthwise)
 *     - This pointwise input channel count is s. (Or, this depthwise filter size is s.)
 *     - output channel count is u.
 *     - per output channel filter weights are S = ( S1, S2, ..., Ss ) and will be modified to S' = ( S1', S2', ..., Ss' ).
 *     - per output channel bias weights are T = ( T1, T2, ..., Tu ) and will be modified to T' = ( T1', T2', ..., Tu' ).
 *     - activation function is g()
 *     - activation escaping is ( scale = c, translate = d )
 *     - per output channel original is Y  = ( y1 , y2 , ..., yu  ) = S  * X  + T
 *     - per output channel modified is Y' = ( y1', y2', ..., yu' ) = S' * X' + T' = c * Y + d
 *     - g(Y') = g( c * Y + d ) is guaranteed still kept linear although activation function g() is non-linear.
 *
 * Find out S' and T'.
 *
 * On one hand:
 *   Y' = S' * X' + T'
 *      = S' * ( a * X + b ) + T'
 *      = ( a * S' ) * X + ( b * S' + T' )
 *
 * On the other hand:
 *   Y' = c * Y + d
 *      = c * ( S * X + T ) + d
 *      = c * S * X + c * T + d
 *      = ( c * S ) * X + ( c * T + d )
 *
 * Implied:
 *   a * S' = c * S
 *   b * S' + T' = c * T + d
 *
 * Got:
 *   S' = ( c / a ) * S
 *   T' = ( c * T ) - ( b * S' ) + d
 *      = ( c * T ) - b * ( ( c / a ) * S ) + d
 *      = ( c * T ) - ( b * ( c / a ) * S ) + d
 *
 * Verification:
 *   Y' = S' * X' + T'
 *      = ( ( c / a ) * S ) * ( a * X + b ) + ( ( c * T ) - ( b * ( c / a ) * S ) + d )
 *      = ( c * S * X ) + ( b * ( c / a ) * S ) + ( c * T ) - ( b * ( c / a ) * S ) + d
 *      = ( c * S * X ) + ( c * T ) + d
 *      = c * ( S * X + T ) + d
 *      = c * Y + d
 *
 * Problem:
 *   - Every element (not only every channel) needs have itself T' because T' depends on S.
 *     - This is difficult to be implemented.
 *
 *   - However, if the activation escaping could have only scale and withou translate (i.e. translate = 0 ), this issue could be
 *       reduced.
 *
 *   - This implies the output range of the activation function should include both negative and positive near the origin point.
 *     - So that only scale is enough to escape their non-linear part of these activation function.
 *
 *   - For example, sin(), tanh(), erf() are feasible.
 *     - But cos(), relu(), relu6(), sigmoid() are not feasible because their output always non-negative near the origin point.
 *         These functions always need bias (i.e. non-zero translate) to escape their non-linear part.
 *
 *

//!!! ...unfinished... (2022/01/06)

 *
 *
 * 2. What if this pointwise (or depthwise) does not have filter weights ( S1, S2, ... Ss )? (e.g avg/max pooling)
 *
 *
 *
 *
 *
 * 3. What if this pointwise (or depthwise) does not have bias weights T?
 *
 *
 *
 *
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleArray} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleArray} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleArraySet {

//!!! ...unfinished... (2022/01/07) should be moved to class ScaleArray.
  /**
   *
   * @param {number} fromLower  The source bounds [ fromLower, fromUpper ]
   * @param {number} fromUpper  The source bounds [ fromLower, fromUpper ]
   * @param {number} toLower    The destination bounds [ toLower, toUpper ]
   * @param {number} toUpper    The destination bounds [ toLower, toUpper ]
   *
   * @return {number}
   *   Return a scale value which could let source bounds [ fromLower, fromUpper ] completely insides destination bounds [ toLower, toUpper ].
   * Return Number.NaN, if it is impossible to do that.
   */
  static calc_scale_by_fromLowerUpper_toLowerUpper( fromLower, fromUpper, toLower, toUpper ) {
    let srcLower = Math.min( fromLower, fromUpper ); // Confirm ( lower <= upper ).
    let srcUpper = Math.max( fromLower, fromUpper );
    let dstLower = Math.min( toLower, toUpper );
    let dstUpper = Math.max( toLower, toUpper );

    let scale;

    // 1. Try lower bound.
    //
    // Note:
    //  (  0 / x ) = 0
    //  ( -1 / 0 ) = -Infinity
    //  ( +1 / 0 ) = +Infinity
    //  (  0 / 0 ) = NaN
    //
    scale = dstLower / srcLower;

//!!! (2022/01/06 Remarked)
//     if ( ( srcLower == 0 ) || ( dstLower == 0 ) ) {
//       scale = 0; // Avoid -Infinity, +Infinity, NaN.
//     } else {
//       scale = dstLower / srcLower;
//     }

    // 1.2 Verification.
    //   - If scale is zero or -Infinity or +Infinity or NaN, it is always failed.
    //   - If scaled source upper bound is out of destination range, it is also failed.
    let adjustedUpper = srcUpper * scale;
    if ( ( scale == 0 ) || ( !Number.isFinite( scale ) ) || ( adjustedUpper < dstLower ) || ( adjustedUpper > dstUpper ) ) {

      // 2. Try upperer bound, since it is failed to fit [ srcLower, srcUpper ] into [ dstLower, dstUpper ] by scale according to lower bound.
      scale = dstUpper / srcUpper;

//!!! (2022/01/06 Remarked)
//       if ( ( srcUpper == 0 ) || ( dstUpper == 0 ) ) {
//         scale = 0; // Avoid -Infinity, +Infinity, NaN.
//       } else {
//         scale = dstUpper / srcUpper;
//       }

      // 2.2 Verification. If scale is zero, it is always failed. Otherwise, check it by lower side.
      //   - If scale is zero or -Infinity or +Infinity or NaN, it is always failed.
      //   - If scaled source lower bound is out of destination range, it is also failed.
      let adjustedLower = srcLower * scale;
      if ( ( scale == 0 ) || ( !Number.isFinite( scale ) ) || ( adjustedLower < dstLower ) || ( adjustedLower > dstUpper ) ) {
        // 3. It is impossible to fit [ srcLower, srcUpper ] into [ dstLower, dstUpper ] only by scale because all cases are tried and failed.
        scale = Number.NaN;
      }
    }

    return scale;
  }

}

//!!! (2022/01/07 Remarked) should be deprecated.
/**
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleTranslateArray} doWithoutPreviousUndo
 *   The scale-translate for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.BoundsArraySet.beforeActivation escape from activation function's non-linear domain into linear domain.
 *
 * @member {FloatValue.ScaleTranslateArray} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleTranslateArray} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleTranslateArraySet {

  constructor() {
    this.doWithoutPreviousUndo = new FloatValue.ScaleTranslateArray();
    this.do = new FloatValue.ScaleTranslateArray();
    this.undo = new FloatValue.ScaleTranslateArray();
  }

  clone() {
    let result = new ScaleTranslateArraySet();
    result.set_byScaleTranslateSet( this );
    return result;
  }

  /**
   * @param {ScaleTranslateArraySet} aScaleTranslateSet
   *   The ScaleTranslateSet to be copied.
   */
  set_byScaleTranslateArraySet( aScaleTranslateArraySet ) {
    this.doWithoutPreviousUndo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.doWithoutPreviousUndo );
    this.do.set_all_byScaleTranslateArray( aScaleTranslateArraySet.do );
    this.undo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.undo );
  }

  /** Reset all scale-translate values. Default is ( scale = 1, translate = 0 ) (i.e. no scale and no translate). */
  reset_by_scale_translate( scale = 1, translate = 0 ) {
    this.doWithoutPreviousUndo.set_all_by_scale_translate( scale , translate );
    this.do.set_all_by_scale_translate( scale , translate );
    this.undo.set_all_by_scale_translate( scale , translate );
  }

//!!! ...unfinished... (2021/12/26)
/**
 * - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
 *   (can not scale-translate). Unless, pad can be non-zero?
 *
 *   - Fortunately, this is not a problem for pass-through channels. Because the pass-through filter value is also 0 in the padded
 *       position, the result is not affected. (But its a big problem for non-pass-through channels.)
 *
 *   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
 *
 *     - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
 *
 *     - In our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
 *         already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
 *
 *     - However, this is not so good for pass-through channels. Because the pass-through filter value is 0 at the right-bottom
 *         corner, the image value will be destroyed.
 *
 * - If previous convolution does not have activation escaping do (i.e. is a normal convolution without pass-through)，this
 *     this convolution's .do should be different.
 *
 * - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
 *     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
 *
 * - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
 *     and their bounds. (The .do and .undo should also affect the value bounds.)
 *
 * - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
 *     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
 *
 */

  /**
   * Set the following properties:
   *   - this.doWithoutPreviousUndo
   *   - this.do
   *   - this.undo
   *
   * @param {ConvBiasActivation.BoundsArraySet} current_ConvBiasActivation_BoundsArraySet
   *   The BoundsArraySet of current convolution-bias-activation for calculating this.doWithoutPreviousUndo.
   *
   * @param {ScaleTranslateArraySet} previous_ActivationEscaping_ScaleTranslateArraySet
   *   The ActivationEscaping.ScaleTranslateArraySet of previous convolution-bias-activation for calculating this.do.
   */
  set_by_currentBoundsArraySet_previousActivationEscaping(
    current_ConvBiasActivation_BoundsArraySet, previous_ActivationEscaping_ScaleTranslateArraySet ) {

    // Calculate the scale-translate for escaping from activation function's non-linear domain into linear domain.
    //
    // Note: This does not work for avg/max pooling.
    this.doWithoutPreviousUndo.set_all_by_fromBoundsArray_ToBoundsArray(
      current_ConvBiasActivation_BoundsArraySet.beforeActivation, current_ConvBiasActivation_BoundsArraySet.output );

    // Combine undoing previous activation escaping scale-translate and doing current activation escaping scale-translate.
    this.do.set_all_byScaleTranslateArray( previous_ActivationEscaping_ScaleTranslateArraySet.undo );
    this.do.scaleTranslate_all_byScaleTranslateArray( this.doWithoutPreviousUndo );

    // Prepare the undoing scale-translate for the next convolution-bias-activation.
    this.undo.set_all_byUndo_ScaleTranslateArray( this.do );
  }

}
<|endoftext|>


<commit_before>export { Params };

import * as Pool from "../../util/Pool.js";
//import * as Recyclable from "../../util/Recyclable.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as ParamDesc from "../../Unpacker/ParamDesc.js";
import * as Weights from "../../Unpacker/Weights.js";
import * as Depthwise from "../Depthwise.js";
import { InferencedParams } from "./Block_InferencedParams.js";

//!!! ...unfinished... (2022/05/28)
//
// Add parameter bPointwise2SqueezeExcitationAtStageEnd control whether this stage's final block's pointwise2 should have
// squeeze-and-excitation.
//

/**
 * Convolution stage parameters.
 *
 * @member {InferencedParams} inferencedParams
 *   The inferenced parameters of this stage parameters.
 *
 */
class Params extends Weights.Params {

  /**
   * Used as default Stage.Params provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Stage.Params.Pool", Params, Params.setAsConstructor );

  /**
   * If a parameter's value is null, it will be extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {number} sourceHeight
   *   The height of the source image which will be processed by apply(). If null, it will be extracted from
   * inputWeightArray (i.e. by evolution).
   *
   * @param {number} sourceWidth
   *   The width of the source image which will be processed by apply(). If null, it will be extracted from
   * inputWeightArray (i.e. by evolution).
   *
   * @param {number} sourceChannelCount
   *   The depth (channel count) of the source image. It may be the output channel count of the previous convolution stage, so
   * it could be large. If null, it will be extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {number} nConvStageTypeId
   *   The convolution stage type (ValueDesc.ConvStageType.Singleton.Ids.Xxx). If null, it will be extracted from inputWeightArray
   * (i.e. by evolution).
   *

//!!! ...unfinished... (2022/06/25)
// Perhaps, just like the ( height, width ) halving and channel count doubling by evey stage,
// the block count could be doubled by evey stage.

   * @param {number} blockCountRequested
   *   How many blocks inside this stage are wanted.
   *   - If null, it will be extracted from inputWeightArray (i.e. by evolution).
   *   - It must be ( >= 2 ). Because this stage will use one tf.depthwiseConv2d( strides = 2, pad = "same" ) to shrink
   *       (i.e. to halve height x width) and use ( blockCountRequested - 1 ) times tf.depthwiseConv2d( strides = 1, pad = "same" )
   *       until the stage end. These can not be achieved by only one block. So there is at least two blocks.
   *
   * @param {boolean} bPointwise1
   *   If false, there will be no pointwise1 (i.e. the 1st pointwise convolution).If null, it will be extracted from inputWeightArray
   * (i.e. by evolution).
   *
   * @param {number} depthwiseFilterHeight
   *   The height of depthwise convolution's filter. At least 1 (so that 1D data could be processed). If null, it will be extracted
   * from inputWeightArray (i.e. by evolution).
   *
   * @param {number} depthwiseFilterWidth
   *   The width of depthwise convolution's filter. At least 2 (so that meaningless ( 1 * 1 ) could be avoided). If null, it will
   * be extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {boolean} bPointwise2ActivatedAtStageEnd
   *   If true, the blockLast's pointwise2 will have activation function. If false, the blockLast's pointwise2 will have no activation
   * function. If null, it will be extracted from inputWeightArray (i.e. by evolution).
   *
   *   - If there will be next stage after this stage, it usually should be true (i.e. has activation function). The reason is that
   *       this stage's blockLast's pointwise2 (if has no activation function) and the next stage's block0's pointwise1 are essentially
   *       just one pointwise convolution. (Multiple affine transformations can always be combined into just one affine transformation.)
   *
   *     - There is one exception: for MobileNetV2_Xxx, its pointwise2 always has no activation function no matter whether
   *         bPointwise2ActivatedAtStageEnd is true or false. The Reason is that it has add-input-to-output to modify pointwise2
   *         output. So, even if its stage output is not affine transformation (even it has no activation function).
   *
   *   - Only when this stage is the last stage of a neural network, it should be false (i.e. has no activation function). There are
   *       two reasons:
   *
   *     - For ShuffleNetV2_ByMobileNetV1, let it undo activation escaping scales.
   *         In ShuffleNetV2_ByMobileNetV1, if an operation has activation function, the pass-through half part will scale its
   *         convolution filters for escaping the activation function's non-linear parts. This results in its output is wrong
   *         (i.e. different from ShuffleNetV2). In order to resolve this issue, the last operation (i.e. pointwise2) should have
   *         no activation (so it will not scale its convolution filters for escaping the activation function's non-linear parts).
   *
   *     - Even if not ShuffleNetV2_ByMobileNetV1 (i.e. for other ConvStageType), it does have practical advantage in fact. The
   *         output could have any value (i.e. the whole number line). If the last operation (i.e. pointwise2) has activation
   *         function, the output value will be restricted by the activation function (e.g. [ -1, +1 ] for tanh()).
   *
   *
   * @param {number} nSqueezeExcitationChannelCountDivisor
   *   An integer represents the channel count divisor for squeeze-and-excitation's intermediate pointwise convolution channel count.
   * (ValueDesc.SqueezeExcitationChannelCountDivisor.Singleton.Ids.Xx)
   *
   * @param {number} nActivationId
   *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) after every convolution. If null, it will be
   * extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {boolean} bKeepInputTensor
   *   If true, apply() will not dispose inputTensor (i.e. will be kept). If null, it will be extracted from
   * inputWeightArray (i.e. by evolution).
   *
   * @return {boolean}
   *   Return false, if initialization failed.
   *
   * @override
   */
  constructor(
    sourceHeight, sourceWidth, sourceChannelCount,
    nConvStageTypeId,
    blockCountRequested,
    bPointwise1,
    depthwiseFilterHeight, depthwiseFilterWidth,
    bPointwise2ActivatedAtStageEnd,
    nSqueezeExcitationChannelCountDivisor,
    nActivationId,
    bKeepInputTensor
  ) {

    // Q: Why the depthwiseChannelMultiplierBlock0 is not listed as a parameter?
    // A: After considering the following reasons, it is worth to drop this parameter.
    //
    //   - In reality, it is almost no reason to use only avg/max pooling to compose a stage because it keep too little information
    //     for the next stage.
    //
    //   - If depthwiseChannelMultiplierBlock0 is specified as Params.depthwiseChannelMultiplierBlock0.valueDesc.Ids.NONE (0), the input
    //     image will not be shrinked a little (for ( blockCountRequested <= 1 )) or will not be halven (for ( blockCountRequested >= 2 ).
    //     If it is still a parameter it should be forced to 1 at least (always needs depthwise operation) in this case.
    //

    super(
      Params.SequenceArray,
      sourceHeight, sourceWidth, sourceChannelCount,
      nConvStageTypeId,
      blockCountRequested,
      bPointwise1,
      depthwiseFilterHeight, depthwiseFilterWidth,
      bPointwise2ActivatedAtStageEnd,
      nSqueezeExcitationChannelCountDivisor,
      nActivationId,
      bKeepInputTensor
    );
    Params.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor(
    sourceHeight, sourceWidth, sourceChannelCount,
    nConvStageTypeId,
    blockCountRequested,
    bPointwise1,
    depthwiseFilterHeight, depthwiseFilterWidth,
    bPointwise2ActivatedAtStageEnd,
    nSqueezeExcitationChannelCountDivisor,
    nActivationId,
    bKeepInputTensor
  ) {
    super.setAsConstructor(
      Params.SequenceArray,
      sourceHeight, sourceWidth, sourceChannelCount,
      nConvStageTypeId,
      blockCountRequested,
      bPointwise1,
      depthwiseFilterHeight, depthwiseFilterWidth,
      bPointwise2ActivatedAtStageEnd,
      nSqueezeExcitationChannelCountDivisor,
      nActivationId,
      bKeepInputTensor
    );
    Params.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    // Do nothing.
  }

  /** @override */
  disposeResources() {
    this.InferencedParams_dispose();
    super.disposeResources();
  }

  /** Release .inferencedParams */
  InferencedParams_dispose() {
    if ( this.inferencedParams ) {
      this.inferencedParams.disposeResources_and_recycleToPool();
      this.inferencedParams = null;
    }
  }

  /**
   * Extract parameters from inputWeightArray.
   *
   * @return {boolean} Return false, if extraction failed.
   *
   * @override
   */
  init( inputWeightArray, elementOffsetBegin ) {
    let bExtractOk = super.init( inputWeightArray, elementOffsetBegin );
    if ( !bExtractOk )
      return false;

    this.InferencedParams_dispose();

    this.inferencedParams = InferencedParams.Pool.get_or_create_by(
      this.sourceHeight, this.sourceWidth,
      this.nConvStageTypeId,
      this.blockCountRequested,
      this.depthwiseFilterHeight, this.depthwiseFilterWidth
    );

    return bExtractOk;
  }

  get sourceHeight()              { return this.getParamValue_byParamDesc( Params.sourceHeight ); }
  get sourceWidth()               { return this.getParamValue_byParamDesc( Params.sourceWidth ); }
  get sourceChannelCount()        { return this.getParamValue_byParamDesc( Params.sourceChannelCount ); }

  /** @return {number} The number version of nConvStageTypeId. */
  get nConvStageTypeId()          { return this.getParamValue_byParamDesc( Params.nConvStageTypeId ); }

  /** @return {string} The string version of nConvStageTypeId. */
  get nConvStageTypeName()        { return Params.nConvStageTypeId.getStringOfValue( this.nConvStageTypeId ); }

  get blockCountRequested()       { return this.getParamValue_byParamDesc( Params.blockCountRequested ); }
  get bPointwise1()               { return this.getParamValue_byParamDesc( Params.bPointwise1 ); }

  get depthwiseFilterHeight()     { return this.getParamValue_byParamDesc( Params.depthwiseFilterHeight ); }
  get depthwiseFilterWidth()      { return this.getParamValue_byParamDesc( Params.depthwiseFilterWidth ); }

  get bPointwise2ActivatedAtStageEnd() { return this.getParamValue_byParamDesc( Params.bPointwise2ActivatedAtStageEnd ); }

  get nSqueezeExcitationChannelCountDivisor()     { return this.getParamValue_byParamDesc( Params.nSqueezeExcitationChannelCountDivisor ); }
  get nSqueezeExcitationChannelCountDivisorName() {
    return Params.nSqueezeExcitationChannelCountDivisor.getStringOfValue( this.nSqueezeExcitationChannelCountDivisor );
  }

  //!!! (2022/07/14 Remarked) Stage.BlockParamsCreator will determine it.
  //get bSqueezeExcitationPrefix()  { return this.getParamValue_byParamDesc( Params.bSqueezeExcitationPrefix ); }

  get nActivationId()             { return this.getParamValue_byParamDesc( Params.nActivationId ); }
  get nActivationName()           { return Params.nActivationId.getStringOfValue( this.nActivationId ); }

  get bKeepInputTensor()          { return this.getParamValue_byParamDesc( Params.bKeepInputTensor ); }
}

// Define parameter descriptions.
Params.sourceHeight =                   new ParamDesc.Int(                "sourceHeight",               1, ( 10 * 1024 ) );
Params.sourceWidth =                    new ParamDesc.Int(                "sourceWidth",                1, ( 10 * 1024 ) );
Params.sourceChannelCount =             new ParamDesc.Int(                "sourceChannelCount",         1, ( 10 * 1024 ) );

Params.nConvStageTypeId =               new ParamDesc.ConvStageType(      "nConvStageTypeId" );

Params.blockCountRequested =            new ParamDesc.Int(                "blockCountRequested",        2, (  1 * 1024 ) );
Params.bPointwise1 =                    new ParamDesc.Bool(               "bPointwise1" );
Params.depthwiseFilterHeight =          new ParamDesc.Int(                "depthwiseFilterHeight",      1, ( 10 * 1024 ) );
Params.depthwiseFilterWidth =           new ParamDesc.Int(                "depthwiseFilterWidth",       2, ( 10 * 1024 ) );
Params.bPointwise2ActivatedAtStageEnd = new ParamDesc.Bool(               "bPointwise2ActivatedAtStageEnd" );

Params.nSqueezeExcitationChannelCountDivisor = new ParamDesc.SqueezeExcitationChannelCountDivisor( "nSqueezeExcitationChannelCountDivisor" );
//Params.bSqueezeExcitationPrefix =       new ParamDesc.Bool(               "bSqueezeExcitationPrefix" );

Params.nActivationId =                  new ParamDesc.ActivationFunction( "nActivationId" );

Params.bKeepInputTensor =               new ParamDesc.Bool(               "bKeepInputTensor" );

/**
 * Define the order of these parameters. (Fills ParamDesc.Xxx.seqId according to this array's order.)
 */
Params.SequenceArray = new ParamDesc.SequenceArray( [
  Params.sourceHeight,
  Params.sourceWidth,
  Params.sourceChannelCount,
  Params.nConvStageTypeId,
  Params.blockCountRequested,
  Params.bPointwise1,
  Params.depthwiseFilterHeight,
  Params.depthwiseFilterWidth,
  Params.bPointwise2ActivatedAtStageEnd,
  Params.nSqueezeExcitationChannelCountDivisor,
  Params.nActivationId,
  Params.bKeepInputTensor,
] );

<commit_msg>Stage_InferencedParams<commit_after>export { Params };

import * as Pool from "../../util/Pool.js";
//import * as Recyclable from "../../util/Recyclable.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as ParamDesc from "../../Unpacker/ParamDesc.js";
import * as Weights from "../../Unpacker/Weights.js";
import * as Depthwise from "../Depthwise.js";
import { InferencedParams } from "./Stage_InferencedParams.js";

//!!! ...unfinished... (2022/05/28)
//
// Add parameter bPointwise2SqueezeExcitationAtStageEnd control whether this stage's final block's pointwise2 should have
// squeeze-and-excitation.
//

/**
 * Convolution stage parameters.
 *
 * @member {InferencedParams} inferencedParams
 *   The inferenced parameters of this stage parameters.
 *
 */
class Params extends Weights.Params {

  /**
   * Used as default Stage.Params provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Stage.Params.Pool", Params, Params.setAsConstructor );

  /**
   * If a parameter's value is null, it will be extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {number} sourceHeight
   *   The height of the source image which will be processed by apply(). If null, it will be extracted from
   * inputWeightArray (i.e. by evolution).
   *
   * @param {number} sourceWidth
   *   The width of the source image which will be processed by apply(). If null, it will be extracted from
   * inputWeightArray (i.e. by evolution).
   *
   * @param {number} sourceChannelCount
   *   The depth (channel count) of the source image. It may be the output channel count of the previous convolution stage, so
   * it could be large. If null, it will be extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {number} nConvStageTypeId
   *   The convolution stage type (ValueDesc.ConvStageType.Singleton.Ids.Xxx). If null, it will be extracted from inputWeightArray
   * (i.e. by evolution).
   *

//!!! ...unfinished... (2022/06/25)
// Perhaps, just like the ( height, width ) halving and channel count doubling by evey stage,
// the block count could be doubled by evey stage.

   * @param {number} blockCountRequested
   *   How many blocks inside this stage are wanted.
   *   - If null, it will be extracted from inputWeightArray (i.e. by evolution).
   *   - It must be ( >= 2 ). Because this stage will use one tf.depthwiseConv2d( strides = 2, pad = "same" ) to shrink
   *       (i.e. to halve height x width) and use ( blockCountRequested - 1 ) times tf.depthwiseConv2d( strides = 1, pad = "same" )
   *       until the stage end. These can not be achieved by only one block. So there is at least two blocks.
   *
   * @param {boolean} bPointwise1
   *   If false, there will be no pointwise1 (i.e. the 1st pointwise convolution).If null, it will be extracted from inputWeightArray
   * (i.e. by evolution).
   *
   * @param {number} depthwiseFilterHeight
   *   The height of depthwise convolution's filter. At least 1 (so that 1D data could be processed). If null, it will be extracted
   * from inputWeightArray (i.e. by evolution).
   *
   * @param {number} depthwiseFilterWidth
   *   The width of depthwise convolution's filter. At least 2 (so that meaningless ( 1 * 1 ) could be avoided). If null, it will
   * be extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {boolean} bPointwise2ActivatedAtStageEnd
   *   If true, the blockLast's pointwise2 will have activation function. If false, the blockLast's pointwise2 will have no activation
   * function. If null, it will be extracted from inputWeightArray (i.e. by evolution).
   *
   *   - If there will be next stage after this stage, it usually should be true (i.e. has activation function). The reason is that
   *       this stage's blockLast's pointwise2 (if has no activation function) and the next stage's block0's pointwise1 are essentially
   *       just one pointwise convolution. (Multiple affine transformations can always be combined into just one affine transformation.)
   *
   *     - There is one exception: for MobileNetV2_Xxx, its pointwise2 always has no activation function no matter whether
   *         bPointwise2ActivatedAtStageEnd is true or false. The Reason is that it has add-input-to-output to modify pointwise2
   *         output. So, even if its stage output is not affine transformation (even it has no activation function).
   *
   *   - Only when this stage is the last stage of a neural network, it should be false (i.e. has no activation function). There are
   *       two reasons:
   *
   *     - For ShuffleNetV2_ByMobileNetV1, let it undo activation escaping scales.
   *         In ShuffleNetV2_ByMobileNetV1, if an operation has activation function, the pass-through half part will scale its
   *         convolution filters for escaping the activation function's non-linear parts. This results in its output is wrong
   *         (i.e. different from ShuffleNetV2). In order to resolve this issue, the last operation (i.e. pointwise2) should have
   *         no activation (so it will not scale its convolution filters for escaping the activation function's non-linear parts).
   *
   *     - Even if not ShuffleNetV2_ByMobileNetV1 (i.e. for other ConvStageType), it does have practical advantage in fact. The
   *         output could have any value (i.e. the whole number line). If the last operation (i.e. pointwise2) has activation
   *         function, the output value will be restricted by the activation function (e.g. [ -1, +1 ] for tanh()).
   *
   *
   * @param {number} nSqueezeExcitationChannelCountDivisor
   *   An integer represents the channel count divisor for squeeze-and-excitation's intermediate pointwise convolution channel count.
   * (ValueDesc.SqueezeExcitationChannelCountDivisor.Singleton.Ids.Xx)
   *
   * @param {number} nActivationId
   *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) after every convolution. If null, it will be
   * extracted from inputWeightArray (i.e. by evolution).
   *
   * @param {boolean} bKeepInputTensor
   *   If true, apply() will not dispose inputTensor (i.e. will be kept). If null, it will be extracted from
   * inputWeightArray (i.e. by evolution).
   *
   * @return {boolean}
   *   Return false, if initialization failed.
   *
   * @override
   */
  constructor(
    sourceHeight, sourceWidth, sourceChannelCount,
    nConvStageTypeId,
    blockCountRequested,
    bPointwise1,
    depthwiseFilterHeight, depthwiseFilterWidth,
    bPointwise2ActivatedAtStageEnd,
    nSqueezeExcitationChannelCountDivisor,
    nActivationId,
    bKeepInputTensor
  ) {

    // Q: Why the depthwiseChannelMultiplierBlock0 is not listed as a parameter?
    // A: After considering the following reasons, it is worth to drop this parameter.
    //
    //   - In reality, it is almost no reason to use only avg/max pooling to compose a stage because it keep too little information
    //     for the next stage.
    //
    //   - If depthwiseChannelMultiplierBlock0 is specified as Params.depthwiseChannelMultiplierBlock0.valueDesc.Ids.NONE (0), the input
    //     image will not be shrinked a little (for ( blockCountRequested <= 1 )) or will not be halven (for ( blockCountRequested >= 2 ).
    //     If it is still a parameter it should be forced to 1 at least (always needs depthwise operation) in this case.
    //

    super(
      Params.SequenceArray,
      sourceHeight, sourceWidth, sourceChannelCount,
      nConvStageTypeId,
      blockCountRequested,
      bPointwise1,
      depthwiseFilterHeight, depthwiseFilterWidth,
      bPointwise2ActivatedAtStageEnd,
      nSqueezeExcitationChannelCountDivisor,
      nActivationId,
      bKeepInputTensor
    );
    Params.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor(
    sourceHeight, sourceWidth, sourceChannelCount,
    nConvStageTypeId,
    blockCountRequested,
    bPointwise1,
    depthwiseFilterHeight, depthwiseFilterWidth,
    bPointwise2ActivatedAtStageEnd,
    nSqueezeExcitationChannelCountDivisor,
    nActivationId,
    bKeepInputTensor
  ) {
    super.setAsConstructor(
      Params.SequenceArray,
      sourceHeight, sourceWidth, sourceChannelCount,
      nConvStageTypeId,
      blockCountRequested,
      bPointwise1,
      depthwiseFilterHeight, depthwiseFilterWidth,
      bPointwise2ActivatedAtStageEnd,
      nSqueezeExcitationChannelCountDivisor,
      nActivationId,
      bKeepInputTensor
    );
    Params.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    // Do nothing.
  }

  /** @override */
  disposeResources() {
    this.InferencedParams_dispose();
    super.disposeResources();
  }

  /** Release .inferencedParams */
  InferencedParams_dispose() {
    if ( this.inferencedParams ) {
      this.inferencedParams.disposeResources_and_recycleToPool();
      this.inferencedParams = null;
    }
  }

  /**
   * Extract parameters from inputWeightArray.
   *
   * @return {boolean} Return false, if extraction failed.
   *
   * @override
   */
  init( inputWeightArray, elementOffsetBegin ) {
    let bExtractOk = super.init( inputWeightArray, elementOffsetBegin );
    if ( !bExtractOk )
      return false;

    this.InferencedParams_dispose();

    this.inferencedParams = InferencedParams.Pool.get_or_create_by(
      this.sourceHeight, this.sourceWidth,
      this.nConvStageTypeId,
      this.blockCountRequested,
      this.depthwiseFilterHeight, this.depthwiseFilterWidth
    );

    return bExtractOk;
  }

  get sourceHeight()              { return this.getParamValue_byParamDesc( Params.sourceHeight ); }
  get sourceWidth()               { return this.getParamValue_byParamDesc( Params.sourceWidth ); }
  get sourceChannelCount()        { return this.getParamValue_byParamDesc( Params.sourceChannelCount ); }

  /** @return {number} The number version of nConvStageTypeId. */
  get nConvStageTypeId()          { return this.getParamValue_byParamDesc( Params.nConvStageTypeId ); }

  /** @return {string} The string version of nConvStageTypeId. */
  get nConvStageTypeName()        { return Params.nConvStageTypeId.getStringOfValue( this.nConvStageTypeId ); }

  get blockCountRequested()       { return this.getParamValue_byParamDesc( Params.blockCountRequested ); }
  get bPointwise1()               { return this.getParamValue_byParamDesc( Params.bPointwise1 ); }

  get depthwiseFilterHeight()     { return this.getParamValue_byParamDesc( Params.depthwiseFilterHeight ); }
  get depthwiseFilterWidth()      { return this.getParamValue_byParamDesc( Params.depthwiseFilterWidth ); }

  get bPointwise2ActivatedAtStageEnd() { return this.getParamValue_byParamDesc( Params.bPointwise2ActivatedAtStageEnd ); }

  get nSqueezeExcitationChannelCountDivisor()     { return this.getParamValue_byParamDesc( Params.nSqueezeExcitationChannelCountDivisor ); }
  get nSqueezeExcitationChannelCountDivisorName() {
    return Params.nSqueezeExcitationChannelCountDivisor.getStringOfValue( this.nSqueezeExcitationChannelCountDivisor );
  }

  //!!! (2022/07/14 Remarked) Stage.BlockParamsCreator will determine it.
  //get bSqueezeExcitationPrefix()  { return this.getParamValue_byParamDesc( Params.bSqueezeExcitationPrefix ); }

  get nActivationId()             { return this.getParamValue_byParamDesc( Params.nActivationId ); }
  get nActivationName()           { return Params.nActivationId.getStringOfValue( this.nActivationId ); }

  get bKeepInputTensor()          { return this.getParamValue_byParamDesc( Params.bKeepInputTensor ); }
}

// Define parameter descriptions.
Params.sourceHeight =                   new ParamDesc.Int(                "sourceHeight",               1, ( 10 * 1024 ) );
Params.sourceWidth =                    new ParamDesc.Int(                "sourceWidth",                1, ( 10 * 1024 ) );
Params.sourceChannelCount =             new ParamDesc.Int(                "sourceChannelCount",         1, ( 10 * 1024 ) );

Params.nConvStageTypeId =               new ParamDesc.ConvStageType(      "nConvStageTypeId" );

Params.blockCountRequested =            new ParamDesc.Int(                "blockCountRequested",        2, (  1 * 1024 ) );
Params.bPointwise1 =                    new ParamDesc.Bool(               "bPointwise1" );
Params.depthwiseFilterHeight =          new ParamDesc.Int(                "depthwiseFilterHeight",      1, ( 10 * 1024 ) );
Params.depthwiseFilterWidth =           new ParamDesc.Int(                "depthwiseFilterWidth",       2, ( 10 * 1024 ) );
Params.bPointwise2ActivatedAtStageEnd = new ParamDesc.Bool(               "bPointwise2ActivatedAtStageEnd" );

Params.nSqueezeExcitationChannelCountDivisor = new ParamDesc.SqueezeExcitationChannelCountDivisor( "nSqueezeExcitationChannelCountDivisor" );
//Params.bSqueezeExcitationPrefix =       new ParamDesc.Bool(               "bSqueezeExcitationPrefix" );

Params.nActivationId =                  new ParamDesc.ActivationFunction( "nActivationId" );

Params.bKeepInputTensor =               new ParamDesc.Bool(               "bKeepInputTensor" );

/**
 * Define the order of these parameters. (Fills ParamDesc.Xxx.seqId according to this array's order.)
 */
Params.SequenceArray = new ParamDesc.SequenceArray( [
  Params.sourceHeight,
  Params.sourceWidth,
  Params.sourceChannelCount,
  Params.nConvStageTypeId,
  Params.blockCountRequested,
  Params.bPointwise1,
  Params.depthwiseFilterHeight,
  Params.depthwiseFilterWidth,
  Params.bPointwise2ActivatedAtStageEnd,
  Params.nSqueezeExcitationChannelCountDivisor,
  Params.nActivationId,
  Params.bKeepInputTensor,
] );

<|endoftext|>