# Simplifying expressions with Sign functions to a single piecewise format

I have the following expression with many Sign functions:

phase = 2 Sign[t] (1/2 (-1 + e) - 1/2 (1 + e) Sign[t]) + (1/2 (-1 + e1) +
1/2 (1 + e1) Sign[d - t1]) (-1 + Sign[t1]) - (1/2 (-e + e1) +
1/2 (e + e1) Sign[d + t - t1]) (-1 +
Sign[-t + t1]) - (1/2 (-1 + e2) + 1/2 (1 + e2) Sign[d - t2]) (-1 +
Sign[t2]) + (1/2 (-e + e2) + 1/2 (e + e2) Sign[d + t - t2]) (-1 +
Sign[-t + t2]) +
2 (1/2 (-e1 + e2) + 1/2 (e1 + e2) Sign[t1 - t2]) Sign[-t1 + t2]


All variables are assumed to be real. There are 10 different arguments inside Sign, namely,

t, d-t1, t1, d+t-t1, -t+t1, d-t2, t2, d+t-t2, -t+t2, t1-t2


I can assume that all of the above values are nonzero, hence Sign function always returns 1 or -1. Then, the above expression exp can be written in piecewise format with $$2^{10} = 1024$$ cases. How can I convert into this form?

I tried as follows. First, borrowing the result from Converting HeavisideTheta[]s and Sign[]s functions to a single Piecewise[], define a function

ToPiecewise[f_] :=
PiecewiseExpand[
f /. {Sign[x_] :>
Piecewise[{{1, x > 0}, {-1, x < 0}, {Indeterminate, x == 0}}]}]


Since I am only interested when the argument of Sign is nonzero, I put Indeterminate for x==0. This ToPiecewise function converts single Sign in piecewise format.

By doing

Sum[Simplify[PiecewiseExpand[ToPiecewise[phase[[i]]]]], {i, 1, 6}]


I obtain the following:

The result correct, but there are some problems.

1. Anyway, I should some over these terms and collapse into a single piecewise function. However,

% // PiecewiseExpand


takes huge amount of time, hence making me gave up.

