4
\$\begingroup\$

I am wondering what the common approach is to substituting any resistance network to one resistance only given that the a voltage is applied between two random nodes.

For example this network took me ages to solve.

enter image description here

I first combined the upper corner (two serial resistances and one more in parallel,) then I realized I could not make progress without transforming the rectangle connection into a star connection.

Certainly, there must be a better way of doing it.

This might be even harder, just in case.

Edit: Some may say there is no common approach but there must at least be an algorithm since computers do the job all the time.

\$\endgroup\$
7
  • 3
    \$\begingroup\$ It's not really a common problem outside of test questions and xkcd. \$\endgroup\$ Commented Nov 22, 2023 at 19:36
  • 1
    \$\begingroup\$ @SpehroPefhany Certainly falstad figured out how to do it. \$\endgroup\$
    – Niclas
    Commented Nov 22, 2023 at 19:43
  • 1
    \$\begingroup\$ the meme is great by the way \$\endgroup\$
    – Niclas
    Commented Nov 22, 2023 at 19:44
  • 2
    \$\begingroup\$ There is an algorithm that computers use all the time, and it's to build a honkin' great matrix in V and I and the components which is then solved by inversion. Humans take a smaller more elegant approach based on features of the network that allow short cuts. \$\endgroup\$
    – Neil_UK
    Commented Nov 22, 2023 at 20:38
  • 1
    \$\begingroup\$ @Niclas It's just a directed graph. Not complicated, actually. There is a standard approach that can be applied by hand, then fed to anything that can handle a matrix and be able to invert it and multiply it. Probably, I'd use the Schur complement to reduce the order a little. Writing a program to process a directed graph of nothing more than resistors would be a piece of cake. (A few hours at most.) Or you can use a numerical checkerboard method with trivial partials (conductance, really) then run some loops until it settles. \$\endgroup\$ Commented Nov 22, 2023 at 21:04

3 Answers 3

16
\$\begingroup\$

Some may say, there is no common approach but there must at least be algorithm since computers can do the job all the time.

More than a few ways. But time for just one. Since you bring up that computers do this routinely, then:

Graph Theory: Spice-Like

We'll follow a concrete example from start to finish to help illustrate a method that is similar to what Spice programs use. It's not the exact same method. But your resistor case is a subset of the problems Spice handles and for these cases the method I'll discuss is quite close enough that the differences are negligible.

If you want more specific details about how Spice programs approach this in more general cases, then please see "SPICE2: A Computer Program to Simulate Semiconductor Circuits" by Laurence W. Nagel, directly from Berkeley. This one is entirely free. Just click on the PDF link at that site. The details are addressed quite early in the text. So feel free.

Concrete Example

Here's a schematic in LTspice, including its netlist:

enter image description here

This netlist is in simple ASCII text. You can parse it, trivially. And I mean trivially! There are 7 lines you care about, obviously. Just write up a routine to read any resistor netlist saved by LTspice (or any Spice program, as all of them support these netlist Spice cards.) Make sure to label the two nodes that you care about making your measurement from. I've called them A and B but obviously you can make up anything you want, there.

Its Directed Graph: aka Incidence Matrix

The code you write (or the method you would use by hand) just converts the above to this directed graph matrix:

