Resistive circuit calculation

Hello everybody,

i’m quite new to prolog and looking at a practice project:

I came up with different code for paths and calculating row and parallel resitances.

Currently my biggest issue seems to be, how to find out in this grid, or another random grid, if the next resistance is parallel or in row without them being predefined as such, how to save that result to use it in further calculation and how to make use of the split and fusion.

This is what i have sofar:

Currently i seem to be missing an important thought to make it work, and probably some coding skills aswell. Would appreciate any help!

Thanks in advance, kind regards and stay healthy!

So i worked on the code a bit and changed some of it. Here is what i got:

For the algorithm, in this specific example it would work to look at the number of possible paths which is max 2.
If the amount of paths is 1, it has to be serial.
If the amount of paths is 2, the path with 1 more member and its position has to be identified.
For example:
a->d
= P1 = a->b->c->d (a->b->c is a row of serial resistors)
P2 = a->c>d (a->c is paralell to the section of P1)

I am not sure yet how to do the needed operations on those paths.
Another problem is that i was not able to think of a solution to handle bigger circuits with more than two possible paths.

Any ideas would be very welcome

I think you may get more input if you provided a specification of the problem, i.e., given your network model as a list of resistances (resistance=a resistor connecting two nodes), what question are you attempting to answer?

One possibility: Find a single “resistance” which is equivalent to the network.

Presented with this problem, I would try to find two resistances in the list that could be combined (parallel or serial). Then replace the two in the list with their equivalent single resistance and recurse until I was left with a single value.

But maybe that’s not your “question”.

That’s exactly the question. Find a single resistance which is equivalent to the network.

Given informations are:

Resistance between two nodes: e.g.
(a,500,b)
(b,50,c)
(a,550,c)
Splits and joints in the network:
split(a)
split©

The splits seem to be of no use to me since, depending on where you start and where you want to go, splits are no longer splits but simply part of resistors in a row. But maybe i’m just not seeing why this would still be usefull.

For smaller networks it seems doable. Find all paths from a to b. Compare them. Try to find rules for resistors in parallel and in row. Combine and replace in the list.

As soon as more then two paths are available and the differences get way bigger i’m lost on what the algorithem should be.

OK, my suggestions:

I think the data model is fairly straight forward: a resistor network is defined by a list of resistor instances which can be represented by term of the form:

resistance(N1,R,N2)

or something similar. N1 and N2 are node labels, e.g., atoms like a, b, etc. and R is the resistor value (a number). To keep things simple for now let’s assume N1 @< N2, i.e., the labels are ordered, which can be done without any loss of generality.

The objective is to find “single resistance which is equivalent to the network”. One way of doing this is to iteratively replace a pair of resistors in the network by a single resistor until the network list consists of a single resistor. A slightly more general predicate might combine pairs of resistors until a minimum network is achieved, i.e., no further combinations can be found:

res_reduce(Rs,R) :-
	res_combine(Rs,NRs),    % combine two into one
	res_reduce(NRs,R).      % and repeat
res_reduce(Rs,Rs).          % minimal network

So now the trick is to write rules which find and replace a pair of resistors in the list with a single value. Such a rule for parallel resistors might look like:

res_combine(Rs,[resistance(N1,Rn,N2)|NRs]) :-
	select(resistance(N1,R1,N2),Rs,Rs1),
	select(resistance(N1,R2,N2),Rs1,NRs),
 	!,  % avoid duplicates due to order selected
	Rn is (R1*R2)/(R1+R2).

This uses select/3 from library(lists) which is autoloaded. If you’re going to use lists, this library provides many useful predicates.

The res_combine rule for serial resistors is a little more complicated since it effectively removes a node from the network and you have to make sure no other resistor is connected to that node, but I’ll leave it to you as an exercise.

Example queries using your test network (using res_network to define the network):

?- res_network(Rs),res_reduce(Rs,[EqR]).
Rs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
EqR = resistance(b, 200, d) ;
Rs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
EqR = resistance(a, 400r3, d) ;
false.

?- res_network(Rs),res_reduce(Rs,[resistance(a,R,T)]).
Rs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
R = 400r3,
T = d ;
false.

The first query finds two equivalent resistance values: 200 ohms between b and d and 400r3 (133.33..) ohms between a and c. The second query looks for the equivalent resistance connected to node a.

Does something like this meet your needs?

Hey ridgeworks,

thanks alot! Very much so. I’m sorry, i haven’t had much time. You are already the greatest help i could wish for, but some questions are left.

I changed “resistance” to “verb”.
Here is what i came up with for parallel:

 member2(N,[H|T]):- verb(N,_,_) = H,!; verb(_,_,N) = H,!; member2(N,T). %to check if node is connected in rest list
 
  res_combine(Rs,[verb(N1,Rn,N2)|NRs]):-
 select(verb(N1,R1,N),Rs,Rs1),
 select(verb(N,R2,N2),Rs1,NRs),
 not(member2(N,NRs)),
 !,
 Rn is R1+R2.

