18

I have a very complicated mathematica expression that I'd like to simplify by using a new, possibly dimensionless parameter.

An example of my expression is:

K=a*b*t/((t+f)c*d);

(the actual expression is monstrously large, thousands of characters). I'd like to replace all occurrences of the expression t/(t+f) with p

p=t/(t+f);

The goal here is to find a replacement so that all t's and f's are replaced by p. In this case, the replacement p is a nondimensionalized parameter, so it seems like a good candidate replacement.

I've not been able to figure out how to do this in mathematica (or if its possible). I tried:

eq1= K==a*b*t/((t+f)c*d);
eq2= p==t/(t+f);
Solve[{eq1,eq2},K]

Not surprisingly, this doesn't work. If there were a way to force it to solve for K in terms of p,a,b,c,d, this might work, but I can't figure out how to do that either. Thoughts?


Edit #1 (11/10/11 - 1:30) [deleted to simplify]

OK, new tact. I've taken p=ton/(ton+toff) and multiplied p by several expressions. I know that p can be completely eliminated. The new expression (in terms of p) is

testEQ = A B p + A^2 B p^2 + (A+B)p^3;

Then I made the substitution for p, and called (normal) FullSimplify, giving me this expression.

testEQ2= (ton (B ton^2 + A^2 B ton (toff + ton) + 
   A (ton^2 + B (toff + ton)^2)))/(toff + ton)^3;

Finally, I tried all of the suggestions below, except the last (not sure how it works yet!)

Only the eliminate option worked. So I guess I'll try this method from now on. Thank you.

EQ1 = a1 == (ton (B ton^2 + A^2 B ton (toff + ton) + 
        A (ton^2 + B (toff + ton)^2)))/(toff + ton)^3;
EQ2 = P1 == ton/(ton + toff);
Eliminate[{EQ1, EQ2}, {ton, toff}]

A B P1 + A^2 B P1^2 + (A + B) P1^3 == a1

I should add, if the goal is to make all substitutions that are possible, leaving the rest, I still don't know how to do that. But it appears that if a substitution can completely eliminate a few variables, Eliminate[] works best.

vector07
  • 339
  • 1
  • 3
  • 12
  • Does f occur also outside the expression to replace? Because otherwise just replacing `f->t/p-t` should probably do the trick. – celtschk Nov 10 '11 at 00:23
  • Related questions: http://stackoverflow.com/questions/6074801/how-to-perform-a-complicated-change-of-variables-for-a-polynomial-in-mathematic http://stackoverflow.com/questions/6210321/question-on-smart-replacing-in-mathematica – Mr.Wizard Nov 10 '11 at 00:45
  • celt--the replacement f->t/p-t doesnt eliminate t! f may or may not appear in situations outside of 'p'. It's a 31,000 character expression, but perhaps if all the t's, f's are expanded it's just a simple replacement. Wizard--will take a look at those guys. – vector07 Nov 10 '11 at 01:08
  • It should eliminate all `t/(t+f)`s (after simplification) because t/(t+(t/p-t)) = t/(t/p) = p. But anyway, if f can occur outside that expression, this solution doesn't apply, of course. – celtschk Nov 10 '11 at 01:29
  • OK, these are helpful. Still relatively new to Mathematica, gimme a bit to try some of these ideas out. – vector07 Nov 10 '11 at 19:07

3 Answers3

13

Have you tried this?

K = a*b*t/((t + f) c*d);
Solve[p == t/(t + f), t]
 -> {{t -> -((f p)/(-1 + p))}}

Simplify[K /. %[[1]] ]
 -> (a b p)/(c d)

EDIT: Oh, and are you aware of Eliminiate?

Eliminate[{eq1, eq2}, {t,f}]
 -> a b p == c d K && c != 0 && d != 0

Solve[%, K]
 -> {{K -> (a b p)/(c d)}}

EDIT 2: Also, in this simple case, solving for K and t simultaneously seems to do the trick, too:

Solve[{eq1, eq2}, {K, t}]
 -> {{K -> (a b p)/(c d), t -> -((f p)/(-1 + p))}}
