On Mon, Nov 24, 2014 at 1:57 PM, Bill Page <bill.p...@newsynthesis.org> wrote:
> On 22 November 2014 at 12:34, Ondřej Čertík <ondrej.cer...@gmail.com> wrote:
>> On Sat, Nov 22, 2014 at 7:23 AM, Bill Page <bill.p...@newsynthesis.org> 
>> wrote:
>>> ...
>>> FriCAS currently does not implement a symbolic 'conjugate' operator.
>>> The issue concerns whether adding 'conjugate' is a good idea and only
>>> secondly how to differentiate it.
>>
>> Ah, I had no idea that FriCAS does not implement conjugate(x).
>> How do you handle complex numbers then?
>
> Sorry, I gave you the wrong impression. I specifically referred to the
> lack of "symbolic 'conjugate operator".  By that I meant that the
> 'Expression' functor does not export a 'conjugate' operator.  My patch
> adds such an operator to Expression.  But FriCAS has many domains of
> computation besides those constructed by 'Expression' and some of them
> do include 'conjugate'. For example the 'Complex' functor includes
> 'conjugate' so we can write:
>
> (1) -> x:Complex Expression Integer := a + %i*b
>
>    (1)  a + b %i
>                                            Type: Complex(Expression(Integer))
> (2) -> conjugate(x)
>
>    (2)  a - b %i
>                                            Type: Complex(Expression(Integer))
>
> This effectively and implicitly treats symbols as real valued.
>
> (3) -> D(x,a)
>
>    (3)  1
>                                            Type: Complex(Expression(Integer))
> (4) -> D(x,b)
>
>    (4)  %i
>                                            Type: Complex(Expression(Integer))
> (5) -> y:=log(x)
>
>              2    2
>         log(b  + a )                b
>    (5)  ------------ + 2atan(--------------)%i
>               2               +-------+
>                               | 2    2
>                              \|b  + a   + a
>                                            Type: Complex(Expression(Integer))
> (6) -> conjugate(y)
>
>              2    2
>         log(b  + a )                b
>    (6)  ------------ - 2atan(--------------)%i
>               2               +-------+
>                               | 2    2
>                              \|b  + a   + a
>                                            Type: Complex(Expression(Integer))
>
> But not this:
>
> (7) -> D(y,x)
> ...
>    Cannot find a definition or applicable library operation named D
>       with argument type(s)
>                         Complex(Expression(Integer))
>                         Complex(Expression(Integer))
>
> So there is no "complex derivative" as such.
>
> We can also define things this way:
>
> (8) -> z:Expression Complex Integer := a + %i*b
>
>    (8)  %i b + a
>                                            Type: Expression(Complex(Integer))
> (9) -> D(z,a)
>
>    (9)  1
>                                            Type: Expression(Complex(Integer))
> (10) -> D(z,b)
>
>    (10)  %i
>                                            Type: Expression(Complex(Integer))
> (11) -> w:=log(z)
>
>    (11)  log(%i b + a)
>                                            Type: Expression(Complex(Integer))
>
> But now we get:
>
> (12) -> conjugate(z)
>  ...
>    Cannot find a definition or applicable library operation named
>       conjugate with argument type(s)
>                         Expression(Complex(Integer))
>
>       Perhaps you should use "@" to indicate the required return type,
>       or "$" to specify which version of the function you need.
>
> (13) -> D(w,z)
> ...
>    Cannot find a definition or applicable library operation named D
>       with argument type(s)
>                         Expression(Complex(Integer))
>                         Expression(Complex(Integer))
>
> The FriCAS 'Expression' functor extends multivariate rational
> functions over a specified domain with a large number of
> transcendental kernels (symbolic functions) as well as differentiation
> and integration operators.  No explicit assumption is made about the
> domain of the variables.  My proposed patch to FriCAS adds 'conjugate'
> as another kernel function and provides a 'conjugate' as an operator.

Ok, thanks for the clarification.

>
>> In SymPy and Sage, conjugate(x) is in it, so then adding a derivative
>> of abs(x) does not make things worse.
>>
>
> In FriCAS 'abs' is already a kernel function and it implemented the
> derivative of 'abs' even before my proposed patch but I think the
> current definition is wrong:
>
> (14) -> D(abs(x),x)
>
>          abs(x)
>    (14)  ------
>             x
>                                                     Type: Expression(Integer)

I think that's correct for real numbers, i.e. x/abs(x) = abs(x) / x.

