Frank Pirata
Frank Pirata

Reputation: 55

Define and solve Equation without redundancy (Python)

The standard way to set up physics equations seems to be the approach found here: physics equation in python You take your equation, in that stackoverflow post it was s = v*t and either write multiple functions, like so:

def s(v, t):
    return v*t

def v(s, t):
    return s/t

def t(s, v):
    return s/v

Or you do the same in one big if-branched equation

def solve_svt_equation(v=None, t=None, s=None):
    if v is not None and t is not None:
        return v * t   # s case
    elif s is not None and t:  # t not None and not 0
        return s / t   # v case
    elif s is not None and v: # v not None and not 0
        return s / v

print(solve_svt_equation(v=10, t=2))
print(solve_svt_equation(s=2, t=7))

If there was only one physics equation, a programmer would have to implement, this would be a workable approach with no further worries. However, if you need to implement a number of equations, this approach is highly prone to errors and quickly becomes confusing.

For my upcoming coding project there are about 50 of such equations. I assume I am in a common situation many programmers already were in.

As v*t - s = 0 fully defines the equation, there could be a solution, where a programmer only has to write the equation once. It could look like this made up idea(or anything else):

Equation svt = Equation("v*t - s")
svt['s'] = 12
svt['v'] = 6
print(svt.get('t'))

So my question is whether there is such a solution anywhere in the Python libraries, whether there is a clean workable standard method, or do I have to come up with a solution myself?

Upvotes: 0

Views: 186

Answers (1)

Recursing
Recursing

Reputation: 648

There are many libraries in the Python ecosystem to handle these problems

One is SymPy

The code could look like this:

import sympy

def solve_svt_equation(v=None, t=None, s=None):
    velocity, space, time = sympy.symbols("v s t")
    expr = velocity * time - space
    if v:
        expr = expr.subs(velocity, v)
    if t:
        expr = expr.subs(time, t)
    if s:
        expr = expr.subs(space, s)
    return sympy.solve(expr)

print(solve_svt_equation(v=10, t=2)) # [20]
print(solve_svt_equation(s=2, t=7)) # [2/7], sympy defaults to rational numbers
print(solve_svt_equation(s=2, t=7, v=1)) # [], no solution
print(solve_svt_equation(s=2)) # [{t: 2/v}], symbolic solution for t

An alternative that might be easier to generalize could be:

def solve_svt_equation(v=None, t=None, s=None):
    velocity, space, time = sympy.symbols("v s t")
    expr = velocity * time - space
    vals = {velocity: v, space: s, time: t}
    for symbol, val in vals.items():
        if val:
            expr = expr.subs(symbol, val)
    return sympy.solve(expr)

If you want to generate an expression from a string, you can make something very similar to your example using sympy.parsing.sympy_parser.parse_expr

svt_expr = sympy.parsing.sympy_parser.parse_expr("v*t - s")
svt_expr = svt_expr.subs("s", 12)
svt_expr = svt_expr.subs("v", 6)
print(sympy.solve(svt_expr, "t")) # [2]

Another great library is z3, it's an SMT solver instead of a symbolic manipulation library, but can solve these simple problems easily. I would suggest using sympy if you have symbolic equations, just showing it as an alternative

import z3
v, s, t = z3.Reals("v s t")
equation = v * t == s
z3.solve([equation, s == 12, v == 6]) # Prints [v = 6, s = 12, t = 2]

Upvotes: 1

Related Questions