2

I have a set of a few hundred simple sum equations. For example here are 3:

 w1 - u1 - u2 = 0 
 w1 + w2 - w3 - u3 = 0
 w1 - w2 - w4 = 0

I am trying to find a way to solve as many as possible given only a few of the values. For example, in the above equation set if I have u1 and u2 I can calculate w1, but nothing else. Given u1, u2 and w2 I can calculate w1 and w4. And so forth...

Currently I'm approaching this in a fairly straight forward way (psudo code):

while there are new results:
     for each equation:
         try to solve equation:
             if solved update result set

This works but feels clunky and inefficient.

Is there a better way? (using Python if that is relevant)

EDIT: I know this could be solved as series of linear equations, IF i know enough of the values. I'm looking for a method to use when I don't know enough to solve as a system of linear equations (or possibly there a fancy way to reduce the problem somehow)

EDIT 2: erroneous, deleted

EDIT 3: For anyone interested in my solution using sympy:

from sympy import linsolve, symbols, linear_eq_to_matrix
w1, w2, w3, w4, u1, u2, u3 = symbols("w1, w2, w3, w4, u1, u2, u3", integer=True)
variables = [w1, w2, w3, w4, u1, u2, u3]

def my_solver(known_vals):

    eqns = [w1 - u1 - u2, 
            w1 + w2 - w3 - u3,
            w1 - w2 - w4]

    #add the known variables to equation list
    for x in known_vals.keys():
        eqns.append(x - (known_vals[x]))

    A, b = linear_eq_to_matrix(eqns, variables)
    solution = linsolve((A, b), variables)

    return solution


my_solver({w1:2, u2:-2})
jprockbelly
  • 1,533
  • 15
  • 30
  • 1
    Can you post some code with your data structures? There are many ways to represent these things, and I'd like to be able to answer using your own set-up. – Prune Oct 27 '16 at 17:17
  • @Prune - sure thing, I'm away from the office today but will get an example up tonight – jprockbelly Oct 27 '16 at 21:32
  • @Prune - added some code based on my example. – jprockbelly Oct 27 '16 at 23:50
  • 2
    Sounds like you should just throw this into [`sympy.solvers.solveset.linsolve`](http://docs.sympy.org/dev/modules/solvers/solveset.html#sympy.solvers.solveset.linsolve) and call it a day. That'll give you a complete basis for the solution set, including the values of any variables whose values can be determined. Your brute-force solution isn't going to find everything. – user2357112 Oct 28 '16 at 00:00
  • @user2357112 - that looks pretty much exactly what I'm after, I'll go read the docs... and thanks for pointing out that my code above is not going to work. I realsie my mistake now. – jprockbelly Oct 28 '16 at 00:46
  • @user2357112 - do you want to make this the an answer so I can accept it? – jprockbelly Oct 28 '16 at 02:42
  • Wouldn't the REEF of the augmented system produce the known solutions in any row with a single coefficient and give the dependent relationships in all other rows? Any inconsistent equations would filter to the bottom as they would be of the form [0 0 ... 1]. – Jared Goguen Oct 28 '16 at 04:24
  • @Jared Goguen - sorry you'll have to help me out here, what's the REEF? It's one of those hard to google acronyms... – jprockbelly Oct 28 '16 at 05:06
  • 1
    (Reduced Row Echelon Form) Googling "RREF" should do it for you... if you have no background in linear algebra, then you might have to do a little background reading, but it should be light, most texts that I've seen go over this material is Chapter 1 or 2. – Jared Goguen Oct 28 '16 at 13:37
  • In case it wasn't clear, I meant that you should encode known variable values as equations in the `eqns` list rather than plugging them in with `subs` afterwards. – user2357112 Oct 28 '16 at 16:23

3 Answers3

5

Just get SymPy and stuff the whole system of linear equations into sympy.solvers.solveset.linsolve. It'll give you the whole solution space, including the values of variables with determined values, in a form dependent on whether the system has 0, 1, or infinite solutions.

There's probably also a NumPy/SciPy way to get the solution set of an underdetermined system, but whatever that way is, I don't know it. Google suggests a singular value decomposition would be useful, but I haven't figured out how you'd get a basis for the solution set out of that.


In case it wasn't clear, "the whole system of linear equations" includes formulating known variable values as equations. For example, if you know that u2 = 5, then u2 = 5 would be one of the linear equations. You'd represent that as u2 - 5 in the eqns list.

user2357112
  • 260,549
  • 28
  • 431
  • 505
2

Your original description was clear enough, complete with example.

What you want for a general solution is a data flow graph and some graph traversal algorithms. However, with only 200 equations to work through, your brute-force method is going to be effective enough. However, for long-term use, I'd set it up the way we did in compiler construction:


Data Objects

Equation has a list of variables and coefficients (LHS) and constant (RHS). Also, keep a count of how many variables are still unsolved. These variables are references to ...

Variable includes a list of equation references (uses) and a value (None if not solved yet).

Open equation list is a list of unsolved equations.


Algorithm

Initialization Sort the equation list by quantity of unknowns -- at least to identify those with only 1 remaining unknown.

Iteration

while there are equations with only 1 unknown:
    for each such equation:
        solve that equation for the remaining variable
        for each equation in the variable's reference list:
            update that equation

Note that "update that equation" can involve several steps. Most notably, make sure you update the count of unknowns; if that's now 1, move it into the queue to solve.


Afters

If you're lucky, you'll have solved all the equations ... or you'll have at least as many equations as unknowns, so you can use the linear solving facilities of your favourite math package to finish the job.

Prune
  • 76,765
  • 14
  • 60
  • 81
  • "your brute-force method is going to be effective enough" - eh, that depends on your standard of "effective enough". It isn't guaranteed to find the values of all variables with determined values; it might not even come close. – user2357112 Oct 28 '16 at 00:04
  • Well, that comment was valid when I wrote it -- roughly 6 hours before the edit with the code. – Prune Oct 28 '16 at 00:07
-1

You may use some linear algebra methods to solve the problem.

assume the 4th equation is

w1 + w2 + w3 + w4 = 2

then you can write the matrix as :

In [28]: martix = np.array([[ 1,  0,  0,  0],
...:        [ 1,  1, -1,  0],
...:        [ 1, -1,  0, -1],
...:        [ 1,  1,  1,  1]])

assume u1 = 10 ,u2 = -5, u3 = 6, u4 = 2 and move them to right:

In [34]: ans = np.array([u1+u2, u3, 0, u4])

then use numpy to solve:

In [35]: np.linalg.solve(martix,ans)
Out[35]: array([  5.,  -7.,  -8.,  12.])

you got w1=5 w2=-7 w3=-8 and w4=12

KIDJourney
  • 1,200
  • 1
  • 9
  • 22
  • this is fine if I know all of u1, u2, u3 and u4. But what if I only know 2 of them? – jprockbelly Oct 27 '16 at 10:36
  • @jprockbelly the `u` you have doesn't mean so much, you have to ensure you have same amount of equations and the unknown number. or you can't get the unknown number. – KIDJourney Oct 27 '16 at 10:57
  • sorry if my question was unclear, I'm looking for a solution for when I DON'T have the same number of unknowns and equations. In my real world case I have ~200 equations and ~75 variables but I will only know a fraction of the variables at one time. See edit above – jprockbelly Oct 27 '16 at 11:35