lib/AxisSetTraverser.js

'use strict';
import * as Utils from './Utils';
/**
 * Manages an {@link AxisSet}.
 * Performs the selection of hyperparameter values during the grid search.
 */
class AxisSetTraverser {
    /**
     * Creates an instance of AxisSetTraverser.
     * @param {AxisSet} _axisSet The collection of {@link Axis} that define the
     *                           grid (parameter space) to be searched. Each
     *                           axis is associated with one hyperparameter.
     */
    constructor(_axisSet) {
        this._axisSet = _axisSet;
        this._totalAxes = 0;
        this._traversed = false;
        this._iterationDescriptorsByIndex = {};
        this._totalIterations = 0;
        // save off our axis count, which never changes
        this._totalAxes = this._axisSet.GetTotalAxes();
        this._traversed = false;
        //NOTE: This function is buried in the c'tor because it actually runs the
        //      traversal (advances the axes).
        //		It cleans up after itself, resetting to a pristine state on the way out.
        this._iterationDescriptorsByIndex = {};
        this._totalIterations = 0;
        const BUILD_SCHEDULE = () => {
            let reportText = '';
            for (let i = 0; !this._traversed; ++i) {
                ++this._totalIterations;
                const DESCRIPTOR = this.WriteReport(true); // compact report
                this._iterationDescriptorsByIndex[i] = DESCRIPTOR;
                //TODO: (maybe) add a "x3" suffix to this printout; needs 'repetitions' from the
                //      owner.
                //      It could make more sense to factor this out of the constructor. The urge
                //      to protect the 'running' state seems misplaced.
                reportText += DESCRIPTOR + '\n';
                this.Advance();
            }
            console.log('iteration schedule:' + '\n' + reportText);
            // reset for traversals managed by the owner
            this._traversed = false;
        };
        BUILD_SCHEDULE();
    }
    get totalIterations() { return this._totalIterations; }
    get traversed() { return this._traversed; }
    /**
     * Iterates the grid. Called after each 'cell' (i.e. unique
     * combination of hyperparameters) is trained and tested. Once all axes
     * are complete, we mark ourselves as traversed (or done).
     */
    Advance() {
        Utils.Assert(!this._traversed);
        let resetCounter = 0;
        for (let i = 0; i < this._totalAxes; ++i) {
            this._axisSet.AdvanceAxis(i);
            if (this._axisSet.CheckAxisComplete(i)) {
                this._axisSet.ResetAxis(i);
                //KEEP: This is useful, but currently too spammy (without digging out the axis
                //      name, it's also too vague). There's a decent chance this comes back
                //      after the first round of user feedback.
                //              console.log('Axis ' + i + ' reset');
                ++resetCounter;
                continue;
            }
            return;
        }
        Utils.Assert(resetCounter === this._totalAxes);
        // this advance completed the traversal; reset
        this._traversed = true;
    }
    /**
     * Produces a simple key: value mapping of the axes current positions,
     * keyed by their names.
     * @return {StringKeyedNumbersObject}
     */
    CreateIterationParams() {
        return this._axisSet.CreateParams();
    }
    /**
     * Traverses the axis set, invoking a callback with the name of each axis.
     * @param {function(string): void} callback The function to be invoked.
     */
    ExamineAxisNames(callback) {
        this._axisSet.Walk((axis) => {
            callback(axis.typeName);
        });
    }
    /**
     * Gets the descriptor for a requested axis. We build these simple strings
     * in the constructor.
     * @param {number} index The axis to be described.
     * @return {string}
     */
    LookupIterationDescriptor(index) {
        if (this._iterationDescriptorsByIndex[index] === undefined) {
            throw new Error('Attempted to lookup descriptor for unknown '
                + 'iteration: ' + index);
        }
        return this._iterationDescriptorsByIndex[index];
    }
    /**
     * Gets a combined report of every axis' status.
     * @param {boolean} compact Whether to get a detailed report.
     * @return {string}
     */
    WriteReport(compact) {
        let reportText = '';
        for (let i = 0; i < this._totalAxes; ++i) {
            reportText += (compact ? '' : '• ')
                + this._axisSet.WriteAxisReport(i, compact)
                + (compact ? ', ' : '\n');
        }
        // remove the trailing newline or comma-and-space
        reportText = reportText.slice(0, (compact ? -2 : -1));
        return reportText;
    }
}
Object.freeze(AxisSetTraverser);
export { AxisSetTraverser };
//# sourceMappingURL=AxisSetTraverser.js.map