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)
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.