$set) $rs[$i] = array_combine($rs_keys, $set); foreach($cs as $i=> $set) $cs[$i] = array_combine($cs_keys, $set); // we need the actual linear solver to get rid of an imported library // the code is taken from https://rosettacode.org/wiki/Gaussian_elimination#PHP ( linalg_solve($m,$b) was gauss_eliminate($A, $b, $N)) // slightly modified but it should do the job and is likely easy to follow // removes: import numpy as np function swap_rows(&$a, &$b, $r1, $r2) { if ($r1 == $r2) return; $tmp = $a[$r1]; $a[$r1] = $a[$r2]; $a[$r2] = $tmp; $tmp = $b[$r1]; $b[$r1] = $b[$r2]; $b[$r2] = $tmp; } function linalg_solve($m, $b) { $e = NULL; // dull errorhandling //get shape of matrix if(!is_array($m)) { $e = "Matrix is not an array!"; return Array('e'=>$e); } $shape[0] = count($m); if(!is_array($m[0])) { $e = "Matrix is not a two dimensional array!"; return Array('e'=>$e); } $shape[1] = count($m[0]); if(count($b) != $shape[1]) { $e = "Vector doesn't match Matrix dimension!"; return Array('e'=>$e); } // gauss_eliminate code below... [mostly] for ($col = 0; $col < $shape[0]; $col++) { $j = $col; $max = $m[$j][$j]; for ($i = $col + 1; $i < $shape[0]; $i++) { $tmp = abs($m[$i][$col]); if ($tmp > $max) { $j = $i; $max = $tmp; } } swap_rows($m, $b, $col, $j); for ($i = $col + 1; $i < $shape[0]; $i++) { $tmp = $m[$i][$col] / $m[$col][$col]; for ($j = $col + 1; $j < $shape[0]; $j++) { $m[$i][$j] -= $tmp * $m[$col][$j]; } $m[$i][$col] = 0; $b[$i] -= $tmp * $b[$col]; } } $vec = Array(); // the return vector we care about for ($col = $shape[0] - 1; $col >= 0; $col--) { $tmp = $b[$col]; for ($j = $shape[0] - 1; $j > $col; $j--) { $tmp -= $vec[$j] * $m[$col][$j]; } $vec[$col] = $tmp / $m[$col][$col]; } return Array('e'=>$e, 'r'=> $vec); } ########################## ## FUNCTIONS ########################## function error($msg) { die("Error: $msg"); } /* def node_factory(): # create empty node return SimpleNamespace(idx=None, rs=[], cs=[]) // No need for a factory, we can inline that // and it saves us from two imports ;) // removes: // from collections import defaultdict // from types import SimpleNamespace */ function create_structure($rs, $cs) { global $gnd; // I know.. but this isn't python and such... // the inlined structure of any given node, to remove node_factory() // $node_proto = Array('idx' => NULL, 'rs'=> Array(array('nd'=>NULL, 'r'=>NULL)), 'cs'=>Array()); // just for visual reference /* # create empty structure as a dictionary of nodes nodes = defaultdict(node_factory) */ $nodes = Array(); // see... no imports ;) # add resistors to structure //for name, nd1, nd2, r in rs: foreach($rs as $set) // not as sophisticated as python, but good enough { if($set['r'] == 0) error("Resistor {$set['name']} has value of zero"); $nodes[$set['nd1']]['rs'][]= Array('nd'=>$set['nd2'], 'r'=>$set['r']); $nodes[$set['nd2']]['rs'][]= Array('nd'=>$set['nd1'], 'r'=>$set['r']); } # add current sources //for nd1, nd2, c in cs: foreach($cs as $set) { $nodes[$set['nd1']]['cs'][]= -$set['c']; $nodes[$set['nd2']]['cs'][]= $set['c']; } # remove GND node from structure if existing or print error message if(isset($nodes[$gnd])) unset($nodes[$gnd]); else error("Circuit has no GND reference (node name {$gnd})"); # enumerate remaining nodes starting with zero // is perfectly pointless in php since we can already access all nodes by its index but let's keep that idx field //for i, key in enumerate(nodes): $i = 0; foreach($nodes as $key => $node) $nodes[$key]['idx'] = $i++; return $nodes; } function solve($nodes) { global $gnd; // I know.. but this isn't python and such... # size of equation system $n = count($nodes); # nxn matrix filled with zeros $m = Array(); # n dimensional vector filled with zeros $b = Array(); // actually filling them the long way for($x = 0; $x < $n; $x++) { $b[$x] = 0; for($y = 0; $y < $n; $y++) $m[$x][$y]=0; } # create equation system //for val in nodes.values(): foreach($nodes as $val) { $i = $val['idx']; # fill matrix with sums of conductances //for nd, r in val.rs: foreach($val['rs'] as $set) { $g = 1 / $set['r']; $m[$i][$i] += $g; if ($set['nd'] != $gnd) { $j = $nodes[$set['nd']]['idx']; $m[$i][$j] -= $g; } } // we need to skip some... if(!isset($val['cs'])) continue; # fill vector with sums of currents //for c in val.cs: foreach($val['cs'] as $c) $b[$i] += $c; } # compute voltage for each node as solutions of the equation system /* try: vs = np.linalg.solve(m, b) except np.linalg.LinAlgError as e: error(e) */ // we don't have the numpy linalg but use the helping hand instead... $vs = linalg_solve($m, $b); if($vs['e'] !=NULL) // simplified try-catch replacement error($vs['e']); return $vs['r']; // remove that stupid error thingy again } function show_results($nodes, $vs) { global $rs, $gnd; // yup... again ;) # show voltage for each node //for nd, val in nodes.items(): echo "
"; // just to have it nicer looking in a browser
	foreach($nodes as $key=>$node)
	printf("V_%-6s = %13.6e V \n",$key, $vs[$node['idx']]);
	//print()