$$ \begin{matrix} \quad\fbox{A} & \\ & \begin{matrix} & & \text{nodes} \\ & & \overbrace{\begin{matrix}N_1 & N_2 & N_A & N_B\end{matrix}} \\ \text{edges}&\left\{\begin{matrix}R_1\\R_2\\R_3\\R_4\\R_5\\R_6\\R_7\end{matrix}\right.& \left[\quad\begin{matrix} \vphantom{R_1}\hfill 1 & \hfill 0 & \hfill 0 & \hfill -1 \\ \vphantom{R_1}\hfill 0 & \hfill 1 & \hfill -1 & \hfill 0 \\ \vphantom{R_1}\hfill 1 & \hfill -1 & \hfill 0 & \hfill 0 \\ \vphantom{R_1}\hfill 0 & \hfill 0 & \hfill -1 & \hfill 1 \\ \vphantom{R_1}\hfill 0 & \hfill -1 & \hfill 1 & \hfill 0 \\ \vphantom{R_1}\hfill 0 & \hfill 1 & \hfill 0 & \hfill -1 \\ \vphantom{R_1}\hfill 1 & \hfill 0 & \hfill -1 & \hfill 0 \end{matrix}\quad\right] \end{matrix} \end{matrix} $$

All I did was to read a line (by eye, obviously) and place a -1 for the first node and a +1 for the second node shown in the netlist for each part. Everything else gets 0. This is just falling off a log. Anyone can do this. And so can a very short piece of code.

Using freely available SymPy and SageMath for this, running on a variety of environments and using the Python language, write:

A = Matrix([[1,0,0,-1],[0,1,-1,0],[1,-1,0,0],[0,0,-1,1],
            [0,-1,1,0],[0,1,0,-1],[1,0,-1,0]])

You could, of course, generate that code automatically. And you may generate it as initializers for a matrix in any other language of your own choosing. Could not be easier.

Its Conductance Matrix

You will need a diagonal conductance matrix. This one is still easier (as if that were possible):

$$ \begin{matrix} \quad\fbox{C} & \\ & \begin{matrix} & \begin{matrix}R_1 & R_2 & R_3 & R_4 & R_5 & R_6 & R_7\end{matrix} \\ \begin{matrix}R_1\vphantom{\frac1{R_1}}\\R_2\vphantom{\frac1{R_1}}\\R_3\vphantom{\frac1{R_1}}\\R_4\vphantom{\frac1{R_1}}\\R_5\vphantom{\frac1{R_1}}\\R_6\vphantom{\frac1{R_1}}\\R_7\vphantom{\frac1{R_1}}\end{matrix}& \left[\quad\begin{matrix} \frac1{R_1} \\ & \frac1{R_2} & & & & \large{0} \\ & & \frac1{R_3} \\ & & & \frac1{R_4} \\ & & & & \frac1{R_5} \\ & \large{0} & & & & \frac1{R_6} \\ & & & & & & \frac1{R_7} \end{matrix}\quad\right] \end{matrix} \end{matrix} $$

Coded up as:

var('r1 r2 r3 r4 r5 r6 r7')
C = Matrix([[1/r1,0,0,0,0,0,0],[0,1/r2,0,0,0,0,0],[0,0,1/r3,0,0,0,0],
            [0,0,0,1/r4,0,0,0],[0,0,0,0,1/r5,0,0],[0,0,0,0,0,1/r6,0],
            [0,0,0,0,0,0,1/r7]])

The above conductance matrix can also include capacitors and inductors, as well. But the details applied by Spice programs become more nuanced and it's better that you refer to "SPICE2: A Computer Program to Simulate Semiconductor Circuits" by Laurence W. Nagel for some expansion.

For now, let's just move on with simple conductance matrices built only from resistor values.

General Matrix Solution Equation and the Schur Complement

It turns out that the product, \$A^T\,C\,A\$, always results in a square symmetric matrix seen as this block matrix:

$$W=A^T\,C\,A=\left[\begin{matrix}P&Q^T\\Q&R\end{matrix}\right]$$ At this point, we've two choices. Either choice works just fine. One is that we set \$v_a=1\:\text{V}\$ and \$v_b=0\:\text{V}\$ and find out what currents we get. The other is that we set the KCL sum at \$v_a\$ to be \$1\:\text{A}\$ and at \$v_b\$ to be \$-1\:\text{A}\$ and find out what the resulting node voltages are for \$v_a\$ and \$v_b\$.

If we set the currents and solve for all the node voltages, the matrix solution is harder since you need to invert a larger matrix. So it's better (my view) to set the node voltages and find out what the resulting currents will be. This allows the use of the Schur complement.

So we will instead just set \$v_a=1\:\text{V}\$ and \$v_b=0\:\text{v}\$ and solve for the complementary currents (what enters must be the same as what leaves.) The value, \$R_{total}=\frac{1\:\text{V}}{I}\$, will be the resistance.

The full-blown setup follows (made with MiKTeX and Latex code I learned about here):

enter image description here

(Note that you would likely use numerical values for \$C\$ and so \$W\$ wouldn't look that complicated. It would just be some numbers there.)

We want to solve for \$\hat{i}\$. We don't care about \$\hat{v}\$, as it really doesn't matter to us. So we use the Schur complement to avoid having to solve for \$\hat{v}\$ (since we don't care about it.) By ignoring what we don't care about, the computation problem is made simpler.

The Schur complement is trivial to derive. Note that (1) \$P\hat{v}+Q^T\hat{e}=\hat{0}\$ and (2) \$Q\hat{v}+R\hat{e}=\hat{i}\$? From (1), solve as \$\hat{v}=-P^{-1}Q^T\hat{e}\$ and then substitute that into (2):

$$\begin{align*} \hat{i}&=Q\hat{v}+R\hat{e} \\\\ &=Q\left(-P^{-1}Q^T\hat{e}\right)+R\hat{e} \\\\ &=-QP^{-1}Q^T\hat{e}+R\hat{e} \\\\ &=R\hat{e}-QP^{-1}Q^T\hat{e} \\\\ &=\left(R-QP^{-1}Q^T\right)\hat{e} \end{align*}$$

And that's the Schur complement.

Do note that it reduces the size of the required matrix inversion, this way. Just a 2x2 rather than inverting an entire 4x4 (which would succeed at getting the right results, but is generally more work because it also solves for those two voltages, \$v_1\$ and \$v_2\$, which we really don't need to know.)

Also, you can easily invert a 2x2 by hand. Not so much with a 4x4, where it starts to get more painful.

Performing the Indicated Computations

So here's the remaining code. I'll later then assign values to each of the resistors where their value is the same as their identification number and get some results.

W = A.T * C * A                    # square symmetric block matrix
P = W.extract([0,1],[0,1])         # P-block
Q = W.extract([2,3],[0,1])         # Q-block
R = W.extract([2,3],[2,3])         # R-block
excite = Matrix([1,0])             # excitation of 1 V and 0 V
for u in list(( R - Q * P.inv() * Q.T) * excite ):
    u.subs({ r1:1, r2:2, r3:3, r4:4, r5:5, r6:6, r7:7 })
315/523
-315/523

So, we'd find that the total resistance is \$\frac{1\:\text{V}}{I=\frac{315}{523}\:\text{A}}=\frac{523}{315}\:\Omega\approx 1.66\:\Omega\$.

That's it. Seriously.

(Note for later that \$I=\frac{315}{523}\:\text{A} \approx 602.294\:\text{mA}\$.)

Trust, but Verify!

So, let's verify. Here's the initial schematic modified to include all the extra stuff needed to get a numerical solution:

enter image description here

Note that the current leaving the voltage supply has the magnitude of \$602.294\:\text{mA}\$ and that \$\frac{1\:\text{V}}{602.294\:\text{mA}}\approx 1.66\:\Omega\$.

And that is a match!!!

(And the current is also seen to have matched, too.)

Summarizing the Algorithm

So, here's what you need to do:

  1. Define for yourself a line by line way of listing out the resistors and the nodes they connect up with. This is called a netlist. You can use the Spice format for this -- it is what I'd choose to do because then I could just use the schematic editor to create any kind of crazy schematic. And it would support other methods of generating netlists beyond the use of the Spice schematic editor. You don't have to do that. You can create your own format. Doesn't matter. You just need something simple to work with.
  2. Write code to read what you produce for step #1 and generate a directed graph matrix \$A\$ from it.
  3. Write extremely simple code to produce the conductance matrix \$C\$.
  4. Using the matrix math I've pointed out above, solve for the resulting current that is developed.
  5. Divide that current into the \$1\:\text{V}\$ excitation supplied to the network to get the resistance.

That's all there is.

There are other ways. But this one is straight forward and will handle any set of cases you present, so long as it is a fully connected graph.

Fully Connected Graphs: What???

Well, there are methods you can apply to the incidence matrix, \$A\$, to verify this.

For example, looking at its right nullspace will tell you if it is fully connected:

pprint(A.nullspace())
⎡⎡1⎤⎤
⎢⎢ ⎥⎥
⎢⎢1⎥⎥
⎢⎢ ⎥⎥
⎢⎢1⎥⎥
⎢⎢ ⎥⎥
⎣⎣1⎦⎦

Getting only one vector with all 1's tells you this is a fully connected graph. This vector says that the only way to get zero currents (the KCL null-space) is to have all of the existing nodes set to the same voltage. All of them. Not just some of them. (Otherwise, you'd see more vectors here.)

(Oh, I should include a short synopsis. So here is a description of the four fundamental subspaces for a short summary.)

Added Value for Graph Theory: Meshes!

It's easy to find the independent meshes using the left null-space:

pprint(A.T.nullspace())
⎡⎡1 ⎤  ⎡0⎤  ⎡-1⎤  ⎡0 ⎤⎤
⎢⎢  ⎥  ⎢ ⎥  ⎢  ⎥  ⎢  ⎥⎥
⎢⎢-1⎥  ⎢1⎥  ⎢0 ⎥  ⎢-1⎥⎥
⎢⎢  ⎥  ⎢ ⎥  ⎢  ⎥  ⎢  ⎥⎥
⎢⎢-1⎥  ⎢0⎥  ⎢1 ⎥  ⎢-1⎥⎥
⎢⎢  ⎥  ⎢ ⎥  ⎢  ⎥  ⎢  ⎥⎥
⎢⎢1 ⎥, ⎢0⎥, ⎢0 ⎥, ⎢0 ⎥⎥
⎢⎢  ⎥  ⎢ ⎥  ⎢  ⎥  ⎢  ⎥⎥
⎢⎢0 ⎥  ⎢1⎥  ⎢0 ⎥  ⎢0 ⎥⎥
⎢⎢  ⎥  ⎢ ⎥  ⎢  ⎥  ⎢  ⎥⎥
⎢⎢0 ⎥  ⎢0⎥  ⎢1 ⎥  ⎢0 ⎥⎥
⎢⎢  ⎥  ⎢ ⎥  ⎢  ⎥  ⎢  ⎥⎥
⎣⎣0 ⎦  ⎣0⎦  ⎣0 ⎦  ⎣1 ⎦⎦

Shows you one set of independent meshes in the circuit's directed graph. There are four, in this case. These can be combined in various ways to find other meshes. But there will only be four independent ones, regardless.

Looking at the circuit's directed graph (made with freely available Dia):

enter image description here

The first column vector above tells you that if you follow edge \$R_1\$ with its arrow, then go oppositely against \$R_3\$, then go oppositely against \$R_2\$, and then go with \$R_4\$, that you will have one mesh there. The second column vector says with \$R_2\$ and with \$R_5\$ is another mesh. (Parallel resistor pair.) The third column vector says against \$R_1\$, with \$R_6\$, with \$R_3\$ is another mesh. And the fourth column vector says against \$R_2\$, with \$R_7\$, against \$R_3\$ is another mesh.

These four meshes are colored in below:

enter image description here

Oh! Wait.

I added a 5th one, didn't I? The purple circle.

What about that mesh?

Well, it's just the sum of the yellow (1st), blue (2nd), and red (3rd) meshes. Let's double-check that:

U = A.T.nullspace()
pprint( U[0] + U[1] + U[2] )
⎡0⎤
⎢ ⎥
⎢0⎥
⎢ ⎥
⎢0⎥
⎢ ⎥
⎢1⎥
⎢ ⎥
⎢1⎥
⎢ ⎥
⎢1⎥
⎢ ⎥
⎣0⎦

Yup! Perfect.

So it seems that other meshes can be constructed from these independent ones.

If we added this one to the list and deleted the red one, then we'd still have four independent meshes that span the same KVL nullspace.

So you can add or subtract meshes to make other meshes.

But it takes exactly four independent ones, in this case, to span the KVL (left) null-space. If more than four, then at least one mesh must be dependent on others in the list.

This is because the rank of this incidence matrix \$A\$ is 3. Given that there are 7 rows, this means \$7-3=4\$ vectors required to span its KVL null-space. So long as the vectors are the minimum sufficient to span that null-space, you are good.

This is a cool way to find meshes needed to solve, when using the mesh method of analysis, by the way!

Final Note: Selecting Random Nodes

only given that the a voltage is applied between two random nodes.

The incidence matrix \$A\$ would keep all the rows just the same as before, since the rows correspond to resistors that are connected up into the circuit and you wouldn't be altering their topology.

So that stays the same.

But you would need to move the columns around so that the two nodes having the excitation applied to them are in the last two columns in order to avoid the need for horrible syntax.

(Technically, we could write some insane syntax. But let's avoid going there as it moves away from understanding things and towards language syntax geekland.)

For example, suppose we wanted to supply the excitation to the first and last nodes (\$v_1\$ and \$v_b\$) as shown in the following schematic:

enter image description here

The schematic topology is unchanged. So the rows in \$A\$ don't change, either. But the excitation can now be applied to any two nodes at will with the above schematic. In the case shown above, it's the first and last nodes of \$A\$.

The following code would take what has already been done above and apply it slightly differently (moving columns around with SymPy syntax) to get the desired result:

A1 = A[:,[1,2,0,3]]
W1 = A1.T * C * A1
P1 = W1.extract([0,1],[0,1])
Q1 = W1.extract([2,3],[0,1])
R1 = W1.extract([2,3],[2,3])
for u in list(( R1 - Q1 * P1.inv() * Q1.T) * excite ):
    u.subs({ r1:1, r2:2, r3:3, r4:4, r5:5, r6:6, r7:7 })
28/23
-28/23

(The [1,2,0,3] bit above says to build a new \$A\$ from the old \$A\$'s 2nd col, 3rd col, 1st col, and 4th col, in that order.)

So we find \$I=\frac{28}{23}\:\text{A}\approx 1.21739\:\text{A}\$.

A run of the above schematic shows:

I(V1):   -1.21739    device_current

Which is the expected result.

(So the resistance in this case would be \$R_{total}\approx 821.43\:\text{m}\Omega\$. And given the value of \$R_1\$, we'd expect something less than 1 here.)

This exposition not only shows you how to develop a solution for one case, but also how to adjust it to ask about pairing any two selected nodes. The topology of the resistor circuit is unchanged. It's just some syntax changes needed to construct the right Schur complement matrices for the solution. That's all.

I'll provide a final demonstration that is more about the power of software like Python/Sympy than the power of graph theory. The power of graph theory is already shown. But this just puts a cap on the example resistor circuit I started with. So may as well.

I can define a function that accepts a list of the four nodes, but ordered in such a way that we test every node pair for its effective resistance:

def findR(L1):
    global A, C                      # access to our A and C matrices
    A1 = A[:, L1]                    # temp re-arranged A
    W1 = A1.T * C * A1               # temp symmetric block matrix
    P1 = W1.extract( [0,1], [0,1] )  # temp P-block
    Q1 = W1.extract( [2,3], [0,1] )  # temp Q-block
    R1 = W1.extract( [2,3], [2,3] )  # temp R-block
    return 1.0/list( ( R1 - Q1 * P1.inv() * Q1.T ) * Matrix([1,0])
                   )[0].subs({ r1:1, r2:2, r3:3, r4:4, r5:5, r6:6, r7:7 })

With that in hand I can now perform the following:

for u in [ list(set([0,1,2,3])-set(i))+list(i)
    for i in itertools.combinations([0,1,2,3],2)]:
        u, findR(u).n()
([2, 3, 0, 1], 1.48571428571429)
([1, 3, 0, 2], 1.67222222222222)
([1, 2, 0, 3], 0.821428571428571)
([0, 3, 1, 2], 1.09126984126984)
([0, 2, 1, 3], 1.59285714285714)
([0, 1, 2, 3], 1.66031746031746)

And there you have all of the allowable equivalent resistances for that circuit, using any pair of nodes. (The last two nodes listed are the ones being probed.)

Summary of Graph Theory

There's a lot you can do when you better understand how KCL, KVL, the column-space, row-space, and two null-spaces all play well together to describe circuits.

I hope this should be sufficient to motivate you to experiment on your own, firming this up in mind.

Further reading:

  1. Wai-Kai Chen's "Graph Theory and Its Engineering Applications". The author assumes some maturity. But by the 2nd chapter you are off and running with circuit graphs that are practical and meaningful.

  2. Gilbert Strang (anything by him is good and usually targeted at undergrads just learning this stuff), 5th edition, "Differential Equations and Linear Algebra". Gilbert Strang really can help you with understanding linear matrices, vector spaces, and their application to solutions. (He taught his last class, May of 2023, at the age of 88! A great many owe him so much for his dedication to those starting out on this kind of learning path. I owe him what I can never hope to pay back. My best to him!)

I've been sniped!

\$\endgroup\$
6
  • 2
    \$\begingroup\$ Live long and prosper! 🤩 \$\endgroup\$ Commented Nov 23, 2023 at 12:27
  • \$\begingroup\$ Greetings! You did a nice job. .Could you apply Tellegen's theorem to the results obtained? \$\endgroup\$ Commented Nov 24, 2023 at 18:38
  • \$\begingroup\$ @Franc Thanks for the kind note. But as to adding anything more (and I'm not sure exactly what you want, anyway) I've already been sufficiently sniped and I consider this page as completed, unless errors are found I need to correct. However, Chen's book covers Tellegen's Theorem in its Chapter 2, Theorem 2.15. You could pose your own detailed question, though. \$\endgroup\$ Commented Nov 24, 2023 at 19:29
  • \$\begingroup\$ @periblepsis All right! it does not matter. \$\endgroup\$ Commented Nov 25, 2023 at 9:28
  • 1
    \$\begingroup\$ Insane answer. Thank you. \$\endgroup\$
    – Niclas
    Commented Nov 26, 2023 at 16:38
1
\$\begingroup\$

Personally I’d do it the good old “inspection route”:

  1. The upper right triangle reduces to 10Ω equivalent.

  2. The upper left triangle reduces to 30||60Ω=20Ω.

  3. Now we’re left with 10||(20+20)Ω=8Ω. That’s the load across the battery.

Hopefully I got that right :)

Should be all there’s to it. Complex networks may need a structured approach to analysis. The stuff you show is specifically designed for textbooks it looks like. If that’s a school assignment/book exercise, it was specifically put together to make mental math possible. Someone spent some time just so you can look at the problem and see the result in a minute or less, no calculator or anything.

At worst you may need a calculator if they use silly numbers for some reason. “Computer-based” homework assignments tend to be that way - when they use random numbers but don’t constrain them enough.

\$\endgroup\$
1
  • \$\begingroup\$ If you are referring to the visible schematic there, it's an unbalanced wheatstone bridge kind of thing. Doing delta-y on the 30/30/30 delta, I get 10 Ohm resistors for the Y. That means 10 Ohm in series with (30 || 20) or 22 Ohms net across the battery. Not 8 Ohms. But I don't always read things right, either. Let me know if I'm looking at the wrong schematic. Happens often enough. ;) And yes, I hope that Dr. Strang lives long and prospers! \$\endgroup\$ Commented Nov 24, 2023 at 7:57
1
\$\begingroup\$

It's a variant of a bridge circuit.

schematic

simulate this circuit – Schematic created using CircuitLab

Algorithmically, you can repeat these steps until reduction is achieved:

  • Find series equivalents
  • Find parallel equivalents
  • Find deltas and convert to wye
  • Fine wyes and convert to deltas

Which of the last two are applied, depends, and you may have to go back and forth several times, or backtrack, to find the correct transformation.

Here, notice R6 and R7 are in series. Reduce them to a single 20Ω.

There is now a parallel combination, R5 || (R6 + R7). 20 and 20 makes 10Ω.

schematic

simulate this circuit

How we have the classic 'H' or bridge circuit. If the legs were equal (R1/R3 = R2/Req) we could ignore R4, but that is not the case here, so we need to reduce further. There are no series or parallel elements, so let's try a delta-wye transform.

schematic

simulate this circuit

We have the following substitutions: \begin{eqnarray} R_a &=& \frac{R_1 R_2}{R_1 + R_2 + R_4} \\ R_b &=& \frac{R_1 R_4}{R_1 + R_2 + R_4} \\ R_c &=& \frac{R_2 R_4}{R_1 + R_2 + R_4} \end{eqnarray}

Since the values are equal, the results are pleasingly simple. We could convert the other one instead (R3-R4-Req), getting 10, 5 and 10/3 Ω (showing these is left an an exercise for the student).

We have more series, and then parallel, equivalents. Rb + R3 = 30, Rc + Req = 20; 20 || 30 = 12. Final series, Ra + 12 = 22 Ω, and that's the answer.

\$\endgroup\$
1
  • \$\begingroup\$ Yeah. Same answer I got and added as a comment to Kuba. I thought the OP wanted to know how to proceed in a general fashion. But perhaps I only got sniped! ;) \$\endgroup\$ Commented Nov 24, 2023 at 9:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.