Make solver sparse, put example set in /examples with all items from https://cdn...
authorNick Downing <nick@ndcode.org>
Thu, 17 Jul 2025 05:22:50 +0000 (15:22 +1000)
committerNick Downing <nick@ndcode.org>
Thu, 17 Jul 2025 05:22:50 +0000 (15:22 +1000)
13 files changed:
.gitignore [new file with mode: 0644]
examples/SlotInPlane.png [moved from stillOKfor32_50x50.png with 57% similarity]
examples/stillOKfor32.png [moved from stillOKfor32_100x100.png with 56% similarity]
examples/stripe1.png [new file with mode: 0644]
examples/stripe1et.png [new file with mode: 0644]
examples/stripe1thin.png [moved from stillOKfor32_100x100_trimmed.png with 51% similarity]
examples/tentacledTriangle.png [new file with mode: 0644]
examples/tooBigFor32.png [new file with mode: 0644]
examples/wideStripe.png [new file with mode: 0644]
examples/wideStripe_punched.png [new file with mode: 0644]
n.sh [new file with mode: 0755]
resistor_solver.py
stillOKfor32.png [deleted file]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..b44ef38
--- /dev/null
@@ -0,0 +1 @@
+/*.png
similarity index 57%
rename from stillOKfor32_50x50.png
rename to examples/SlotInPlane.png
index 7f8b9dd..bf754f3 100644 (file)
Binary files a/stillOKfor32_50x50.png and b/examples/SlotInPlane.png differ
similarity index 56%
rename from stillOKfor32_100x100.png
rename to examples/stillOKfor32.png
index bac61b8..66824be 100644 (file)
Binary files a/stillOKfor32_100x100.png and b/examples/stillOKfor32.png differ
diff --git a/examples/stripe1.png b/examples/stripe1.png
new file mode 100644 (file)
index 0000000..c67161c
Binary files /dev/null and b/examples/stripe1.png differ
diff --git a/examples/stripe1et.png b/examples/stripe1et.png
new file mode 100644 (file)
index 0000000..9af8838
Binary files /dev/null and b/examples/stripe1et.png differ
similarity index 51%
rename from stillOKfor32_100x100_trimmed.png
rename to examples/stripe1thin.png
index 368a3d0..d7323e5 100644 (file)
Binary files a/stillOKfor32_100x100_trimmed.png and b/examples/stripe1thin.png differ
diff --git a/examples/tentacledTriangle.png b/examples/tentacledTriangle.png
new file mode 100644 (file)
index 0000000..c5fea75
Binary files /dev/null and b/examples/tentacledTriangle.png differ
diff --git a/examples/tooBigFor32.png b/examples/tooBigFor32.png
new file mode 100644 (file)
index 0000000..9dadd2c
Binary files /dev/null and b/examples/tooBigFor32.png differ
diff --git a/examples/wideStripe.png b/examples/wideStripe.png
new file mode 100644 (file)
index 0000000..932d0e3
Binary files /dev/null and b/examples/wideStripe.png differ
diff --git a/examples/wideStripe_punched.png b/examples/wideStripe_punched.png
new file mode 100644 (file)
index 0000000..0a12957
Binary files /dev/null and b/examples/wideStripe_punched.png differ
diff --git a/n.sh b/n.sh
new file mode 100755 (executable)
index 0000000..1e25e22
--- /dev/null
+++ b/n.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+./resistor_solver.py examples/SlotInPlane.png SlotInPlane.png
+./resistor_solver.py examples/stillOKfor32.png stillOKfor32.png
+./resistor_solver.py examples/stripe1et.png stripe1et.png
+./resistor_solver.py examples/stripe1.png stripe1.png
+./resistor_solver.py examples/stripe1thin.png stripe1thin.png
+./resistor_solver.py examples/tentacledTriangle.png tentacledTriangle.png
+./resistor_solver.py examples/tooBigFor32.png tooBigFor32.png
+./resistor_solver.py examples/wideStripe.png wideStripe.png
+./resistor_solver.py examples/wideStripe_punched.png wideStripe_punched.png
index af87228..8529da9 100755 (executable)
@@ -4,6 +4,8 @@
 
 import PIL.Image
 import numpy
+import scipy.sparse
+import scipy.sparse.linalg
 import sys
 
 PIL.Image.warnings.simplefilter('ignore', PIL.Image.DecompressionBombWarning)
@@ -26,9 +28,20 @@ image = numpy.array(image_pil)
 #print('shape', image.shape)
 #print('dtype', image.dtype)
 assert len(image.shape) == 2 and image.dtype == numpy.uint8
-assert numpy.all(image < 4)
 # colours are 0 = black, 1 = blue, 2 = red, 3 = white
-#print('xxx', image_pil.palette.tobytes()[:12])
+palette = numpy.array(list(image_pil.palette.tobytes()), numpy.uint8)
+#print('palette', palette)
+assert numpy.all(
+  (palette >= 0x80) == numpy.array(
+    [
+      False, False, False,
+      False, False, True,
+      True, False, False,
+      True, True, True,
+    ],
+    bool
+  )
+)
 ys, xs = image.shape
 n = ys * xs
 
@@ -42,25 +55,32 @@ image_vert[:-1, :] = numpy.logical_and(image_nz[:-1, :], image_nz[1:, :])
 
 # KCL is enforced as follows:
 # A V = I where V = voltage at node, I = net current out of node
-# I is current "unaccounted for", i.e. current that cancels R currents
+# (when node is electrode, positive I means current is entering the array)
 # resistor R connected n1 -> n2:
-#   causes a current of (V(n2) - V(n1)) / R, positive when V(n2) > V(n1)
-#   => when current is positive, it should decrease I(n1) and increase I(n2)
-#   => I(n1) -= 1/R V(n2) - 1/R V(n1), I(n2) += 1/R V(n2) - 1/R V(n1)
-#   => I(n1) += 1/R V(n1) - 1/R V(n1), I(n2) += -1/R V(n1) + 1/R V(n2)
-A = numpy.zeros((n, n), numpy.int8)
+#   causes a current of (V(n1) - V(n2)) / R, positive when V(n1) > V(n2)
+#   => when current is positive, it should increase I(n1) and decrease I(n2)
+#   => I(n1) += 1/R V(n1) - 1/R V(n2), I(n2) -= 1/R V(n1) - 1/R V(n2)
+v = []
+i = []
+j = []
 for n1 in numpy.nonzero(image_horiz.reshape((n,)))[0]:
   n2 = n1 + 1
-  A[n1, n1] += 1
-  A[n1, n2] -= 1
-  A[n2, n1] -= 1
-  A[n2, n2] += 1
+  v.extend([1, -1, -1, 1])
+  i.extend([n1, n1, n2, n2])
+  j.extend([n1, n2, n1, n2])
 for n1 in numpy.nonzero(image_vert.reshape((n,)))[0]:
   n2 = n1 + ys
-  A[n1, n1] += 1
-  A[n1, n2] -= 1
-  A[n2, n1] -= 1
-  A[n2, n2] += 1
+  v.extend([1, -1, -1, 1])
+  i.extend([n1, n1, n2, n2])
+  j.extend([n1, n2, n1, n2])
+v = numpy.array(v, numpy.int32)
+i = numpy.array(i, numpy.int32)
+j = numpy.array(j, numpy.int32)
+A = scipy.sparse.coo_array(
+  (v, (i, j)),
+  shape = (n, n),
+  dtype = numpy.int32
+).tocsr()
 
 # partition nodes into 3 groups:
 # 0: black + blue (electrode), 1: red (electrode), 2: white (conductor)
@@ -93,7 +113,7 @@ A22 = A[g2, :][:, g2]
 A11_V1 = numpy.sum(A11.astype(numpy.int32), 1)
 A21_V1 = numpy.sum(A21.astype(numpy.int32), 1)
 print('solve')
-V2 = numpy.linalg.solve(
+V2 = scipy.sparse.linalg.spsolve(
   A22.astype(numpy.double),
   -A21_V1.astype(numpy.double)
 )
diff --git a/stillOKfor32.png b/stillOKfor32.png
deleted file mode 100644 (file)
index 4b51a32..0000000
Binary files a/stillOKfor32.png and /dev/null differ