Bug in abs(I*x).diff(x)

458 views
Skip to first unread message

Ondřej Čertík

unread,
Nov 13, 2014, 12:19:07 AM11/13/14
to sage-...@googlegroups.com
Hi,

With Sage 6.3, I am getting:

sage: abs(x).diff(x)
x/abs(x)
sage: abs(I*x).diff(x)
-x/abs(I*x)

But abs(I*x) == abs(x). So also abs(x).diff(x) and abs(I*x).diff(x)
must be the same. But in the first case we get x/abs(x), and in the
second we got -x/abs(x).

In SymPy, the answer is:

In [1]: abs(x).diff(x)
Out[1]:
d d
re(x)⋅──(re(x)) + im(x)⋅──(im(x))
dx dx
─────────────────────────────────
│x│


In [2]: x = Symbol("x", real=True)

In [3]: abs(x).diff(x)
Out[3]: sign(x)

In [4]: abs(I*x).diff(x)
Out[4]: sign(x)

In [26]: var("x")
Out[26]: x

Which seems all correct --- in the complex case [1] we get a little
messy expression, but a correct one. For a real case, we get the
correct answer.

In Wolfram Alpha, the answer of abs(I*x).diff(x) is x/abs(x):

http://www.wolframalpha.com/input/?i=Diff%5BAbs%5Bi*x%5D%2C+x%5D

Which is only correct for real "x", but at least it is correct for
this special case.

The Sage result seems wrong for any "x".

Ondrej

Clemens Heuberger

unread,
Nov 13, 2014, 2:18:02 AM11/13/14
to sage-...@googlegroups.com

possibly related to http://trac.sagemath.org/ticket/12588 ?

Regards, CH
--
Univ.-Prof. Dr. Clemens Heuberger Alpen-Adria-Universität Klagenfurt
Institut für Mathematik, Universitätsstraße 65-67, 9020 Klagenfurt, Austria
Tel: +43 463 2700 3121 Fax: +43 463 2700 99 3121
clemens....@aau.at http://wwwu.aau.at/cheuberg

Ondřej Čertík

unread,
Nov 13, 2014, 10:29:41 AM11/13/14
to sage-...@googlegroups.com
Yes. Note also here:

http://mathworld.wolfram.com/AbsoluteValue.html

which says that complex derivative of d|z|/dz does not exist, as
Cauchy-Riemann equations do not hold for Abs(z). And:

"As a result of the fact that computer algebra programs such as
Mathematica generically deal with complex variables (i.e., the
definition of derivative always means complex derivative), d|z|/dz
correctly returns unevaluated by such software."

So perhaps SymPy and Sage should return Derivative(Abs(x), x). Only
when user specifies that "x" is real, only then we can differentiate.

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+...@googlegroups.com.
> To post to this group, send email to sage-...@googlegroups.com.
> Visit this group at http://groups.google.com/group/sage-devel.
> For more options, visit https://groups.google.com/d/optout.

Bill Page

unread,
Nov 13, 2014, 12:16:36 PM11/13/14
to sage-devel
It has always seemed very inconvenient to me that "computer algebra
programs such as Mathematica" choose to define derivative as
complex-derivative. I believe a reasonable alternative is what is
known as a Wirtinger derivative. Wirtinger derivatives exist for all
continuous complex-valued functions including non-holonomic functions
and permit the construction of a differential calculus for functions
of complex variables that is analogous to the ordinary differential
calculus for functions of real variables

http://en.wikipedia.org/wiki/Wirtinger_derivatives

Wirtinger derivatives come in conjugate pairs but we have

