From 2fdb91f84f6de8c03dcc9c50c20ea2fa79e255c1 Mon Sep 17 00:00:00 2001
From: sanine <sanine.not@pm.me>
Date: Sun, 11 Jun 2023 20:05:14 -0500
Subject: add self-connections

---
 src/mind/topology.js      |  58 ++++++++++++++++++++---
 src/mind/topology.test.js | 118 ++++++++++++++++++++++++++++++++++++----------
 2 files changed, 146 insertions(+), 30 deletions(-)

diff --git a/src/mind/topology.js b/src/mind/topology.js
index 58e229e..755fe4a 100644
--- a/src/mind/topology.js
+++ b/src/mind/topology.js
@@ -1,14 +1,60 @@
 'use strict';
 
 
-const PointProto = {
-	
+const DEFAULT_WEIGHT_MAX = 4;
+
+
+const graph_proto = {
+	connect: function(source, sink, weight) {
+		return network_connect(this, source, sink, weight);
+	},
 };
 
+export function network(input_count, internal_count, output_count, weight_max = 4) {
+	const count = input_count + internal_count + output_count;
+	const n = Object.create(graph_proto);
+	n.input_count = input_count;
+	n.output_count = output_count;
+	n.adjacency = new Array(count).fill([]);
+	n.weight = [];
+	return Object.freeze(n);
+}
+
+
+function is_input(n, index) {
+	return index < n.input_count;
+}
+function is_output(n, index) {
+	return index >= (n.adjacency.length - n.output_count);
+}
+
 
-export function Point(layer, index) {
-	return Object.freeze({
-		layer,
-		index,
+function network_connect(n, source, sink, weight) {
+	if (is_input(n, sink)) {
+		// inputs cannot be sinks
+		throw new Error("attempt to use input as sink");
+	}
+	if (is_output(n, source)) {
+		// outputs cannot be sources
+		throw new Error("attempt to use output as source");
+	}
+
+	const nn = Object.create(graph_proto);
+	nn.input_count = n.input_count;
+	nn.output_count = n.output_count;
+	nn.adjacency = n.adjacency.map((row, i) => {
+		if (i === source && i === sink) {
+			// self-loop
+			return [...row, 2];
+		} else if (i === source) {
+			return [...row, 1];
+		} else if (i === sink) {
+			return [...row, -1];
+		} else {
+			return [...row, 0];
+		}
 	});
+	nn.weight = [...n.weight, weight];
+
+	return Object.freeze(nn);
 }
diff --git a/src/mind/topology.test.js b/src/mind/topology.test.js
index 421eb17..5aab350 100644
--- a/src/mind/topology.test.js
+++ b/src/mind/topology.test.js
@@ -1,31 +1,101 @@
 'use strict';
 
-import { Point } from './topology';
+import { network } from './topology';
 
-test('Point works correctly', () => {
-	const p = Point(0, 1);
-	expect(p.layer).toBe(0);
-	expect(p.index).toBe(1);
-	expect(() => p.layer = 5).toThrow();
+
+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('Point equality works correctly', () => {
-	const a00 = Point(0, 0);
-	const b00 = Point(0, 0);
-	const a01 = Point(0, 1);
-	const b01 = Point(0, 1);
-	const a10 = Point(1, 0);
-	const b10 = Point(1, 0);
-	const a11 = Point(1, 1);
-	const b11 = Point(1, 1);
-
-	expect(a00 == b00).toBeTruthy();
-	expect(a01 == b01).toBeTruthy();
-	expect(a10 == b10).toBeTruthy();
-	expect(a11 == b11).toBeTruthy();
-
-	expect(a00 == a01).toBeFalsy();
-	expect(a00 == a10).toBeFalsy();
-	expect(a00 == a11).toBeFalsy();
+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 ],
+	});
 });
-- 
cgit v1.2.1