# factoring out a term - not only a variable - in a symbolic expression

I know that I can use collect to "factor out" a variable or arrange a symbolic expressions in powers of that variable.

But if there's a term, e.g. x+1, as common factor, I don't know how to factor it out. collect doesn't work for this, e.g.:

sage: var('c0 c1 c2 x0 x1 x')
sage: (c0 * (x+1) + c1*(x+1) + c2*c1*x - c0*c1*(x+1)).collect(x+1)
output: -c0*c1*(x + 1) + c1*c2*x + c0*(x + 1) + c1*(x + 1)


What I would like to have here is:

(-c0*c1 + c0 + c1)*(x + 1) + c1*c2*x

edit retag close merge delete

Sort by » oldest newest most voted

sage: sum([v*SR("u")^v for v in (c0 * (x+1) + c1*(x+1) + c2*c1*x - c0*c1*
(x+1)).subs(x+1==SR("u")).coefficients(SR("u"))]).subs(SR("u")==x+1)
c1*c2*x - (c0*c1 - c0 - c1)*(x + 1)


This can be programmed as :

def mycollect(e1, e2):
"""
Collects e2 coefficients in e1, even when e2 is not a factor of
some of the terms of e1
"""
g = maxima_calculus.gensym()._sage_()
while g in e1.variables(): g = maxima_calculus.gensym()._sage_()
return sum([v*g^v
for v in e1.subs(e2==g).coefficients(g)]).subs(g==e2)


Test :

sage: mycollect(c0 * (x+1) + c1*(x+1) + c2*c1*x - c0*c1*(x+1),x+1)
c1*c2*x - (c0*c1 - c0 - c1)*(x + 1)


This (with some possible generalizations) might become a new method for symbolic expressions. Advice ?

HTH,

more

I like your solution. It also works e.g. for this

sage: mycollect(c0 * (cos(x)+exp(x)) + c1*(cos(x)+exp(x)) + c2*c1*x - c0*c1*(cos(x)+exp(x)), cos(x)+exp(x))

c1*c2*x - (c0*c1 - c0 - c1)*(cos(x) + e^x)


Unfortunately this seems not to work when factoring out multiple factors at the same time:

sage: var('x1 y1 y2 y3')
sage: mycollect(x1*y1*y2 + y3*x1*y1, x1*y1)
x1*y1*y2 + x1*y1*y3


I would like to get

(y2 + y3) * x1*y1
here.


I also tried

sage: mycollect(mycollect(x1*y1*y2 + y3*x1*y1, x1), y1)
(x1*y2 + x1*y3)*y1


Has anyone a solution that also works for this ?

I reformulate my wish for a function - call it my_collect2(expr, fact) - more precisely:

Assume expr is a sum of products. Each factor in each product can be a constant, a single variable or any other expression like a+b, xy, sin(axy), ... If the sum contains also a single variable (rather than a product), I consider this variable also as "product", e.g. in (a+b)x + x the last x shall also be considered as "product" and so this whole expression is a sum of products. For this expression

my_collect2((a+b)*x + x, x)


shall yield

(a+b+1) * x


Generally assume that fact is also any expression. If fact occures as common factor in two or more of the products, fact should be factored out by the call my_collect2(expr, fact).

Now assume expr is not a sum of products, but when seeing expr as a tree, there may be one or more sums of products inside. Then the factoring-out shall also be applied recursively to each 'inner' sum of products.

More generally, the recursion shall also be made for a python list of expressions, i.e. each expression in the list shall be searched for possible factoring-out's.

Apart from factoring out I do not want to apply any mathemetical rearrangements of the expression (that keep the expression value unchanged). With this I mean e.g. if you have

var('a b c x y z')
my_collect2( (a+b)*(x+y-1) + a+b + 2*c*(x+y) , x*y)


I do not want to get

(a+b+2*c) * (x+y)


since this would first require to rearrange the expression to (a+b)(x+y) + 2c*(x+y) and then factor out (x+y).

Further more, my_collect2 should also be callable with three arguments:

my_collect2(expr, fact, subs_fact)


should work like my_collect2(expr, fact) except that the each factored out term fact should be substituted by subs_fact, e.g.

sage: var('x1 y1 y2 y3 z1 z2 x1y1')
sage: mycollect2(x1*y1*y2 + y3*x1*y1, x1*y1, x1y1)


Expected: (y2 + y3) * x1y1

It should be possible to write such a function my_collect2(), but since I'm new to sage it will probably be easier to do for someone with more experience. My idea is to recursively scan the expression tree for sums of products and then look if fact is a common factor of at least two of the products.

I give some examples:

sage: var('x1 y1 y2 y3 z1 z2')
sage: my_collect2(x1*y1*y2 + y3*x1*y1, x1*y1)


Expected: (y2 + y3) * x1*y1

sage: var('x1 y1 y2 y3 z1')
sage: my_collect2(sin(exp( (x1+cos(y1))*z1*y2 + y3*(x1+cos(y1))*z1  )), (x1+cos(y1))*z1)
Expected: sin(exp( (y2 + y3) * (x1+cos(y1))*z1  ))

sage: my_collect2([z1*(x1+y1) + z2*(x1+y1), (z1-1)*(x1+y1) + x1 + y1 + z2*(x1+y1)], x1+y1)
Expected: [(z1 + z2)*(x1+y1), (z1-1)*(x1+y1) + x1 + y1 + z2*(x1+y1)]

more

First off, notice the result you request is rather arbitrary - why it's (-c0*c1 + c0 + c1)*(x + 1) + c1*c2*x but not (-c0*c1 + c0 + c1 + c1*c2)*(x + 1) - c1*c2?

The latter can be achieved via using a new variable $y=x+1$:

var('c0 c1 c2 x0 x1 x y')
(c0 * (x+1) + c1*(x+1) + c2*c1*x - c0*c1*(x+1)).subs({x:y-1}).collect(y)


However, if your expressions are polynomial it may be worth to work in polynomial rather than symbolic ring, which provides much advanced functionality with respect to handling polynomials.

more

Yes, you're right, it would have been clearer if I had written e.g. -c0c1(x + 1) + c1c2x1 + c0(x + 1) + c1(x + 1). But sometimes I get long results from a calculation and want to factor out only some terms that I see in the output as common factors of some products (not those that could be used when writing the expression in another way).