f(x).diff(conjugate(x)) = conjugate(conjugate(f(x).diff(x))

so we really only need one derivative given an appropriate conjugate
function. The Cauchy-Riemann equations reduce to

f(x).diff(conjugate(x)) = 0

I also like that abs is related to the sgn function

abs(x).diff(x) = x/abs(x)

This is consistent with

abs(x)=sqrt(x*conjugate(x))

The Wirtinger derivative of abs(x) is 1/2 x/abs(x). Its total
Wirtinger derivative is x/abs(x).

I have implemented conjugate and Wirtinger derivatives in FriCAS

http://axiom-wiki.newsynthesis.org/SandBoxWirtinger

Unfortunately I have not yet been able to convince the FriCAS
developers of the appropriateness of this approach. I would be happy
to find someone with whom to discuss this further, pro and con. The
discussion on the FriCAS email list consisted mostly of the related
proper treatment of conjugate without making explicit assumptions
about variables.

Regards,
Bill Page.

Bill Page

unread,
Nov 13, 2014, 12:51:40 PM11/13/14
to sage-devel
On 13 November 2014 12:16, Bill Page <bill...@newsynthesis.org> wrote:
>
> The Wirtinger derivative of abs(x) is 1/2 x/abs(x). Its total
> Wirtinger derivative is x/abs(x).
>

Sorry, I should have written that the Wirtinger derivative of abs(x) is

1/2 conjugate(x)/abs(x)

Bill.

maldun

unread,
Nov 13, 2014, 2:47:03 PM11/13/14
to sage-...@googlegroups.com
We had a similar problem with the complex derivative of logarithms in combination with the complex conjugate, where I also the
use of Wirtinger Operators would solve the problem: https://groups.google.com/forum/?hl=en#!topic/sage-support/bEMPMEYeZKU

Having them in Sage would be a great achievement!

Although this has some sense in complex analysis one should be careful with 'deriving' the absolute value, since
it results in the weak derivative ( http://en.wikipedia.org/wiki/Weak_derivative) , which is in a broader sense the derivative in the distribution sense. 
Thus we have infinite possible derivatives

With this expression it is indirectly forbidden to asign a specific value to the unspecified value at zero:

sage: f = abs(x).diff(x)
sage: f(x=0)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-14-615bbebcb37c> in <module>()
----> 1 f(x=Integer(0))

/home/maldun/sage/sage-6.3/local/lib/python2.7/site-packages/sage/symbolic/expression.so in sage.symbolic.expression.Expression.__call__ (build/cythonized/sage/symbolic/expression.cpp:21933)()

/home/maldun/sage/sage-6.3/local/lib/python2.7/site-packages/sage/symbolic/ring.so in sage.symbolic.ring.SymbolicRing._call_element_ (build/cythonized/sage/symbolic/ring.cpp:8493)()

/home/maldun/sage/sage-6.3/local/lib/python2.7/site-packages/sage/symbolic/expression.so in sage.symbolic.expression.Expression.substitute (build/cythonized/sage/symbolic/expression.cpp:21183)()

ValueError: power::eval(): division by zero



This is in some sense good, since  we don't have to care about the derivative at zero, 
but in an other sense it is not so good, since the subdifferential ∂abs(0) = [0,1] is a bounded and with this definition one could come to the false conclusion that abs(x)
has a pole, althoug by taking limits one can easily see that it should be bounded at zero.

For symbolic purposes, of course one could live on with this.

maldun

unread,
Nov 13, 2014, 2:51:48 PM11/13/14
to sage-...@googlegroups.com
The only clean solution for this behaviour would be a warning e.g: "Warning: This Identity holds only almost everywhere!"
But I don't know if it's worth the effort ...

maldun

unread,
Nov 13, 2014, 3:04:14 PM11/13/14
to sage-...@googlegroups.com

This is in some sense good, since  we don't have to care about the derivative at zero, 
but in an other sense it is not so good, since the subdifferential ∂abs(0) = [0,1] is a bounded and with this definition one could come to the false conclusion that abs(x)
has a pole, althoug by taking limits one can easily see that it should be bounded at zero.




Sorry I meant  ∂abs(0) = [-1,1] ...

And another thing to add: I think the only clean solution could be a warning like: "Warning: This is not a derivative in the classical sense!"
But I don't know if this is really worth the effort ... 

Ondřej Čertík

unread,
Nov 13, 2014, 4:00:24 PM11/13/14
to sage-...@googlegroups.com
Hi Bill,
Thanks for your email! I haven't talked to you in a long time.
Literally just today I learned about Wirtinger derivatives. The
wikipedia page is *really* confusing to me. It took me a while to
realize, that Wirtinger derivatives is simply the derivative with
respect to z or conjugate(z). I.e.

z = x + i*y
conjugate(z) = x - i*y

From this it follows:

x = 1/2*(z + conjugate(z))
y = i/2*(-z+conjugate(z))

Then I take any function and write it in terms of z and conjugate(z),
some examples:

|z| = sqrt(z*conjugate(z))
Re z = x = 1/2 * (z + conjugate(z))
z^2 = (x+i*y)^2

And then I simply differentiate with respect to z or conjugate(z).
This is called the Wirtinger derivative. So:

d|z|/dz = d sqrt(z*conjugate(z)) / dz = 1/2*conjugate(z) / |z|

As you said, the function is analytic if it doesn't functionally
depend on conjugate(z), as can be shown easily. So |z| or Re z are not
analytic, while z^2 is. If the function is analytic, then df/d
conjugate(z) = 0, and df/dz is the complex derivative. Right?

So for analytic functions, Wirtinger derivative gives the same answer
as Mathematica. For non-analytic functions, Mathematica leaves it
unevaluated, but Wirtinger derivative gives you something.

How do you calculate the total Wirtinger derivative? How is that defined?

Because I would like to get

d|x| / d x = x / |x|

for real x. And I don't see currently how is this formula connected to
Wirtinger derivatives. Finally, the derivative operator in a CAS could
return Wirtinger derivatives, I think it's a great idea, if somehow we
can recover the usual formula for abs(x) with real "x".

What are the cons of this approach?

Ondrej

Ondřej Čertík

unread,
Nov 13, 2014, 7:24:05 PM11/13/14
to sage-...@googlegroups.com
To elaborate on this point, if the function has a complex derivative
(i.e. it is analytic),
then the complex derivative f'(z) = \partial f / \partial x. So to
calculate a complex derivative
with respect to z=x+i*y, we just need to differentiate with respect to x.

It can be shown, that the Wirtinger derivatives df/dz is equal to
\partial f / \partial x for analytic functions,
i.e. when df/d conjugate(z) = 0.

So in a CAS, we can simply define the derivative f'(z) as \partial f /
\partial x for any function, even if it doesn't have a complex
derivative.
For any function we can show that:

\partial f / \partial x = d f / d z + d f / d conjugate(z)

Bill, is this what you call the "total Wirtinger derivative"?

For example, for |z| we get:

|z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d
conjugate(z) = conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|

Using our definition, this holds for any complex "z". Then, if "z" is
real, we get:

|z|' = z / |z|

Which is exactly the usual real derivative. Bill, is this what you had
in mind? That a CAS could return the derivative of abs(z)
as Re(z) / abs(z) ?

Ondrej

Bill Page

unread,
Nov 13, 2014, 8:12:55 PM11/13/14
to sage-devel
On 13 November 2014 19:24, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Thu, Nov 13, 2014 at 2:00 PM, Ondřej Čertík <ondrej...@gmail.com> wrote:
>>
>> As you said, the function is analytic if it doesn't functionally
>> depend on conjugate(z), as can be shown easily. So |z| or
>> Re z are not analytic, while z^2 is. If the function is analytic,
>> then df/d conjugate(z) = 0, and df/dz is the complex derivative.
>> Right?
>

Yes. In my email I notice that I wrote "holonomic" but what I meant
was "holomorphic". Complex-analytic functions are holomorphic and
vice-versa.

> ...
> So in a CAS, we can simply define the derivative f'(z) as
> \partial f / \partial x for any function, even if it doesn't have a
> complex derivative.

Yes.

> For any function we can show that:
>
> \partial f / \partial x = d f / d z + d f / d conjugate(z)
>
> Bill, is this what you call the "total Wirtinger derivative"?
>

Yes

> For example, for |z| we get:
>
> |z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d
> conjugate(z) = conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|
>
> Using our definition, this holds for any complex "z". Then, if "z" is
> real, we get:
>
> |z|' = z / |z|
>
> Which is exactly the usual real derivative. Bill, is this what you had
> in mind? That a CAS could return the derivative of abs(z)
> as Re(z) / abs(z) ?
>
> Ondrej
>
>>
>> So for analytic functions, Wirtinger derivative gives the same answer
>> as Mathematica. For non-analytic functions, Mathematica leaves it
>> unevaluated, but Wirtinger derivative gives you something.
>>
>> How do you calculate the total Wirtinger derivative? How is that defined?
>>
>> Because I would like to get
>>
>> d|x| / d x = x / |x|
>>
>> for real x. And I don't see currently how is this formula connected to
>> Wirtinger derivatives. Finally, the derivative operator in a CAS could
>> return Wirtinger derivatives, I think it's a great idea, if somehow we
>> can recover the usual formula for abs(x) with real "x".
>>
>> What are the cons of this approach?
>>
>> Ondrej
>

Bill Page

unread,
Nov 13, 2014, 8:56:24 PM11/13/14
to sage-devel
Sorry, I hit send before I was quite ready. To continue ...

On 13 November 2014 19:24, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Thu, Nov 13, 2014 at 2:00 PM, Ondřej Čertík <ondrej...@gmail.com> wrote:
> ...
> For example, for |z| we get:
>
> |z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d
> conjugate(z) = conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|
>
> Using our definition, this holds for any complex "z". Then, if "z"
> is real, we get:
>
> |z|' = z / |z|
>
> Which is exactly the usual real derivative. Bill, is this what you
> had in mind? That a CAS could return the derivative of abs(z)
> as Re(z) / abs(z) ?
>

Yes, exactly. I think a question might arise whether we should treat
conjugate or Re as elementary.

>> ...
>> What are the cons of this approach?
>>

First, care needs to be taken to properly extend the chain rule to
include the conjugate Wirtinger derivative where necessary.

Second, in principle problems can arise when defining a test for
constant functions. For example this is necessary as part of
rewriting expressions in terms of the smallest number of elementary
functions (normalize) as a kind of zero test for expressions in
FriCAS/Axiom. Usually we assume that

df(x)/dx = 0

is necessary and sufficient for f to be a constant function. But
requiring that the total derivative

d f / d z + d f / d conjugate(z) = 0

is not what we mean by constant. In fact it seems to be an open
question whether Richardson's theorem can be extended to include
conjugate as an elementary function in such a way that the zero test
is still computable. This is the last point of discussion on the
FriCAS email list.

Bill.

Ondřej Čertík

unread,
Nov 14, 2014, 2:14:25 AM11/14/14
to sage-...@googlegroups.com
On Thu, Nov 13, 2014 at 6:56 PM, Bill Page <bill...@newsynthesis.org> wrote:
> Sorry, I hit send before I was quite ready. To continue ...
>
> On 13 November 2014 19:24, Ondřej Čertík <ondrej...@gmail.com> wrote:
>> On Thu, Nov 13, 2014 at 2:00 PM, Ondřej Čertík <ondrej...@gmail.com> wrote:
>> ...
>> For example, for |z| we get:
>>
>> |z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d
>> conjugate(z) = conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|
>>
>> Using our definition, this holds for any complex "z". Then, if "z"
>> is real, we get:
>>
>> |z|' = z / |z|
>>
>> Which is exactly the usual real derivative. Bill, is this what you
>> had in mind? That a CAS could return the derivative of abs(z)
>> as Re(z) / abs(z) ?
>>
>
> Yes, exactly. I think a question might arise whether we should treat
> conjugate or Re as elementary.

Ok, thanks for the confirmation.

There is an issue though --- since |z| is not analytic, the
derivatives depend on the direction. So along "x" you get

>
>>> ...
>>> What are the cons of this approach?
>>>
>
> First, care needs to be taken to properly extend the chain rule to
> include the conjugate Wirtinger derivative where necessary.
>
> Second, in principle problems can arise when defining a test for
> constant functions. For example this is necessary as part of
> rewriting expressions in terms of the smallest number of elementary
> functions (normalize) as a kind of zero test for expressions in
> FriCAS/Axiom. Usually we assume that
>
> df(x)/dx = 0
>
> is necessary and sufficient for f to be a constant function. But
> requiring that the total derivative
>
> d f / d z + d f / d conjugate(z) = 0
>
> is not what we mean by constant. In fact it seems to be an open
> question whether Richardson's theorem can be extended to include
> conjugate as an elementary function in such a way that the zero test
> is still computable. This is the last point of discussion on the
> FriCAS email list.
>
> Bill.
>

Ondřej Čertík

unread,
Nov 14, 2014, 2:19:56 AM11/14/14
to sage-...@googlegroups.com
On Fri, Nov 14, 2014 at 12:14 AM, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Thu, Nov 13, 2014 at 6:56 PM, Bill Page <bill...@newsynthesis.org> wrote:
>> Sorry, I hit send before I was quite ready. To continue ...
>>
>> On 13 November 2014 19:24, Ondřej Čertík <ondrej...@gmail.com> wrote:
>>> On Thu, Nov 13, 2014 at 2:00 PM, Ondřej Čertík <ondrej...@gmail.com> wrote:
>>> ...
>>> For example, for |z| we get:
>>>
>>> |z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d
>>> conjugate(z) = conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|
>>>
>>> Using our definition, this holds for any complex "z". Then, if "z"
>>> is real, we get:
>>>
>>> |z|' = z / |z|
>>>
>>> Which is exactly the usual real derivative. Bill, is this what you
>>> had in mind? That a CAS could return the derivative of abs(z)
>>> as Re(z) / abs(z) ?
>>>
>>
>> Yes, exactly. I think a question might arise whether we should treat
>> conjugate or Re as elementary.
>
> Ok, thanks for the confirmation.
>
> There is an issue though --- since |z| is not analytic, the
> derivatives depend on the direction. So along "x" you get

Sorry, a bug in gmail sent the message....

along "x" you get:

|z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d
conjugate(z) = conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|

but along "y" you get:

|z|' = \partial |z| / \partial i*y = d |z| / d z - d |z| / d
conjugate(z) = conjugate(z) / (2*|z|) - z / (2*|z|) = i*Im(z) / |z|

So I get something completely different. So which direction should be preferred
in the CAS convention and why?

Ondrej

Bill Page

unread,
Nov 14, 2014, 9:43:18 AM11/14/14
to sage-devel
On 13 November 2014 14:47, maldun <dom...@gmx.net> wrote:
>
> Although this has some sense in complex analysis one should be careful
> with 'deriving' the absolute value, since it results in the weak derivative
> ( http://en.wikipedia.org/wiki/Weak_derivative) , which is in a broader sense
> the derivative in the distribution sense.

Yes, I first became interesting in Wirtinger derivatives in the
context of distributions.

> Thus we have infinite possible derivatives
>

Maybe it is better to the derivative of abs to be a partial function,
i.e. just not defined everywhere.

> With this expression it is indirectly forbidden to assign a specific value to
> the unspecified value at zero:
>

Yes.

Bill Page

unread,
Nov 14, 2014, 10:57:58 AM11/14/14
to sage-devel
On 14 November 2014 02:19, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Fri, Nov 14, 2014 at 12:14 AM, Ondřej Čertík <ondrej...@gmail.com> wrote:
>> ...
>> Ok, thanks for the confirmation.
>>
>> There is an issue though --- since |z| is not analytic, the
>> derivatives depend on the direction. So along "x" you get
>
> |z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d conjugate(z) =
> conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|
>
> but along "y" you get:
>
> |z|' = \partial |z| / \partial i*y = d |z| / d z - d |z| / d conjugate(z) =
> conjugate(z) / (2*|z|) - z / (2*|z|) = i*Im(z) / |z|
>
> So I get something completely different.

It seems to me that we should forget about x and y. All we really need is

|z|' = d |z| / d z = conjugate(z) / (2*|z|)

and the appropriate algebraic properties of conjugate.

> So which direction should be preferred in the CAS convention and why?
>

Well, um, you did write: "Because I would like to get

d|x| / d x = x / |x|

for real x".

The constant 1/2 is irrelevant.

Bill.

Ondřej Čertík

unread,
Nov 14, 2014, 1:18:36 PM11/14/14
to sage-...@googlegroups.com


On Nov 14, 2014 8:57 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
>
> On 14 November 2014 02:19, Ondřej Čertík <ondrej...@gmail.com> wrote:
> > On Fri, Nov 14, 2014 at 12:14 AM, Ondřej Čertík <ondrej...@gmail.com> wrote:
> >> ...
> >> Ok, thanks for the confirmation.
> >>
> >> There is an issue though --- since |z| is not analytic, the
> >> derivatives depend on the direction. So along "x" you get
> >
> > |z|' = \partial |z| / \partial x = d |z| / d z + d |z| / d  conjugate(z) =
> > conjugate(z) / (2*|z|) + z / (2*|z|) = Re(z) / |z|
> >
> > but along "y" you get:
> >
> > |z|' = \partial |z| / \partial i*y = d |z| / d z - d |z| / d  conjugate(z) =
> > conjugate(z) / (2*|z|) - z / (2*|z|) = i*Im(z) / |z|
> >
> > So I get something completely different.
>
> It seems to me that we should forget about x and y.  All we really need is
>
>  |z|'  = d |z| / d z = conjugate(z) / (2*|z|)
>
> and the appropriate algebraic properties of conjugate.

Sure, we can make a CAS return this. But then you get the 1/2 there.

>
> > So which direction should be preferred in the CAS convention and why?
> >
>
> Well, um, you did write: "Because I would like to get
>
>   d|x| / d x = x / |x|
>
>   for real x".
>
> The constant 1/2 is irrelevant.

Well, but how do I recover the real derivative from the complex one if they differ by a factor of 1/2?

In other words, what is the utility of such a definition then?

I can see the utility of differentiating with respect to x, as at least you must recover the real derivative results.

Ondrej

Bill Page

unread,
Nov 14, 2014, 1:30:24 PM11/14/14
to sage-devel
On 14 November 2014 13:18, Ondřej Čertík <ondrej...@gmail.com> wrote:
>
> On Nov 14, 2014 8:57 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
>>
>> It seems to me that we should forget about x and y. All we really need is
>>
>> |z|' = d |z| / d z = conjugate(z) / (2*|z|)
>>
>> and the appropriate algebraic properties of conjugate.
>
> Sure, we can make a CAS return this. But then you get the 1/2 there.
>

Yes.

>> ...
>> The constant 1/2 is irrelevant.
>
> Well, but how do I recover the real derivative from the complex one if they
> differ by a factor of 1/2?
>

What do you mean by "the real derivative"? Perhaps we can just define that as

d f / d z + d f / d conjugate(z)

> In other words, what is the utility of such a definition then?
>
> I can see the utility of differentiating with respect to x, as at least you
> must recover the real derivative results.
>

You are not differentiating with respect to x, you are differentiating
with respect to

(z+conjugate(z))/2

Bill.

Ondřej Čertík

unread,
Nov 14, 2014, 2:29:45 PM11/14/14
to sage-...@googlegroups.com


On Nov 14, 2014 11:30 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
>
> On 14 November 2014 13:18, Ondřej Čertík <ondrej...@gmail.com> wrote:
> >
> > On Nov 14, 2014 8:57 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
> >>
> >> It seems to me that we should forget about x and y.  All we really need is
> >>
> >>  |z|'  = d |z| / d z = conjugate(z) / (2*|z|)
> >>
> >> and the appropriate algebraic properties of conjugate.
> >
> > Sure, we can make a CAS return this. But then you get the 1/2 there.
> >
>
> Yes.
>
> >> ...
> >> The constant 1/2 is irrelevant.
> >
> > Well, but how do I recover the real derivative from the complex one if they
> > differ by a factor of 1/2?
> >
>
> What do you mean by "the real derivative"? 

The absolute value doesn't have a complex derivative, but it has a real derivative, over the real axis.

> Perhaps we can just define that as
>
>   d f / d z + d f / d  conjugate(z)
>
> > In other words, what is the utility of such a definition then?
> >
> > I can see the utility of differentiating with respect to x, as at least you
> > must recover the real derivative results.
> >
>
> You are not differentiating with respect to x, you are differentiating
> with respect to
>
>   (z+conjugate(z))/2

Is that how you propose to define the derivatives for non-analytic functions? I am a little confused what exactly is your proposal.

I think one either leaves the derivatives of non analytic functions unevaluated, or defines them in such a way that one recovers the real derivative as a special case, as long as there are no inconsistencies.

Bill Page

unread,
Nov 15, 2014, 11:18:55 AM11/15/14
to sage-devel
On 14 November 2014 14:29, Ondřej Čertík <ondrej...@gmail.com> wrote:
>
> On Nov 14, 2014 11:30 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
>>
>> What do you mean by "the real derivative"?
>
> The absolute value doesn't have a complex derivative, but it has a real
> derivative, over the real axis.
>

It seems to me that the concept of "real axis" is rather foreign to
the algebra. Assuming conjugate is implemented properly, i.e.
"algebraically", what you are actually saying is just that

z = conjugate(z)

> ...
>> You are not differentiating with respect to x, you are differentiating
>> with respect to
>>
>> (z+conjugate(z))/2
>
> Is that how you propose to define the derivatives for non-analytic
> functions? I am a little confused what exactly is your proposal.
>

I am sorry for the confusion. What I am proposing is that the
Wirtinger derivative(s) be considered the fundamental case (valid for
complex or even quaternion variables). As you noted previously this is
fine and doesn't change anything for the case of analytic functions.
If someone wants the derivative of a non-analytic function over a
given domain that should be called something else.

> I think one either leaves the derivatives of non analytic functions
> unevaluated,

No, this is just giving up. We should be able to do much better than that.

> or defines them in such a way that one recovers the real derivative
> as a special case, as long as there are no inconsistencies.
>

Yes exactly, the concept of "real derivative" is a special case.

Bill.

kcrisman

unread,
Nov 17, 2014, 10:14:40 AM11/17/14
to sage-...@googlegroups.com
For reference (since Sage uses Ginac for most derivatives) see http://www.cebix.net/pipermail/ginac-devel/2014-April/002105.html

Bill Page

unread,
Nov 17, 2014, 11:17:41 AM11/17/14
to sage-devel
Vladimir V. Kisil kisilv's patch

http://www.ginac.de/pipermail/ginac-devel/2013-November/002053

looks like a good start to me especially if one doesn't want to
consider the issue of derivatives of non-analytic functions in
general.

On 17 November 2014 10:14, kcrisman <kcri...@gmail.com> wrote:
> For reference (since Sage uses Ginac for most derivatives) see
> http://www.cebix.net/pipermail/ginac-devel/2014-April/002105.html
>

kcrisman

unread,
Nov 17, 2014, 11:24:01 AM11/17/14
to sage-...@googlegroups.com

Vladimir V. Kisil kisilv's patch

http://www.ginac.de/pipermail/ginac-devel/2013-November/002053

looks like a good start to me especially if one doesn't want to
consider the issue of derivatives of non-analytic functions in
general.


Ondřej Čertík

unread,
Nov 17, 2014, 3:17:15 PM11/17/14
to sage-...@googlegroups.com
Hi Bill,

On Sat, Nov 15, 2014 at 9:18 AM, Bill Page <bill...@newsynthesis.org> wrote:
> On 14 November 2014 14:29, Ondřej Čertík <ondrej...@gmail.com> wrote:
>>
>> On Nov 14, 2014 11:30 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
>>>
>>> What do you mean by "the real derivative"?
>>
>> The absolute value doesn't have a complex derivative, but it has a real
>> derivative, over the real axis.
>>
>
> It seems to me that the concept of "real axis" is rather foreign to
> the algebra. Assuming conjugate is implemented properly, i.e.
> "algebraically", what you are actually saying is just that
>
> z = conjugate(z)

That's fine.

>
>> ...
>>> You are not differentiating with respect to x, you are differentiating
>>> with respect to
>>>
>>> (z+conjugate(z))/2
>>
>> Is that how you propose to define the derivatives for non-analytic
>> functions? I am a little confused what exactly is your proposal.
>>
>
> I am sorry for the confusion. What I am proposing is that the
> Wirtinger derivative(s) be considered the fundamental case (valid for
> complex or even quaternion variables). As you noted previously this is
> fine and doesn't change anything for the case of analytic functions.
> If someone wants the derivative of a non-analytic function over a
> given domain that should be called something else.

I still don't understand exactly your proposal. We've played with a
few ideas above, in particular we have considered at least (below d/dz
is the Wirtinger derivative, d/dx and d/d(iy) are partial derivatives
with respect to "x" or "iy" in z=x+i*y) :

1) d/dz
2) d/dz + d/d conjugate(z) = d/dx
3) d/dz - d/d conjugate(z) = d/d(iy)
4) 2 * (d/dz + d/d conjugate(z))
5) 2 * d/dz