The code now works perfectly for the whole network.

But i need another predicate that allows me to search for the resistance between specific nodes.
res_specific_nodes/4 searches the reduced network:

  res_specific_nodes([H|T],StartN,Goal,R):-
H = verb(StartN,R,Goal),!;  %check for original node order
H = verb(GoalN,R,StartN),!; %check in case query asks the other way around
res_specific_nodes(T,StartN,GoalN,R).

r_netz/3 which should be the final goal of the task delivers a user friendly query:

r_netz(A,B,R):- res_network(Rs),res_reduce(Rs,Rn),res_specific_nodes(Rn,A,B,R).

Now there is one last problem.
Nr. 1:(solved)
r_netz/3 delivers multiple solutions. Each reduce step.

?- r_netz(a,c,R).
R = 83.33333333333333 ;
R = 500 ;
R = 100 ;
false.

While i only need the first one. (Solved stupidly simple by a “,!.” at the end of r_netz/3… :slight_smile:
2:
Another scenario that doesn’t work as intended:

 ?- r_netz(a,b,R).
 R = 200 ;
 false.

While this is not necessarily false since there is a fact verb(a,200,b)/resistance(a,200,b), this ignores the additional path a-100c, c-300-b. Which in combination with a-200-b should result in R=133,33.

How can i solve this without destroying the cases that work correctly?

Thanks alot and kind regards!

Here the code in total, working and ready to be used(with the mentioned special cases not working):
ResistiveCircuit

Before getting into your second question, look again at your solution for combining serial resistors. You correctly matched the pattern: where ( verb(N1,R1,N) and verb(N,R2,N2) form possible serial pair providing no other "verb"s are connected to N. But aren’t there two more possibilities, namely:

  • verb(N,R1,N1) and verb(N,R2,N2) ==> verb(N1,R1+R2,N2)
  • verb(N1,R1,N) and verb(N2,R2,N) ==> verb(N1,R1+R2,N2)

So I had an additional goal after the two select/3 goals which looked for the different configurations combining two resistors in a serial connection (using your verb functor name:

series_connection(verb(N1,R1,Nc),verb(Nc,R2,N2),verb(N1,Rn,N2),Nc) :-
	!,
	Rn is R1+R2.
series_connection(verb(Nc,R1,N1), verb(Nc,R2,N2), verb(N1,Rn,N2),Nc) :-
	N1@<N2, !,
	Rn is R1+R2.
series_connection(verb(N1,R1,Nc), verb(N2,R2,Nc), verb(N1,Rn,N2),Nc) :-
	N1@<N2, !,
	Rn is R1+R2.

I think your member2 check is correct but i had too look at it for while to figure out what it really does. Rather than using ";"s (three of them, I think it’s preferable to split the cases into separate clauses using unification in the head:

member2(N,[verb(N,_,_)|T]) :- !.   % begin node = N
member2(N,[verb(_,_,N)|T]) :- !.   % end node = N
member2(N,[_|T]) :-                % not connected to N, continue looking
    member2(N,T).

For testing the resistance between specific nodes, is there a reason why you can just use memberchk/2 on the reduced network:

?- res_network(Rs),res_reduce(Rs,RRs),memberchk(resistance(a,R,c),RRs).
Rs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
RRs = [resistance(a, 83.33333333333333, c), resistance(c, 50, d)],
R = 83.33333333333333 ;
Rs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
RRs = [resistance(a, 500, c), resistance(a, 100, c), resistance(c, 50, d)],
R = 500 ;
Rs = RRs, RRs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
R = 100.

This makes it a little clearer why there are multiple answers; there are multiple equivalent networks, each with a resistance between a and c. Is one better than another? Why?

FInal query:

?- res_network(Rs),res_reduce(Rs,RRs),memberchk(resistance(a,R,b),RRs).
Rs = RRs, RRs = [resistance(a, 100, c), resistance(a, 200, b), resistance(b, 300, c), resistance(c, 50, d)],
R = 200.

From looking at the equivalent network, it’s clearer why a-100-c,c-300-b can’t be combined to form a parallel path from a to b. There’s another connection to c (from d). So a-133.33…-b isn’t a valid solution. Looks to me like the program is correct.

Thanks again for your contribution!

Right now i am looking for a way to not always reduce the whole network, but to only use the verbs, that are necessary for the path. And then changing the order, so it is not always verb(a,,) in the beginning, but rather the node i want to start from. Rather then doing this step at last when there are no more varriations possible. This may be a way to work around the a to b problem, cause it would get rid of c to d.

I just woke up. Will implement your ideas now and try to get a hold of that approach. Even tho the program seems to be working, it doen’t answer every query correctly yet.

Maybe a work around for this is considering the splits and joins, which are given in the original exercise? I guess they are given for a prctical reason. I just don’t see it.