Substitution of IndexedBase inside a summation - sympy

I'm trying to substitute an IndexedBase inside a summation in sympy. Outside the summation it works, but inside I'm getting back the expression without the substitution:
In [125]: (f, n) = sympy.symbols("f n")
In [126]: foo = sympy.IndexedBase(f)[n]
In [127]: foo.subs(foo, 1)
Out[127]: 1
In [128]: sympy.Sum(foo, (n, 0, 5)).subs(foo, 1)
Out[128]:
5
___
╲
╲ f[n]
╱
╱
‾‾‾
n = 0
Why does the last step not replace f[n] by 1, and how do I need to change my code to ensure that it does?

It doesn't work because n is a dummy variable and it would change the value of the summation. That is, .subs( ) should give you the same value as when applied to the expanded summation:
In [7]: sympy.Sum(foo, (n, 0, 5)).doit()
Out[7]: f[0] + f[1] + f[2] + f[3] + f[4] + f[5]
In this expression there is no f[n] anymore, as it has been replaced by the respective numerical values. Substitution won't work:
In [8]: sympy.Sum(foo, (n, 0, 5)).doit().subs(foo, 1)
Out[8]: f[0] + f[1] + f[2] + f[3] + f[4] + f[5]
Therefore, .subs() will ignore matches on variables that are summed over (in this case n).
replace( ) on the other hand doesn't care about value consistency and replaces the matching expression anyway:
In [9]: sympy.Sum(foo, (n, 0, 5)).replace(foo, 1)
Out[9]:
5
___
╲
╲ 1
╱
╱
‾‾‾
n = 0
Of course, the resulting value is now different:
In [10]: sympy.Sum(foo, (n, 0, 5)).replace(foo, 1).doit()
Out[10]: 6

It works if you use the replace method instead:
In [129]: sympy.Sum(foo, (n, 0, 5)).replace(foo, 1)
Out[129]:
5
___
╲
╲ 1
╱
╱
‾‾‾
n = 0
See also this answer.

Related

SymPy unable to simplify solution

