diff --git a/index.html b/index.html
index 236f8d2..b44715e 100644
--- a/index.html
+++ b/index.html
@@ -472,6 +472,7 @@
Rules
+
@@ -499,7 +500,7 @@
Rules
-
+
diff --git a/js/inputs/settings.js b/js/inputs/settings.js
index 77f3afe..54125e0 100644
--- a/js/inputs/settings.js
+++ b/js/inputs/settings.js
@@ -1,5 +1,6 @@
import { automata, NeuralCA, setAutomata } from "../automata.js";
import {
+ genShieldedFunction,
getConsoleText,
mooreNeighborhood,
reshape2DArray,
@@ -377,7 +378,9 @@ presetSelector.onchange = (event) => {
[-0.9, -0.66, -0.9],
[0.68, -0.9, 0.68],
],
- activation: (x) => -(1 / Math.pow(2, 0.6 * Math.pow(x, 2))) + 1,
+ activation: eval(
+ `(${"function activation(x) {\n\treturn -(1 / Math.pow(2, 0.6 * Math.pow(x, 2))) + 1\n}"})`
+ ),
},
stars: {
weights: [
@@ -385,7 +388,9 @@ presetSelector.onchange = (event) => {
[-0.759, 0.627, -0.759],
[0.565, -0.716, 0.565],
],
- activation: (x) => Math.abs(x),
+ activation: eval(
+ `(${"function activation(x) {\n\treturn Math.abs(x)\n}"})`
+ ),
},
"slime-mold": {
weights: [
@@ -393,7 +398,9 @@ presetSelector.onchange = (event) => {
[-0.85, -0.2, -0.85],
[0.8, -0.85, 0.8],
],
- activation: (x) => -1 / (0.89 * Math.pow(x, 2) + 1) + 1,
+ activation: eval(
+ `(${"function activation(x) {\n\treturn -1 / (0.89 * Math.pow(x, 2) + 1) + 1\n}"})`
+ ),
},
waves: {
weights: [
@@ -401,7 +408,9 @@ presetSelector.onchange = (event) => {
[-0.716, 0.627, -0.716],
[0.565, -0.716, 0.565],
],
- activation: (x) => Math.abs(1.2 * x),
+ activation: eval(
+ `(${"function activation(x) {\n\treturn Math.abs(1.2 * x)\n}"})`
+ ),
},
mitosis: {
weights: [
@@ -409,7 +418,9 @@ presetSelector.onchange = (event) => {
[0.88, 0.4, 0.88],
[-0.939, 0.88, -0.939],
],
- activation: (x) => -1 / (0.91 * Math.pow(x, 2) + 1) + 1,
+ activation: eval(
+ `(${"function activation(x) {\n\treturn -1 / (0.91 * Math.pow(x, 2) + 1) + 1\n}"})`
+ ),
},
pathways: {
weights: [
@@ -417,7 +428,19 @@ presetSelector.onchange = (event) => {
[1.0, 1.0, 1.0],
[0.0, 1.0, 0.0],
],
- activation: (x) => 1 / Math.pow(2, Math.pow(x - 3.5, 2)),
+ activation: eval(
+ `(${"function activation(x) {\n\treturn 1 / Math.pow(2, Math.pow(x - 3.5, 2))\n}"})`
+ ),
+ },
+ "game-of-life": {
+ weights: [
+ [1.0, 1.0, 1.0],
+ [1.0, 9.0, 1.0],
+ [1.0, 1.0, 1.0],
+ ],
+ activation: eval(
+ `(${"function activation(x) {\n\tif (x == 3 || x == 11 || x == 12) {\n\treturn 1;\n}\nreturn 0}"})`
+ ),
},
};
@@ -430,11 +453,10 @@ presetSelector.onchange = (event) => {
createGrid("neural-settings");
// Update activation
- const activationSelector = document.getElementById(
- "neural-activation-select"
- );
+ console.log(event.target.value);
activationSelector.selectedIndex = activationSelector.options.length - 1; // Set to custom activation
automata.activation = argMap[event.target.value].activation;
+ editor.setValue(automata.activation.toString());
automata.randomize();
};
@@ -445,15 +467,27 @@ activationSelector.onchange = (event) => {
setActivation(event.target.value);
};
-function setActivation(type, args = []) {
+function setActivation(type) {
+ // String representations of preset strings
const funcMap = {
- identity: (x) => x,
- power: (x) => Math.pow(x, 2),
- absolute: (x) => Math.abs(x),
- tanh: (x) => (Math.exp(2 * x) - 1) / (Math.exp(2 * x) + 1),
- "inverse-gaussian": (x) => -(1 / Math.pow(2, Math.pow(x, 2))) + 1,
+ identity: "function activation(x) {\n\treturn x;\n}",
+ power: "function activation(x) {\n\treturn Math.pow(x, 2);\n}",
+ absolute: "function activation(x) {\n\treturn Math.abs(x);\n}",
+ tanh: "function activation(x) {\n\treturn (Math.exp(2 * x) - 1) / (Math.exp(2 * x) + 1);\n}",
+ "inverse-gaussian":
+ "function activation(x) {\n\treturn -(1 / Math.pow(2, Math.pow(x, 2))) + 1;\n}",
};
- automata.activation = funcMap[type];
+
+ // Convert string representations to actual functions using eval
+ const evaluatedFuncMap = Object.fromEntries(
+ Object.entries(funcMap).map(([key, funcString]) => {
+ return [key, eval(`(${funcString})`)];
+ })
+ );
+ automata.activation = evaluatedFuncMap[type];
+
+ // Update code editor
+ editor.setValue(automata.activation.toString());
}
// Intialize activation func code editor
@@ -470,14 +504,21 @@ editor.session.setUseSoftTabs(true);
editor.setValue(
"function activation(x) {\n\treturn -(1 / Math.pow(2, 0.6 * Math.pow(x, 2))) + 1;\n}"
);
-editor.session.on("change", function (delta) {
+editor.session.on("change", function (_) {
+ // Set to custom activation
+ activationSelector.selectedIndex = activationSelector.options.length - 1;
+ // Parse the code
const code = editor.getValue();
try {
- // Evaluate the code and assign the function to a variable
- const activation = eval(`(${code})`);
- console.log(eval(`(${code})`));
+ // Evaluate the code and test it on a value
+ const activation = genShieldedFunction(eval(`(${code})`));
+ const testValues = [Math.random(), Math.random(), Math.random(), 0, 1];
+ testValues.forEach((value) => activation(value));
+ automata.activation = activation;
+ setConsoleText("Updated Activation Function!");
} catch (error) {
- console.error("Error in the code:", error);
+ console.log(`Error: ${error}`);
+ setConsoleText("Invalid Activation Function!");
}
});
diff --git a/js/utils.js b/js/utils.js
index 6319cf7..92c49bd 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -176,6 +176,17 @@ export function reshape2DArray(
return newArray;
}
+// Parses a function string and shields against misuse
+export function genShieldedFunction(func) {
+ return (x) => {
+ // Create a new scope for `f`
+ return (function () {
+ const document = null;
+ const window = null;
+ return func(x);
+ })();
+ };
+}
// Generates random number using a normal distribution
export function gaussianRandom(mean, variance) {
let u1 = Math.random();
@@ -237,7 +248,7 @@ export function shiftHue(rgbColor, hueShift = 0) {
const newR = r * matrix[0][0] + g * matrix[0][1] + b * matrix[0][2];
const newG = r * matrix[1][0] + g * matrix[1][1] + b * matrix[1][2];
const newB = r * matrix[2][0] + g * matrix[2][1] + b * matrix[2][2];
- // Clamp values within range od RGB
+ // Clamp values within range of RGB
return [
Math.round(Math.max(Math.min(newR, 255), 0)),
Math.round(Math.max(Math.min(newG, 255), 0)),