# Are there any techniques to improve the results of simplify or full_simplify?

(Updated to simplify the starting expression)

I have the following long expression, which I want to simplify for non-negative integer values of n:

var('A, B, C, E, α, β, γ')
fn = (
(12*A + 6*B + 4*C + 2*E + 4*α + 2*β + γ)^n
- (11*A + 6*B + 4*C + 2*E + 4*α + 2*β + γ)^n
- (11*A + 5*B + 4*C + 2*E + 4*α + 2*β + γ)^n
+ (10*A + 5*B + 4*C + 2*E + 4*α + 2*β + γ)^n
- (10*A + 5*B + 3*C + 2*E + 4*α + 2*β + γ)^n
+ ( 9*A + 5*B + 3*C + 2*E + 4*α + 2*β + γ)^n
+ ( 9*A + 4*B + 3*C + 2*E + 4*α + 2*β + γ)^n
- ( 8*A + 4*B + 3*C + 2*E + 4*α + 2*β + γ)^n
- ( 8*A + 4*B + 3*C +   E + 4*α + 2*β + γ)^n
+ ( 7*A + 4*B + 3*C +   E + 4*α + 2*β + γ)^n
+ ( 7*A + 3*B + 3*C +   E + 4*α + 2*β + γ)^n
- ( 6*A + 3*B + 3*C +   E + 4*α + 2*β + γ)^n
+ ( 6*A + 3*B + 2*C +   E + 4*α + 2*β + γ)^n
- ( 6*A + 3*B + 2*C +   E + 3*α + 2*β + γ)^n
- ( 6*A + 3*B + 2*C +   E + 3*α +   β + γ)^n
+ ( 6*A + 3*B + 2*C +   E + 2*α +   β + γ)^n
- ( 6*A + 3*B + 2*C +   E + 2*α +   β)^n
+ ( 6*A + 3*B + 2*C +   E +   α +   β)^n
+ ( 6*A + 3*B + 2*C +   E +   α)^n
- ( 6*A + 3*B + 2*C +   E)^n
+ ( 6*A + 3*B +   C +   E)^n
- ( 5*A + 3*B +   C +   E)^n
- ( 5*A + 2*B +   C +   E)^n
+ ( 4*A + 2*B +   C +   E)^n
+ ( 4*A + 2*B +   C)^n
- ( 3*A + 2*B +   C)^n
- ( 3*A +   B +   C)^n
+ ( 2*A +   B +   C)^n
- ( 2*A +   B)^n
+ (   A +   B)^n
+     A^n
)/factorial(n)


After replacing all ns with 0s, the command f0.full_simplify() returns the correct result of 1. After replacing all ns with 1s or 2s, the commands f1.full_simplify() or f2.full_simplify() return the correct result of 0.

However, for higher integer values of n, the output is no longer fully simplified. The command f3.full_simplify() returns the result

2*A^3 + 3*A^2*B + A*B^2 + 2*α^3 + α*β^2 - 4*(A^2 + A*B)*α - (2*A^2 + 2*A*B - 3*α^2)*β - (A^2 + A*B - α^2 - α*β)*γ


which I was able to further simplify manually to get

A*(A + B)*(2*A + B - 4*α - 2*β - γ) + α*(α + β)*(2*α + β + γ)


Similarly, the command f4.full_simplify() returns the result

12*A^4 + 24*A^3*B + 15*A^2*B^2 + 3*A*B^3 + 2*(6*A + 3*B + 2*C + E)*α^3 + 4*α^4 + α*β^3 - 8*(A^2 + A*B)*α^2 - (2*A^2 + 2*A*B - (6*A + 3*B + 2*C + E)*α - 5*α^2)*β^2 - 1/2*(A^2 + A*B - α^2 - α*β)*γ^2 + 2*(2*A^3 + 3*A^2*B + A*B^2)*C + (2*A^3 + 3*A^2*B + A*B^2)*E - 2*(10*A^3 + 15*A^2*B + 5*A*B^2 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E)*α - (10*A^3 + 15*A^2*B + 5*A*B^2 - 3*(6*A + 3*B + 2*C + E)*α^2 - 8*α^3 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E + 8*(A^2 + A*B)*α)*β - 1/2*(10*A^3 + 15*A^2*B + 5*A*B^2 - 2*(6*A + 3*B + 2*C + E)*α^2 - 6*α^3 - 3*α*β^2 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E + 8*(A^2 + A*B)*α + (4*A^2 + 4*A*B - 2*(6*A + 3*B + 2*C + E)*α - 9*α^2)*β)*γ


which I was able to manually simplify down to

