6

Hi I am trying to get the common terms of a list to simplify it for example if the list I have is:

List=[['1','A1','B1','Kc','Ka'],['1','A1','B1','D2','Kc','Ka'],
['-1','A1','B1','D1','Kc','Ka'],['1','A1','B1','D1','KD','Ka'],
['-1','B1','D1','C1','Kc','Ka','KF'],['1','B1','D1','F1','Kc','Kz','Kl']]    

is there any function that could give me as a result:

List_output=[['A1', 'B1', [['D1', [['KD', 'Ka'],
['-1', 'Ka', 'Kc']]], ['Ka', 'Kc'], ['D2', 'Ka', 'Kc']]],
['B1', 'D1', [['F1', 'Kc', 'Kl', 'Kz'], ['-1', 'C1', 'KF', 'Ka', 'Kc']]]]

What I basically want to do is an algebraic reduction.

(A1 B1 Kc Ka + A1 B1 D2 Kc Ka - A1 B1 D1 Kc Ka + A1 B1 D1 KD Ka -
B1 D1 C1 Kc Ka KF + B1 D1 F1 Kc Kz Kl ) ->
A1B1[D1[-KcKa + KDKa] + D2KcKa +KcKa] + B1D1[-C1[KcKaKF] + F1[KcKzKl]]  

The only requirement for the simplification is that all terms simplified need to depend on a sum or rest of K's. In other words, everything needs to be a function of a linear combination of K's: [-KcKa + KDKa]; [KcKaKF]; [['-1','Kc','Ka'],['+1','KD','Ka']].

I am trying to use SymPy but the problem I have is that the terms to reduce come from elsewhere so I never know what the symbols will be. To use SymPy you need to declare the symbols, right? Any idea of how I can tackle this problem?

smichr
  • 16,948
  • 2
  • 27
  • 34
user3671704
  • 161
  • 7
  • A very interesting question. Do you have any requirement for the solution to be optimal in some way, or will any good simplification work? – Yash Tewari Jul 03 '16 at 12:48
  • Your problem has no unique solution: subexpressions may be factored in more than one equivalent ways. – Francesco Bonazzi Jul 03 '16 at 12:48
  • The only requirement is that all simplifications need to depend on a sum or rest of K's. In other words, everything needs to be a function of a linear combination of K's – user3671704 Jul 03 '16 at 13:32
  • @user3671704 Please edit your question to include this requirement. – Yash Tewari Jul 03 '16 at 13:38
  • Has it by chance to do with chemical reaction speed? (just guessing) – Francesco Bonazzi Jul 03 '16 at 19:00
  • @FrancescoBonazzi yes It is related to Chemistry but is not kinetics, it's about the functionalization of spectra's signals and the transition. The A,B,... are the modes and the K's are the signals in the spectrometer. – user3671704 Jul 03 '16 at 20:28
  • `horner` gets pretty close to what you want (after converting to a SymPy expression, as in Francesco's answer). It doesn't factor the D1 term from the last expression properly, for some reason. – asmeurer Jul 05 '16 at 21:49
  • Some discussion about this here https://github.com/sympy/sympy/issues/11349 – asmeurer Jul 08 '16 at 22:52

2 Answers2

2

I think you know what algebraic manipulations you want to do, but you are hung up on getting the "K" symbols out of sympy? Sympy is pretty good about guessing variable names. You can just construct the expression:

In [1]: import sympy

In [2]: List=[['1','A1','B1','Kc','Ka'],['1','A1','B1','D2','Kc','Ka'],['-1','A
   ...: 1','B1','D1','Kc','Ka'],['1','A1','B1','D1','KD','Ka'],['-1','B1','D1',
   ...: 'C1','Kc','Ka','KF'],['1','B1','D1','F1','Kc','Kz','Kl']]    

In [3]: expression = sympy.Add(*[sympy.Mul(*[sympy.S(y) for y in x]) for x in L
   ...: ist] )

In [4]: expression
Out[4]: A1*B1*D1*KD*Ka - A1*B1*D1*Ka*Kc + A1*B1*D2*Ka*Kc + A1*B1*Ka*Kc - B1*C1*D1*KF*Ka*Kc + B1*D1*F1*Kc*Kl*Kz

And then get the list of symbols:

In [5]: all_symbols = [x for x in expression.atoms() if type(x)==sympy.Symbol]

In [6]: all_symbols
Out[6]: [Kc, B1, KF, A1, Kz, Ka, D1, C1, F1, D2, KD, Kl]

Once you have the list of symbols, it is trivial to grab those which start with a 'K', or don't:

In [7]: solvefor = [x for x in all_symbols if str(x)[0]!="K"]

In [8]: solvefor
Out[8]: [B1, A1, D1, C1, F1, D2]

In [9]: sympy.horner(expression, wrt=solvefor)
Out[9]: B1*(A1*(D1*(KD*Ka - Ka*Kc) + D2*Ka*Kc + Ka*Kc) + D1*(-C1*KF*Ka*Kc + F1*Kc*Kl*Kz))
RLC
  • 1,264
  • 1
  • 8
  • 5
0

First of all, transform your list to a SymPy expression:

In [1]: List=[['1','A1','B1','Kc','Ka'],['1','A1','B1','D2','Kc','Ka'],['-1','A1','B1','D1','Kc','Ka'],['1','A1','B1','D1','KD','Ka'],['-1','B1','D1','C1','Kc','Ka','KF'],['1','B1','D1','F1','Kc','Kz','Kl']]

In [2]: list_add_mul = sympify(List)

In [4]: expr = Add(*map(lambda x: Mul(*x), list_add_mul))

In [5]: expr
Out[5]: 
A₁⋅B₁⋅D₁⋅KD⋅Ka - A₁⋅B₁⋅D₁⋅Ka⋅Kc + A₁⋅B₁⋅D₂⋅Ka⋅Kc + A₁⋅B₁⋅Ka⋅Kc - B₁⋅C₁⋅D₁⋅KF⋅K
a⋅Kc + B₁⋅D₁⋅F₁⋅Kc⋅Kl⋅Kz

Now expr is the SymPy expression you want to work with. If you simply want to replace some values, use .subs:

Remember to define the symbols you're going to use:

>>> var("Ka, Kc, Kz")

Then you can replace:

In [6]: expr.subs({Ka: 25.0, Kc: 7.0, Kz: 3.5})
Out[6]: 
25.0⋅A₁⋅B₁⋅D₁⋅KD - 175.0⋅A₁⋅B₁⋅D₁ + 175.0⋅A₁⋅B₁⋅D₂ + 175.0⋅A₁⋅B₁ - 175.0⋅B₁⋅C₁
⋅D₁⋅KF + 24.5⋅B₁⋅D₁⋅F₁⋅Kl

Otherwise, you could try to define a substitution rule for your variables. For example, put them in a dict:

{
    Ka: ... ,
    Kc: ... ,
    KD: ... ,
    KF: ... ,
}

You should replace the dots with a suitable expression containing new variables. These new variables should represent the combinations of your K constants.

For example: c1 = -Kc*Ka + KD*Ka, _c2 = ... _ then you invert these equations.

In your case, it appears that your expressions cannot be properly inverted:

>>> solve([Eq(-Kc*Ka + KD*Ka, c1 ), Eq(Kc*Ka*KF, c2), Eq(-Kc*Ka + KD*Ka, c3), Eq(Kc*Ka*KF, c4)], Ka, Kc, KD, KF)
[]
Francesco Bonazzi
  • 1,917
  • 9
  • 10