Search code examples
maxima

Transform expression so that all if/then/else are on top level


I have series of simple equations, which has one input variable x and handful of helper variables, and the result should be an expression in terms of x. Those equations have if/then/else in them. How can I force the final result to only have if/then/else at top level, and not in subexpressions? Ideally I want to receive a result that resembles jagged linear plot equation in the form of if x < step1 then m1 * x + k1 else if x < step2 then m2 * x + k2 else ... where step, m and k would be numeric constants. To give an example, I have

eq1: var1 = if x > 1678 then 400 - 0.18*(x - 642)  else 460 - 0.26*(x-730);
eq2: var2 = subst([eq1], if x <= 7520.5 then (x - var1)*0.2 else 7520.5*0.2+(x - 7520.5)*-0.32);
eq3: x - var2;
res: subst([eq1, eq2], eq3);
expand(res);

which currently evaluates to

x-(if x<=7520.5 then 0.2*x-0.2*(if x>1678 then 515.56-0.18*x else 649.8-0.26*x) else 3910.66-0.32*x)

Solution

  • Great question, unfortunately, I don't see an easy way to do that. Here is a sketch of some ideas; I'll try to come back in the next day or two to fill in the details.

    First of all, I thought about replacing if/then/else with unit_step, charfun, or charfun2. However, it looks like simplifications for arithmetic on those expressions (e.g. product of unit_step) isn't implemented, so working with any of those wouldn't be any simpler. But if someone else can see how to do it, please, by all means, show us.

    The outline of a way that I see to do this is (1) define rules for arithmetic on if/then/else, and (2) define rules for flattening nested if/then/else. I would use defrule to define the rules, although tellsimpafter is also a possibility.

    EDIT: I've put together some code to handle (1) and (2) above. See robert-dodier/simplify_conditionals at: https://github.com/maxima-project-on-github/maxima-packages

    Here's the result I get for the problem you mentioned.

    (%i2) load ("simplify_conditionals.mac");
    (%o2)               simplify_conditionals.mac
    

    First assign e the expression you showed.

    (%i3) e: x-(if x<=7520.5 then 0.2*x-0.2*(if x>1678 then 515.56-0.18*x else 649.8-0.26*x) else 3910.66-0.32*x);
    (%o3) x - (if x <= 7520.5 then 0.2 x
     - 0.2 (if x > 1678 then 515.56 - 0.18 x else 649.8 - 0.26 x)
     else 3910.66 - 0.32 x)
    

    Carry out + and * arithmetic. (- and / are also covered since those are defined in terms of + and * in Maxima.)

    (%i4) arithmetic_with_conditionals (e);
    (%o4) if x <= 7520.5 then (if x > 1678
     then 0.8 x + 0.2 (515.56 - 0.18 x)
     else 0.8 x + 0.2 (649.8 - 0.26 x)) else 1.32 x - 3910.66
    

    Call expand to distribute * over + (this is aside from the conditional stuff).

    (%i5) e1: expand (%);
    (%o5) if x <= 7520.5 then (if x > 1678 then 0.764 x + 103.112
                         else 0.748 x + 129.96) else 1.32 x - 3910.66
    

    Flatten nested conditionals.

    (%i6) e2: flatten_conditionals (e1);
    (%o6) if (x <= 7520.5) and (x > 1678) then 0.764 x + 103.112
       elseif x <= 7520.5 then 0.748 x + 129.96 else 1.32 x - 3910.66
    

    Okay, that's it. At this point you could verify the result by, e.g., plotting both e and e2 and verifying they're the same, or evaluating e2 - e for values of x and verifying the result is zero or nearly so (since floating point arithmetic can yield very small, nonzero results).

    I made some effort to verify the implementation; see rtest_simplify_conditionals.mac. But the tests aren't exhaustive. Also, I didn't try to handle other operations, e.g. exponents or arbitrary functions. All the same, I hope the package is useful to you in some way.