>
>
>>>
>>> In FriCAS with my patch functions defined by
>>>
>>>   f := operator 'f
>>>
>>> are currently assume to be holomorphic and log is holomorphic by definition 
>>> so
>>>
>>> conjugate(log(x)) = log(conjugate(x))
>>>
>>> Perhaps you are considering the wrong branch.
>>> ...
>>> Complex 'log' is a multi-valued like 'sqrt' so you need to consider
>>> more than one branch.
>>
>> Well, you are right that in theory you define log(z) as
>> log(z)=log|z|+i*arg(z), and you define arg(z) as multivalued,
>> i.e. you can add 2*pi*n to it, then you can add 2*pi*i*n to log(z).
>> Since [6] and [7] differs by 2*pi*i, they are indeed the same number.
>
> I would not say that they are the same "number".  Also I don't want to
> define log(z) the way to suggest.  Rather, I think the correct
> definition of 'log(z)' is the solution of
>
>   z = exp(?)
>
> So we can write z=exp(log(z)) by definition.

Indeed, exp(log(z))=z always, see the formula (2) here:

http://www.theoretical-physics.net/dev/math/complex.html#logarithm

Just to clarify, I suggested the following definitions for (A) and (B):

(A) log(z) = log|z| + i*arg(z) + 2*pi*i*n

(B) log(z) = log|z| + i*arg(z)

I both, -pi < arg(z) <= pi, and "n" is integer. Note that in (A) I've
seen people add the 2*pi term into arg(z), so then arg(z) itself is
multivalued, then you get just (A) log(z) = log|z| + i*arg(z). In
either case, in (A), there is an explicit or implicit dependence on
"n", which denotes all the multiple values for log(z). In this email I
assume that arg(z) is single valued and I put the "n" dependence in
all equations explicitly.

In both (A) and (B),
it is true that z = exp(log(z)). However, these are different:

(A) log(exp(z)) = z + 2*pi*i*n
(B) log(exp(z)) = z + 2*pi*i*floor((pi-Im z) / 2*pi)

In (B), you get a single value, but in (A) you get multiple values,
one for each "n".

I am very familiar with the approach (B) and I think I understand
exactly what follows from what and how to derive all the formulas. But
I am not 100% sure with (A), I was hoping you can help, since that's
the approach that you want to use in FriCAS. I think what I wrote is
correct for (A), but please correct me if I am wrong.

> This is exactly analogous
> to the treatment of 'sqrt(z)' as the solution to
>
>   z = ? * ?
>
>> However, this definition quickly becomes impractical, because you
>> need to be able to numerically evaluate symbolic expressions, and
>> you would need to carry the symbolic term 2*pi*i*n around.
>
> We do not need an extra term.  We only need axioms for the correct
> behavior of the expression 'log(z)'. But 'log(z)' does not denote a
> function in the sense of a many-to-one mapping.  The inverse of a
> function is only a function (possibly partial) if the function is
> injective (one-to-one).

Sure, that's why you have "n" in the formula for log(z) in (A), and
the function is multivalued over all "n".

