# Solution for equation system with piece-wise defined functions

As I could swear this worked just yesterday, I am probably just doing something stupid here and I am sorry to bother you :)

I am trying to find the point where a curve crosses a line. In this case, the curve is a box, but it should work for other things, too, so I need to find the problem here, not better ways for a solution for boxes.

boxCurve[boundaries_List] := Module[{b = boundaries, bH, bW, cf},
bH = b[[2, 2]] - b[[2, 1]];
bW = b[[1, 2]] - b[[1, 1]];
cf = 2 bH + 2 bW;
Function[t, Piecewise[{
{{b[[1, 1]] + cf t, b[[2, 1]]}, 0 <= cf t < bW},
{{b[[1, 2]], b[[2, 1]] + (cf t - bW)}, bW <= cf t < bW + bH},
{{b[[1, 2]] - (cf t - bW - bH), b[[2, 2]]},
bW + bH <= cf t < 2 bW + bH},
{{b[[1, 1]], b[[2, 2]] - (cf t - 2 bW - bH)},
2 bW + bH <= cf t < 2 bW + 2 bH}
}, {b[[1, 1]], b[[2, 1]] + bH/4}]]
]


The box does work ParametricPlot[boxCurve[{{-1, 1}, {-1, 1}}][s], {s, 0, 1}] But, when I am trying to calculate the crossing point

Solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {-1, 1} + {1, -1} t, {s, t}]
Solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {-0.9, 1} + {1, -1} t, {s, t}]
Solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {0, 0} + {1, 0} t, {s, t}]


I get no result, although two of the base points of the lines are already on the box. This is true for both NSolve and Solve.

As said I could swear that something like this worked yesterday, because I got conditional expressions depending on s, when I only solved for t.

• I would prefer a general solution, but if nothing else comes, I am willing to give the bounty to an answer that solves the problem for the case where one curve is a straight line and only the other curve is arbitrarily continuous. Also, the first crossing point in a given direction from a defined point on the line would be enough. Jun 15, 2012 at 7:50
• It seems like the problem in what you post really boils down to solve not handling the conditions of piece-wise unless you hit the default conditions. This fails: Solve[Piecewise[{{{0, t}, 0 < t <= 1}}] == {t2,1/2},{t, t2}] yet this succeeds: Solve[ConditionalExpression[{0, t}, 0 < t <= 1] == {t2, 0.5}, {t, t2}]. If you are having problems with general shapes defined without piecewise, I would suggest you update your question with simple examples. Jun 15, 2012 at 10:26
• No, I definitely need piecewise defined functions. Strictly speaking, this is exactly on of the cases (but not the only one), where I need this function. Want I am trying to do is writing a Module, where you input a "surface" and the starting point and direction of a light beam, and it calculates the light path upon potentially multiple reflection. All in 2D, so curves are enough. Jun 15, 2012 at 11:24
• Well then I'm unsure of your remaining problem is. It seems Heike's answer solves the only special case where your code doesn't work. Jun 15, 2012 at 11:43
• It does not consider the else case of Piecewise, does it? Then, I am not quite sure how to differentiate if the submitted function is defined piecewise. And finally, I would really like to know how I got conditional answers from Solve (or NSolve, not sure) that one time, because that Solution would be much cleaner. Jun 15, 2012 at 11:51

For a function defined using Piecewise you could try something like this

SetAttributes[solve, HoldAll];

solve[(bc : boxCurve[a_][s_]) == b_, c__] := Solve[
Or @@ Append[(#1 == b && #2) & @@@ bc[[1]],
bc[[2]] == b && Not[Or @@ bc[[1, All, 2]]]], c]

solve[boxCurve[{{-1, 1}, {-1, 1}}][s] == {t, t/2}, {s, t}]

(* {{t -> ConditionalExpression[-1, s >= 1 || s < 0]},
{s -> 7/16, t -> 1}, {s -> 15/16, t -> -1}} *)


Edit

I've wrapped the whole thing in a function to make it more manageable. The default value works as well. This actually already worked in the previous version, but I've added the conditions for which the default value should be taken to the equations used in Solve.

• Hm, this does not consider the "else" case of the Piecewise and it makes the whole thing specific to Piecewise functions. I don't fully understand 1) Why my code does not work, especially numerically and 2) What I did successfully yesterday. Unfortunately I deleted the test code, integrated this into a module and now it stopped working. Jun 5, 2012 at 12:16
• Your skill with Plot is needed here. :-) Jun 5, 2012 at 12:26