Niki
  • 15,662
  • 5
  • 48
  • 74
9

Something along these lines is discussed in the MathGroup post at

http://forums.wolfram.com/mathgroup/archive/2009/Oct/msg00023.html

(I see it has an apocryphal note that is quite relevant, at least to the author of that post.)

Here is how it might be applied in the example above. For purposes of keeping this self contained I'll repeat the replacement code.

replacementFunction[expr_, rep_, vars_] := 
 Module[{num = Numerator[expr], den = Denominator[expr], 
   hed = Head[expr], base, expon}, 
  If[PolynomialQ[num, vars] && 
    PolynomialQ[den, vars] && ! NumberQ[den], 
   replacementFunction[num, rep, vars]/
    replacementFunction[den, rep, vars], 
   If[hed === Power && Length[expr] == 2, 
    base = replacementFunction[expr[[1]], rep, vars];
    expon = replacementFunction[expr[[2]], rep, vars];
    PolynomialReduce[base^expon, rep, vars][[2]], 
    If[Head[hed] === Symbol && 
      MemberQ[Attributes[hed], NumericFunction], 
     Map[replacementFunction[#, rep, vars] &, expr], 
     PolynomialReduce[expr, rep, vars][[2]]]]]]

Your example is now as follows. We take the input, and also the replacement. For the latter we make an equivalent polynomial by clearing denominators.

kK = a*b*t/((t + f) c*d);
rep = Numerator[Together[p - t/(t + f)]];

Now we can invoke the replacement. We list the variables we are interested in replacing, treating 'p' as a parameter. This way it will get ordered lower than the others, meaning the replacements will try to remove them in favor of 'p'.

In[127]:= replacementFunction[kK, rep, {t, f}]
Out[127]= (a b p)/(c d)

This approach has a bit of magic in figuring out what should be the listed "variables". Possibly some further tweakage could be done to improve on that. But I believe that, generally, simply not listing the things we want to use as new replacements is the right way to go.

Over the years there have been variants of this idea on MathGroup. It is possible that some others may be better suited to the specific expression(s) you wish to handle.

--- edit ---

The idea behind this is to use PolynomialReduce to do algebraic replacement. That is to say, we do not try for pattern matching but instead use polynomial "canonicalization" a method. But in general we're not working with polynomial inputs. So we apply this idea recursively on PolynomialQ arguments inside NumericQ functions.

Earlier versions of this idea, along with some more explanation, can be found at the note referenced below, as well as in notes it references (how's that for explanatory recursion?).

http://forums.wolfram.com/mathgroup/archive/2006/Aug/msg00283.html

--- end edit ---

--- edit 2 ---

As observed in the wild, this approach is not always a simplifier. It does algebraic replacement, which involves, under the hood, a notion of "term ordering" (roughly, "which things get replaced by which others?") and thus simple variables may expand to longer expressions.

Another form of term rewriting is syntactic replacement via pattern matching, and other responses discuss using that approach. It has a different drawback, insofar as the generality of patterns to consider might become overwhelming. For example, what does one do with k^2/(w + p^4)^3 when the rule is to replace k/(w + p^4) with q? (Specifically, how do we recognize this as being equivalent to (k/(w + p^4))^2*1/(w + p^4)?)

The upshot is one needs to have an idea of what is desired and what methods might be feasible. This of course is generally problem specific.

One thing that occurs is perhaps you want to find and replace all commonly occurring "complicated" expressions with simpler ones. This is referred to as common subexpression elimination (CSE). In Mathematica this can be done using a function called Experimental`OptimizeExpression[]. Here are several links to MathGroup posts that discuss this.

http://forums.wolfram.com/mathgroup/archive/2009/Jul/msg00138.html

http://forums.wolfram.com/mathgroup/archive/2007/Nov/msg00270.html

http://forums.wolfram.com/mathgroup/archive/2006/Sep/msg00300.html

http://forums.wolfram.com/mathgroup/archive/2005/Jan/msg00387.html

http://forums.wolfram.com/mathgroup/archive/2002/Jan/msg00369.html

Here is an example from one of those notes.

InputForm[Experimental`OptimizeExpression[(3 + 3*a^2 + Sqrt[5 + 6*a + 5*a^2] +
      a*(4 + Sqrt[5 + 6*a + 5*a^2]))/6]]

Out[206]//InputForm=
Experimental`OptimizedExpression[Block[{Compile`$1, Compile`$3, Compile`$4, 
   Compile`$5, Compile`$6}, Compile`$1 = a^2; Compile`$3 = 6*a; 
   Compile`$4 = 5*Compile`$1; Compile`$5 = 5 + Compile`$3 + Compile`$4; 
   Compile`$6 = Sqrt[Compile`$5]; (3 + 3*Compile`$1 + Compile`$6 + 
     a*(4 + Compile`$6))/6]]