Which of these do you propose to use? For analytic functions, only 1)
and 2) reduce to the usual complex derivative. 4) and 5) will be off
by a factor of 2. For example, for a function z^2 we get:

1) 2*z
2) 2*z
3) 2*z
4) 4*z
5) 4*z

Since z^2 is analytic, the correct derivative is 2*z, so 1), 2) and 3)
give the right answer.

For abs(z), we get:

1) conjugate(z) / (2*|z|)
2) Re(z) / |z|
3) -i*Im(z) / |z|
4) 2*Re(z) / |z|
5) conjugate(z) / |z|

When "z" is real, then the (real) derivative of |z|' = z/|z|. We want
our complex formula to be equal to z/|z| if "z" is real. Of the above,
only 2) and 5) is equal to z/|z| when "z" is real. Note that I made a
sign mistake in my previous email regarding 3).


Comparing these two cases, options 1) and 3) are eliminated because
the results for abs(z) do not reduce to the correct real derivative.
Option 4) is eliminated, because it gives wrong results for analytic
functions, as well as it doesn't reduce to the correct real derivative
for abs(z). Option 5) is eliminated because it gives wrong results for
analytic functions.

As such, only option 2) is consistent. For all analytic functions, it
gives the correct complex derivative, and for non-analytic functions,
at least for abs(z) it reduces to the correct real derivative in the
special case when "z" is real, i.e. z = conjugate(z). Note that you
cannot apply z = conjugate(z) to the definition of 2) to obtain the
(incorrect) result 2*d/dz, you need to treat z and conjugate(z)
separately when differentiating and only at the end apply z =
conjugate(z).

It seems to work for other non-analytic functions, for example for
Re(z) = (z+conjugate(z))/2, we get:

2) 1

for Im(z) = (-z+conjugate(z))*i/2 we get:

2) 0

So that all works as expected. To prove that this works for all
non-analytic functions, we just use the fact that d/dz + d/d
conjugate(z) = d/dx, as we talked about above. So we are just
calculating the partial derivative with respect to "x" (we use the
notation z = x+i*y). When "z" is real, i.e. z = conjugate(z), it
follows that y = 0, and d/dx is just the usual (real) derivative. When
y is non-zero, we still calculate d/dx but using z and conjugate(z).
As shown above, for analytic functions this d/dx derivative is equal
to the complex derivative. For non-analytic functions the complex
derivative doesn't exist, so it's just our definition, but it reduces
to the usual real derivative (which is always equal to d/dx). In one
of my previous emails, I raised a question for non-analytic functions,
why we can't define it in some other way, perhaps d/d(iy), i.e. the
option 3) above. I think the answer is that we can, but it will not
reduce to the real derivative d/dx if "z" is real, simply because
d/d(iy) is not equal to d/dx, unless the function is analytic, i.e. 3)
works for analytic functions, but fails for abs(z), as shown above (as
well as in my previous email --- note again that I made a sign mistake
there).

>
>> I think one either leaves the derivatives of non analytic functions
>> unevaluated,
>
> No, this is just giving up. We should be able to do much better than that.
>
>> or defines them in such a way that one recovers the real derivative
>> as a special case, as long as there are no inconsistencies.
>>
>
> Yes exactly, the concept of "real derivative" is a special case.

Hopefully the above clarifies, that from everything that we have
considered so far, only the option 2) can work. It turns out that
that's also precisely what also ginac considered for abs(z)'. So the
conclusion seems clear --- simply use 2) for any function, be it
analytic or not.



However, Bill, from your emails, you seem to be giving conflicting
statements. It seems you agree that 2) is the way to go in some
emails, but then in some other emails you write:

> It seems to me that we should forget about x and y. All we really need is
>
> |z|' = d |z| / d z = conjugate(z) / (2*|z|)

Which is the case 1) above, and it is shown that it doesn't work.

Right in the next paragraph you wrote:

>
> The constant 1/2 is irrelevant.

What do you mean that the constant 1/2 is irrelevant? I think it is
very relevant, as it makes the answer incorrect.

Or:

> You are not differentiating with respect to x, you are differentiating
> with respect to
>
> (z+conjugate(z))/2

But differentiating with respect to (z+conjugate(z))/2 is just the
case 5) above, and it is shown that it doesn't work. While
differentiating with respect to "x" is the case 2) above and it is
shown that it works.

Finally, in your last email you wrote:

> I am sorry for the confusion. What I am proposing is that the
> Wirtinger derivative(s) be considered the fundamental case (valid for
> complex or even quaternion variables). As you noted previously this is
> fine and doesn't change anything for the case of analytic functions.
> If someone wants the derivative of a non-analytic function over a
> given domain that should be called something else.

I am completely confused with this paragraph. Let's try to clarify this.

When you say "I am proposing that the Wirtinger derivative(s) be
considered the fundamental case", which of the five cases above are
you proposing? Strictly speaking, Wirtinger derivative is the case 1),
but that doesn't work. Are you proposing the case 2) instead?

> As you noted previously this is
> fine and doesn't change anything for the case of analytic functions.

Correct, cases 1), 2) and 3) don't change anything for analytic functions.

> If someone wants the derivative of a non-analytic function over a
> given domain that should be called something else.