I am trying to solve the following system of equation using sympy.
from sympy import *
n = 4
K = 2
a = symbols(f"a_:{int(n)}", real=True)
b = symbols(f"b_:{int(n)}", real=True)
X = symbols(f"X_:{int(K)}", real=True)
Y = symbols(f"Y_:{int(K)}", real=True)
lambda_ = symbols("lambda",real=True)
mu = symbols(f"mu_:{int(K)}", real=True)
list_eq = [
# (1)
Eq(a[0] + a[1] + a[2] + a[3], 0),
Eq(a[0] + a[1], X[0]),
Eq(a[2] + a[3], X[1]),
# (2)
Eq(b[0] + b[1] + b[2] + b[3], 0),
Eq(b[0] + b[1], Y[0]),
Eq(b[2] + b[3], Y[1]),
# (3)
Eq(b[0], a[0] - lambda_ - mu[0]),
Eq(b[1], a[1] - lambda_ - mu[0]),
Eq(b[2], a[2] - lambda_ - mu[1]),
Eq(b[3], a[3] - lambda_ - mu[1]),
]
solve(list_eq, dict=True)
[{X_0: -b_2 - b_3 + mu_0 - mu_1,
X_1: b_2 + b_3 - mu_0 + mu_1,
Y_0: -b_2 - b_3,
Y_1: b_2 + b_3,
a_0: -b_1 - b_2 - b_3 + mu_0/2 - mu_1/2,
a_1: b_1 + mu_0/2 - mu_1/2,
a_2: b_2 - mu_0/2 + mu_1/2,
a_3: b_3 - mu_0/2 + mu_1/2,
b_0: -b_1 - b_2 - b_3,
lambda: -mu_0/2 - mu_1/2}]
The analytical solution for b is
b_0 = a_0 + (1/2)*(Y_0 - X_0)
b_1 = a_1 + (1/2)*(Y_0 - X_0)
b_2 = a_2 + (1/2)*(Y_1 - X_1)
b_3 = a_3 + (1/2)*(Y_1 - X_1)
However sympy does not manage to simplify the results and is still using mu_0 and mu_1 in the solution.
Is it possible to simplify those variables in the solution ?
For more details, the system i'm trying to solve is an optimization problem under constraints:
min_b || a - b ||^2 such that b_0 + b_1 + b_2 + b_3 = 0 and b_0 + b_1 = Y_0 and b_2 + b_3 = Y_1.
We assume that a_0 + a_1 + a_2 + a_3 = 0 and a_0 + a_1 = X_0 and a_2 + a_3 = X_1.
Therefore, the equations (1) are the assumptions on a and the equations (2) and (3) are the KKT equations.
You can eliminate variables from a system of linear or polynomial equations using a Groebner basis:
In [61]: G = groebner(list_eq, [*mu, lambda_, *b, *a, *X, *Y])
In [62]: for eq in G: pprint(eq)
X₁ - Y₁ + 2⋅λ + 2⋅μ₀
-X₁ + Y₁ + 2⋅λ + 2⋅μ₁
X₁ + Y₁ + 2⋅a₁ + 2⋅b₀
-X₁ + Y₁ - 2⋅a₁ + 2⋅b₁
-X₁ - Y₁ + 2⋅a₃ + 2⋅b₂
X₁ - Y₁ - 2⋅a₃ + 2⋅b₃
X₁ + a₀ + a₁
-X₁ + a₂ + a₃
X₀ + X₁
Y₀ + Y₁
Here the first two equations have mu and lambda but the others have these symbols eliminated. You can use G[2:] to get the equations that do not involve mu and lambda. The order of the symbols in a lex Groebner basis determines which symbols are eliminated first from the equations. You can solve specifically for b in terms of a, X and Y by picking out the equations involving b:
In [63]: solve(G[2:6], b)
Out[63]:
⎧ X₁ Y₁ X₁ Y₁ X₁ Y₁ X₁ Y₁ ⎫
⎨b₀: - ── - ── - a₁, b₁: ── - ── + a₁, b₂: ── + ── - a₃, b₃: - ── + ── + a₃⎬
⎩ 2 2 2 2 2 2 2 2 ⎭
This is not exactly the form you suggested but the form of solution for the problem is not unique because of the constraints among the variables it is expressed in. There are many equivalent ways to express b in terms of a, X and Y even after eliminating mu and lambda because a, X and Y are not independent (they are 8 symbols connected by 4 constraints).
Sometimes adding auxiliary equations with the pattern you desire and indicating what you don't want as a solution variable can help you get closer to what you desired:
[38] eqs = list_eq + [Y[0]-X[0]-var('z0'), Y[1]-X[1]-var('z1')]
[39] sol = Dict(solve(eqs, exclude=a, dict=True)[0]); sol

Sympy conversion between piecewise and min/max functions

Is there a way to convert min/max to piecewise functions and vice versa? For example:
x = Symbol('x')
Min(Max(x, 0), 1)
Is the same as the piecewise function:
⎧0 for x < 0
⎪
⎨x for x > 0 ∧ x < 1
⎪
⎩1 for x > 1
With sympy you can write this as:
Piecewise(
(0, x < 0),
(x, (x > 0) & (x < 1)),
(1, x > 1)
)
There is probably a mathematical term for this which I don't know.
You can go from Min/Max to Piecewise using rewrite but I'm not sure how to go the other way (maybe not implemented yet):
In [15]: e = Min(Max(x, 0), 1)
In [16]: e
Out[16]: Min(1, Max(0, x))
In [17]: e.rewrite(Piecewise)
Out[17]:
⎧ 1 for x ≥ 1
⎪
⎨⎧0 for x ≤ 0
⎪⎨ otherwise
⎩⎩x otherwise
In [18]: piecewise_fold(_)
Out[18]:
⎧1 for x ≥ 1
⎪
⎨0 for x ≤ 0
⎪
⎩x otherwise
https://docs.sympy.org/latest/tutorial/simplification.html#rewrite
https://docs.sympy.org/latest/modules/core.html#sympy.core.basic.Basic.rewrite