(6*A + 3*B + 2*C + E + 1/2*(4*α + 2*β + γ))*(A*(A + B)*(2*A + B - 4*α - 2*β - γ) + α*(α + β)*(2*α + β + γ))


The command f5.full_simplify() returns the even longer result

137/2*A^5 + 685/4*A^4*B + 925/6*A^3*B^2 + 60*A^2*B^3 + 103/12*A*B^4 + 4*(6*A + 3*B + 2*C + E)*α^4 + 9/2*α^5 + 7/12*α*β^4 + 2*(A^2 + A*B)*C^3 + 1/3*(76*A^2 + 76*A*B + 27*B^2 + 36*(2*A + B)*C + 12*C^2 + 6*(6*A + 3*B + 2*C)*E + 3*E^2)*α^3 - 1/3*(4*A^2 + 4*A*B - 3*(6*A + 3*B + 2*C + E)*α - 12*α^2)*β^3 - 1/6*(A^2 + A*B - α^2 - α*β)*γ^3 + 10*(2*A^3 + 3*A^2*B + A*B^2)*C^2 + 1/2*(6*A^3 + 9*A^2*B + 3*A*B^2 + 2*(A^2 + A*B)*C)*E^2 - 4*(10*A^3 + 15*A^2*B + 5*A*B^2 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E)*α^2 - 1/6*(60*A^3 + 90*A^2*B + 30*A*B^2 - 30*(6*A + 3*B + 2*C + E)*α^2 - 61*α^3 + 24*(A^2 + A*B)*C + 12*(A^2 + A*B)*E - 3*(20*A^2 + 20*A*B + 9*B^2 + 12*(2*A + B)*C + 4*C^2 + 2*(6*A + 3*B + 2*C)*E + E^2)*α)*β^2 - 1/4*(10*A^3 + 15*A^2*B + 5*A*B^2 - 2*(6*A + 3*B + 2*C + E)*α^2 - 6*α^3 - 3*α*β^2 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E + 8*(A^2 + A*B)*α + (4*A^2 + 4*A*B - 2*(6*A + 3*B + 2*C + E)*α - 9*α^2)*β)*γ^2 + 16*(4*A^4 + 8*A^3*B + 5*A^2*B^2 + A*B^3)*C + (28*A^4 + 56*A^3*B + 35*A^2*B^2 + 7*A*B^3 + 3*(A^2 + A*B)*C^2 + 9*(2*A^3 + 3*A^2*B + A*B^2)*C)*E - 1/3*(55*A^4 + 110*A^3*B + 69*A^2*B^2 + 14*A*B^3 + 12*(A^2 + A*B)*C^2 + 6*(A^2 + A*B)*E^2 + 24*(2*A^3 + 3*A^2*B + A*B^2)*C + 6*(6*A^3 + 9*A^2*B + 3*A*B^2 + 2*(A^2 + A*B)*C)*E)*α - 1/12*(110*A^4 + 220*A^3*B + 138*A^2*B^2 + 28*A*B^3 - 96*(6*A + 3*B + 2*C + E)*α^3 - 135*α^4 + 24*(A^2 + A*B)*C^2 + 12*(A^2 + A*B)*E^2 - 6*(76*A^2 + 76*A*B + 27*B^2 + 36*(2*A + B)*C + 12*C^2 + 6*(6*A + 3*B + 2*C)*E + 3*E^2)*α^2 + 48*(2*A^3 + 3*A^2*B + A*B^2)*C + 12*(6*A^3 + 9*A^2*B + 3*A*B^2 + 2*(A^2 + A*B)*C)*E + 48*(10*A^3 + 15*A^2*B + 5*A*B^2 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E)*α)*β - 1/12*(55*A^4 + 110*A^3*B + 69*A^2*B^2 + 14*A*B^3 - 36*(6*A + 3*B + 2*C + E)*α^3 - 55*α^4 - 14*α*β^3 + 12*(A^2 + A*B)*C^2 + 6*(A^2 + A*B)*E^2 - 6*(20*A^2 + 20*A*B + 9*B^2 + 12*(2*A + B)*C + 4*C^2 + 2*(6*A + 3*B + 2*C)*E + E^2)*α^2 + 3*(8*A^2 + 8*A*B - 6*(6*A + 3*B + 2*C + E)*α - 23*α^2)*β^2 + 24*(2*A^3 + 3*A^2*B + A*B^2)*C + 6*(6*A^3 + 9*A^2*B + 3*A*B^2 + 2*(A^2 + A*B)*C)*E + 24*(10*A^3 + 15*A^2*B + 5*A*B^2 + 4*(A^2 + A*B)*C + 2*(A^2 + A*B)*E)*α + 2*(60*A^3 + 90*A^2*B + 30*A*B^2 - 27*(6*A + 3*B + 2*C + E)*α^2 - 55*α^3 + 24*(A^2 + A*B)*C + 12*(A^2 + A*B)*E - 3*(20*A^2 + 20*A*B + 9*B^2 + 12*(2*A + B)*C + 4*C^2 + 2*(6*A + 3*B + 2*C)*E + E^2)*α)*β)*γ