Are you proposing to only consider analytic functions? I thought the
whole conversation in this thread was about how to extend this to
non-analytic functions... If you only consider analytic functions,
then we don't need Wirtinger derivatives at all, since we can just use
the usual complex derivative, as is already the case in most CAS
systems. Of course, then we need to leave abs(z) unevaluated, as it is
not analytic.

I thought the goal was rather to extend the definition of "derivative"
to also apply for non-analytic functions, in the whole complex domain
in such a way, so that it reduces to a complex derivative for analytic
functions, and a real derivative if we restrict "z" to be real. It
seems that 2) above is one such definition that would allow that.

Bill, would you mind clarifying the above misunderstandings? I think
we are on the same page, probably we both just understood something
else with the terminology we used, but I want to make 100% sure.

Ondrej

Ondřej Čertík

unread,
Nov 17, 2014, 3:21:52 PM11/17/14
to sage-...@googlegroups.com
> I still don't understand exactly your proposal. We've played with a
> few ideas above, in particular we have considered at least (below d/dz
> is the Wirtinger derivative, d/dx and d/d(iy) are partial derivatives
> with respect to "x" or "iy" in z=x+i*y) :
>
> 1) d/dz
> 2) d/dz + d/d conjugate(z) = d/dx
> 3) d/dz - d/d conjugate(z) = d/d(iy)
> 4) 2 * (d/dz + d/d conjugate(z))
> 5) 2 * d/dz
>
> Which of these do you propose to use? For analytic functions, only 1)
> and 2) reduce to the usual complex derivative. 4) and 5) will be off
> by a factor of 2. For example, for a function z^2 we get:


Correction: For analytic functions, only 1), 2) and 3) reduce to the
usual complex derivative.
(As is shown below on particular examples.)

Bill Page

unread,
Nov 17, 2014, 9:52:47 PM11/17/14
to sage-devel
On 17 November 2014 15:17, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Sat, Nov 15, 2014 at 9:18 AM, Bill Page <bill...@newsynthesis.org> wrote:
>>
>> I am sorry for the confusion. What I am proposing is that the
>> Wirtinger derivative(s) be considered the fundamental case (valid
>> for complex or even quaternion variables). As you noted previously
>> this is fine and doesn't change anything for the case of analytic
>> functions. If someone wants the derivative of a non-analytic
>> function over a given domain that should be called something
>> else.
>
> I still don't understand exactly your proposal. We've played with a
> few ideas above, in particular we have considered at least (below
> d/dz is the Wirtinger derivative, d/dx and d/d(iy) are partial derivatives
> with respect to "x" or "iy" in z=x+i*y) :
>
> 1) d/dz
> 2) d/dz + d/d conjugate(z) = d/dx
> 3) d/dz - d/d conjugate(z) = d/d(iy)
> 4) 2 * (d/dz + d/d conjugate(z))
> 5) 2 * d/dz
>
> Which of these do you propose to use?

Both d/dz and d/d conjugate(z), i.e. the Wirtinger derivatives.

> ...
> When "z" is real, then the (real) derivative of |z|' = z/|z|. We want
> our complex formula to be equal to z/|z| if "z" is real.

Presumably you intend to choose only one of these? But this cannot
work in the general case.

> ...
> As such, only option 2) is consistent. For all analytic functions, it
> gives the correct complex derivative, and for non-analytic functions,
> at least for abs(z) it reduces to the correct real derivative in the
> special case when "z" is real, i.e. z = conjugate(z).

Yes.

>> ...
>> Yes exactly, the concept of "real derivative" is a special case.
>
> Hopefully the above clarifies, that from everything that we have
> considered so far, only the option 2) can work. It turns out that
> that's also precisely what also ginac considered for abs(z)'. So
> the conclusion seems clear --- simply use 2) for any function, be
> it analytic or not.
>

If there is only one derivative, how will you handle the chain rule?

>
> However, Bill, from your emails, you seem to be giving conflicting
> statements. It seems you agree that 2) is the way to go in some
> emails, but then in some other emails you write:
>
>> It seems to me that we should forget about x and y. All we really
>> need is
>>
>> |z|' = d |z| / d z = conjugate(z) / (2*|z|)
>
> Which is the case 1) above, and it is shown that it doesn't work.
>

We need both Wirtinger derivatives. Option 2) is their sum.

> Right in the next paragraph you wrote:
>
>>
>> The constant 1/2 is irrelevant.
>
> What do you mean that the constant 1/2 is irrelevant? I think it is
> very relevant, as it makes the answer incorrect.
>

I said that actually only one Wirtinger derivative was required
because the other can be expressed in terms of conjugate but I did not
mean to imply that it would meet your criteria of reducing to exactly
to the real case. It just happens that both Wirtinger derivatives are
the same in the case of abs.

>
> When you say "I am proposing that the Wirtinger derivative(s) be
> considered the fundamental case", which of the five cases above
> are you proposing? Strictly speaking, Wirtinger derivative is the case
> 1), but that doesn't work. Are you proposing the case 2) instead?
>

No, I meant that other derivatives (such as the real derivative) can
be obtained from the Wirtinger derivatives but not vice versa.

> ...
>> If someone wants the derivative of a non-analytic function over a
>> given domain that should be called something else.
>
> Are you proposing to only consider analytic functions?

No.

> I thought the whole conversation in this thread was about how to
> extend this to non-analytic functions...

Yes. The main issue is non-analytic (non-holomorphic) functions.

> ...
> I thought the goal was rather to extend the definition of "derivative"
> to also apply for non-analytic functions, in the whole complex domain
> in such a way, so that it reduces to a complex derivative for analytic
> functions, and a real derivative if we restrict "z" to be real. It
> seems that 2) above is one such definition that would allow that.
>

2) alone is not sufficient. In general for non-analytic functions two
derivatives are required.

> Bill, would you mind clarifying the above misunderstandings?
> I think we are on the same page, probably we both just understood
> something else with the terminology we used, but I want to make
> 100% sure.
>

Thank you. I am happy to continue the discussion.

Bill.

Ondřej Čertík

unread,
Nov 17, 2014, 11:16:15 PM11/17/14
to sage-...@googlegroups.com
Hi Bill,

Thanks for the clarification. So your point is that 2) is not
sufficient, that we really need two Wirtinger derivatives --- it's
just that one can be expressed using the other and a conjugate, so
perhaps CAS can only return one, but a chain rule needs modification
and probably some other derivatives handling as well. I need to think
about this harder.

Here is a relation that I found today in [1] (see also the references
there), I don't know if you are aware of it:

D f / D z = df/dz + df/d conjugate(z) * e^{-2*i*theta}

Where Df/Dz is the derivative in a complex plane along the direction
theta (the angle between the direction and the x-axis) and df/dz and
df/d conjugate(z) are the two Wirtinger derivatives. This formula
holds for any function. So all the derivatives no matter which
direction lie on a circle of radius df/d conjugate(z) and center
df/dz.

For analytic functions, we have df/d conjugate(z) = 0, and so the
above formula proves that all the derivatives are independent of
direction theta and equal to df/dz.

For non-analytic functions, the above formula gives all the possible
derivatives, and besides df/dz, the derivatives also depend on df/d
conjugate(z) and theta. But that's it. So as you said, the two
Wirtinger derivatives allow us to calculate the derivative along any
direction theta we want.

From my last email, the case 1) corresponds to df/d conjugate(z)=0,
i.e. analytic functions and the result is independent of theta. Case
2) is theta = 0, pi, 2*pi, ..., i.e. taking the derivative along the
x-axis. Case 3) is theta = pi/2, 3*pi/2, 5*pi/2, ..., i.e. taking the
derivative along the y-axis.


A real derivative of a real function g(x) is simply taken along the
x-axis. You can imagine that g(x) is also (arbitrarily) defined in the
whole complex plane and you are taking the Dg/gz derivative above with
theta = 0. The result is the same. So that's why the case 2), i.e.
theta=0, always reproduces the real derivative, because real
derivative is defined as theta=0.

For CAS, one could probably just say that theta=0 in our definition,
and then everything is consistent, and we only have one derivative,
2). The other option is to return both derivatives and make the
derivative Df/Dz of non-analytic function equal to the above formula,
i.e. depending on df/dz, df/d conjugate(z) and theta.

I need to think about the chain rule. I would simply introduce the
theta dependence into all formulas, as that gives all possible
derivatives and gives the exact functional dependence of all
possibilities. And then see whether we need to keep all formulas in
terms of theta, or perhaps if we can set theta = 0 for everything.

Ondrej

[1] Pyle, H. R., & Barker, B. M. (1946). A Vector Interpretation of
the Derivative Circle. The American Mathematical Monthly, 53(2), 79.
doi:10.2307/2305454

Bill Page

unread,
Nov 18, 2014, 8:57:11 AM11/18/14
to sage-devel
On 17 November 2014 23:16, Ondřej Čertík <ondrej...@gmail.com> wrote:
> Hi Bill,
>
> Thanks for the clarification. So your point is that 2) is not
> sufficient, that we really need two Wirtinger derivatives --- it's
> just that one can be expressed using the other and a conjugate,
> so perhaps CAS can only return one, but a chain rule needs
> modification and probably some other derivatives handling as
> well. I need to think about this harder.
>

Yes, that is a good summary. My tentative conclusion was that we
could implement just one (Wirtinger) derivative, a modified chain rule
and a sufficiently strong conjugate operation. This derivative is the
same as the usual derivative in the case of analytic functions but we
would have to live with the fact that it is slightly different (factor
of 1/2) for the case of common real derivatives of non-analytic
functions such as abs. Introducing a factor of 2, such as in the case
of the definition of the sign function seems like a small price to
pay.

> Here is a relation that I found today in [1] (see also the references
> there), I don't know if you are aware of it:
>
> D f / D z = df/dz + df/d conjugate(z) * e^{-2*i*theta}
>
> Where Df/Dz is the derivative in a complex plane along the direction
> theta (the angle between the direction and the x-axis) and df/dz and
> df/d conjugate(z) are the two Wirtinger derivatives. This formula
> holds for any function. So all the derivatives no matter which
> direction lie on a circle of radius df/d conjugate(z) and center
> df/dz.
>
> [1] Pyle, H. R., & Barker, B. M. (1946). A Vector Interpretation of
> the Derivative Circle. The American Mathematical Monthly, 53(2), 79.
> doi:10.2307/2305454

http://phdtree.org/pdf/36421281-a-vector-interpretation-of-the-derivative-circle/

Thank you. I was not aware of that specific publication. I think
their geometric interpretation is useful.

>
> For CAS, one could probably just say that theta=0 in our definition,
> and then everything is consistent, and we only have one derivative,
> 2). The other option is to return both derivatives and make the
> derivative Df/Dz of non-analytic function equal to the above formula,
> i.e. depending on df/dz, df/d conjugate(z) and theta.

I think you are overly focused on trying to define a derivative that
reduces to the conventional derivative of non-analytic functions over
the reals.

>
> I need to think about the chain rule. I would simply introduce the
> theta dependence into all formulas, as that gives all possible
> derivatives and gives the exact functional dependence of all
> possibilities. And then see whether we need to keep all formulas
> in terms of theta, or perhaps if we can set theta = 0 for everything.
>

It is not clear to me how to use such as "generic" derivative in the
application of the chain rule.

Bill.

