'use strict'; import { network } from './topology'; test('basic network functionality', () => { const n = network(0, 5, 0); expect(n).toEqual({ input_count: 0, output_count: 0, adjacency: [ [], [], [], [], [] ], weight: [], }); expect(() => n.adjacency = []).toThrow(); expect(() => n.weight = []).toThrow(); const nn = n.connect(0, 1, -2); expect(nn).toEqual({ input_count: 0, output_count: 0, adjacency: [ [ 1 ], [ -1 ], [ 0 ], [ 0 ], [ 0 ] ], weight: [ -2 ], }); expect(() => nn.adjacency = []).toThrow(); expect(() => nn.weight = []).toThrow(); const nnn = nn.connect(2, 4, 3); expect(nnn).toEqual({ input_count: 0, output_count: 0, adjacency: [ [ 1, 0 ], [ -1, 0 ], [ 0, 1 ], [ 0, 0 ], [ 0, -1 ] ], weight: [ -2, 3 ], }); expect(() => nnn.adjacency = []).toThrow(); expect(() => nnn.weight = []).toThrow(); }); test( 'networks are restricted from sinking to inputs or sourcing from outputs', () => { const n = network(2, 2, 2); expect(n.connect(1,2,0)).toEqual({ input_count: 2, output_count: 2, adjacency: [ [ 0 ], [ 1 ], [ -1 ], [ 0 ], [ 0 ], [ 0 ], ], weight: [ 0 ], }); expect(() => n.connect(2, 1, 0)).toThrow(); expect(n.connect(3, 4, 2)).toEqual({ input_count: 2, output_count: 2, adjacency: [ [ 0 ], [ 0 ], [ 0 ], [ 1 ], [ -1 ], [ 0 ], ], weight: [ 2 ], }); expect(() => n.connect(4, 3, 2)).toThrow(); }); test('self-connections work correctly', () => { const n = network(0, 1, 0).connect(0, 0, 2.0); expect(n).toEqual({ input_count: 0, output_count: 0, adjacency: [ [ 2 ], ], 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))], [], ]); }); test('multiple outputs', () => { const n = network(4, 0, 2) .connect(0, 4, -1) .connect(1, 4, 1) .connect(2, 5, -1) .connect(3, 5, 1); expect(n.compute([1,2,3,5], [])).toEqual([ [ Math.tanh(2-1), Math.tanh(5-3) ], [], ]); }); test('hidden neurons', () => { const n = network(4, 2, 1) .connect(0, 4, -1) .connect(1, 4, 1) .connect(2, 5, -1) .connect(3, 5, 1) .connect(4, 6, -1) .connect(5, 6, 1); expect(n.compute([1,2,3,5], [ 0, 0 ])).toEqual([ [ Math.tanh( Math.tanh(5-3) - Math.tanh(2-1) ) ], [ Math.tanh(2-1), Math.tanh(5-3) ], ]); }); test('arbitrary hidden neurons', () => { const n = network(1, 2, 1) .connect(0, 1, 1) .connect(1, 2, -1) .connect(2, 3, 2) const [output, state] = n.compute([1], [0, 0]); expect(output).toEqual([ Math.tanh( 2*Math.tanh( -1*Math.tanh(1) ) ) ]); expect(state).toEqual([ Math.tanh(1), Math.tanh( -Math.tanh(1) ), ]); }); test('memory', () => { const n = network(0, 1, 1).connect(0, 0, -0.5).connect(0, 1, 2); expect(n.compute([], [1])).toEqual([ [ Math.tanh( 2 * Math.tanh( -0.5 * 1 ) ) ], [ Math.tanh( -0.5 * 1) ], ]); }); test('memory and input', () => { const n = network(1, 1, 1) .connect(0, 1, 1) .connect(1, 1, 1) .connect(1, 2, 1); expect(n.compute([2], [-1])).toEqual([ [ Math.tanh( Math.tanh( 2-1 ) ) ], [ Math.tanh( 2-1 ) ], ]); }); test('input and state must be the correct size', () => { const n = network(2, 1, 1) .connect(0, 2, 1) .connect(1, 2, 1) .connect(2, 3, 1); // wrong input size expect(() => n.compute([], [4])).toThrow(); expect(() => n.compute([1], [4])).toThrow(); expect(() => n.compute([1, 1, 1], [4])).toThrow(); // wrong state size expect(() => n.compute([1, 1], [])).toThrow(); expect(() => n.compute([1, 1], [4, 4])).toThrow(); // prove correct sizes work n.compute([1, 1], [4]); });