diff options
author | sanine <sanine.not@pm.me> | 2023-06-11 22:20:04 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-06-11 22:20:04 -0500 |
commit | 3b0b005b952b1092404fdd5ae1732ec9561794af (patch) | |
tree | 1121694d35ad513d816cba7759eef7ea04d647d2 | |
parent | 2fdb91f84f6de8c03dcc9c50c20ea2fa79e255c1 (diff) |
add basic computations
-rw-r--r-- | src/mind/topology.js | 56 | ||||
-rw-r--r-- | src/mind/topology.test.js | 38 |
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))], + [], + ]); +}); |