and I'm getting tired of manually slogging through these rapidly-increasingly-long expressions to completely simplify them.

Is there any way to get Sage to do a better job of completely simplifying these expressions?

edit retag close merge delete

Maybe use polynomial rings instead of the symbolic ring ?

( 2023-05-11 17:23:36 +0200 )edit

What does that mean? I'm very new to Sage and don't know what rings are.

( 2023-05-11 22:17:44 +0200 )edit

@FrédéricC can you explain how to use polynomial rings for this? Or do you know of a good resource that explains it? This is my first time using Sage and I can't figure out how to deal with rings.

( 2023-05-16 15:42:34 +0200 )edit

Replace usage of var by A,B,C,D,E,F,G,H=polygens(QQ,'A,B,C,D,E,F,G,H').

( 2023-05-18 08:38:44 +0200 )edit

Sort by » oldest newest most voted

Maybe a better way to write the code that should answer the computational question behind the question is as follows. The posted code uses abs(E) at many places. Well, let us use an other variable for this expression. Since retyping and/or replacing is complicated, i will use E instead below. Since n runs in 0, 1, 2, 3, 4, 5, 6, ... it is a good idea to define a function $f$ of n (depending on the many parameters). So let us start with:

var('A, B, C, D, E, α, β, γ')

f = lambda n : (
(8*A + 6*B + 4*C + 4*D + 2*E + 4*α + 2*β + γ)^n
- (7*A + 6*B + 4*C + 4*D + 2*E + 4*α + 2*β + γ)^n
- (7*A + 5*B + 4*C + 4*D + 2*E + 4*α + 2*β + γ)^n
+ (6*A + 5*B + 4*C + 4*D + 2*E + 4*α + 2*β + γ)^n
- (6*A + 5*B + 3*C + 4*D + 2*E + 4*α + 2*β + γ)^n
+ (5*A + 5*B + 3*C + 4*D + 2*E + 4*α + 2*β + γ)^n
+ (5*A + 4*B + 3*C + 4*D + 2*E + 4*α + 2*β + γ)^n
- (5*A + 4*B + 3*C + 3*D + 2*E + 4*α + 2*β + γ)^n
- (5*A + 4*B + 3*C + 3*D +   E + 4*α + 2*β + γ)^n
+ (5*A + 4*B + 3*C + 2*D +   E + 4*α + 2*β + γ)^n
+ (5*A + 3*B + 3*C + 2*D +   E + 4*α + 2*β + γ)^n
- (4*A + 3*B + 3*C + 2*D +   E + 4*α + 2*β + γ)^n
+ (4*A + 3*B + 2*C + 2*D +   E + 4*α + 2*β + γ)^n
- (4*A + 3*B + 2*C + 2*D +   E + 3*α + 2*β + γ)^n
- (4*A + 3*B + 2*C + 2*D +   E + 3*α +   β + γ)^n
+ (4*A + 3*B + 2*C + 2*D +   E + 2*α +   β + γ)^n
- (4*A + 3*B + 2*C + 2*D +   E + 2*α +   β)^n
+ (4*A + 3*B + 2*C + 2*D +   E +   α +   β)^n
+ (4*A + 3*B + 2*C + 2*D +   E +   α)^n
- (4*A + 3*B + 2*C + 2*D +   E)^n
+ (4*A + 3*B +   C + 2*D +   E)^n
- (3*A + 3*B +   C + 2*D +   E)^n
- (3*A + 2*B +   C + 2*D +   E)^n
+ (3*A + 2*B +   C +   D +   E)^n
+ (3*A + 2*B +   C +   D)^n
- (3*A + 2*B +   C)^n
- (3*A +   B +   C)^n
+ (2*A +   B +   C)^n
- (2*A +   B)^n
+ (  A +   B)^n
+    A^n
)/factorial(n)


Then for instance f(4) is an expression in the many variables $A, B, C, \bbox[yellow]{\qquad D\qquad }, E, α, β, γ$, and what we want now is to replace $D$ by either $A+E$, or by $A$. OK, let's see what happens when we substitute, then factorize, since you have a factor:

sage: f(4).subs(D==A+E).simplify_full().factor()
1/2*(2*A^3 + 3*A^2*B + A*B^2 + 8*A*E^2 + 4*B*E^2 + 4*C*E^2 + 6*E^3 - 4*A^2*α - 4*A*B*α
+ 8*E^2*α + 2*α^3 - 2*A^2*β - 2*A*B*β + 4*E^2*β + 3*α^2*β + α*β^2
- A^2*γ - A*B*γ + 2*E^2*γ + α^2*γ + α*β*γ)
*(12*A + 6*B + 4*C + 6*E + 4*α + 2*β + γ)

sage: f(4).subs(D==A).simplify_full().factor()
1/2*(2*A^3 + 3*A^2*B + A*B^2 - 4*A^2*α - 4*A*B*α
+ 2*α^3 - 2*A^2*β - 2*A*B*β + 3*α^2*β + α*β^2
- A^2*γ - A*B*γ + α^2*γ + α*β*γ)
*(12*A + 6*B + 4*C + 2*E + 4*α + 2*β + γ)


(Results were manually adjusted, lines broken to fit in the asksage page.)

Now there is no computer algebra functionality to split a polynomial in two, so that the two pieces have "good factorizations each". However, we have some information from the manual computation. One piece does not involve $A$. So let us take that factor, and split it w.r.t. $A$ into an $A$-free part, and a part where we can factorize $A$:

In the command line:

sage: expression = [e for e, mul in f(4).subs(D==A).simplify_full().factor().factor_list()][0]
sage: expression
2*A^3 + 3*A^2*B + A*B^2 - 4*A^2*α - 4*A*B*α + 2*α^3 - 2*A^2*β - 2*A*B*β + 3*α^2*β + α*β^2 - A^2*γ - A*B*γ + α^2*γ + α*β*γ
sage: expression.subs(A==0).factor()
(2*α + β + γ)*(α + β)*α
sage: (expression - _).factor()
(2*A + B - 4*α - 2*β - γ)*(A + B)*A


However, when we substitute D==A+E instead, there is no good parallel found this way.

The question comes without the mathematical structure, so that missing more insight i tried to factorize also some further expression. It turns out for instance that when asking for

f(6).subs(D==A).factor()


there is indeed a non-trivial factor,

(12*A + 6*B + 4*C + 2*E + 4*α + 2*β + γ)


which is not really expected when building a sum of many six power linear expressions. And

f(6).subs(D==A+E).factor()


shows the factor

(12*A + 6*B + 4*C + 6*E + 4*α + 2*β + γ)


This motivated me to break the linearity in each base taken to the sixth power, so let us introduce a $t$ and cover both cases by substituting $D=A+tE$:

sage: var('t');
sage: f(6).subs(D==A+t*E).factor()


And there is a "small factor"

(4*E*t + 12*A + 6*B + 4*C + 2*E + 4*α + 2*β + γ)


(And further mess around.) I am stopping here the answer. (But with more insight, one must be able to do / to guess more...)

more

I worked out that all instances of D and abs(E), regardless of if E is taken to be negative or positive, completely cancel out into the same result. I think you got different results for the factors because you missed the sign change (if E is negative, + abs(E) becomes - E which cancels out the + E in D = A + E). I've updated my question with that result.

( 2023-05-12 18:07:50 +0200 )edit

Using f(4).simplify_full().factor() does provide an almost-completely-condensed version of f(4), but f(5).simplify_full().factor() doesn't condense the result of f(5).simplify_full() at all. Do you know why, or how to make it work better?

( 2023-05-12 18:13:52 +0200 )edit

@dan_fulea What type of insight would help in answering my question?

( 2023-05-17 17:00:54 +0200 )edit

Dear @ZL,

@dan_fulea's answer is right : parameterizing your expression is the rightthing to do ; and, as he wrote, this parameterization needs some hinsight in the problem you want to solve.

I just want to add that Sympy's simplify seems more agressive tn a Sage's simplifu and sometimes full simplify ; the idiom X._sympy_().simplify._sage_() might come in handy. Ditto for mathematica.FullSimplify(X).sage() (which, of course, requires Mathematica or the gratis Wolfram Engine...).

HTH,

more

( 2023-05-12 18:07:58 +0200 )edit

What problem are you trying to solve ?

( 2023-05-12 20:27:06 +0200 )edit

At the moment I'm just trying to simplify these expressions for f(n). If you mean "where does f(n) come from", the background is extremely long. Much too long to fit in this comment field. Is there anything specific you think would be helpful to know about the expression and/or its origin?

( 2023-05-12 21:18:16 +0200 )edit

Also, I tried using f(5)._sympy_().simplify._sage_() but got the error AttributeError: 'function' object has no attribute '_sage_'. I don't have Mathematica, either.

( 2023-05-12 21:39:41 +0200 )edit