From 6859ce537f13a9180b833d0ea0cb1113dfc2804f Mon Sep 17 00:00:00 2001 From: Bamdad Sabbagh Date: Tue, 16 Nov 2021 19:38:24 +0100 Subject: [PATCH 1/7] fix: add unicity check before adding a node to selected nodes --- src/playground/playground.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/playground/playground.ts b/src/playground/playground.ts index 1416e02..fd6597b 100644 --- a/src/playground/playground.ts +++ b/src/playground/playground.ts @@ -1151,10 +1151,12 @@ function simulationStarted () { } export const addToSelectedNodes = function (nodeIndex: number) { - selectedNodes = [ - ...selectedNodes, - nodeIndex, - ]; + if (selectedNodes.indexOf (nodeIndex) === -1) { + selectedNodes = [ + ...selectedNodes, + nodeIndex, + ]; + } }; export const removeFromSelectedNodes = function (nodeIndex: number) { From 7c3bee8b08c7cc49b4bfaf4b0b06f095fb058d54 Mon Sep 17 00:00:00 2001 From: Bamdad Sabbagh Date: Tue, 16 Nov 2021 19:44:29 +0100 Subject: [PATCH 2/7] fix(mappings): do not update parameter right after having learned a new control --- src/app/devices/controller.device.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/devices/controller.device.ts b/src/app/devices/controller.device.ts index c3b18e8..cec26b7 100644 --- a/src/app/devices/controller.device.ts +++ b/src/app/devices/controller.device.ts @@ -153,9 +153,9 @@ controllerDevice.attachButtonsDefault = function () { } const note = parseInt (e.note.number); + const { isLearning, learningParameter } = mappingsState; // learning a new mapping - const { isLearning, learningParameter } = mappingsState; if (isLearning && learningParameter) { mappingsUi.learn ({ parameter: learningParameter, @@ -163,12 +163,13 @@ controllerDevice.attachButtonsDefault = function () { type: 'button', }); } - // update targets of already mapped parameters - const mappedParameters = mappingsState.getParametersByControl (note); - mappedParameters.forEach ((parameter) => { - playgroundUi.updateParameter (parameter, 1); - }); + else { + const mappedParameters = mappingsState.getParametersByControl (note); + mappedParameters.forEach ((parameter) => { + playgroundUi.updateParameter (parameter, 1); + }); + } // draw feedback lights this.playNote ({ From 7ce274ca34e65e4c59124a936f80bfe2f0c6a001 Mon Sep 17 00:00:00 2001 From: Bamdad Sabbagh Date: Wed, 17 Nov 2021 11:02:09 +0100 Subject: [PATCH 3/7] fix(device/novation-launchpad-mini): specify the right notes for the first row of function buttons (still can't get them to light up though) --- src/app/devices/known-devices/novation-launchpad-mini.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/devices/known-devices/novation-launchpad-mini.ts b/src/app/devices/known-devices/novation-launchpad-mini.ts index aef2157..c14119a 100644 --- a/src/app/devices/known-devices/novation-launchpad-mini.ts +++ b/src/app/devices/known-devices/novation-launchpad-mini.ts @@ -30,7 +30,7 @@ export const novationLaunchpadMini: Selector = { [7, 23, 39, 55, 71, 87, 103, 119], ], functionKeys: { - firstRow: [91, 92, 93, 94, 95, 96, 97, 98], // not available + firstRow: [104, 105, 106, 107, 108, 109, 110, 111], lastColumn: [8, 24, 40, 56, 72, 88, 104, 120], }, colors: { From 0a1e4ffe439167ab5b3b4bddaf9c913c6ab36776 Mon Sep 17 00:00:00 2001 From: Bamdad Sabbagh Date: Wed, 17 Nov 2021 13:05:18 +0100 Subject: [PATCH 4/7] fix(select-card): add bias `onchange` event + rename mutation functions with explicit wording --- src/app/devices/controller.device.ts | 26 ++--- src/app/state/network.state.ts | 101 +++++++++++++----- src/app/ui/select-card.ui.ts | 146 +++++++++++++++++---------- 3 files changed, 181 insertions(+), 92 deletions(-) diff --git a/src/app/devices/controller.device.ts b/src/app/devices/controller.device.ts index cec26b7..97457e2 100644 --- a/src/app/devices/controller.device.ts +++ b/src/app/devices/controller.device.ts @@ -282,8 +282,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const learningRate = selectCardUi.options.learningRate[learningRateOptionIndex]; if (learningRate !== links[index].source.learningRate) { - networkState.updateSourceLearningRate (index, learningRate); - selectCardUi.setLearningRate (index, learningRate); + networkState.setSourceLearningRate (index, learningRate); + selectCardUi.updateSourceLearningRate (index, learningRate); playgroundFacade.updateUI (); } } @@ -310,8 +310,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const activation = selectCardUi.options.activation[activationOptionIndex]; if (activation !== links[index].source.activation.name) { - networkState.updateSourceActivation (index, activation); - selectCardUi.setActivation (index, activation); + networkState.setSourceActivation (index, activation); + selectCardUi.updateSourceActivation (index, activation); playgroundFacade.updateUI (); } } @@ -340,8 +340,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const regularization = selectCardUi.options.regularization[regularizationOptionIndex]; if (regularization !== links[index].source.regularization.name) { - networkState.updateSourceRegularization (index, regularization); - selectCardUi.setRegularization (index, regularization); + networkState.setSourceRegularizationType (index, regularization); + selectCardUi.updateSourceRegularizationType (index, regularization); playgroundFacade.updateUI (); } } @@ -360,8 +360,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const regularizationRate = selectCardUi.options.regularizationRate[regularizationRateOptionIndex]; if (regularizationRate !== links[index].source.regularizationRate) { - networkState.updateSourceRegularizationRate (index, regularizationRate); - selectCardUi.setRegularizationRate (index, regularizationRate); + networkState.setSourceRegularizationRate (index, regularizationRate); + selectCardUi.updateSourceRegularizationRate (index, regularizationRate); playgroundFacade.updateUI (); } } @@ -404,8 +404,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void } if (links[index].hasSnapped) { - networkState.setWeight (index, value); - selectCardUi.setWeight (index, value); + networkState.setSourceWeight (index, value); + selectCardUi.updateSourceWeight (index, value); playgroundFacade.updateWeightsUI (); this.playNote ({ note: this.settings.outputByInput[inputNote], @@ -423,8 +423,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void else { const value = rangeMap (e.value, 0, 127, -1, 1); if (value.toFixed (2) !== links[index].source.bias.toFixed (2)) { - links[index].source.bias = value; - selectCardUi.setBias (index, value); + networkState.setSourceBias (index, value); + selectCardUi.updateSourceBias (index, value); playgroundFacade.updateBiasesUI (); } } @@ -615,7 +615,7 @@ controllerDevice.attachControlsToLayer = function (): void { const value = rangeMap (e.value, 0, 127, -1, 1); if (value.toFixed (2) !== neurons[index].bias.toFixed (2)) { neurons[index].bias = value; - layerCardUi.setBias (index, value); + layerCardUi.setSourceBias (index, value); playgroundFacade.updateBiasesUI (); } } diff --git a/src/app/state/network.state.ts b/src/app/state/network.state.ts index adfca3f..edff985 100644 --- a/src/app/state/network.state.ts +++ b/src/app/state/network.state.ts @@ -202,32 +202,99 @@ networkState.getSelectedNeurons = function () { return targets; }; -networkState.setWeight = function (index, value) { +networkState.setSourceWeight = function (index, value) { this.getSelectedNeurons ().forEach ((neuron) => { - const weight = neuron.inputLinks?.[index]?.weight; - if (typeof weight !== 'undefined') { - neuron.inputLinks[index].weight = value; + const link = neuron.inputLinks?.[index]; + if (typeof link === 'undefined') { + return; + } + if (link?.isDead) { + return; + } + + if (typeof link?.weight !== 'undefined') { + link.weight = value; } }); }; -networkState.updateSourceLearningRate = function (index, value) { +networkState.setSourceBias = function (index, value) { this.getSelectedNeurons ().forEach ((neuron) => { - neuron.inputLinks[index].source.learningRate = value; + const link = neuron.inputLinks?.[index]; + if (typeof link === 'undefined') { + return; + } + if (link?.isDead) { + return; + } + + if (typeof link?.source?.bias !== 'undefined') { + link.source.bias = value; + } }); }; -networkState.setLearningRate = function (index: number, value: number): void { - const { neuron } = this.getNeuron (index); - neuron.learningRate = value; +networkState.setSourceLearningRate = function (index, value) { + this.getSelectedNeurons ().forEach ((neuron) => { + const link = neuron.inputLinks?.[index]; + if (typeof link === 'undefined') { + return; + } + if (link?.isDead) { + return; + } + + link.source.learningRate = value; + }); }; -networkState.updateSourceActivation = function (index, value) { +networkState.setSourceActivation = function (index, value) { this.getSelectedNeurons ().forEach ((neuron) => { - neuron.inputLinks[index].source.activation = activations[value]; + const link = neuron.inputLinks?.[index]; + if (typeof link === 'undefined') { + return; + } + if (link?.isDead) { + return; + } + + link.source.activation = activations[value]; }); }; +networkState.setSourceRegularizationType = function (index, value) { + this.getSelectedNeurons ().forEach ((neuron) => { + const link = neuron.inputLinks?.[index]; + if (typeof link === 'undefined') { + return; + } + if (link?.isDead) { + return; + } + + link.source.regularization = regularizations[value]; + }); +}; + +networkState.setSourceRegularizationRate = function (index, value) { + this.getSelectedNeurons ().forEach ((neuron) => { + const link = neuron.inputLinks?.[index]; + if (typeof link === 'undefined') { + return; + } + if (link?.isDead) { + return; + } + + link.source.regularizationRate = value; + }); +}; + +networkState.setLearningRate = function (index: number, value: number): void { + const { neuron } = this.getNeuron (index); + neuron.learningRate = value; +}; + networkState.setActivation = function (index: number, name: string): void { if (typeof index === 'undefined') { throw new Error ('index must be defined'); @@ -246,12 +313,6 @@ networkState.setActivation = function (index: number, name: string): void { neuron.activation = activations[name]; }; -networkState.updateSourceRegularization = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { - neuron.inputLinks[index].source.regularization = regularizations[value]; - }); -}; - networkState.setRegularization = function (index: number, name: string) { if (typeof index === 'undefined') { throw new Error ('index must be defined'); @@ -270,12 +331,6 @@ networkState.setRegularization = function (index: number, name: string) { neuron.regularization = regularizations[name]; }; -networkState.updateSourceRegularizationRate = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { - neuron.inputLinks[index].source.regularizationRate = value; - }); -}; - networkState.setRegularizationRate = function (index: number, value: number) { if (typeof index === 'undefined') { throw new Error ('index must be defined'); diff --git a/src/app/ui/select-card.ui.ts b/src/app/ui/select-card.ui.ts index 133e92c..3ba8d8f 100644 --- a/src/app/ui/select-card.ui.ts +++ b/src/app/ui/select-card.ui.ts @@ -100,12 +100,12 @@ selectCardUi.updateCard = function () { const link = networkState.getNeuron (selectedNodes[0]).neuron.inputLinks[index]; if (typeof link === 'undefined') { - this.setWeight (index); - this.setBias (index); - this.setLearningRate (index); - this.setActivation (index); - this.setRegularization (index); - this.setRegularizationRate (index); + this.updateSourceWeight (index); + this.updateSourceBias (index); + this.updateSourceLearningRate (index); + this.updateSourceActivation (index); + this.updateSourceRegularizationType (index); + this.updateSourceRegularizationRate (index); return; } @@ -113,30 +113,30 @@ selectCardUi.updateCard = function () { const bias = link.source.bias; if (link.isDead === true) { - this.setWeight (index); - this.setBias (index); - this.setLearningRate (index); - this.setActivation (index); - this.setRegularization (index); - this.setRegularizationRate (index); + this.updateSourceWeight (index); + this.updateSourceBias (index); + this.updateSourceLearningRate (index); + this.updateSourceActivation (index); + this.updateSourceRegularizationType (index); + this.updateSourceRegularizationRate (index); } else { - this.setWeight (index, weight); - this.setBias (index, bias); - this.setLearningRate (index, link.source.learningRate); - this.setActivation (index, link.source.activation.name); - this.setRegularization (index, link.source.regularization.name); - this.setRegularizationRate (index, link.source.regularizationRate); + this.updateSourceWeight (index, weight); + this.updateSourceBias (index, bias); + this.updateSourceLearningRate (index, link.source.learningRate); + this.updateSourceActivation (index, link.source.activation.name); + this.updateSourceRegularizationType (index, link.source.regularization.name); + this.updateSourceRegularizationRate (index, link.source.regularizationRate); } } // multi selection else { - this.setWeight (index, null); - this.setBias (index, null); - this.setLearningRate (index, null); - this.setActivation (index, null); - this.setRegularization (index, null); - this.setRegularizationRate (index, null); + this.updateSourceWeight (index, null); + this.updateSourceBias (index, null); + this.updateSourceLearningRate (index, null); + this.updateSourceActivation (index, null); + this.updateSourceRegularizationType (index, null); + this.updateSourceRegularizationRate (index, null); } }); @@ -147,55 +147,113 @@ selectCardUi.updateCard = function () { }; selectCardUi.attachEvents = function () { + this.attachSourceWeightsEvents (); + this.attachSourceBiasesEvents (); + this.attachSourceLearningRatesEvents (); + this.attachSourceActivationsEvents (); + this.attachSourceRegularizationTypesEvents (); + this.attachSourceRegularizationRatesEvents (); +}; + +selectCardUi.attachSourceWeightsEvents = function () { if (this.weights) { this.weights.forEach ((weight, index) => { weight.onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.setWeight (index, value); - playgroundFacade.updateUI (); + networkState.setSourceWeight (index, value); + playgroundFacade.updateWeightsUI (); weight.blur (); }; }); } +}; +selectCardUi.attachSourceBiasesEvents = function () { + if (this.biases) { + this.biases.forEach ((bias, index) => { + bias.onchange = (e: InputEvent) => { + const value = parseFloat ((e.target as HTMLInputElement).value); + networkState.setSourceBias (index, value); + playgroundFacade.updateBiasesUI (); + bias.blur (); + }; + }); + } +}; + +selectCardUi.attachSourceLearningRatesEvents = function () { if (this.learningRates) { this.learningRates.forEach ((learningRate, index) => { learningRate.children[0].onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.updateSourceLearningRate (index, value); + networkState.setSourceLearningRate (index, value); + playgroundFacade.updateUI (); }; }); } +}; +selectCardUi.attachSourceActivationsEvents = function () { if (this.activations) { this.activations.forEach ((activation, index) => { activation.children[0].onchange = (e: InputEvent) => { const value = (e.target as HTMLInputElement).value; - networkState.updateSourceActivation (index, value); + networkState.setSourceActivation (index, value); + playgroundFacade.updateUI (); }; }); } +}; +selectCardUi.attachSourceRegularizationTypesEvents = function () { if (this.regularizations) { this.regularizations.forEach ((regularization, index) => { regularization.children[0].onchange = (e: InputEvent) => { const value = (e.target as HTMLInputElement).value; - networkState.updateSourceRegularization (index, value); + networkState.setSourceRegularizationType (index, value); + playgroundFacade.updateUI (); }; }); } +}; +selectCardUi.attachSourceRegularizationRatesEvents = function () { if (this.regularizationRates) { this.regularizationRates.forEach ((regularizationRate, index) => { regularizationRate.children[0].onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.updateSourceRegularizationRate (index, value); + networkState.setSourceRegularizationRate (index, value); + playgroundFacade.updateUI (); }; }); } }; -selectCardUi.setInput = function (pool, index, payload) { +selectCardUi.updateSourceWeight = function (index, weight?) { + this.updateSourceInput (this.weights, index, weight); +}; + +selectCardUi.updateSourceBias = function (index, bias?) { + this.updateSourceInput (this.biases, index, bias); +}; + +selectCardUi.updateSourceLearningRate = function (index, learningRate?) { + this.updateSourceDropdown (this.learningRates, index, learningRate); +}; + +selectCardUi.updateSourceActivation = function (index, activation?) { + this.updateSourceDropdown (this.activations, index, activation); +}; + +selectCardUi.updateSourceRegularizationType = function (index, regularization?) { + this.updateSourceDropdown (this.regularizations, index, regularization); +}; + +selectCardUi.updateSourceRegularizationRate = function (index, regularizationRate?) { + this.updateSourceDropdown (this.regularizationRates, index, regularizationRate); +}; + +selectCardUi.updateSourceInput = function (pool, index, payload) { const isFocused = pool[index] === document.activeElement; if (isFocused) { return; @@ -215,15 +273,7 @@ selectCardUi.setInput = function (pool, index, payload) { } }; -selectCardUi.setWeight = function (index, weight?) { - this.setInput (this.weights, index, weight); -}; - -selectCardUi.setBias = function (index, bias?) { - this.setInput (this.biases, index, bias); -}; - -selectCardUi.setDropdown = function (pool, index, payload) { +selectCardUi.updateSourceDropdown = function (pool, index, payload) { const didNotChange = pool[index].children[0].value === payload; if (didNotChange) { return; @@ -242,19 +292,3 @@ selectCardUi.setDropdown = function (pool, index, payload) { pool[index].children[0].value = payload; } }; - -selectCardUi.setLearningRate = function (index, learningRate?) { - this.setDropdown (this.learningRates, index, learningRate); -}; - -selectCardUi.setActivation = function (index, activation?) { - this.setDropdown (this.activations, index, activation); -}; - -selectCardUi.setRegularization = function (index, regularization?) { - this.setDropdown (this.regularizations, index, regularization); -}; - -selectCardUi.setRegularizationRate = function (index, regularizationRate?) { - this.setDropdown (this.regularizationRates, index, regularizationRate); -}; From a1e848730a1f0fd5a1ffa333172baf4a575a6963 Mon Sep 17 00:00:00 2001 From: Bamdad Sabbagh Date: Wed, 17 Nov 2021 14:53:42 +0100 Subject: [PATCH 5/7] fix(cards): return change status from state mutation to trigger cards and UI updates + attach mouse events for layer-card --- src/app/devices/controller.device.ts | 69 ++++---- src/app/state/network.state.ts | 241 +++++++++++++++++---------- src/app/ui/layer-card.ui.ts | 139 +++++++++++++-- src/app/ui/select-card.ui.ts | 36 ++-- 4 files changed, 344 insertions(+), 141 deletions(-) diff --git a/src/app/devices/controller.device.ts b/src/app/devices/controller.device.ts index 97457e2..c3d3304 100644 --- a/src/app/devices/controller.device.ts +++ b/src/app/devices/controller.device.ts @@ -281,8 +281,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const learningRate = selectCardUi.options.learningRate[learningRateOptionIndex]; - if (learningRate !== links[index].source.learningRate) { - networkState.setSourceLearningRate (index, learningRate); + const hasChanged = networkState.setSourceLearningRate (index, learningRate); + if (hasChanged) { selectCardUi.updateSourceLearningRate (index, learningRate); playgroundFacade.updateUI (); } @@ -309,8 +309,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const activation = selectCardUi.options.activation[activationOptionIndex]; - if (activation !== links[index].source.activation.name) { - networkState.setSourceActivation (index, activation); + const hasChanged = networkState.setSourceActivation (index, activation); + if (hasChanged) { selectCardUi.updateSourceActivation (index, activation); playgroundFacade.updateUI (); } @@ -338,9 +338,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void ); const regularization = selectCardUi.options.regularization[regularizationOptionIndex]; - - if (regularization !== links[index].source.regularization.name) { - networkState.setSourceRegularizationType (index, regularization); + const hasChanged = networkState.setSourceRegularizationType (index, regularization); + if (hasChanged) { selectCardUi.updateSourceRegularizationType (index, regularization); playgroundFacade.updateUI (); } @@ -359,8 +358,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void const regularizationRate = selectCardUi.options.regularizationRate[regularizationRateOptionIndex]; - if (regularizationRate !== links[index].source.regularizationRate) { - networkState.setSourceRegularizationRate (index, regularizationRate); + const hasChanged = networkState.setSourceRegularizationRate (index, regularizationRate); + if (hasChanged) { selectCardUi.updateSourceRegularizationRate (index, regularizationRate); playgroundFacade.updateUI (); } @@ -404,9 +403,11 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void } if (links[index].hasSnapped) { - networkState.setSourceWeight (index, value); - selectCardUi.updateSourceWeight (index, value); - playgroundFacade.updateWeightsUI (); + const hasChanged = networkState.setSourceWeight (index, value); + if (hasChanged) { + selectCardUi.updateSourceWeight (index, value); + playgroundFacade.updateWeightsUI (); + } this.playNote ({ note: this.settings.outputByInput[inputNote], color: this.settings.colorByState.snap, @@ -422,8 +423,8 @@ controllerDevice.attachControlsToNeuron = function (selectedNode: number): void // biases else { const value = rangeMap (e.value, 0, 127, -1, 1); - if (value.toFixed (2) !== links[index].source.bias.toFixed (2)) { - networkState.setSourceBias (index, value); + const hasChanged = networkState.setSourceBias (index, value); + if (hasChanged) { selectCardUi.updateSourceBias (index, value); playgroundFacade.updateBiasesUI (); } @@ -515,7 +516,7 @@ controllerDevice.attachControlsToLayer = function (): void { this.addControlListener ((e) => { const inputNote = e.controller.number; - // first row: learning rate + // learning rate if (this.settings.rows.firstPots.indexOf (inputNote) !== -1) { const index = this.settings.rows.firstPots.indexOf (inputNote); if (neurons[index].isEnabled === false) { @@ -534,11 +535,13 @@ controllerDevice.attachControlsToLayer = function (): void { const learningRate = selectCardUi.options.learningRate[learningRateOptionIndex]; - if (learningRate !== neurons[index].learningRate) { - networkState.setLearningRate (parseInt (neurons[index].id), learningRate); - layerCardUi.setLearningRate (index, learningRate); + const hasChanged = networkState.setLearningRate (index, learningRate); + if (hasChanged) { + layerCardUi.updateLearningRate (index, learningRate); + playgroundFacade.updateUI (); } } + // activation else if (this.settings.rows.secondPots.indexOf (inputNote) !== -1) { const index = this.settings.rows.secondPots.indexOf (inputNote); if (neurons[index].isEnabled === false) { @@ -557,9 +560,10 @@ controllerDevice.attachControlsToLayer = function (): void { const activation = selectCardUi.options.activation[activationOptionIndex]; - if (activation !== neurons[index].activation.name) { - networkState.setActivation (parseInt (neurons[index].id), activation); - layerCardUi.setActivation (index, activation); + const hasChanged = networkState.setActivation (index, activation); + if (hasChanged) { + layerCardUi.updateActivation (index); + playgroundFacade.updateUI (); } } else if (this.settings.rows.thirdPots.indexOf (inputNote) !== -1) { @@ -568,7 +572,7 @@ controllerDevice.attachControlsToLayer = function (): void { return; } - // regularization (shifted) + // regularization type (shifted) if (this.shifted[index] === true) { const regularizationOptionIndex = parseInt ( rangeMap ( @@ -582,9 +586,10 @@ controllerDevice.attachControlsToLayer = function (): void { const regularization = selectCardUi.options.regularization[regularizationOptionIndex]; - if (regularization !== neurons[index].regularization.name) { - networkState.setRegularization (parseInt (neurons[index].id), regularization); - layerCardUi.setRegularization (index, regularization); + const hasChanged = networkState.setRegularizationType (index, regularization); + if (hasChanged) { + layerCardUi.updateRegularizationType (index); + playgroundFacade.updateUI (); } } // regularization rate @@ -601,21 +606,23 @@ controllerDevice.attachControlsToLayer = function (): void { const regularizationRate = selectCardUi.options.regularizationRate[regularizationRateOptionIndex]; - if (regularizationRate !== neurons[index].regularizationRate) { - networkState.setRegularizationRate (parseInt (neurons[index].id), regularizationRate); - layerCardUi.setRegularizationRate (index, regularizationRate); + const hasChanged = networkState.setRegularizationRate (index, regularizationRate); + if (hasChanged) { + layerCardUi.updateRegularizationRate (index); + playgroundFacade.updateUI (); } } } + // bias else if (this.settings.rows.faders.indexOf (inputNote) !== -1) { const index = this.settings.rows.faders.indexOf (inputNote); if (neurons[index].isEnabled === false) { return; } const value = rangeMap (e.value, 0, 127, -1, 1); - if (value.toFixed (2) !== neurons[index].bias.toFixed (2)) { - neurons[index].bias = value; - layerCardUi.setSourceBias (index, value); + const hasChanged = networkState.setBias (index, value); + if (hasChanged) { + layerCardUi.updateBias (index); playgroundFacade.updateBiasesUI (); } } diff --git a/src/app/state/network.state.ts b/src/app/state/network.state.ts index edff985..06748ed 100644 --- a/src/app/state/network.state.ts +++ b/src/app/state/network.state.ts @@ -202,151 +202,222 @@ networkState.getSelectedNeurons = function () { return targets; }; -networkState.setSourceWeight = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { +networkState.setSourceWeight = function (index, value): boolean { + let hasChanged = false; + + const selectedNeurons = this.getSelectedNeurons (); + selectedNeurons.forEach ((neuron) => { const link = neuron.inputLinks?.[index]; - if (typeof link === 'undefined') { - return; - } - if (link?.isDead) { + if ( + typeof link === 'undefined' + || link?.isDead + || typeof link?.weight === 'undefined' + ) { return; } - if (typeof link?.weight !== 'undefined') { - link.weight = value; - } + link.weight = value; + hasChanged = true; }); + + return hasChanged; }; -networkState.setSourceBias = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { +networkState.setSourceBias = function (index, value): boolean { + let hasChanged = false; + + const selectedNeurons = this.getSelectedNeurons (); + selectedNeurons.forEach ((neuron) => { const link = neuron.inputLinks?.[index]; - if (typeof link === 'undefined') { - return; - } - if (link?.isDead) { + if ( + typeof link === 'undefined' + || link?.isDead + || typeof link?.source?.bias === 'undefined' + ) { return; } - if (typeof link?.source?.bias !== 'undefined') { - link.source.bias = value; - } + link.source.bias = value; + hasChanged = true; }); + + return hasChanged; }; -networkState.setSourceLearningRate = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { +networkState.setSourceLearningRate = function (index, value): boolean { + let hasChanged = false; + + const selectedNeurons = this.getSelectedNeurons (); + selectedNeurons.forEach ((neuron) => { const link = neuron.inputLinks?.[index]; - if (typeof link === 'undefined') { - return; - } - if (link?.isDead) { + if ( + typeof link === 'undefined' + || link?.isDead + || value === link.source.learningRate + ) { return; } link.source.learningRate = value; + hasChanged = true; }); + + return hasChanged; }; -networkState.setSourceActivation = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { +networkState.setSourceActivation = function (index, name): boolean { + let hasChanged = false; + + const selectedNeurons = this.getSelectedNeurons (); + selectedNeurons.forEach ((neuron) => { const link = neuron.inputLinks?.[index]; - if (typeof link === 'undefined') { - return; - } - if (link?.isDead) { + if ( + typeof link === 'undefined' + || link?.isDead + || name === link.source.activation.name + ) { return; } - link.source.activation = activations[value]; + link.source.activation = activations[name]; + hasChanged = true; }); + + return hasChanged; }; -networkState.setSourceRegularizationType = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { +networkState.setSourceRegularizationType = function (index, name): boolean { + let hasChanged = false; + + const selectedNeurons = this.getSelectedNeurons (); + selectedNeurons.forEach ((neuron) => { const link = neuron.inputLinks?.[index]; - if (typeof link === 'undefined') { - return; - } - if (link?.isDead) { + if ( + typeof link === 'undefined' + || link?.isDead + || name === link.source.regularization.name + ) { return; } - link.source.regularization = regularizations[value]; + link.source.regularization = regularizations[name]; + hasChanged = true; }); + + return hasChanged; }; -networkState.setSourceRegularizationRate = function (index, value) { - this.getSelectedNeurons ().forEach ((neuron) => { +networkState.setSourceRegularizationRate = function (index, value): boolean { + let hasChanged = false; + + const selectedNeurons = this.getSelectedNeurons (); + selectedNeurons.forEach ((neuron) => { const link = neuron.inputLinks?.[index]; - if (typeof link === 'undefined') { - return; - } - if (link?.isDead) { + if ( + typeof link === 'undefined' + || link?.isDead + || value === link.source.regularizationRate + ) { return; } link.source.regularizationRate = value; + hasChanged = true; }); -}; -networkState.setLearningRate = function (index: number, value: number): void { - const { neuron } = this.getNeuron (index); - neuron.learningRate = value; + return hasChanged; }; -networkState.setActivation = function (index: number, name: string): void { - if (typeof index === 'undefined') { - throw new Error ('index must be defined'); - } - if (typeof index !== 'number') { - throw new Error ('index must be a number'); +networkState.setBias = function (index: number, value: number): boolean { + let hasChanged = false; + const neurons = this.neurons[this.selectedLayerIndex]; + const neuron = neurons[index]; + + if ( + neuron.isEnabled === false + || typeof neuron?.bias === 'undefined' + || value === neuron?.bias + ) { + return hasChanged; } - if (typeof name === 'undefined') { - throw new Error ('name must be defined'); + + neuron.bias = value; + + hasChanged = true; + return hasChanged; +}; + +networkState.setLearningRate = function (index: number, value: number): boolean { + let hasChanged = false; + const neurons = this.neurons[this.selectedLayerIndex]; + const neuron = neurons[index]; + + if ( + neuron.isEnabled === false + || value === neuron.learningRate + ) { + return hasChanged; } - if (typeof name !== 'string') { - throw new Error ('name must be a string'); + + neuron.learningRate = value; + + hasChanged = true; + return hasChanged; +}; + +networkState.setActivation = function (index: number, name: string): boolean { + let hasChanged = false; + const neurons = this.neurons[this.selectedLayerIndex]; + const neuron = neurons[index]; + + if ( + neuron.isEnabled === false + || name === neuron.activation.name + ) { + return hasChanged; } - const { neuron } = this.getNeuron (index); neuron.activation = activations[name]; + + hasChanged = true; + return hasChanged; }; -networkState.setRegularization = function (index: number, name: string) { - if (typeof index === 'undefined') { - throw new Error ('index must be defined'); - } - if (typeof index !== 'number') { - throw new Error ('index must be a number'); - } - if (typeof name === 'undefined') { - throw new Error ('name must be defined'); - } - if (typeof name !== 'string') { - throw new Error ('name must be a string'); +networkState.setRegularizationType = function (index: number, name: string): boolean { + let hasChanged = false; + const neurons = this.neurons[this.selectedLayerIndex]; + const neuron = neurons[index]; + + if ( + neuron.isEnabled === false + || name === neuron.regularization.name + ) { + return hasChanged; } - const { neuron } = this.getNeuron (index); neuron.regularization = regularizations[name]; + + hasChanged = true; + return hasChanged; }; -networkState.setRegularizationRate = function (index: number, value: number) { - if (typeof index === 'undefined') { - throw new Error ('index must be defined'); - } - if (typeof index !== 'number') { - throw new Error ('index must be a number'); - } - if (typeof value === 'undefined') { - throw new Error ('value must be defined'); - } - if (typeof value !== 'number') { - throw new Error ('value must be a number'); +networkState.setRegularizationRate = function (index: number, value: number): boolean { + let hasChanged = false; + const neurons = this.neurons[this.selectedLayerIndex]; + const neuron = neurons[index]; + + if ( + neuron.isEnabled === false + || typeof neuron?.regularizationRate === 'undefined' + || value === neuron?.regularizationRate + ) { + return hasChanged; } - const { neuron } = this.getNeuron (index); neuron.regularizationRate = value; + + hasChanged = true; + return hasChanged; }; networkState.selectedLayerIndex = null; diff --git a/src/app/ui/layer-card.ui.ts b/src/app/ui/layer-card.ui.ts index f977ef6..cedc7b6 100644 --- a/src/app/ui/layer-card.ui.ts +++ b/src/app/ui/layer-card.ui.ts @@ -1,5 +1,6 @@ import { selectCardUi } from './select-card.ui'; import { networkState } from '../state/network.state'; +import { playgroundFacade } from '../facades/playground.facade'; export const layerCardUi = Object.create (selectCardUi); @@ -8,6 +9,15 @@ layerCardUi.nodeSelectors = { node: '#layer-card', }; +layerCardUi.init = function () { + this.fetchCard (); + this.createLearningRates (); + this.createActivations (); + this.createRegularizations (); + this.createRegularizationRates (); + this.attachEvents (); +}; + layerCardUi.updateCard = function () { if (networkState.selectedLayerIndex === null) { this.node.style ('display', 'none'); @@ -17,23 +27,126 @@ layerCardUi.updateCard = function () { const neurons = networkState.neurons[networkState.selectedLayerIndex]; neurons.forEach ((neuron, index) => { - this.biases[index].value = neuron.bias.toFixed (3); - this.biases[index].disabled = !neuron.isEnabled; + this.updateBias (index); + this.updateLearningRate (index); + this.updateActivation (index); + this.updateRegularizationType (index); + this.updateRegularizationRate (index); + }); - this.learningRates[index].children[0].value = neuron.learningRate; - this.learningRates[index].children[0].disabled = !neuron.isEnabled; + this.node.style ('display', 'flex'); +}; - this.activations[index].children[0].value = neuron.activation.name; - this.activations[index].children[0].disabled = !neuron.isEnabled; +layerCardUi.attachEvents = function () { + this.attachBiasesEvents (); + this.attachLearningRatesEvents (); + this.attachActivationsEvents (); + this.attachRegularizationTypesEvents (); + this.attachRegularizationRatesEvents (); +}; - this.regularizations[index].children[0].value = neuron.regularization.name; - this.regularizations[index].children[0].disabled = !neuron.isEnabled; +layerCardUi.attachBiasesEvents = function () { + if (this.biases) { + this.biases.forEach ((bias, index) => { + bias.onchange = (e: InputEvent) => { + const value = parseFloat ((e.target as HTMLInputElement).value); + const hasChanged = networkState.setBias (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } + bias.blur (); + }; + }); + } +}; - this.regularizationRates[index].value = neuron.regularizationRate; - this.regularizationRates[index].children[0].disabled = !neuron.isEnabled; - }); +layerCardUi.attachLearningRatesEvents = function () { + if (this.learningRates) { + this.learningRates.forEach ((learningRate, index) => { + learningRate.children[0].onchange = (e: InputEvent) => { + const value = parseFloat ((e.target as HTMLInputElement).value); + const hasChanged = networkState.setLearningRate (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } + }; + }); + } +}; - this.node.style ('display', 'flex'); +layerCardUi.attachActivationsEvents = function () { + if (this.activations) { + this.activations.forEach ((activation, index) => { + activation.children[0].onchange = (e: InputEvent) => { + const value = (e.target as HTMLInputElement).value; + const hasChanged = networkState.setActivation (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } + }; + }); + } +}; + +layerCardUi.attachRegularizationTypesEvents = function () { + if (this.regularizations) { + this.regularizations.forEach ((regularization, index) => { + regularization.children[0].onchange = (e: InputEvent) => { + const value = (e.target as HTMLInputElement).value; + const hasChanged = networkState.setRegularizationType (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } + }; + }); + } +}; - requestAnimationFrame (this.updateCard.bind (this)); +layerCardUi.attachRegularizationRatesEvents = function () { + if (this.regularizationRates) { + this.regularizationRates.forEach ((regularizationRate, index) => { + regularizationRate.children[0].onchange = (e: InputEvent) => { + const value = parseFloat ((e.target as HTMLInputElement).value); + const hasChanged = networkState.setRegularizationRate (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } + }; + }); + } +}; + +layerCardUi.updateBias = function (index) { + const neurons = networkState.neurons[networkState.selectedLayerIndex]; + const neuron = neurons[index]; + this.biases[index].value = neuron.bias.toFixed (3); + this.biases[index].disabled = !neuron.isEnabled; +}; + +layerCardUi.updateLearningRate = function (index) { + const neurons = networkState.neurons[networkState.selectedLayerIndex]; + const neuron = neurons[index]; + this.learningRates[index].children[0].value = neuron.learningRate; + this.learningRates[index].children[0].disabled = !neuron.isEnabled; +}; + +layerCardUi.updateActivation = function (index) { + const neurons = networkState.neurons[networkState.selectedLayerIndex]; + const neuron = neurons[index]; + this.activations[index].children[0].value = neuron.activation.name; + this.activations[index].children[0].disabled = !neuron.isEnabled; +}; + +layerCardUi.updateRegularizationType = function (index) { + const neurons = networkState.neurons[networkState.selectedLayerIndex]; + const neuron = neurons[index]; + this.regularizations[index].children[0].value = neuron.regularization.name; + this.regularizations[index].children[0].disabled = !neuron.isEnabled; +}; + +layerCardUi.updateRegularizationRate = function (index) { + const neurons = networkState.neurons[networkState.selectedLayerIndex]; + const neuron = neurons[index]; + this.regularizationRates[index].children[0].value = neuron.regularizationRate; + this.regularizationRates[index].children[0].disabled = !neuron.isEnabled; }; diff --git a/src/app/ui/select-card.ui.ts b/src/app/ui/select-card.ui.ts index 3ba8d8f..5a39afa 100644 --- a/src/app/ui/select-card.ui.ts +++ b/src/app/ui/select-card.ui.ts @@ -160,8 +160,10 @@ selectCardUi.attachSourceWeightsEvents = function () { this.weights.forEach ((weight, index) => { weight.onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.setSourceWeight (index, value); - playgroundFacade.updateWeightsUI (); + const hasChanged = networkState.setSourceWeight (index, value); + if (hasChanged) { + playgroundFacade.updateWeightsUI (); + } weight.blur (); }; }); @@ -173,8 +175,10 @@ selectCardUi.attachSourceBiasesEvents = function () { this.biases.forEach ((bias, index) => { bias.onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.setSourceBias (index, value); - playgroundFacade.updateBiasesUI (); + const hasChanged = networkState.setSourceBias (index, value); + if (hasChanged) { + playgroundFacade.updateBiasesUI (); + } bias.blur (); }; }); @@ -186,8 +190,10 @@ selectCardUi.attachSourceLearningRatesEvents = function () { this.learningRates.forEach ((learningRate, index) => { learningRate.children[0].onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.setSourceLearningRate (index, value); - playgroundFacade.updateUI (); + const hasChanged = networkState.setSourceLearningRate (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } }; }); } @@ -198,8 +204,10 @@ selectCardUi.attachSourceActivationsEvents = function () { this.activations.forEach ((activation, index) => { activation.children[0].onchange = (e: InputEvent) => { const value = (e.target as HTMLInputElement).value; - networkState.setSourceActivation (index, value); - playgroundFacade.updateUI (); + const hasChanged = networkState.setSourceActivation (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } }; }); } @@ -210,8 +218,10 @@ selectCardUi.attachSourceRegularizationTypesEvents = function () { this.regularizations.forEach ((regularization, index) => { regularization.children[0].onchange = (e: InputEvent) => { const value = (e.target as HTMLInputElement).value; - networkState.setSourceRegularizationType (index, value); - playgroundFacade.updateUI (); + const hasChanged = networkState.setSourceRegularizationType (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } }; }); } @@ -222,8 +232,10 @@ selectCardUi.attachSourceRegularizationRatesEvents = function () { this.regularizationRates.forEach ((regularizationRate, index) => { regularizationRate.children[0].onchange = (e: InputEvent) => { const value = parseFloat ((e.target as HTMLInputElement).value); - networkState.setSourceRegularizationRate (index, value); - playgroundFacade.updateUI (); + const hasChanged = networkState.setSourceRegularizationRate (index, value); + if (hasChanged) { + playgroundFacade.updateUI (); + } }; }); } From 06632de9ed87ea03f1c3088542f3c1c1e0206ff6 Mon Sep 17 00:00:00 2001 From: Bamdad Sabbagh Date: Wed, 17 Nov 2021 14:56:53 +0100 Subject: [PATCH 6/7] feat: add learning-rate option `0` --- index.html | 1 + src/app/ui/select-card.ui.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 65c5305..4a23c25 100644 --- a/index.html +++ b/index.html @@ -127,6 +127,7 @@

Tinker With a