summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-06-11 22:20:04 -0500
committersanine <sanine.not@pm.me>2023-06-11 22:20:04 -0500
commit3b0b005b952b1092404fdd5ae1732ec9561794af (patch)
tree1121694d35ad513d816cba7759eef7ea04d647d2
parent2fdb91f84f6de8c03dcc9c50c20ea2fa79e255c1 (diff)
add basic computations
-rw-r--r--src/mind/topology.js56
-rw-r--r--src/mind/topology.test.js38
2 files changed, 94 insertions, 0 deletions
diff --git a/src/mind/topology.js b/src/mind/topology.js
index 755fe4a..1cd52d3 100644
--- a/src/mind/topology.js
+++ b/src/mind/topology.js
@@ -8,6 +8,9 @@ const graph_proto = {
connect: function(source, sink, weight) {
return network_connect(this, source, sink, weight);
},
+ compute: function(inputs, state) {
+ return network_compute(this, inputs, state);
+ },
};
export function network(input_count, internal_count, output_count, weight_max = 4) {
@@ -58,3 +61,56 @@ function network_connect(n, source, sink, weight) {
return Object.freeze(nn);
}
+
+
+function incident_edges(n, adj) {
+ const incident = adj
+ .map((edge, index) => edge < 0 ? index : null)
+ .filter(index => index !== null);
+
+ return incident;
+}
+
+
+function edge_ends(n, edge) {
+ const ends = n.adjacency
+ .map((adj, index) => adj[edge] !== 0 ? index : null)
+ .filter(index => index != null);
+
+ ends.sort((a, b) => n.adjacency[a][edge] < n.adjacency[b][edge] ? -1 : 1);
+
+ return ends;
+}
+
+
+function get_value(n, index, input) {
+ if (is_input(n, index)) {
+ return input[index];
+ }
+ const adj = n.adjacency[index];
+ const incident = incident_edges(n, adj);
+ const weight = incident.map(x => n.weight[x]);
+ const sources = incident
+ .map(x => edge_ends(n, x))
+ .map(x => x.length === 2 ? x[1] : x[0]);
+
+ const sum = sources
+ .reduce((acc, x, i) => acc + (weight[i] * get_value(n, x, input)), 0);
+
+ return Math.tanh(sum);
+}
+
+
+function network_compute(n, input, state) {
+ const outputs = n.adjacency
+ .map((x, i) => is_output(n, i) ? i : null)
+ .filter(i => i !== null);
+
+ const output = Object.freeze(
+ outputs.map(x => get_value(n, x, input))
+ );
+
+ const newstate = Object.freeze([]);
+
+ return Object.freeze([output, newstate]);
+}
diff --git a/src/mind/topology.test.js b/src/mind/topology.test.js
index 5aab350..e1c5f87 100644
--- a/src/mind/topology.test.js
+++ b/src/mind/topology.test.js
@@ -99,3 +99,41 @@ test('self-connections work correctly', () => {
weight: [ 2 ],
});
});
+
+
+test('network computations', () => {
+ const n = network(1, 0, 1).connect(0, 1, 2.0);
+ const input = [ -0.5 ];
+ const state = [];
+ const result = n.compute(input, state);
+ expect(result).toEqual([
+ [ Math.tanh(-0.5 * 2.0) ],
+ [],
+ ]);
+
+ expect(input).toEqual([ -0.5 ]);
+ expect(state).toEqual([]);
+
+ expect(() => result[0] = 'hi').toThrow();
+ expect(() => result[0].push('hi')).toThrow();
+ expect(() => result[1] = 'hi').toThrow();
+ expect(() => result[1].push('hi')).toThrow();
+});
+
+
+test('multiple input network', () => {
+ const n = network(4, 0, 1)
+ .connect(0, 4, -1.0)
+ .connect(1, 4, -2.0)
+ .connect(2, 4, 1.0)
+ .connect(3, 4, 2.0)
+
+ expect(n.compute([1, 2, 3, 5], [])).toEqual([
+ [ Math.tanh(
+ (-1.0 * 1) +
+ (-2.0 * 2) +
+ (1.0 * 3) +
+ (2.0 * 5))],
+ [],
+ ]);
+});