4
$\begingroup$

This question and answer was inspired by this closed as duplicate question.

One possible definition of the n-th order root of x is the number y, such that y^n == x. That means, for example, that both 1 and -1 are square roots of 1. After looking at the request in the OP of the linked question (have f[z] := Sqrt[z] return {-1, 1}) I thought it would be nice, to have a parser, that handles this kind of thing on the fly for a more general case.

Here's an example of desired output:

parse[x^(1/2)+y^(1/2)] (* Assume for the moment, that x and y are positive *)
(* {Sqrt[x] + Sqrt[y], Sqrt[x] - Sqrt[y], -Sqrt[x] + Sqrt[y], -Sqrt[x] - Sqrt[y]} *)

That is, as x^(1/2) has two possible solutions, as does y^(1/2), there are four possible combinations. If I consider an expression like x^(1/2) + y^(1/3), then I'd like to get a list of 6 solutions.

This much - capturing all branches of integer roots - I have managed to achieve, as shown in my answer.

What I'm looking for is a) a better coding style; b) generalization to a wider class of functions; c) see below.

Clarification for (b): I would, for example, like to have Log be reinterpreted as {Log[x], Log[x] + 2 Pi I, Log[x] + 4 Pi I}. Realizing, that Log has an infinite number of branches, it is of course necessary to stop somewhere. Perhaps allow an option to be passed, saying "I want the branches from m to n" or something akin to that.

Regarding point (c): consider the expression Sqrt[x] + Sqrt[x]. For the sake of this question it is not the same as 2 Sqrt[x]. Indeed, I would expect here to be three or four (doubly degenerate zero) results: {-2 Sqrt[x], 0, 0, 2 Sqrt[x]}

EDIT 13.08.15
Jens found the question somewhat unclear. I suppose, it's due to the mathematical considerations distracting him, such as my last point where I claim Sqrt[x] + Sqrt[x] is not always 2 Sqrt[x]. I'll try to present an alternative formulation of my question that doesn't have anything to do with mathematics, but is purely a task on expression-manipulation.

Suppose, we have an expression which contains, among other things, two, three or even more parameters. Let's call them a, b, and c. I do not know, how deeply a, b and c may be nested, at which level, etc.

Let's introduce a function called values:

values[a] = {a1, a2, a3}
values[b] = {b1, b2, b3}
values[c] = {c1, c2, c3}

I want to transform

expression[{a_, b_, c_}, vars___]  

to