>
>> This multivalued approach has always been very confusing to me. But
>> it is a valid approach (i.e. see 
>> http://en.wikipedia.org/wiki/Riemann_surface),
>> so let's call this is an approach (A).
>
> The Riemann surface is an important tool in complex analysis but I
> have yet to see it used explicitly in any computer algebra system as a
> representation of complex functions.

I thought the Riemann surface gives you a way to couple "n" in
log(a*b), log(a) and log(b) in such a way that you get exactly
log(a*b)-log(a)-log(b)=0. I.e. that the Riemann surface is (A). I
agree that I haven't seen it used in CAS. But I think it must be
implicitly used in FriCAS somehow. Maybe you can clarify that.

>
>>
>> The other approach, let's call it approach (B), is that languages like
>> Fortran, C, Python, and CAS like Mathematica, SymPy, Sage all pick
>> a branch cut, and all of them (as far as I know) pick it along the
>> negative real axis. For this example I think it doesn't matter where
>> you choose the branch cut, as the conjugate of log(-1) simply flips
>> the sign of it, so it won't be equal to log(-1) anymore. In this
>> approach you need to carry the corrections for branch cuts.
>>
>
> In order to numerically evaluate a symbolic expression it is indeed
> necessary to choose a branch in the case of "multi-valued functions",
> i.e. expressions like 'sqrt(x)' and 'log(x)'.  But this choice should
> not effect the axiomatic properties of these expressions.

I see. I think in the definition:

(A) log(z) = log|z| + i*arg(z) + 2*pi*i*n

the result is multivalued, and you obtain all the numerical values by
plugging different integers for "n".

>
>> Some examples of identities valid in each approach:
>>
>> (A) conjugate(log(z)) = log(conjugate(z))
>> (B) conjugate(log(z)) = log(conjugate(z)) -2*pi*i*floor((arg(z)+pi)/(2*pi))
>>
>> or
>>
>> (A) log(a*b) = log(a) + log(b)
>> (B) log(a*b) = log(a) + log(b) + 2*pi*i*floor((pi-arg(a)-arg(b))/(2*pi))
>>
>> Bill, why don't you check if FriCAS is using approach (A) or (B)?
>
> In FrCAS we have
>
> (2) -> normalize(log(a*b)-log(a)-log(b))
>
>    (2)  0
>                                                     Type: Expression(Integer)
>
> and with my proposed patch we also have 'conjugate(log(z)) =
> log(conjugate(z))' by definition. So this is like your (A).

What does Expression(Integer) mean? Does it mean that "a" and "b" are
integers? I.e. does the above hold if a=b=-1?

This is precisely the part that I don't understand with the approach
(A). log(a*b), log(a) and log(b) are all multivalued, so you would
naively think, that log(a*b)-log(a)-log(b) = 0 + 2*pi*i*n, for all
"n". But I think this is not the case, I think the "n" in log(a*b) is
coupled to the implicit "n" in log(a) and log(b) in such a way, that
the result is exactly 0. Can you clarify exactly how this works?

Using the approach (B), with a=b=-1, we get:

log(a*b) = 0
log(a) = log(b) = i*pi

and finally, in the equation log(a*b) = log(a) + log(b) +
2*pi*i*floor((pi-arg(a)-arg(b))/(2*pi)), the floor() is "-1", so we
get -2*pi*i as the correction and the result is 0. So it all works and
everything is single valued.

>
>>
>> I would assume that FriCAS is also using the approach (B), and thus
>> conjugate(log(z)) is not equal to log(conjugate(z)), but let's wait
>> until what you find.
>>
>
> For numeric domains which support 'conjugate' FriCAS behaves in the
> same way (B) as Python and the other languages you mentioned.  For
> example:
>
> (3) -> z:Complex Float := 1 - %i
>
>    (3)  1.0 - %i
>                                                          Type: Complex(Float)
> (4) -> test( conjugate(log(z)) = log(conjugate(z)) -
> 2*%pi*%i*floor((argument(z)+%pi)/(2*%pi)) )
>
>    (4)  true
>                                                                 Type: Boolean
> (5) -> a:Complex Float := -1
>
>    (5)  - 1.0
>                                                          Type: Complex(Float)
> (6) -> b:Complex Float := -2
>
>    (6)  - 2.0
>                                                          Type: Complex(Float)
> (7) -> test( log(a*b) = log(a)+log(b) +
> 2*%pi*%i*floor((%pi-argument(a)-argument(b))/(2*%pi)) )
>
>    (7)  true
>                                                                 Type: Boolean
>
> Now I am a little unclear on what you are proposing.  Are you
> suggesting that symbolic computations (such as those using
> 'Expression' in FriCAS) should somehow introduce an independent
> 'argument' function instead of defining it as
>
>   argument(x) = log(x/abs(x))/%i

I think that the following equations hold, at least in (B), so I think
you can use any of them to define arg(x):

arg(x) = log(x/abs(x)) / i
arg(x) = im(log(z))
arg(x) = atan2(Im x, Re x)

In the approach (B), I think there is multiple ways how to
define/derive things. I chose one such way, and I start from arg(x) =
atan2(Im x, Re x) and derive everything else. But I am pretty sure you
can also just start from log(z) and derive arg(z) and there are
probably couple other different approaches. In the approach that I
chose, it all becomes just simple algebra, you don't need to think at
all, it's just plugging one expression into another, everything is
single valued and you always obtain correct expressions. So all I am
saying is that in (B) you can directly evaluate things numerically and
everything is self-consistent.

>
> or that 'abs' is somehow an important part of these equalities?

I am not sure what you mean by this question.

> Note
> that 'normalize' does not have to introduce these sort of terms to in
> order to return 0 in result  (2) above.

See my questions above about this.

Ondrej

-- 
You received this message because you are subscribed to the Google Groups 
"sage-devel" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sage-devel+unsubscr...@googlegroups.com.
To post to this group, send email to sage-devel@googlegroups.com.
Visit this group at http://groups.google.com/group/sage-devel.
For more options, visit https://groups.google.com/d/optout.

Reply via email to