--- end edit 2 ---

Daniel Lichtblau

Daniel Lichtblau
  • 6,854
  • 1
  • 23
  • 30
  • Daniel, would you provide an explanation of what this code is doing? It appears a bit involved. – Mr.Wizard Nov 10 '11 at 19:21
  • I'm with wizard - I'm intrigued, but I don't know what's going on in the function. In particular, I don't understand it enough to modify it for slightly different problems. – vector07 Nov 10 '11 at 20:33
  • @Daniel Lichtblau, OK, I tried messsing around with this. I'm treating your function as a blackbox, because I don't *really* understand how it's working. It's pretty cute actually. For the actual expression I'm trying to simplify, it sometimes becomes simpler, and sometimes not! It does make replacements even when it cannot eliminate a variable, which is good in some situations but not in others. For fun, I'm running the function on my GIANT expression, which will probably take a few hours. Will post results later. – vector07 Nov 11 '11 at 20:16
  • @vector07 See second edit for discussion of this "enlargement" phenomenon and related matters. – Daniel Lichtblau Nov 11 '11 at 21:52
  • Yet another interesting internal function I've never seen before. Please tell me, why has this been Experimental since at least 2002? (btw, +1 already) – Mr.Wizard Nov 12 '11 at 02:25
  • @Mr.Wizard I do not know. I am not terribly familiar with the reasons for promoting, or not, from various internally used contexts to System`. – Daniel Lichtblau Nov 12 '11 at 22:39
  • @DanielLichtblau , in the end, Mathematica crashed after crunching for about ~20 hours :( . This sometimes happens when I try to manipulate this expression. Will try again another day. Haven't gotten to your edit #2 suggestion yet, but will continue to play with these ideas. Thanks for your time. – vector07 Nov 15 '11 at 06:39
8
K = a*b*t/((t+f)c*d);

FullSimplify[ K, 
 TransformationFunctions -> {(# /. t/(t + f) -> p &), Automatic}]
(a b p) / (c d)

Corrected update to show another method:

EQ1 = a1 == (ton (B ton^2 + A^2 B ton (toff + ton) + 
        A (ton^2 + B (toff + ton)^2)))/(toff + ton)^3;

f = # /. ton + toff -> ton/p &;

FullSimplify[f @ EQ1]
a1 == p (A B + A^2 B p + (A + B) p^2)

I don't know if this is of any value at this point, but hopefully at least it works.

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • OK, tried this. Was able to reproduce the result you got here, but not for a slightly more complicated case. The qualification is, though, I don't actually know if the substitution t/t+f is actually sufficient to eliminate t and f from the equation. I've added to the question a more complicated example. – vector07 Nov 10 '11 at 19:28
  • @vector `Eliminate` does appear to be better suited to this task, though I more robust transformation function would likely also work. I posted this answer mostly for completeness, after linking to the similar questions in a comment. I shall see if I can make this method work for your new test case, but I suspect that other answers are better. I assume Daniel's answer is the most robust, but I am waiting for his explanation of it before I give it my vote. – Mr.Wizard Nov 10 '11 at 23:03
  • Wiz - is your new solution setting ton to 1? – vector07 Nov 11 '11 at 20:01
  • @vector um... yes. I thought I had a logical reason for that yesterday, but now I don't know what I was thinking. I will try *again*. Thanks for calling me on it. – Mr.Wizard Nov 12 '11 at 02:10