Getting used indices of indexed terms in a sympy sum - sympy

Say I have variables x and y which are indexed
from sympy.tensor import IndexedBase
x = IndexedBase('x')`
And I have an expression like e = x[1] y[2] + x[5] y[10]
. I want to find all indices used by each of x and y. I'm looking for a function which might work like this: e.indices(y) = [2, 10] and e.indicies(x) = [1, 5]
Is there a way I can iterate through the terms x[i] y[j]? And if so, is there a way to split a product into terms and for each of those pull out which letter is being used and which index appears?

The following should get you headed in the right direction:
>>> from sympy.tensor import IndexedBase, Indexed
>>> from sympy import sift
>>> x = IndexedBase('x')
>>> y = IndexedBase('y')
>>> e = x[1]* y[2] + x[5]* y[10]
>>> e.atoms(IndexedBase)
set([y, x])
>>> e.atoms(Indexed)
set([x[5], y[10], x[1], y[2]])
>>> sifted = sift(_,lambda i: i.base)
>>> sifted[x]
[x[5], x[1]]
>>> sifted[y]
[y[10], y[2]]
>>> [i.indices for i in _]
[(10,), (2,)]
>>> flatten(_)
[10, 2]

Related

Systems of Linear Equations

I am using the following code to find x and y in this linear equation.
I was wondering if there is a way to add two more constrains to the following equation?
For example, how can we add x>0 , and y>0 to the following equation(3x+4y=7 and 5x+6y=8)to get a positive output?
from sympy import *
x, y = symbols(['x', 'y'])
system = [Eq(3*x + 4*y, 7), Eq(5*x + 6*y, 8)]
soln = solve(system, [x, y])
print(soln)
You can declare the symbols x and y to be positive:
In [4]: from sympy import *
...: x, y = symbols(['x', 'y'])
...: system = [Eq(3*x + 4*y, 7), Eq(5*x + 6*y, 8)]
...: soln = solve(system, [x, y])
...: print(soln)
{x: -5, y: 11/2}
In [5]: from sympy import *
...: x, y = symbols(['x', 'y'], positive=True)
...: system = [Eq(3*x + 4*y, 7), Eq(5*x + 6*y, 8)]
...: soln = solve(system, [x, y])
...: print(soln)
[]

Unique lists in nested list in numba

I would like to find the number of unique lists within a nested list in a nopython numba function, e.g:
from collections import Counter
def number_of_unique_lists_v1(a):
uniques = Counter(tuple(item) for item in a)
number = len(uniques.keys())
return number
print(number_of_unique_lists_v1([[1,2,3],[1,2],[3,4],[1,2,3])
>>> 3
or
def number_of_unique_lists_v2(a):
uniques = [list(x) for x in set(tuple(x) for x in a)]
number = len(uniques)
return number
print(number_of_unique_lists_v2([[1,2,3],[1,2],[3,4],[1,2,3])
>>> 3
Unfortunately, both ideas don't work with #nb.njit. How can I make it work?
Edit:
Using the approach of mpw2 I found that in principle the following code works:
from numba.typed import List
#nb.njit
def number_of_unique_lists_v3():
a = [[1, 2, 3], [1, 2], [3, 4], [1, 2, 3]]
s = List()
for x in a:
if not x in s:
s.append(x)
number = len(s)
return number
print(number_of_unique_lists())
>>> 3
BUT this does not work for me since the list a is created slightly differently in my function, similar as shown in a minimal example below.
from numba.typed import List
#nb.njit
def number_of_unique_lists():
a = [[0] for _ in range(4)]
a[0] = [1, 2, 3]
a[1] = [1, 2]
a[2] = [3, 4]
a[3] = [1, 2, 3]
s = List()
for x in a:
if not x in s:
s.append(x)
number = len(s)
return number
Now I get an error which I don't understand...
Here is one working solution using numba.typed.List() objects
import numba as nb
from numba.typed import List
#nb.njit
def number_of_unique_lists(a):
s = List()
for x in a:
if not x in s:
s.append(x)
number = len(s)
return number
a = [[1,2,3],[1,2],[3,4],[1,2,3]]
typed_a = List()
for x in a:
s = List()
for y in x:
s.append(y)
typed_a.append(s)
print(number_of_unique_lists(typed_a))
>>> 3

How do you count how many maximum values there are in a list?

Lets say I have the list below:
list1=[1,2,4,6,8,3,2,5,8,4,2]
I want to return the integer, 2, because 8 is the maximum value and there are two 8s in the list. How can I do this? Edit: I also want to assume that the maximum number in the list can be any negative or non-negative number including zero.
Well you can use something like this:
list1=[1,2,4,6,8,3,2,5,8,4,2]
print list1.count(max(list1))
ans = 0
mx = 0
for x in list1:
if x > mx:
mx = x
ans = 1
elif x == mx :
ans += 1
print ans
assume max number is bigger than 0, otherwise you should initial mx with the negative infinity
>>> list1=[1,2,4,6,8,3,2,5,8,4,2]
>>> x = max(list1)
>>> l = []
>>> for i in list1:
if i == x:
l.append(i)
>>> l
[8, 8]
>>> len(l)
2
OR
>>> list1=[1,2,4,6,8,3,2,5,8,4,2]
>>> x = max(list1)
>>> result = len(filter(lambda i: i == x, list1))
>>> result
2

Turning a sympy expression into a vector to find linearly independent subset

I have a list of expressions like 4.0*x[0] + 5.0*x[10] + 1 = 0
I would like to turn these into vectors according to the coefficients like [4.0, 0, 0, ..., 5.0, ... , 1]. The reason is that some of my equations may be linearly dependent and I want to run QR from the numpy library so I can find a linearly independent subset.
I can get the constant term by doing expr.replace(x[i], 0) with i a wildcard index. I can also get most of the other terms by expr.atoms(Mul) which gives me the set 4.0*x[0], 5.0*x[10] and then for each of these expressions I can do expr.atoms(Indexed).pop() and expr.atoms(Float).pop() to split the parts.
The trouble is when I have an expression like x[0] + 5.0*x[10] + 1 = 0, where the first variable appears with an implicit coefficient of 1. The term is no longer recognized as a Mul object.
In any case, I think there might be a better way to achieve my goal?
If you give your symbols a specific order, as in the code below, you could convert the expression to a polynomial and get its coefficients:
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> a1, a2, a3, a4 = symbols('a[1], a[2], a[3], a[4]')
>>> used_symbols = (a1, a2, a3, a4)
>>> replacements = [(n, x**(enu+1)) for enu,n in enumerate(used_symbols)]
>>> expr = 5 + a1 + 4*a4
>>> Poly(expr.subs(replacements)).all_coeffs()
[4, 0, 0, 1, 5]
And you could retrieve a list of the used symbols too if they are not known beforehand with the following recursive function:
def retrieve_used_symbols(expr):
"""Return the symbols used in the `expr` in a list."""
used_symbols = []
for term in expr.args:
if term.is_Atom and term.is_Symbol:
used_symbols.append(term)
else:
used_symbols.extend(retrieve_used_symbols(term))
return used_symbols
The latter comes in handy when you have mixed symbols:
>>> crazy_expr = expr + 10*y-2*z
>>> crazy_expr
a[1] + 4*a[4] + 10*y - 2*z + 5
>>> used_symbols = retrieve_used_symbols(crazy_expr)
>>> replacements = [(n, x**(enu+1)) for enu,n in enumerate(used_symbols)]
>>> Poly(crazy_expr.subs(replacements)).all_coeffs()
[4, -2, 1, 10, 5]
>>> list(reversed(used_symbols))
[a[4], z, a[1], y]
For an IndexedBase object, it's even simpler:
coeffs = [expr.coeff(x[i]) for i in range(10)]
But you'll still need to add the constant term, which, like you said, you can obtain from a wildcard substitution:
ind = Wild('i')
constant_term = expr.replace(x[ind], 0)
{as requested by #(Oliver W.)}
Given
>>> x = IndexedBase('x')
>>> eqs = 4*x[0] + 5*x[5] + 1, x[1] - x[2]
>>> v = list(ordered(Tuple(*eqs).atoms(Indexed)))
One could do it like this
>>> [[eq.coeff(vi) for vi in v] + [eq.as_coeff_Add()[0]] for eq in eqs]
[[4, 0, 0, 5, 1], [0, 1, -1, 0, 0]]
But much of this is available through the matrix method jacobian. But to use it you have to replace the x[i] with symbols (since diff only works with functions are symbols, IIRC):
>>> d = [Dummy() for vi in v]
>>> z = dict(zip(d, [0]*len(d)))
>>> m = Matrix([eq.xreplace(dict(zip(v, d))) for eq in eqs])
>>> m.jacobian(d)
Matrix([
[4, 0, 0, 5],
[0, 1, -1, 0]])
>>> m.subs(z)
Matrix([
[1],
[0]])

Sympy substitution of x[i]*x[j] with x[i,j]

I have an indexed symbol x in Sympy and an expression which is a sum of second degree monomials like x[1]*x[2] + x[3]**2 + x[4]*x[1]. I would like to turn such an expression into x[1,2] + x[3,3] + x[4,1], i.e. replacing x[i]*x[j] -> x[i,j]
There is an upper bound on the indices which may appear, so I could construct a large table hard coding each substitution. Is there a better way?
Responding to the comment - to create x I write
from sympy.tensor import IndexedBase
x = IndexedBase('x')
You can use ordered to put the indices in order:
>>> from sympy import *
>>> i, j = symbols('i j', cls=Wild)
>>> x = IndexedBase('x')
>>> e = x[1]*x[3] + x[2]*x[1] + x[3]**2
>>> def new(o, x):
... if o.is_Mul:
... i,j=list(ordered([i.args[1] for i in o.args]))
... elif o.is_Pow:
... i = j = o.base.args[1]
... else:
... raise NotImplementedError
... return x[i, j]
...
>>> e.xreplace(dict([(o, new(o, x)) for o in e.find(x[i]*x[j])]))
x[1, 2] + x[1, 3] + x[3, 3]
But a simpler way to do the same thing is to use a Piecewise result in the replace call:
>>> e.replace(x[i]*x[j], Piecewise((x[i,j],i<j),(x[j,i],True)))
x[1, 2] + x[1, 3] + x[3, 3]
You can use replace with a Wild.
In [1]: i, j = symbols('i j', cls=Wild)
In [2]: x = IndexedBase('x')
In [3]: e = x[1]*x[3] + x[2]*x[1]
In [4]: e.replace(x[i]*x[j], x[i, j])
Out[4]: x[1, 2] + x[1, 3]