David Roe

unread,
Nov 18, 2014, 9:02:55 AM11/18/14
to sage-devel
I've just been casually following this conversation, but I think it's
important that the derivative of abs(x) be sign(x) not 2*sign(x) or
1/2*sign(x).

If you use a different function, like f.wirtinger_derivative(), then
it doesn't matter so much.
David

>
>>
>> I need to think about the chain rule. I would simply introduce the
>> theta dependence into all formulas, as that gives all possible
>> derivatives and gives the exact functional dependence of all
>> possibilities. And then see whether we need to keep all formulas
>> in terms of theta, or perhaps if we can set theta = 0 for everything.
>>
>
> It is not clear to me how to use such as "generic" derivative in the
> application of the chain rule.
>
> Bill.
>

kcrisman

unread,
Nov 18, 2014, 10:11:10 AM11/18/14
to sage-...@googlegroups.com
> I think you are overly focused on trying to define a derivative that
> reduces to the conventional derivative of non-analytic functions over
> the reals.

I've just been casually following this conversation, but I think it's
important that the derivative of abs(x) be sign(x) not 2*sign(x) or
1/2*sign(x).

If you use a different function, like f.wirtinger_derivative(), then
it doesn't matter so much.
David


+1

That notwithstanding, this conversation is really great to see and I hope we get something that works for the usual cases in the original post too!

Bill Page

unread,
Nov 18, 2014, 11:05:54 AM11/18/14
to sage-devel
On 18 November 2014 09:02, David Roe <roed...@gmail.com> wrote:
> On Tue, Nov 18, 2014 at 5:57 AM, Bill Page <bill...@newsynthesis.org> wrote:
>>
>> > I think you are overly focused on trying to define a derivative that
>> > reduces to the conventional derivative of non-analytic functions
>> > over the reals.
>>
>> I've just been casually following this conversation, but I think it's
>> important that the derivative of abs(x) be sign(x) not 2*sign(x) or
>> 1/2*sign(x).
>>

What makes it important that "the" derivative of abs(x) be sign(x)?
An important point here is that there is no one single unique
derivative of non-analytic functions like abs, but rather than all of
their derivatives can be expressed in terms of just two. I am
seriously interested in reasons for retaining the status quo.

>> If you use a different function, like f.wirtinger_derivative(), then
>> it doesn't matter so much.
>> David
>>

On 18 November 2014 10:11, kcrisman <kcri...@gmail.com> wrote:
>
> +1
>

Although I guess this would be consistent with the over all
"assimilation philosophy" adopted by Sage, I am rather strongly
against this in general. In my opinion it is in part what has lead to
the rather confusing situation in most other computer algebra systems.
I think rather that one should strive for the most general solution
consistent with the mathematics. I suppose that to some extent this
is conditioned by how the subject is taught. It came as a surprise to
me that a solution of this problem (Wirtinger calculus or CR-calculus)
was apparently "well-known" is some circles but considered only a
marginal curiosity in others (if at all).

> That notwithstanding, this conversation is really great to see and I hope
> we get something that works for the usual cases in the original post
> too!
>

Provided that one realizes its limitations I think the solution
proposed by Vladimir V. Kisil for ginac and in more generality by
Ondrej is quite good. I don't think a new name for this is desirable.

Bill.

David Roe

unread,
Nov 18, 2014, 11:28:41 AM11/18/14
to sage-devel
On Tue, Nov 18, 2014 at 8:05 AM, Bill Page <bill...@newsynthesis.org> wrote:
> On 18 November 2014 09:02, David Roe <roed...@gmail.com> wrote:
>> On Tue, Nov 18, 2014 at 5:57 AM, Bill Page <bill...@newsynthesis.org> wrote:
>>>
>>> > I think you are overly focused on trying to define a derivative that
>>> > reduces to the conventional derivative of non-analytic functions
>>> > over the reals.
>>>
>>> I've just been casually following this conversation, but I think it's
>>> important that the derivative of abs(x) be sign(x) not 2*sign(x) or
>>> 1/2*sign(x).
>>>
>
> What makes it important that "the" derivative of abs(x) be sign(x)?
> An important point here is that there is no one single unique
> derivative of non-analytic functions like abs, but rather than all of
> their derivatives can be expressed in terms of just two. I am
> seriously interested in reasons for retaining the status quo.

Because derivative is not just used in the context of functions of a
complex variable (whether they are analytic or not). Probably more
than 90% of Sage users don't know any complex analysis (as frequently
lamented by rtf). I will certainly acknowledge that people get things
wrong with regard to sqrt and log by not knowing about branch cuts.
But when it comes to the definition of derivative, we need to stay
consistent with the standard definition of lim_{h -> 0} (f(x + h) -
f(x))/h for functions of a real variable (or functions that many
people interpret as just functions of a real variable). Any other
decision will cause frustration for the vast majority of our users.
David

>
>>> If you use a different function, like f.wirtinger_derivative(), then
>>> it doesn't matter so much.
>>> David
>>>
>
> On 18 November 2014 10:11, kcrisman <kcri...@gmail.com> wrote:
>>
>> +1
>>
>
> Although I guess this would be consistent with the over all
> "assimilation philosophy" adopted by Sage, I am rather strongly
> against this in general. In my opinion it is in part what has lead to
> the rather confusing situation in most other computer algebra systems.
> I think rather that one should strive for the most general solution
> consistent with the mathematics. I suppose that to some extent this
> is conditioned by how the subject is taught. It came as a surprise to
> me that a solution of this problem (Wirtinger calculus or CR-calculus)
> was apparently "well-known" is some circles but considered only a
> marginal curiosity in others (if at all).
>
>> That notwithstanding, this conversation is really great to see and I hope
>> we get something that works for the usual cases in the original post
>> too!
>>
>
> Provided that one realizes its limitations I think the solution
> proposed by Vladimir V. Kisil for ginac and in more generality by
> Ondrej is quite good. I don't think a new name for this is desirable.
>
> Bill.
>

Ondřej Čertík

unread,
Nov 18, 2014, 12:29:26 PM11/18/14
to sage-...@googlegroups.com
Well, I think it doesn't matter if you know complex analysis or not.
The point is rather that there is a real derivative and a complex
derivative. The complex derivative being a generalization of the real
one (http://en.wikipedia.org/wiki/Derivative#Generalizations,
http://en.wikipedia.org/wiki/Generalizations_of_the_derivative#Complex_analysis).
As such, it must reduce to the real derivative as a special case when
all variables are real, otherwise you get inconsistencies.

For example for real numbers, you get:

|x|' = x / |x| = sign(x)

and you can do this numerically. Here is a function that does this for
any angle theta in the complex plane:

def diff(f, z0, theta, eps=1e-8):
h = eps*exp(I*theta)
return (f(z0+h)-f(z0)) / h

For real numbers, you need to set theta=0. This then obviously becomes
the standard definition of a real derivative. So any other definition
than |x|' = sign(x) gives wrong answers. No matter if you know complex
analysis or not.

As far as the derivative of abs(x) in the complex plane for any theta,
the above "diff" function is just the directional derivative, i.e.
derivative in the direction theta. Based on my previous email, the
(only) correct analytic answer is, using Python notation:

x.conjugate()/(2*abs(x)) + x/(2*abs(x)) * exp(-2*I*theta)

And you can check numerically using the function "diff" above that
this is indeed the correct answer (just plug in various complex or
real values for "x" and check that "diff" and the above formula gives
the same numerical answer for all theta).

Bill, you wrote "I think rather that one should strive for the most
general solution
consistent with the mathematics.". Well, the above (i.e.
x.conjugate()/(2*abs(x)) + x/(2*abs(x)) * exp(-2*I*theta)) is the most
general solution consistent with mathematics.

Of these options, only theta=0 gives the real derivative as a special
case, that's what the GiNaC proposal does.

Ondrej

Bill Page

unread,
Nov 18, 2014, 1:08:36 PM11/18/14
to sage-devel
On 18 November 2014 12:29, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Tue, Nov 18, 2014 at 9:28 AM, David Roe <roed...@gmail.com> wrote:
>> ...
>> Because derivative is not just used in the context of functions of a
>> complex variable (whether they are analytic or not). Probably more
>> than 90% of Sage users don't know any complex analysis (as frequently
>> lamented by rtf). I will certainly acknowledge that people get things
>> wrong with regard to sqrt and log by not knowing about branch cuts.
>> But when it comes to the definition of derivative, we need to stay
>> consistent with the standard definition of lim_{h -> 0} (f(x + h) -
>> f(x))/h for functions of a real variable (or functions that many
>> people interpret as just functions of a real variable). Any other
>> decision will cause frustration for the vast majority of our users.
>
> Well, I think it doesn't matter if you know complex analysis or not.

I agree, but apparently for a different reason.

> The point is rather that there is a real derivative and a complex
> derivative. The complex derivative being a generalization of the
> real one (http://en.wikipedia.org/wiki/Derivative#Generalizations,
> http://en.wikipedia.org/wiki/Generalizations_of_the_derivative#Complex_analysis).
> As such, it must reduce to the real derivative as a special case when
> all variables are real, otherwise you get inconsistencies.
>

As I said in another email, I think this is highly dependent on one's
education and experience. Although I admit that it is very common
(almost ubiquitous) to teach calculus starting from the notion of
continuity and limits, I my opinion these references on wikipedia are
especially biased. To me from the point of view of computer algebra,
the algebraic properties of derivatives are more important.

For the sake of continuing the argument, from the point of view of
algebra why should we consider derivatives of complex functions as a
generalization of the real one rather than the real derivative as a
defined in terms of something more general? In particular notice that
the so called Wirtinger derivatives also make sense in the case of
quaternion analysis, so should we be expecting to view quaternion
calculus also as a "generalization' of real derivatives?

OK, maybe I am pushing this a little too far. I admit that the
argument from the point of view of limits and without any reference to
conjugate is quite convincing.

> ...
> Bill, you wrote "I think rather that one should strive for the most
> general solution
> consistent with the mathematics.". Well, the above (i.e.
> x.conjugate()/(2*abs(x)) + x/(2*abs(x)) * exp(-2*I*theta)) is the
> most general solution consistent with mathematics.
>
> Of these options, only theta=0 gives the real derivative as a special
> case, that's what the GiNaC proposal does.
>

Have you had a chance to consider the issue of the chain-rule yet?

Bill.

Ondřej Čertík

unread,
Nov 18, 2014, 1:42:01 PM11/18/14
to sage-...@googlegroups.com
I don't know that much about quaternions. Real numbers and real
analysis is essentially a subset of complex analysis. Is that true
that complex analysis is a subset of quaternion analysis? But I think
it's true that quaternion analysis is a generalization of a real
analysis, so I would definitely expect the quaternion derivatives to
match the real ones.

>
> OK, maybe I am pushing this a little too far. I admit that the
> argument from the point of view of limits and without any reference to
> conjugate is quite convincing.
>
>> ...
>> Bill, you wrote "I think rather that one should strive for the most
>> general solution
>> consistent with the mathematics.". Well, the above (i.e.
>> x.conjugate()/(2*abs(x)) + x/(2*abs(x)) * exp(-2*I*theta)) is the
>> most general solution consistent with mathematics.
>>
>> Of these options, only theta=0 gives the real derivative as a special
>> case, that's what the GiNaC proposal does.
>>
>
> Have you had a chance to consider the issue of the chain-rule yet?