Can you get a list of the powers in a polynomial? Pari GP

I'm working with single variable polynomials with coefficients +1/-1 (and zero). These can be very long and the range of powers can be quite big. It would be convenient for me to view the powers as a vector - is there any way of doing this quickly? I had hoped there would be a command already in Pari to do this, but I can't seem to see one?
Just an example to confirm what I'm trying to do...
Input:x^10 - x^8 + x^5 - x^2 + x + 1
Desired output: [10, 8, 5, 2, 1, 0]
You can use Vecrev to get the polynomial coefficients. After that just enumerate them to select the zero-based positions of non-zeros. You want the following one-liner:
nonzeros(xs) = Vecrev([x[2]-1 | x <- select(x -> x[1] != 0, vector(#xs, i, [xs[i], i]))])
Now you can easily get the list of polynomial powers:
p = x^10 - x^8 + x^5 - x^2 + x + 1
nonzeros(Vecrev(p))
>> [10, 8, 5, 2, 1, 0]

Changed order for parse_expr()

I use parse_expr("-5 + 2*x + 3 - 7*x + 5 - 3*x", evaluate=False).
According to documentation for evaluate=False, I expected to keep the order of the expression:
"When False, the order of the arguments will remain as they were in the string ..."
But the result is sorted:
-7*x - 3*x + 2*x - 5 + 3 + 5
sympy=1.4
It is as advertised:
>>> u = parse_expr("-5 + 2*x + 3 - 7*x + 5 - 3*x", evaluate=False); u.args
(-5, 2*x, 3, -7*x, 5, -3*x)
The printer, however, prints them in sorted order. It seems like there should be an easier way to do the following, but it works:
>>> s=StrPrinter(dict(order='none'))
>>> s._print_Add(u)
-5 + 2*x + 3 - 7*x + 5 - 3*x

Extended polynomials in library NTL

There is code is written using NTL library:
int main()
{
ZZ_p::init(ZZ(5)); // define GF(5)
ZZ_pX P;
BuildIrred(P, 4); // generate an irreducible polynomial P
// of degree 4 over GF(5)
ZZ_pE::init(P); // define GF(5^4)
ZZ_pEX f, g, h; // declare polynomials over GF(5^4)
random(f, 3); // f is a random, monic polynomial of degree 3
SetCoeff(f, 3);
cout << f << endl<< endl;
}
The output is:
[[3 1 1 4] [2 1 3 2] [1 0 3 1] [1]]
For example, [1 2 3] is mean 3x² + 2x + 1.
What the form of notation polynomial over GF in this case?
Your question is a little bit difficult to understand. If I understand you right, the question is how to interpret the NTL representation [[3 1 1 4] [2 1 3 2] [1 0 3 1] [1]] of a polynomial over the finite field with 5⁴ elements.
First: The elements in the finite field with 5⁴ elements (called GF(5⁴)) are represented as the polynomials GF(5)[X] mod f, where f is an irreducible polynomial of degree 4.
This means a polynomial over GF(5⁴) is a Polynomial where the coefficients are polynomials in GF(5)[X] mod f.
So [[3 1 1 4] [2 1 3 2] [1 0 3 1] [1]] can be interpreted as
Y³ + (X³ + 3X² + 1)⋅Y² + (2X³ + 3X² + X + 2)⋅Y + (4X³ + X² + X + 3)
Notice: The comment in
random(f, 3); // f is a random, monic polynomial of degree 3
SetCoeff(f, 3);
is a little bit misleading. random(f,3) sets f to a random polynomial of degree less than 3. SetCoeff(f, 3) sets the coefficient of Y³ to 1 and after that it is a polynomial of degree 3.