1. Some results in the picture are redundant. For example, why one needs the case marked in red? I think removing these redundancy is the key to boost up the performance, but don't know how to do that.
• Re the circled-red True: The conditions in the pieces are not exhaustive if x is complex. Try PiecewiseExpand[expr, assumptions, Reals] (use assumptions = True if no assumptions). Oct 10, 2021 at 15:25
• The following does some meaty simplifications but produces a single (if large) Piecewise expression within half a minute. ToPiecewise[expr_] := expr /. Sign[x_] -> Which[x > 0, 1, x < 0, -1, True, Indeterminate]; and then PiecewiseExpand[phase // ToPiecewise, True, Reals, Method -> {"ConditionSimplifier" -> Reduce}] Oct 10, 2021 at 22:59
• @MichaelE2 Sadly, using PiecewiseExpand[ToPiecewise[phase[[6]]], True, Reals] gives the same redundant result. Oct 11, 2021 at 1:04
• Try PiecewiseExpand with Method -> {"OrderlessConditions" -> True}. Oct 11, 2021 at 1:32
• Oct 12, 2021 at 12:46

First set up the contingencies

phase = 2 Sign[
t] (1/2 (-1 + e) - 1/2 (1 + e) Sign[t]) + (1/2 (-1 + e1) +
1/2 (1 + e1) Sign[d - t1]) (-1 + Sign[t1]) - (1/2 (-e + e1) +
1/2 (e + e1) Sign[d + t - t1]) (-1 +
Sign[-t + t1]) - (1/2 (-1 + e2) +
1/2 (1 + e2) Sign[d - t2]) (-1 + Sign[t2]) + (1/2 (-e + e2) +
1/2 (e + e2) Sign[d + t - t2]) (-1 + Sign[-t + t2]) +
2 (1/2 (-e1 + e2) + 1/2 (e1 + e2) Sign[t1 - t2]) Sign[-t1 + t2];

ToPiecewise[expr_] := expr /. Sign[x_] :>
Piecewise[{{1, x > 0}, {-1, x < 0}, {Indeterminate, x == 0}}];

ToIf[expr_] := expr /. Sign[x_] :> If[x > 0, 1, -1];

ToWhich[expr_] := expr /. Sign[x_] :>
Which[x > 0, 1, x < 0, -1, True, Indeterminate];

ToSwitch[expr_] := expr /. Sign[x_] :> Switch[x, y_ /; y > 0, 1, _, -1];


With these in place it all depends on what you want to do with your result.

If you want a deep result but want to leave it to the next generation to sort out:

ByteCount[FullSimplify[PiecewiseExpand[phase // ToPiecewise]]] // Timing

(* to the next generation *)


If you want an impressive, publishable result but are ready to move onto new challenges

ByteCount[FullSimplify[PiecewiseExpand[phase // ToIf]]] // Timing

(* {8.05217, 56584} *)


If you want a promising result but one that evidently needs more funding

 ByteCount[FullSimplify[PiecewiseExpand[phase // ToWhich]]] // Timing

(* {18.3734, 73240} *)


If you want a promising result that but one that clearly needs a new research group to investigate further

ByteCount[PiecewiseExpand[phase // ToWhich, True, Reals,
Method -> {
"ConditionSimplifier" -> Reduce,
"ValueSimplifier" -> Together}]] // Timing

(* {22.3115, 168328} *)


If you're desperate for a spectacular result in a Hail Mary for a Nobel

ByteCount[nobel = FullSimplify[PiecewiseExpand[phase // ToSwitch]]] // Timing
nobel

(*
{0.008306, 200}
-2 e + 2 e1
*)

• I was impressed by your answer! May I ask the subsequent question, explained in mathematica.stackexchange.com/questions/256765/… ? In this question, I want to perform a simple summation over the result that you find, but it takes a very long time... Oct 11, 2021 at 13:44
• @eigenvalue Well I hope you weren't too impressed! The intent was to show that one should be a little suspicious of such wildly different outcomes both in the time taken and the resulting simplifications. And especially here where the timing out of ToPiecewise is perhaps the way with the most precedence followed by If, Which and finally the rarely used Switch for this usage. Hence while PiecewiseExpand is a powerful tool I would be carefully prodding and checking the equivalence of all outputs. I'm afraid don't have much time currently but perhaps providing some extra context ... Oct 12, 2021 at 2:38
• ... might prod a wider response (e.g. what are equivalent outputs? which outputs make sense for your application? what is the motivation and why does it need to be PiecewiseExpanded?) Oct 12, 2021 at 2:41
• As explained in mathematica.stackexchange.com/questions/256765/… , my motivation for this question related to numerically integrate a function. I have a integrand that is a product of some function and the phase. I expect that by using PiecewiseExpand, I can locate where the sum over phase is nonzero. This makes me to integrate only in the subregion, thereby simplifying the problem. Oct 12, 2021 at 10:45

You can get the complete result quite fast, if you take into accout phase is a linear sum phase == pp0 + e ppe + e1 ppe1 + e2 ppe2 with the ppi dependent on {d,t,t1,t2}.

First regard the coefficients separately and than combine them.

phase = 2 Sign[
t] (1/2 (-1 + e) - 1/2 (1 + e) Sign[t]) + (1/2 (-1 + e1) +
1/2 (1 + e1) Sign[d - t1]) (-1 + Sign[t1]) - (1/2 (-e + e1) +
1/2 (e + e1) Sign[d + t - t1]) (-1 +
Sign[-t + t1]) - (1/2 (-1 + e2) +
1/2 (1 + e2) Sign[d - t2]) (-1 + Sign[t2]) + (1/2 (-e + e2) +
1/2 (e + e2) Sign[d + t - t2]) (-1 + Sign[-t + t2]) +
2 (1/2 (-e1 + e2) + 1/2 (e1 + e2) Sign[t1 - t2]) Sign[-t1 + t2] //
Expand;

vars = DeleteCases[Variables[phase] /. Sign[aa_] -> aa, e | e1 | e2]

coe = Coefficient[phase, #] & /@ {e, e1, e2} // Simplify

coe0 = phase - {e, e1, e2}.coe // Simplify

(pp0 = PiecewiseExpand[coe0, cond, Reals] //
Simplify[#, cond] &) // Timing

(ppe = PiecewiseExpand[coe[[1]], cond, Reals] //
Simplify[#, cond] &) // Timing

(ppe1 = PiecewiseExpand[coe[[2]], cond, Reals] //
Simplify[#, cond] &) // Timing

(ppe2 = PiecewiseExpand[coe[[3]], cond, Reals] //
Simplify[#, cond] &) // Timing

(ppall = PiecewiseExpand[pp0 + e ppe + e1 ppe1 + e2 ppe2, cond,
Reals] // Simplify[#, cond] &) // Timing


Test it 1000 times

And @@ Table[ppall == phase /.
Thread[{d, t, t1, t2} -> RandomReal[{-10, 10}, 4]] //
Simplify, {1000}]

(*   True   *)