Yes. Very straightforward, as I suggested in my last email. Just start with:

D f / D z = df/dz + df/d conjugate(z) * e^{-2*i*theta}

and then consider the chain rule for Wirtinger derivatives
(http://en.wikipedia.org/wiki/Wirtinger_derivatives#Functions_of_one_complex_variable_2),
I am sure that can be proven quite easily. Then you just calculate
directly:

D f(g) / D z = df(g)/dz + df(g)/d conjugate(z) * e^{-2*i*theta} =

= (df/dg * dg/dz + df/d conjugate(g) * d conjugate(g) / dz) + (df/dg *
dg/d conjugate(z) + df/d conjugate(g) * d conjugate(g) / d
conjugate(z)) * e^{-2*i*theta} =

= df/dg * (dg/dz + dg/d conjugate(z) * e^{-2*i*theta}) + df/d
conjugate(g) * (d conjugate(g)/dz + d conjugate(g)/d conjugate(z) *
e^{-2*i*theta}) =

= df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz

So at the end, the theta dependence got absorbed into Dg/Dz and D
conjugate(g) / Dz, but it assumes that these directional derivatives
are taken with the same angle theta.

You can now use it to do the example from GiNaC:

cout << abs(log(z)).diff(z) << endl;
// (before) -> D[0](abs)(log(z))*z^(-1)
// (now) -> 1/2*(z^(-1)*conjugate(log(z))+log(z)*conjugate(z)^(-1))*abs(log(z))^(-1)

I.e. f(g) = |g|, g(z) = log(z) and you get:

D |log(z)| / D z = conjugate(g)/(2*|g|) * D log(z) / Dz + g / (2*|g|)
* D conjugate(log(z)) / Dz =

= conjugate(log(z)) / (2*|log(z)|) * 1/z + log(z) / (2*|log(z)|) *
1/conjugate(z) * e^{-2*i*theta})

= 1/(2*|log(z)|) * (conjugate(log(z)) / z + log(z) / conjugate(z) *
e^{-2*i*theta})

So it exactly agrees, except that there is a theta dependence in the
final answer and GiNaC implicitly chose theta=0. Everything in this
example should be straightforward except perhaps:

D conjugate(log(z)) / Dz = d conjugate(log(z)) / dz + d
conjugate(log(z)) / d conjugate(z) * e^{-2*i*theta}

where we first write conjugate(log(z)) = log|z| - I*arg(z) =
log|conjugate(z)| + I*arg(conjugate(z)) = log(conjugate(z)) and then
we can see that the first Wirtinger derivative is zero (no functional
dependence on "z"), and the second one is 1/conjugate(z). So the
answer is:

D conjugate(log(z)) / Dz = 1/conjugate(z) * e^{-2*i*theta}


I hope I didn't make some mistake somewhere, but it looks all
straightforward to me.

Ondrej

Bill Page

unread,
Nov 18, 2014, 2:14:40 PM11/18/14
to sage-devel
On 18 November 2014 13:41, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Tue, Nov 18, 2014 at 11:08 AM, Bill Page <bill...@newsynthesis.org> wrote:
>> ...
>> Have you had a chance to consider the issue of the chain-rule yet?
>
> Yes. Very straightforward, as I suggested in my last email. Just start with:
>
> D f / D z = df/dz + df/d conjugate(z) * e^{-2*i*theta}
>
> and then consider the chain rule for Wirtinger derivatives
> (http://en.wikipedia.org/wiki/Wirtinger_derivatives#Functions_of_one_complex_variable_2),
> I am sure that can be proven quite easily.

Let me make sure I understand your proposal. Are you saying that you
would introduce the symbolic expression

e^{-2*i*theta}

with theta undefined in the result of all derivatives? So that
diff(x) is always the sum of two terms. In particular

abs(x).diff(x)

would return the symbolic expression

conjugate(x)/(2*abs(x)) + conjugate(x)/(2*abs(x))* e^{-2*i*theta}

If you are, then clearly one can recover both Wirtinger derivatives
from this expression and the rest holds.

> Then you just calculate directly:
> ...
> So it exactly agrees, except that there is a theta dependence in the
> final answer and GiNaC implicitly chose theta=0.
>...
> I hope I didn't make some mistake somewhere, but it looks all
> straightforward to me.
>

It looks OK to me but I must say, it probably seems rather peculiar
from the point of view expressed earlier by David Roe.

How can you explain the presence of the e^theta term to someone
without experience in complex analysis or at least multi-variable
calculus?

I thought rather that what you were proposing was to set theta=0 from
the start. If you did that, then I think you still have problems with
the chain rule.

Bill.

Bill Page

unread,
Nov 18, 2014, 2:38:12 PM11/18/14
to sage-devel
On 18 November 2014 14:14, Bill Page <bill...@newsynthesis.org> wrote:
> On 18 November 2014 13:41, Ondřej Čertík <ondrej...@gmail.com> wrote:
>> On Tue, Nov 18, 2014 at 11:08 AM, Bill Page <bill...@newsynthesis.org> wrote:
>>> ...
>>> Have you had a chance to consider the issue of the chain-rule yet?
>>
>> Yes. Very straightforward, as I suggested in my last email. Just start with:
>>
>> D f / D z = df/dz + df/d conjugate(z) * e^{-2*i*theta}
>>
>> and then consider the chain rule for Wirtinger derivatives
>> (http://en.wikipedia.org/wiki/Wirtinger_derivatives#Functions_of_one_complex_variable_2),
>> I am sure that can be proven quite easily.
> ...
> I thought rather that what you were proposing was to set theta=0 from
> the start. If you did that, then I think you still have problems with
> the chain rule.
>

Let me add that the kind of solution to this problem that I did
imagine was to implement two derivatives, for example both

f.diff(z) = df/dz + df/d conjugate(z)

and

f.diff2(z) = df/dz - df/d conjugate(z)

diff(z) would equal diff2(z) for all analytic functions and diff would
reduce to the derivative of real non-analytic functions as you desire.
Note that for abs we have

abs(z).diff2(z) = 0

but not in general. There would be no need to discuss this 2nd
derivative with less experienced users until they were ready to
consider more "advanced" mathematics.

Clearly we could implement the chain rule given these two derivatives.

Bill.

Ondřej Čertík

unread,
Nov 18, 2014, 3:19:20 PM11/18/14
to sage-...@googlegroups.com
On Tue, Nov 18, 2014 at 12:14 PM, Bill Page <bill...@newsynthesis.org> wrote:
> On 18 November 2014 13:41, Ondřej Čertík <ondrej...@gmail.com> wrote:
>> On Tue, Nov 18, 2014 at 11:08 AM, Bill Page <bill...@newsynthesis.org> wrote:
>>> ...
>>> Have you had a chance to consider the issue of the chain-rule yet?
>>
>> Yes. Very straightforward, as I suggested in my last email. Just start with:
>>
>> D f / D z = df/dz + df/d conjugate(z) * e^{-2*i*theta}
>>
>> and then consider the chain rule for Wirtinger derivatives
>> (http://en.wikipedia.org/wiki/Wirtinger_derivatives#Functions_of_one_complex_variable_2),
>> I am sure that can be proven quite easily.
>
> Let me make sure I understand your proposal. Are you saying that you
> would introduce the symbolic expression
>
> e^{-2*i*theta}
>
> with theta undefined in the result of all derivatives? So that
> diff(x) is always the sum of two terms. In particular
>
> abs(x).diff(x)
>
> would return the symbolic expression
>
> conjugate(x)/(2*abs(x)) + conjugate(x)/(2*abs(x))* e^{-2*i*theta}

I think you made a mistake, the correct expression is:

conjugate(x)/(2*abs(x)) + x/(2*abs(x)) * e^{-2*i*theta}

>
> If you are, then clearly one can recover both Wirtinger derivatives
> from this expression and the rest holds.

For now I just wanted to get the math right in the most general case.
I wasn't even considering what a CAS should do.

>
>> Then you just calculate directly:
>> ...
>> So it exactly agrees, except that there is a theta dependence in the
>> final answer and GiNaC implicitly chose theta=0.
>>...
>> I hope I didn't make some mistake somewhere, but it looks all
>> straightforward to me.
>>
>
> It looks OK to me but I must say, it probably seems rather peculiar
> from the point of view expressed earlier by David Roe.
>
> How can you explain the presence of the e^theta term to someone
> without experience in complex analysis or at least multi-variable
> calculus?
>
> I thought rather that what you were proposing was to set theta=0 from
> the start. If you did that, then I think you still have problems with
> the chain rule.

For a CAS, I was leaning towards using theta=0. But given your
objections, I first needed to figure out the most general case that
covers everything. I think that's now sufficiently clarified.

> Let me add that the kind of solution to this problem that I did
> imagine was to implement two derivatives, for example both
>
> f.diff(z) = df/dz + df/d conjugate(z)
>
> and
>
> f.diff2(z) = df/dz - df/d conjugate(z)
>
> diff(z) would equal diff2(z) for all analytic functions and diff would
> reduce to the derivative of real non-analytic functions as you desire.

Right, diff() is for theta = 0. diff2() is for theta=pi/2, i.e. taking
the derivative along the imaginary axis.

> Note that for abs we have
>
> abs(z).diff2(z) = 0

Actually, for abs you have:

abs(z).diff2(z) = (conjugate(z)-z)/(2*abs(z))

> but not in general. There would be no need to discuss this 2nd
> derivative with less experienced users until they were ready to
> consider more "advanced" mathematics.
>
> Clearly we could implement the chain rule given these two derivatives.

So I think that functions can return their own correct derivative, for
example analytic functions just return the unique complex derivative,
for example:

log(z).diff(z) = 1/z

This holds for all cases. Non-analytic functions like abs(f) can return:

abs(f).diff(z) = (conjugate(f)*f.diff(z) +
f*conjugate(f).diff(z)*e^{-2*i*theta}) / (2*abs(f))

I think that's the correct application of the chain rule. We can set
theta=0, so we would just return:

abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))

Which for real "f" (i.e. conjugate(f)=f) simplifies to (as a special case):

abs(f).diff(z) = (f*f.diff(z) + f*f.diff(z)) / (2*abs(f)) = f/abs(f) *
f.diff(z) = sign(f) * f.diff(z)

So it all works.

Unless there is some issue that I don't see, it seems to me we just
need to have one diff(z) function, no need for diff2().

Ondrej

Ondřej Čertík

unread,
Nov 18, 2014, 3:46:37 PM11/18/14
to sage-...@googlegroups.com
Actually, I think I made a mistake. Let's do abs(f).diff(x) again for
the most general case. We use:

D f(g) / D z =

= df/dg * (dg/dz + dg/d conjugate(z) * e^{-2*i*theta}) + df/d
conjugate(g) * (d conjugate(g)/dz + d conjugate(g)/d conjugate(z) *
e^{-2*i*theta}) =

= df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz

Which we derived above. We have f(g) -> |g| and g(z) -> f(z). So we get:

D |f| / Dz = d|f|/df * Df/Dz + d|f|/d conjugate(f) * D conjugate(f) / Dz =

= (conjugate(f) * Df/Dz + f * D conjugate(f) / Dz) / (2*abs(f))

And then:

Df/Dz = f.diff(z)
D conjugate(f) / Dz = conjugate(f).diff(z)

So I think the formula:

abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))

