Phase 2 — Pinning + History: - snapshot-stack.js: ring buffer (20 max) with auto-snapshot on train/randomize/thumbs-down, multi-level undo, tagged entries - ab-compare.js: A/B weight state comparison with capture/toggle/accept/revert - region-pin.js: pin rectangular input-space regions (Approach A: example pinning), pinned examples always included in training - param-pin.js: per-output pin flags, pin mask skips pinned nodes in moveWeights - phase2-ui.js: undo button with history popup, A/B toggle, long-press region pin, double-tap param pin - Modified mlp.js/iml.js/nisps-wasm.js to accept outputPinMask in moveWeights Phase 3 — Input Refinement + Exploration: - pressure-feedback.js: touch force + hold duration → intensity multiplier - auto-explore.js: automated thumbs-down at configurable interval, zoom-scaled - input-heatmap.js: 16×16 MLP sampling, 3 color modes (luminance/variance/ divergence), zoom-aware resampling, offscreen canvas rendering - phase3-ui.js: auto-explore toggle with progress ring, heatmap eye icon, pressure indicators, settings drawer section - joy-map-enhanced.js: added setHeatmap() for background layer rendering Phase 4 — Output Pipeline + Visualization + Polish: - output-pipeline.js: global curve → smoothing → slew rate → freeze gate - weight-health.js: weight magnitude histogram, dead/saturating/healthy status - gradient-flow.js: per-layer weight-delta analysis, vanishing/exploding detection - session-presets.js: save/load full state, URL sharing via compact params - phase4-ui.js: freeze button, network health panel, session preset UI All phases merged into a-app.js with proper integration: auto-snapshots, pressure-modulated RL, heatmap triggers, output pipeline in routeOutputs, gradient capture around training, persistence for all new state.
160 lines
4 KiB
JavaScript
160 lines
4 KiB
JavaScript
/**
|
|
* Auto-Explore — automated thumbs-down at regular intervals.
|
|
*
|
|
* "Wander" mode for weight space: the system drifts continuously while
|
|
* the user only gives thumbs-up when it lands on something good.
|
|
*
|
|
* Respects noise cap, spread, zoom-aware feedback — same rules as manual
|
|
* thumbs-down. Intensity scales with zoom: zoomed in = gentler steps,
|
|
* zoomed out = bigger leaps.
|
|
*
|
|
* @module auto-explore
|
|
*/
|
|
|
|
// ---- Constants ----
|
|
const MIN_INTERVAL = 0.5; // seconds
|
|
const MAX_INTERVAL = 10; // seconds
|
|
const DEFAULT_INTERVAL = 2; // seconds
|
|
const MIN_INTENSITY = 0.1;
|
|
const MAX_INTENSITY = 1.0;
|
|
const DEFAULT_INTENSITY = 0.5;
|
|
|
|
export class AutoExplore {
|
|
/**
|
|
* @param {object} [options]
|
|
* @param {number} [options.interval=2] - seconds between auto-steps
|
|
* @param {number} [options.intensity=0.5] - 0.1-1.0, scales noise per step
|
|
*/
|
|
constructor(options = {}) {
|
|
this._interval = clamp(options.interval ?? DEFAULT_INTERVAL, MIN_INTERVAL, MAX_INTERVAL);
|
|
this._intensity = clamp(options.intensity ?? DEFAULT_INTENSITY, MIN_INTENSITY, MAX_INTENSITY);
|
|
this._active = false;
|
|
this._elapsed = 0; // seconds since last auto-step
|
|
this._callback = null;
|
|
}
|
|
|
|
// ---- Control ----
|
|
|
|
/** Start auto-exploration. */
|
|
start() {
|
|
this._active = true;
|
|
this._elapsed = 0;
|
|
}
|
|
|
|
/** Stop auto-exploration. */
|
|
stop() {
|
|
this._active = false;
|
|
this._elapsed = 0;
|
|
}
|
|
|
|
/** Toggle on/off. */
|
|
toggle() {
|
|
if (this._active) this.stop();
|
|
else this.start();
|
|
}
|
|
|
|
/** Whether auto-explore is currently running. */
|
|
get active() {
|
|
return this._active;
|
|
}
|
|
|
|
// ---- Configuration ----
|
|
|
|
/**
|
|
* Set interval between auto-steps.
|
|
* @param {number} seconds - 0.5 to 10
|
|
*/
|
|
setInterval(seconds) {
|
|
this._interval = clamp(seconds, MIN_INTERVAL, MAX_INTERVAL);
|
|
}
|
|
|
|
/** @returns {number} */
|
|
getInterval() { return this._interval; }
|
|
|
|
/**
|
|
* Set exploration intensity.
|
|
* @param {number} intensity - 0.1 to 1.0
|
|
*/
|
|
setIntensity(intensity) {
|
|
this._intensity = clamp(intensity, MIN_INTENSITY, MAX_INTENSITY);
|
|
}
|
|
|
|
/** @returns {number} */
|
|
getIntensity() { return this._intensity; }
|
|
|
|
// ---- Tick (call every frame) ----
|
|
|
|
/**
|
|
* Advance the auto-explore timer. When the interval elapses, fires
|
|
* the registered callback with { intensity, zoomLevel }.
|
|
*
|
|
* @param {number} deltaTime - seconds since last frame
|
|
* @param {number} [zoomLevel=1.0] - current zoom level (0-1)
|
|
*/
|
|
tick(deltaTime, zoomLevel = 1.0) {
|
|
if (!this._active) return;
|
|
if (deltaTime <= 0) return;
|
|
|
|
this._elapsed += deltaTime;
|
|
|
|
if (this._elapsed >= this._interval) {
|
|
this._elapsed -= this._interval;
|
|
// Prevent runaway accumulation if tab was backgrounded
|
|
if (this._elapsed > this._interval) this._elapsed = 0;
|
|
|
|
this._fireExplore(zoomLevel);
|
|
}
|
|
}
|
|
|
|
// ---- Callback ----
|
|
|
|
/**
|
|
* Register the explore callback. Called when auto-step fires.
|
|
* @param {function} callback - receives { intensity }
|
|
*/
|
|
onExplore(callback) {
|
|
this._callback = callback;
|
|
}
|
|
|
|
// ---- Visual state ----
|
|
|
|
/**
|
|
* Progress toward next auto-step (0-1).
|
|
* Useful for a countdown/progress ring UI.
|
|
* @returns {number}
|
|
*/
|
|
getProgress() {
|
|
if (!this._active) return 0;
|
|
return Math.min(1, this._elapsed / this._interval);
|
|
}
|
|
|
|
// ---- Serialization ----
|
|
|
|
getConfig() {
|
|
return {
|
|
interval: this._interval,
|
|
intensity: this._intensity,
|
|
active: this._active,
|
|
};
|
|
}
|
|
|
|
setConfig(config) {
|
|
if (config.interval != null) this.setInterval(config.interval);
|
|
if (config.intensity != null) this.setIntensity(config.intensity);
|
|
// Note: we don't auto-start from config — caller decides
|
|
}
|
|
|
|
// ---- Internal ----
|
|
|
|
_fireExplore(zoomLevel) {
|
|
if (!this._callback) return;
|
|
// Scale intensity by zoom: zoomed in = gentler, zoomed out = bigger leaps
|
|
const scaledIntensity = this._intensity * Math.max(0.05, zoomLevel);
|
|
this._callback({ intensity: scaledIntensity });
|
|
}
|
|
}
|
|
|
|
// ---- Utility ----
|
|
function clamp(v, lo, hi) {
|
|
return v < lo ? lo : v > hi ? hi : v;
|
|
}
|