My friend Sam noticed that if you split it up into two functions it works:

Second = Part[#, 2] &;
boxCurve1[boundaries_List] :=
Module[{b = boundaries, bH, bW, cf}, bH = b[[2, 2]] - b[[2, 1]];
bW = b[[1, 2]] - b[[1, 1]];
cf = 2 bH + 2 bW;
Function[t, Piecewise[{{First@{b[[1, 1]] + cf t, b[[2, 1]]},
0 <= cf t < bW}, {First@{b[[1, 2]], b[[2, 1]] + (cf t - bW)},
bW <= cf t <
bW + bH}, {First@{b[[1, 2]] - (cf t - bW - bH), b[[2, 2]]},
bW + bH <= cf t <
2 bW + bH}, {First@{b[[1, 1]], b[[2, 2]] - (cf t - 2 bW - bH)},
2 bW + bH <= cf t < 2 bW + 2 bH}},
First @ {b[[1, 1]], b[[2, 1]]}]]];

boxCurve2[boundaries_List] :=
Module[{b = boundaries, bH, bW, cf}, bH = b[[2, 2]] - b[[2, 1]];
bW = b[[1, 2]] - b[[1, 1]];
cf = 2 bH + 2 bW;
Function[t, Piecewise[{{Second@{b[[1, 1]] + cf t, b[[2, 1]]},
0 <= cf t < bW}, {Second@{b[[1, 2]], b[[2, 1]] + (cf t - bW)},
bW <= cf t < bW + bH},
{Second@{b[[1, 2]] - (cf t - bW - bH), b[[2, 2]]},
bW + bH <= cf t < 2 bW + bH}, {Second@{b[[1, 1]],
b[[2, 2]] - (cf t - 2 bW - bH)},
2 bW + bH <= cf t < 2 bW + 2 bH}},
Second@{b[[1, 1]], b[[2, 1]]}]]];

Solve[boxCurve1[{{-1, 1}, {-1, 1}}][s] == t &&
boxCurve2[{{-1, 1}, {-1, 1}}][s] == t - 1/2, {s, t}]
boxCurve[{{-1, 1}, {-1, 1}}][1/16]
boxCurve[{{-1, 1}, {-1, 1}}][7/16]
(*
{{s -> 1/16, t -> -(1/2)}, {s -> 7/16, t -> 1}}
{-(1/2), -1}
{1, 1/2}
*)


I got an answer from Wolfram Premier support. Their final solution boiled down to basically what Heike wrote. However, they provided an explanation that explains Mike's remark (and renders it really close to the solution) and also helped me to develop a Solution that suits me better (I'm not saying that it is generally better. I'll give the bounty to whoever has the most votes on Thursday).

The reason for the problem is that Solve (and Reduce) use Thread internally. If fun1 is not a list, but e.g. a Piecewise defined function, that fun1 == {t-1,t+2} is evaluated to fun1 == t-1 && fun1 == t+2

Knowing this, it is clear that Mike's example works, as it does not contain lists anymore.

My final solution now is to use

Solve[Evaluate[
Simplify[
boxCurve[{{-1, 1}, {-1, 1}}][s].{1, 0}] == {t/2, t}.{1, 0} &&
Simplify[boxCurve[{{-1, 1}, {-1, 1}}][s].{0, 1}] == {t/2, t}.{0, 1}
], {s, t}]


as this still perfectly works for normal, non-Piecewise function.

• Yup, list does not thread within Solve unless its at the top level... Nice solution!
– M.R.
Jun 19, 2012 at 18:05