is the most general formula for any theta. The theta dependence is hidden
in conjugate(f).diff(z), since if "f" is analytic, like f=log(z), the
conjugate(f) is
not analytic, and so the derivative is theta dependent.

The below holds though:

Bill Page

unread,
Nov 18, 2014, 4:50:20 PM11/18/14
to sage-devel
On 18 November 2014 15:19, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Tue, Nov 18, 2014 at 12:14 PM, Bill Page <bill...@newsynthesis.org> wrote:
>>
>> abs(x).diff(x)
>>
>> would return the symbolic expression
>>
>> conjugate(x)/(2*abs(x)) + conjugate(x)/(2*abs(x))* e^{-2*i*theta}
>
> I think you made a mistake, the correct expression is:
>
> conjugate(x)/(2*abs(x)) + x/(2*abs(x)) * e^{-2*i*theta}
>

Yes, sorry.

>> ...
>> I thought rather that what you were proposing was to set theta=0
>> from the start. If you did that, then I think you still have problems
>> with the chain rule.
>
> For a CAS, I was leaning towards using theta=0. But given your
> objections, I first needed to figure out the most general case that
> covers everything. I think that's now sufficiently clarified.
>

OK.

>> Let me add that the kind of solution to this problem that I did
>> imagine was to implement two derivatives, for example both
>>
>> f.diff(z) = df/dz + df/d conjugate(z)
>>
>> and
>>
>> f.diff2(z) = df/dz - df/d conjugate(z)
>>
>> diff(z) would equal diff2(z) for all analytic functions and diff would
>> reduce to the derivative of real non-analytic functions as you desire.
>
> Right, diff() is for theta = 0. diff2() is for theta=pi/2, i.e. taking
> the derivative along the imaginary axis.
>
>> Note that for abs we have
>>
>> abs(z).diff2(z) = 0
>
> Actually, for abs you have:
>
> abs(z).diff2(z) = (conjugate(z)-z)/(2*abs(z))
>

Yes again, sorry. Of course 0 only if conjugate(z)=z.

>> but not in general. There would be no need to discuss this 2nd
>> derivative with less experienced users until they were ready to
>> consider more "advanced" mathematics.
>>
>> Clearly we could implement the chain rule given these two derivatives.
> ...

On 18 November 2014 15:46, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Tue, Nov 18, 2014 at 1:19 PM, Ondřej Čertík <ondrej...@gmail.com> wrote:
> ..
>>
>> So I think that functions can return their own correct derivative, for
>> example analytic functions just return the unique complex derivative,
>> for example:
>>
>> log(z).diff(z) = 1/z
>>
>> This holds for all cases. Non-analytic functions like abs(f) can return:
>>
>> abs(f).diff(z) = (conjugate(f)*f.diff(z) +
>> f*conjugate(f).diff(z)*e^{-2*i*theta}) / (2*abs(f))
>
> Actually, I think I made a mistake. Let's do abs(f).diff(x) again for
> the most general case. We use:
>
> D f(g) / D z =
>
> = df/dg * (dg/dz + dg/d conjugate(z) * e^{-2*i*theta}) + df/d
> conjugate(g) * (d conjugate(g)/dz + d conjugate(g)/d conjugate(z) *
> e^{-2*i*theta}) =
>
> = df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz
>
> Which we derived above. We have f(g) -> |g| and g(z) -> f(z). So we get:
>
> D |f| / Dz = d|f|/df * Df/Dz + d|f|/d conjugate(f) * D conjugate(f) / Dz =
>
> = (conjugate(f) * Df/Dz + f * D conjugate(f) / Dz) / (2*abs(f))
>
> And then:
>
> Df/Dz = f.diff(z)
> D conjugate(f) / Dz = conjugate(f).diff(z)
>
> So I think the formula:
>
> abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))
>
> is the most general formula for any theta. The theta dependence is hidden
> in conjugate(f).diff(z), since if "f" is analytic, like f=log(z), the
> conjugate(f) is not analytic, and so the derivative is theta dependent.
>
> The below holds though:
>
>>
>> I think that's the correct application of the chain rule. We can set
>> theta=0, so we would just return:
>>
>> abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))
>>
>> Which for real "f" (i.e. conjugate(f)=f) simplifies to (as a special case):
>>
>> abs(f).diff(z) = (f*f.diff(z) + f*f.diff(z)) / (2*abs(f)) = f/abs(f) *
>> f.diff(z) = sign(f) * f.diff(z)
>>
>> So it all works.
>>
>> Unless there is some issue that I don't see, it seems to me we just
>> need to have one diff(z) function, no need for diff2().
>>

Hmmm... So given only f(z).diff(z) as you have defined it above, how
do I get Df(z)/D conjugate(z), i.e. the other Wirtinger derivative?
Or are you claiming that this is not necessary in general in spite of
the Wirtinger formula for the chain rule?

Bill.

Ondřej Čertík

unread,
Nov 18, 2014, 5:40:04 PM11/18/14
to sage-...@googlegroups.com
In my notation, the Wirtinger derivative is d f(z) / d z and d f(z) /
d conjugate(z). The Df(z) / Dz is the complex derivative taking in
direction theta (where it could be theta=0). Given the chain rule, as
I derived above using chain rules for the Wirtinger derivative:

D f(g) / D z = df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz

I don't see why you would need the isolated Wirtinger derivatives. The
method that implements the derivative of the given function, like
log(z) or abs(z) would simply return the correct formula, as I said
above, e.g.

log(z).diff(z) = 1/z

abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))

Both formulas hold for any theta. I guess it depends on how the CAS is
implemented, maybe some CASes have a general machinery for
derivatives. But I am pretty sure you can simply implemented it as I
outlined.

Let me know if you found any issue with this.

Ondrej

w huang

unread,
Nov 18, 2014, 8:43:05 PM11/18/14
to sage-...@googlegroups.com
Hi,

With Sage 6.3, I am getting:

sage: abs(x).diff(x)
x/abs(x)
sage: abs(I*x).diff(x)
-x/abs(I*x)

But abs(I*x) == abs(x). So also abs(x).diff(x) and abs(I*x).diff(x)
must be the same. But in the first case we get x/abs(x), and in the
second we got -x/abs(x).

In SymPy, the answer is:
------///

www.mathHandbook.com show
http://www.mathHandbook.com/input/?guess=d(abs(x))

Bill Page

unread,
Nov 18, 2014, 8:51:18 PM11/18/14
to sage-devel
On 18 November 2014 17:40, Ondřej Čertík <ondrej...@gmail.com> wrote:
>
> In my notation, the Wirtinger derivative is d f(z) / d z and d f(z) /
> d conjugate(z). The Df(z) / Dz is the complex derivative taking in
> direction theta (where it could be theta=0). Given the chain rule, as
> I derived above using chain rules for the Wirtinger derivative:
>
> D f(g) / D z = df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz
>
> I don't see why you would need the isolated Wirtinger derivatives.

You mean that only the function being differentiated needs to the
Writinger derivatives (as part of the "formula" that it implements for
the chain rule)?

> The
> method that implements the derivative of the given function, like
> log(z) or abs(z) would simply return the correct formula, as I said
> above, e.g.
>
> log(z).diff(z) = 1/z
>
> abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))
>

If the chain rule must be implemented by each function then I suppose
that you also have

log(f).diff(z) = f.diff(z) / z

right?

> Both formulas hold for any theta.

The generality provided by theta seems not be be of much interest.

> I guess it depends on how the CAS is implemented, maybe
> some CASes have a general machinery for derivatives. But
> I am pretty sure you can simply implemented it as I outlined.
>
> Let me know if you found any issue with this.
>

Is this how derivatives are implemented in sympy?

Bill.

Ondřej Čertík

unread,
Nov 18, 2014, 9:22:10 PM11/18/14
to sage-...@googlegroups.com
On Tue, Nov 18, 2014 at 6:51 PM, Bill Page <bill...@newsynthesis.org> wrote:
> On 18 November 2014 17:40, Ondřej Čertík <ondrej...@gmail.com> wrote:
>>
>> In my notation, the Wirtinger derivative is d f(z) / d z and d f(z) /
>> d conjugate(z). The Df(z) / Dz is the complex derivative taking in
>> direction theta (where it could be theta=0). Given the chain rule, as
>> I derived above using chain rules for the Wirtinger derivative:
>>
>> D f(g) / D z = df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz
>>
>> I don't see why you would need the isolated Wirtinger derivatives.
>
> You mean that only the function being differentiated needs to the
> Writinger derivatives (as part of the "formula" that it implements for
> the chain rule)?

Did you mean to write "You mean that only the function being
differentiated needs to do the
Writinger derivatives..."?

Yes.

>
>> The
>> method that implements the derivative of the given function, like
>> log(z) or abs(z) would simply return the correct formula, as I said
>> above, e.g.
>>
>> log(z).diff(z) = 1/z
>>
>> abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))
>>
>
> If the chain rule must be implemented by each function then I suppose
> that you also have
>
> log(f).diff(z) = f.diff(z) / z
>
> right?

Yes, correct.

>
>> Both formulas hold for any theta.
>
> The generality provided by theta seems not be be of much interest.

I agree, I think one can implicitly set theta=0 in a CAS. So that's
the option 2) in my email above.

>
>> I guess it depends on how the CAS is implemented, maybe
>> some CASes have a general machinery for derivatives. But
>> I am pretty sure you can simply implemented it as I outlined.
>>
>> Let me know if you found any issue with this.
>>
>
> Is this how derivatives are implemented in sympy?

