0

I am writing a program to process various equations. I have a list of equations, and a function that can solve a given equation for an unknown value.

Eg: a + b + c = 0 -> can be solved for any variable, given the other 2.

I now need to write an algorithm that takes the list of relations, a list of known values and a target, then finds a series of steps that must be taken to find the unknown.

Eg: (a = b, b = c) a = 1, find c:
Result would show that you first convert a -> b with "a = b", then b -> c with "b = c"

Help with the algorithm or search terms I could research would be greatly appreciated

Delta qyto
  • 125
  • 8
  • 1
    What types of equations in your list? Are they first-degree or multiple-degree? Suppose that you are talking about a list of all first-degree (linear) equations, using matrix is the way that is the easiest to implement with coding. You can read more about it here https://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html – Hung Thai May 23 '22 at 04:40
  • The equations for this purpose are essentially black boxes, processing a set of provided inputs into desired outputs, the way normal equations do. Some of the equations are linear, but many are not. Perhaps equation is not the right word here, I wasn't sure what would be more apt. – Delta qyto May 23 '22 at 04:45
  • "The equations for this purpose are essentially black boxes, processing a set of provided inputs into desired outputs, the way normal equations do": that does not make an equation solver. And what about "a sin(a)+b cos(b)+c tan(c)=0" ? (An implicit equation does not have inputs and outputs.) –  May 23 '22 at 08:23
  • @YvesDaoust I have a set of 'relations' between different variables, and a system that can solve for unknown ones in a particular relation. So the example "a sin(a)+b cos(b)+c tan(c)=0" can be solved for "a", given other variables "b", "c". What i need is a 'controller' that can decide which relations will help find the target variable, provided a number of relations between various variables. I will rephrase my question to better explain this – Delta qyto May 23 '22 at 08:33
  • A very common situation is a system like f(a,b,c)=0, g(a,b,c)=0, h(a,b,c)=0. What do you expect the controller to do ? –  May 23 '22 at 08:43
  • @YvesDaoust if no variables are known, then it will request a 3-way simultaneous solve, then whatever variable was the target can be returned. if a variable is known, any two functions are picked to be solved in a 2-way simultaneous solve – Delta qyto May 23 '22 at 08:46
  • So ,in case of f(a,b)=0, g(a,b)=0, h(a,b,c)=0, if a or b is requested, only the first two should be solved, and if c is requested, the first two will be solved, then the third ? –  May 23 '22 at 08:56
  • @YvesDaoust Exactly – Delta qyto May 23 '22 at 08:58
  • How would your solver solve `f(x) = 0`? `f` is a generic function the solver knows absolutely nothing about. What would the desired output of such an equation be? – n. m. could be an AI May 23 '22 at 09:08
  • @n.1.8e9-where's-my-sharem. The controller part would respond with a request to first find x, given f(x) = 0. Since the target (x) is now assumed to be found, it would terminate. Under the hood, the solver part would perform newton's method or some other algorithm, but this is not a concern for the controller or this post to handle. – Delta qyto May 23 '22 at 09:16
  • "the solver part would perform newton's method or some other algorithm" So what is the list of steps you are required to provide? If the step is "solve for f(x) = 0", why can't the step be "solve for (a-b)^2 + (a-c)^2 + (c-1)^2 = 0"? – n. m. could be an AI May 23 '22 at 09:46
  • What if those two equations appear in your system: eq1: `(x+1)(x-1) = 0`; eq2: `(x+1)(x-2) = 0`. Then using both equations together, you can figure out the value of `x`, but using only one equation, you can't figure out the value of `x`. How would your controller handle that situation? – Stef May 23 '22 at 09:58
  • @Stef it would be acceptable if the solver simply returns a valid solution to either equation, even if it doesnt work in the other. I will find a way about this, probably through curation of the equation lists or domain limits. The controller can assume either equation will fully solve x (even though it wouldn't here) – Delta qyto May 23 '22 at 10:11
  • @n.1.8e9-where's-my-sharem. solve for (a-b)^2 + (a-c)^2 + (c-1)^2 = 0 is unacceptable as a single step because it involves three unknowns and only one equation. But a generic f(x) = 0 has a single unknown and one equation, so it can be solved. so the earlier example can be solved if b and c are known – Delta qyto May 23 '22 at 10:13
  • Are three unknowns and three equations OK? Add "1=1" and "2=2". Note you cannot always extract a single variable, i.e. you can only solve "f(x,y)=0" and g(x,y)=0" for x and y simultaneously. – n. m. could be an AI May 23 '22 at 10:22
  • @n.1.8e9-where's-my-sharem. umm to clarify: f(a, b c) is not solvable on its own. but f(a, b, c), g(a, b, c) and h(a, b, c) all relate to a, b, c. Therefore 3 matching equations are able to solve for a, b and c. So 1 = 1 would not involve the variables and cannot be counted. I am not sure what "extracting a single variable" means, but there should be no need. It would simply solve for all simultaneous variables a, b and c, then discard the unneeded variables. – Delta qyto May 23 '22 at 10:32
  • "would not involve the variables" this is not something a driver can actually check. 1 is a constant function of three variables x, y, z. You can write it as (x-x+y-y+z-z+1) or in any other way and easily hide the fact that it is a constant from any given static analyzer. – n. m. could be an AI May 23 '22 at 10:46
  • @n.1.8e9-where's-my-sharem. I will be populating the equations by hand, so it can be assumed that if a function expects the input x, x affects the output in some fashion. – Delta qyto May 23 '22 at 10:51
  • You can also represent the same thing as a system of equations (a-b=0), (b-c=0), (c-1=0). Note you cannot require than each of the three equations depends on all three variables, i.e. f(x,y)=0, g(y,z)=0, h(z,x)=0 can only be solved simultaneously. TL;DR the "sequence of steps" is not something you can reasonably require from such a system in general. It can be provided in some special cases (e.g. systems of linear equations, or equations with one variables). – n. m. could be an AI May 23 '22 at 11:52

2 Answers2

1

You build a dependency graph. Then you fill in given variables, compute what depends on the, compute what depends on what you've just computed, etc.

This of course doesn't always work, and you may come to a dead end, immediately or after some such steps. The simplest example is a system of equations:

a + b + c + 3 = 0
a - b + c - 5 = 0

and you are given c = 42, what's a? Or worse, what's some other variable that depends on a (and may be a part of some other system of equations)? You can perform Gaussian elimination here to compute a, but what if you have a non-linear system, or equations you cannot analyze? You need to send a bunch of equations to a solver then.

So now you have not variables that depend on other variables, but sets of variables that depend on other sets. In the example above, {a, b} depends on c (and {a, c} depends on b, and {b, c} depends on a).

One could be tempted to run some kind of shortest-path algorithm on a graph of sets of variables. However, in a system of 50 equations with 50 variables the number of sets that depend on other sets is beyond our abilities to compute. And yet an entire system can be feasible to solve all at once numerically.

So there is a generic method of solving this. Just feed it all to the solver as one big system of equations. You get back a bunch of variable assignments, without any clear sequence of solution steps (unless you consider iterations of Newton-Raphson or whatever "solution steps"). And then there are specific ways to solve specific restricted kinds of systems that give you back clear solution steps (linear systems, systems of single-variable equations, and their combinations). And of course you can have some clearly defined solution steps, then solve a big set of equations all at once because you have no other way to solve them, then have some more clear steps, then solve a different system of equations, etc etc.

So you can try to walk the dependency graph and produce solution steps, but there is always a chance you will get stuck at some big system that you can only solve all at once. Worse, there may be different ways that lead to a solution, all through solving different systems of equations, and of course some systems do not behave nicely (have unstable solutions, or solutions that are hard to guess a decent approximation to, or whatever).

Might as well save all this trouble, throw everything you have to the numeric solver, and let it sort out the mess.

(By equation I mean any object that looks like 'f(x, y, ...) = 0', where f is some function. Sometimes you get to know what the function is, i.e. it is given to you symbolically, and sometimes you don't, i.e. it is computed by an external algorithm opaque to you).

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Took some time to experiment with options, and it seems that the mixed bag method of solving is really the only feasible one. Thanks – Delta qyto May 26 '22 at 07:45
0

Assume we have:

  • a list of equations eqs;
  • an initial list of variables xs;
  • a function f(eq, xs) that, given an equation eq and a list of variable names xs, returns the list ys of variables whose value can be found using equation eq if we know the values of variables in xs;
  • a target variable target.

Then your problem is somewhat of a "shortest path" problem, trying to find a path from an initial list of variables, to a list that contains the target variable.

We can perform a breadth-first-search, trying to expand the list of known variables until it contains the target.

eqs = '''3 * a + 2 * b + c == 0
a + b = 12
c + 4 * b = 4'''.split('\n')

def f(eq, xs, all_variables='abc'):
    ys = set(eq).intersection(all_variables).difference(xs)
    return ys.pop() if len(ys) == 1 else None

target = 'c'

xs = ['a']

def find_steps(eqs, xs, f, target):
    if target in xs:
        return (('', xs),)
    paths = [(('', frozenset(xs)),)]
    for _ in range(len(eqs)):
        new_paths = []
        for path in paths:
            _,xs = path[-1]
            for eq in eqs:
                y = f(eq, xs)
                if y is not None:
                    new_path = path + ((eq, xs.union({y})),)
                    if y == target:
                        return new_path
                    new_paths.append(new_path)
        paths = new_paths
    return None

print( find_steps(eqs, xs, f, target) )
# (('', {'a'}),
#  ('a + b = 12', {'b', 'a'}),
#  ('3 * a + 2 * b + c == 0', {'b', 'c', 'a'}))

A few explanations. The outer loop for _ in range(len(eqs)): is conceptually not a for-loop. It's a loop that runs until either the target is found (in which case the loop is broken by return new_path) or until the loop has run for too long without finding a target: the longest path cannot be longer than the number of equations. Or can it? If it can, the stopping condition needs to be modified.

We implement a breadth-first-search. At iteration i of the outer loop, paths is the list of all paths of length i. For each path of length i, we try to extend it into paths of length i+1 in all ways that we can, and add these new paths to new_paths.

A path is a list of steps. A step is a pair (equation, known_variables). The initial step is always ('', initial_known_variables). We only extend a path if the new step contains a new known variable.

Stef
  • 13,242
  • 2
  • 17
  • 28
  • This solution works well when the equations can be chained together, but the case: a + b = 12, 3 * a + 4 * b = 20 with no known variables does not work, as the system does not consider simultaneous equations – Delta qyto May 23 '22 at 10:46
  • @Deltaqyto Yes you're right. I suppose one possible workaround would be a preprocessing of the equations where we take variables out of equations. For instance, add equations `3 * (12 - b) + 4 * b = 20` and `3 * a + 4 * (12 - a) = 20`. – Stef May 23 '22 at 10:51
  • @Deltaqyto Another possible workaround would be to use a more complex function `f`, which can not only consider single equations, but also pairs of equations. But note that the number of pairs of equations is quadratic in the number of equations; the number of triplets of equations is cubic; etc; so the time complexity can quickly explode. – Stef May 23 '22 at 10:52
  • the first approach is what I used to do by hand, before realising that the number of combinations i needed to account for made it beyond infeasible. Additionally, some "equations" are not actually equations, but other algorithms masquerading as one. So the solution must be capable of solving it without the preprocessing step. I have a large number of equations, so option 2 is also infeasible – Delta qyto May 23 '22 at 10:53