expression[#, vars___] &/@ Tuples[{values[a], values[b], values[c]}]. 

BUT, and this is a very important "but" that complicates things, I don't have expression explicitly defined. All I have, is the actual form of the expression. For example, it may be something like

expr = (a + b)/c

In that case I can do

expr /. Thread /@ (Cases[expr, a | b | c, Infinity] -> # & /@ 
Tuples[values /@ Cases[expr, a | b | c, Infinity]])

and get

{(a1 + b1)/c1, (a1 + b1)/c2, (a1 + b1)/c3, (a1 + b2)/c1, 
(a1 + b2)/c2, (a1 + b2)/c3, (a1 + b3)/c1, (a1 + b3)/c2, 
(a1 + b3)/c3, (a2 + b1)/c1, (a2 + b1)/c2, (a2 + b1)/c3, 
(a2 + b2)/c1, (a2 + b2)/c2, (a2 + b2)/c3, (a2 + b3)/c1, 
(a2 + b3)/c2, (a2 + b3)/c3, (a3 + b1)/c1, (a3 + b1)/c2, 
(a3 + b1)/c3, (a3 + b2)/c1, (a3 + b2)/c2, (a3 + b2)/c3, 
(a3 + b3)/c1, (a3 + b3)/c2, (a3 + b3)/c3}

However, if my expression looks like

expr = (a + b)/a

I would want to get exactly the same result as above, but with c1, c2, c3 replaced with a1, a2, a3 respectively. How would be a nice way to force Mathematica to treat every new occurrence of a (or any other expression for which values has a DownValue) as a completely new variable?

$\endgroup$
  • 2
    $\begingroup$ You're starting to veer into the world of Riemann surfaces. In that respect, I will encourage you to look at Michael Trott's series of articles on plotting the Riemann surfaces of algebraic and elementary transcendental functions. He addresses this particular problem in the course of generating faithful Riemann surface representations. $\endgroup$ – J. M. will be back soon Aug 12 '15 at 15:27
  • $\begingroup$ Mathematically, this is very dubious. Sqrt is defined to be one particular inverse function with a branch cut on the negative real axis. You can define other inverse functions by changing the branch cut, see here, but then they are different functions unless you extend the domain. Therefore, Sqrt[x] + Sqrt[x] == 2 Sqrt[x] is always true. Any parser that violates this will lead to big trouble. It's safer to stick with Reduce if you want multiple roots. $\endgroup$ – Jens Aug 12 '15 at 16:45
  • $\begingroup$ @Jens To reword the question a bit, I want a parser, that returns a list of all branches of a function with branch cuts. If you don't like the Sqrt[x]+Sqrt[x] example, it's perfectly fine to reinterpret it as parse[Sqrt[x]+Sqrt[y]]/.y->x. I imagine, what you're trying to tell me, is that if I choose one branch for Sqrt[x] I must necessarily choose the same one for Sqrt[y]? What about combining roots of different powers then? The concept of consistency then becomes somewhat ambiguous. $\endgroup$ – LLlAMnYP Aug 12 '15 at 17:15
  • $\begingroup$ Everything depends on the particular problem you actually are trying to solve. I think the real "killer app" for this kind of task will usually turn out to be Reduce, combined with a formulation that avoids abusing the notation for inverse function in the first place. $\endgroup$ – Jens Aug 12 '15 at 17:28
  • $\begingroup$ @Jens well, tbh, there is no particular problem. I saw that duplicate question and came up with a problem on expression-manipulation that I, and hopefully others, find interesting. I tried to describe the task as exhaustively as I could, but if something's not clear, please ask. Regarding Reduce though... I'm looking through the docs right now, but I don't see a straighforward way to utilize it. Maybe you could offer some minimal example? $\endgroup$ – LLlAMnYP Aug 12 '15 at 17:45
2
$\begingroup$

Here's how I handle roots of integer power:

myroot[x_, n_, k_] := Root[#^n - x &, k]

allroots[multivalexpr_] := 
 FixedPoint[
  Function[{expr}, 
   Flatten[MapAt[
     Function[{ex}, 
      Replace[ex, 
       myroot[sym_, i_] :> (myroot[sym, i, #] & /@ Range[i])]], expr, 
     FirstPosition[expr, myroot[_, _], {0}]]]], multivalexpr]

Then

allroots[myroot[x, 2] + myroot[x, 3]]
{-Sqrt[x] + Root[-x + #1^3 &, 1],
 -Sqrt[x] + Root[-x + #1^3 &, 2],
 -Sqrt[x] + Root[-x + #1^3 &, 3],
  Sqrt[x] + Root[-x + #1^3 &, 1],
  Sqrt[x] + Root[-x + #1^3 &, 2], 
  Sqrt[x] + Root[-x + #1^3 &, 3]
}

And here's how it plots (showing imaginary parts of all possible branches):

Plot[Evaluate@Flatten@Through[{Im}[N@allroots[myroot[x, 2] + myroot[x, 3]]]], {x, -1, 1}]

6 possible imaginary parts

As an aside, I realized, that I really need a Replace function that only works on the first matched pattern that it encounters. Something like ReplaceFirstOccurence. Instead I was forced to use MapAt[Replace[#, Rule[pattern, goal]]&, expr, FirstPosition[expr, pattern]]. I wonder if there's a neater way to do this.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.