It's a little more complicated in sympy, but that's how derivatives
are implemented in csympy (https://github.com/sympy/csympy).

Ondrej

Bill Page

unread,
Nov 19, 2014, 9:36:29 AM11/19/14
to sage-devel


On 18 November 2014 21:22, Ondřej Čertík <ondrej...@gmail.com> wrote:
> On Tue, Nov 18, 2014 at 6:51 PM, Bill Page <bill...@newsynthesis.org> wrote:
>> On 18 November 2014 17:40, Ondřej Čertík <ondrej...@gmail.com> wrote:
>>>
>>> In my notation, the Wirtinger derivative is d f(z) / d z and d f(z) /
>>> d conjugate(z). The Df(z) / Dz is the complex derivative taking in
>>> direction theta (where it could be theta=0). Given the chain rule, as
>>> I derived above using chain rules for the Wirtinger derivative:
>>>
>>> D f(g) / D z = df/dg Dg/Dz + df/d conjugate(g) D conjugate(g) / Dz
>>>
>>>
>>> abs(f).diff(z) = (conjugate(f)*f.diff(z) + f*conjugate(f).diff(z)) / (2*abs(f))
>>>
>>
>>>
>>> Let me know if you found any issue with this.
>>>

I implemented this in FriCAS and tried a few examples, e.g.

(4) -> D(abs(f(z,conjugate(z))),z)

            _     _        _         _
        f(z,z)f  (z,z) + f(z,z)f  (z,z)
               ,2               ,1
   (4)  -------------------------------
                           _
                  2abs(f(z,z))
                                                    Type: Expression(Integer)



where the ,1 and ,2 notation represents the derivative with respect the the first and second variable of f, respectively.

Then I noticed that if we have f=z we get

  conjugate(z).diff(z)

which is 0.  So the 2nd term is 0 and the result is just the first Wirtinger derivative.

Perhaps I am misinterpreting something?

Bill.

Bill Page

unread,
Nov 19, 2014, 10:19:07 AM11/19/14
to sage-devel


On 2014-11-19 9:36 AM, "Bill Page" <bill...@newsynthesis.org> wrote:
> ...

> Then I noticed that if we have f=z we get
>
>   conjugate(z).diff(z)
>
> which is 0.  So the 2nd term is 0 and the result is just the first Wirtinger derivative.
>
> Perhaps I am misinterpreting something?
>

Oops, my fault.  According to your definition

  conjugate(z).diff(z) = 1

Bill.

Bill Page

unread,
Nov 19, 2014, 11:32:22 AM11/19/14
to sage-devel
OK, this looks better!

(1) -> D(abs(x),x)

         _
         x + x
   (1)  -------
        2abs(x)
                                                    Type: Expression(Integer)
(2) -> D(conjugate(x),y)

   (2)  0
                                                    Type: Expression(Integer)
(3) -> D(conjugate(x),x)

   (3)  1
                                                    Type: Expression(Integer)
(4) -> f:=operator 'f

   (4)  f
                                                          Type: BasicOperator
(5) -> D(abs(f(x)),x)

             , _      _  ,
        f(x)f (x) + f(x)f (x)

   (5)  ---------------------
              2abs(f(x))
                                                    Type: Expression(Integer)
(6) -> D(abs(log(x)),x)

        _    _
        xlog(x) + x log(x)
   (6)  ------------------
            _
          2xxabs(log(x))
                                                    Type: Expression(Integer)

Ondřej Čertík

unread,
Nov 19, 2014, 11:39:57 AM11/19/14
to sage-...@googlegroups.com
Right, because this "diff" is the total derivative in the direction
theta, so the first Wirtinger derivative is 0, the second one is 1 and
you get:

0 + 1*e^{-2*i*theta})

and if you implicitly set theta=0, then you get 1.

Ondrej

Ondřej Čertík

unread,
Nov 19, 2014, 11:42:27 AM11/19/14
to sage-...@googlegroups.com
That looks good, right? What about arg(z). What are the Wirtinger
derivatives of arg(z)? Do you have other examples of non-analytic
functions?

Would you mind posting your patch to FriCAS somewhere? I would be
interested in how you implemented it.

Ondrej

Ondřej Čertík

unread,
Nov 19, 2014, 11:51:21 AM11/19/14
to sage-...@googlegroups.com
I'll try to compile FriCAS myself and apply your patch, so that I can
play with it. Can you also try:

abs(I*x)

1/abs(x)
1/abs(x)^2
x/abs(x)^3
abs(x)^2

The x/abs(x)^3 is a Coulomb's law in 1D.

Ondrej

Bill Page

unread,
Nov 19, 2014, 12:29:37 PM11/19/14
to sage-devel, fricas-devel
Since this mostly concerns FriCAS I am cross posting to that group.  I will also post the patch there.  For FriCAS list reference the original email thread is here:

https://groups.google.com/forum/#!topic/sage-devel/6j-LcC6tpkE

Here is the result of compiling the patch against the current SourceForge svn trunk:

wspage@opensuse:~> fricas
The directory for FriCAS, /usr/local/lib/fricas/target/x86_64-suse-linux, does not exist.
Goodbye.
wspage@opensuse:~> fricas
Checking for foreign routines
AXIOM="/usr/local/lib64/fricas/target/x86_64-suse-linux"
spad-lib="/usr/local/lib64/fricas/target/x86_64-suse-linux/lib/libspad.so"
foreign routines found
openServer result 0
                       FriCAS Computer Algebra System
                         Version: FriCAS 2014-11-14
                   Timestamp: Wed Nov 19 11:57:49 EST 2014
-----------------------------------------------------------------------------
   Issue )copyright to view copyright notices.
   Issue )summary for a summary of useful system commands.
   Issue )quit to leave FriCAS and return to shell.
-----------------------------------------------------------------------------

 

(1) -> D(abs(x),x)

         _
         x + x
   (1)  -------
        2abs(x)
                                                    Type: Expression(Integer)
(2) -> D(conjugate(x),x)

   (2)  1
                                                    Type: Expression(Integer)
(3) -> f:=operator 'f

   (3)  f
                                                          Type: BasicOperator
(4) -> D(abs(f(x)),x)


             , _      _  ,
        f(x)f (x) + f(x)f (x)

   (4)  ---------------------
              2abs(f(x))
                                                    Type: Expression(Integer)
(5) -> D(abs(log(x)),x)


        _    _
        xlog(x) + x log(x)
   (5)  ------------------
            _
          2xxabs(log(x))
                                                    Type: Expression(Integer)
(6) -> D(log(abs(x)),x)

          _
          x + x
   (6)  --------
               2
        2abs(x)
                                                    Type: Expression(Integer)
(7) -> D(abs(%i*x),x)

           _
           x + x
   (7)  ----------
        2abs(%i x)
                                           Type: Expression(Complex(Integer))
(8) -> D(1/abs(x),x)

           _
         - x - x
   (8)  --------
               3
        2abs(x)
                                                    Type: Expression(Integer)
(9) -> D(1/abs(x)^2,x)

          _
        - x - x
   (9)  -------
              4
        abs(x)
                                                    Type: Expression(Integer)
(10) -> D(x/abs(x)^3,x)

             _          2     2
         - 3xx + 2abs(x)  - 3x
   (10)  ----------------------
                       5
                2abs(x)
                                                    Type: Expression(Integer)
(11) -> D(abs(x)^2,x)

         _
   (11)  x + x
                                                    Type: Expression(Integer)

Bill.

kcrisman

unread,
Nov 19, 2014, 9:23:57 PM11/19/14
to sage-...@googlegroups.com

Since this mostly concerns FriCAS I am cross posting to that group.  I will also post the patch there.  For FriCAS list reference the original email thread is here:


But if you come up with a solution Sage (or Ginac, or whatever) can implement too, please let us know!

Bill Page

unread,
Nov 19, 2014, 9:36:25 PM11/19/14
to sage-devel
On 19 November 2014 21:23, kcrisman <kcri...@gmail.com> wrote:
>
>
>> Since this mostly concerns FriCAS I am cross posting to that group. I will also post the patch there. For FriCAS list reference the original email thread is here:
>>
>
> But if you come up with a solution Sage (or Ginac, or whatever) can implement too, please let us know!
>

Right now Ondrej's proposed definition is looking pretty good to me
but I think it needs more extensive testing. Apparently Ginac with
Vladimir V. Kisil's patch is able to compute at least some of the
results I showed with FriCAS. If someone has used Ginac and is able
to compile it with the patch, it would be good to have these results
for comparison.

Yes, certainly. We can also continue this thread.

Bill.

Ondřej Čertík

unread,
Nov 20, 2014, 1:54:32 AM11/20/14
to sage-...@googlegroups.com
What you posted looks good. But we need to test it for arg(z), re(z),
im(z) and any other non-analytic function that we can find.

Ondrej

Bill Page

unread,
Nov 20, 2014, 9:41:09 AM11/20/14
to sage-devel
On 20 November 2014 01:54, Ondřej Čertík <ondrej...@gmail.com> wrote:
>
> What you posted looks good. But we need to test it for arg(z), re(z),
> im(z) and any other non-analytic function that we can find.
>

(1) -> re(x)==(conjugate(x)+x)/2  
                                                                   Type: Void
(2) -> im(x)==%i*(conjugate(x)-x)/2
                                                                   Type: Void
(3) -> arg(x)==log(x/abs(x))/%i  
                                                                   Type: Void
(4) -> re %i
   Compiling function re with type Complex(Integer) -> Fraction(Complex
      (Integer))

   (4)  0
                                             Type: Fraction(Complex(Integer))
(5) -> im %i
   Compiling function im with type Complex(Integer) -> Fraction(Complex
      (Integer))

   (5)  1
                                             Type: Fraction(Complex(Integer))
(6) -> arg %i
   Compiling function arg with type Complex(Integer) -> Expression(
      Complex(Integer))

   (6)  - %i log(%i)
                                           Type: Expression(Complex(Integer))
(7) -> complexNumeric %

   (7)  1.5707963267_948966192
                                                         Type: Complex(Float)
(8) -> D(re(x),x)
   Compiling function re with type Variable(x) -> Expression(Integer)

   (8)  1
                                                    Type: Expression(Integer)
(9) -> D(im(x),x)
   Compiling function im with type Variable(x) -> Expression(Complex(
      Integer))

   (9)  0
                                           Type: Expression(Complex(Integer))
(10) -> D(arg(x),x)
   Compiling function arg with type Variable(x) -> Expression(Complex(
      Integer))

             _             2       2
         %i xx - 2%i abs(x)  + %i x
   (10)  ---------------------------
                           2
                  2x abs(x)
                                           Type: Expression(Complex(Integer))


I had a thought. I suppose that all non-analytic (nonholomorphic) functions of interest can be written in terms of conjugate and some analytic functions, e.g.

  abs(x)=sqrt(x*conjugate(x))

so perhaps all we really need is to know how to differentiate conjugate properly?

Bill

Bill Page

unread,
Nov 20, 2014, 9:53:05 AM11/20/14
to sage-devel
So here (20) is a simpler expression for derivative of arg:

(16) -> abs(x)==sqrt(x*conjugate(x))
   Compiled code for abs has been cleared.
   Compiled code for arg has been cleared.
   1 old definition(s) deleted for function or rule abs
                                                                   Type: Void
(17) -> arg(x)==log(x/abs(x))/%i  
   1 old definition(s) deleted for function or rule arg
                                                                   Type: Void
(18) -> arg %i                      
   Compiling function abs with type Complex(Integer) -> Expression(

      Complex(Integer))
   Compiling function arg with type Complex(Integer) -> Expression(
      Complex(Integer))

   (18)  - %i log(%i)
                                           Type: Expression(Complex(Integer))
(19) -> complexNumeric %          

   (19)  1.5707963267_948966192
                                                         Type: Complex(Float)
(20) -> D(arg(x),x)                
   Compiling function abs with type Variable(x) -> Expression(Integer)

   Compiling function arg with type Variable(x) -> Expression(Complex(
      Integer))

             _
         - %ix + %i x
   (20)  ------------
                _
              2xx
                                           Type: Expression(Complex(Integer))


In general I am a little uncertain if, how and when to deal with simplifications of expressions like abs that can be expressed in terms of more fundamental/elementary functions.  What do you think?

Bill.

On 20 November 2014 09:41, Bill Page <bill...@newsynthesis.org> wrote:
> ...
>

Ondřej Čertík

unread,
Nov 20, 2014, 11:08:01 AM11/20/14
to sage-...@googlegroups.com
I haven't thought of that, but I think you are right. It's definitely
true for abs(x), arg(x), re(x), im(x) and conjugate(x). Other
non-analytic functions are combinations of those. The only other way
to create some non-analytic functions is to define their real and
complex parts using "x" and "y", e.g.

f(x+iy) = (x^2+y^2) + i*(2*x*y)

You can imagine arbitrary complicated expressions. But then you just
substitute z, conjugate(z) for x, y.

So I think that for most things that people would use a CAS for, this is true.

>
> Bill

Ondřej Čertík

unread,
Nov 20, 2014, 11:16:39 AM11/20/14