SymPy – Arbitrary number of Symbols

Question:

I am coding a function that solves an arbitrary number of simultaneous equations. The number of equations is set by one of the parameters of the function and each equation is built from a number of symbols – as many symbols as there are equations. This means that I can’t simply hardcode the equations, or even the symbols needed to put together the equations; the function needs to be able to handle any number of equations. So, my question is, how do I produce a list of symbols?

I have one possible solution, but my gut tells me that it’s not going to be very efficient. Please let me know if there is a better way of doing this.

I’m new to SymPy and am still feeling my way about. As far as I can see, Symbols need to be defined with a string. Therefore, I can produce a series strings via appending an incrementing number to a letter (say ‘t0’, ‘t1’, etc), add them to a list and then create the symbols using those strings as parameters. Those symbols would themselves be stored in a list and would be used to produce the equations.

def solveEquations(numEquations):
    symbolNameList = []
    symbolList = []
    equationList = []
    for i in range(numEquations):
        name = 't' + str(i)
        symbolNameList.append(name)
        symbolList.append(Symbol(name))

    for i in range(numEquations):
        equation = 0
        for sym in symbolList:
            equation += sym ** i # Or whatever structure the equation needs
        equationList.append(equation)


    #Then go on to solve the equations...

Is this the best way of doing this, or is there a more efficient approach?

Asked By: thornate

||

Answers:

Your approach is fine, though there’s no need to store the symbol names separately (you can access a symbol’s name via its name property).

Also, you could express the symbol creation a little more concisely (though no more efficiently), e.g.:

symbolList = map(lambda i: Symbol('t' + str(i)), xrange(numEquations))

However, for your use case (temporary variables), dummy variables are probably the way to go:

symbolList = map(Dummy, xrange(numEquations))

This isn’t really any more efficient, since internally the Dummy class is also using a counter to generate unique names, but it’s a bit cleaner and clearer.

Answered By: Cameron

You could make a subclass of dict which automatically returns Symbols:

import sympy as sym

class SymDict(dict):
    # http://stackoverflow.com/a/3405143/190597
    def __missing__(self, key):
        self[key]=sym.Symbol(key)
        return self[key]

def solveEquations(numEquations):
    symbol = SymDict()
    symbolList = ['t'+str(i) for i in range(numEquations)]
    equationList = [sum(symbol[s]**i for s in symbolList)
                    for i in range(numEquations)]
    print(equationList)

solveEquations(3)    
# [3, t0 + t1 + t2, t0**2 + t1**2 + t2**2]
Answered By: unutbu

The symbols function can be used to easily generate lists of symbols

In [1]: symbols('a0:3')
Out[1]: (a₀, a₁, a₂)

In [2]: numEquations = 15

In [3]: symbols('a0:%d'%numEquations)
Out[3]: (a₀, a₁, a₂, a₃, a₄, a₅, a₆, a₇, a₈, a₉, a₁₀, a₁₁, a₁₂, a₁₃, a₁₄)
Answered By: MRocklin

numbered_symbols("t") will return a generator that generates t0, t1, t2, etc. You can use the start parameter to choose a different starting value. And if you want to use dummy variables, use numbered_symbols("t", cls=Dummy).

Answered By: asmeurer

With locals() and dictionary comprehension, you could iteratively generate both symbols and python local variables with a similar name. For example:

>>> symbols_dict = dict(('a%d'%k, symbols('a%d'%k)) for k in range(3))
>>> locals().update(symbols_dict)

Checking that it works:

>>> print(expand((a0+a2)*(a0+a1**2)))
a0**2 + a0*a1**2 + a0*a2 + a1**2*a2
Answered By: Yuval Atzmon

Don’t know if add any more useful information to the topic, but I use the following method to create a list of symbolic variables:

x = [sympy.symbols('x%d' % i) for i in range(3)]

And then I can use it normally in an equation:

eq = x[0]**2 + x[1]*2 + x[2]
print(sympy.diff(eq,x[0]))
>>> 2*x0
Answered By: J P Sena

I like the approach given by @j-p-sena and what I am going to suggest looks a lot like it. The difference is that you don’t have to know how many symbols you are going to need — you will just have access to as many as you need by index. Use the IndexedBase as your symbol:

>>> x = IndexedBase('x')  # you've got access to a virtual array of x values
>>> solve(x[1]**2 + 1/x[4], x[4])
[-1/x[1]**2]

For display purposes you might want to create a replacement dictionary. To create numbered symbols you could do

>>> reps = dict(zip([x[i] for i in range(n_used+1)], numbered_symbols('c')))
>>> (x[2]**2 + 1/x[4]).subs(reps)
c2**2 + 1/c4

Or, if you are using less than 26 symbols you could use letters with

>>> reps = dict(zip([x[i] for i in range(n_used+1)], symbols('a:z')))
>>> (x[2]**2 + 1/x[4]).subs(reps)
c**2 + 1/e

BTW, x is an IndexedBase and x[1] is an Indexed object whose .base is x and whose .indices are a tuple of whatever numbers appear in the brackets. Both the IndexedBase and Indexed will show up in a .free_symbols query.

>>> (x[1,2] + 3).free_symbols
{x, x[1, 2]}
>>> x[1, 2].indices
(1, 2)
>>> x[1].base
x
Answered By: smichr