# compute and show current for each resistor
	//for name, nd1, nd2, r in rs:
	foreach($rs as $resistor)
	{
		$u1 = ($resistor['nd1']==$gnd)? 0 : $vs[$nodes[$resistor['nd1']]['idx']];
		$u2 = ($resistor['nd2']==$gnd)? 0 : $vs[$nodes[$resistor['nd2']]['idx']];
		$c = ($u1 - $u2) / $resistor['r'];
		printf("I_%-6s = %13.6e A \n", $resistor['name'], $c);
	}
	echo "
"; // you can remove them of course if you console your way through } ########################## ## EXECUTION ########################## $nodes = create_structure($rs, $cs); $vs = solve($nodes); show_results($nodes, $vs); #DEBUG INFOS /* ## OUTPUT python V_A = 1.780984e-01 V V_B = 1.810636e-01 V V_C = 3.061216e-01 V I_R1 = -1.976798e-05 A I_R2 = -1.280232e-03 A I_R3 = -5.684455e-04 A I_R4 = 5.486775e-04 A I_R5 = 6.513225e-04 A php V_A = 1.780984e-1 V V_B = 1.810636e-1 V V_C = 3.061216e-1 V I_R1 = -1.976798e-5 A I_R2 = -1.280232e-3 A I_R3 = -5.684455e-4 A I_R4 = 5.486775e-4 A I_R5 = 6.513225e-4 A intermediates... $nodes: Array ( [A] => Array ( [rs] => Array ( [0] => Array ( [nd] => B [r] => 150 ) [1] => Array ( [nd] => C [r] => 100 ) ) [cs] => Array ( [0] => 0.0012 [1] => -0.0025 ) [idx] => 0 ) [B] => Array ( [rs] => Array ( [0] => Array ( [nd] => A [r] => 150 ) [1] => Array ( [nd] => C [r] => 220 ) [2] => Array ( [nd] => GND [r] => 330 ) ) [idx] => 1 ) [C] => Array ( [rs] => Array ( [0] => Array ( [nd] => A [r] => 100 ) [1] => Array ( [nd] => B [r] => 220 ) [2] => Array ( [nd] => GND [r] => 470 ) ) [cs] => Array ( [0] => 0.0025 ) [idx] => 2 ) ) $vs: Array ( [2] => 0.30612157772622 [1] => 0.18106357308585 [0] => 0.17809837587007 ) */ ?>