Boost logo

Boost :

From: Mickey Moore (mgmoore_at_[hidden])
Date: 2004-11-27 16:43:26


I appreciate everyone's comments (and I'll respond to a number below), but
it seems interest in this is not widespread enough to justify trying to
formally include it. I'll probably clean it up a bit and post it on my own
web-site at some point in case someone somewhere might find it useful.

It was a good exercise in humility to note that, judging from the message
frequency, people are at least a hundred times more interested in the new
logo for the Boost web-site. :)

------------------------------------------------------------

Regarding specific comments:

Roland Schwarz wrote:
> > For some applications, polar complex numbers have significant
> > advantages over Cartesian complex (e.g., std::complex) in efficiency
> > and numerical accuracy.
>
>Just out of curiosity: Can you give me an example of an algorithm where
>this would be of advantage?

Well, Hubert Holin gave one: tracking orbits (trajectories in polar
coordinates). There are actually a number of subtleties to be aware of in
mapping a physical 2D plane to the complex plane, so in that case, I would
actually probably use a true polar_vector2D class rather than polar_complex.

However, for field equations (including those from fluid dynamics, my
area), using tools from complex analysis can create new, simpler
formulations of problems in the complex plane. If the resulting mapping
has a high order of circular symmetry, then polar_complex numbers have
advantages.

The most obvious use of polar_complex numbers would be in wave
mechanics. The phase and magnitude are often weakly coupled; you can
sometimes work with just one component in different parts of the calculations.

At the lowest level, polar_complex numbers are best when the primary
operations are multiplications/divisions and powers/roots. When you start
adding/subtracting, then cartesian representations begin to win out
(because of the cost of the trig functions).

There's another advantage of polar_complex numbers that I didn't spell out
separately: *development* efficiency, i.e. how quickly one can write an
acceptable program for a simulation prototype or a quick-and-dirty
calculation. Many problems (in fact, a majority of those I have worked
with) are more naturally formulated in polar form; it's therefore easier to
write a program that uses that representation. This is just the old
programming adage "Think/write in the problem domain, not the language
domain.".

I'd address run-time efficiency later in those cases where it was a
concern; I haven't yet seen any cases where the polar_complex
representation was obviously inefficient.

Hubert Holin wrote:
> The main problems I foresee are in the commingling of the two
>different classes. It is interesting to see a complex number (among
>other ways) either as its two canonical coordinates, or in a polar
>representation, but sometimes we just want to switch between the two for
>a given instance. So I fear a substantial amount of typical run time
>will be spent doing conversions.

I defined explicit conversions to and from std::complex, but I used them
fairly rarely. If the problem truly belongs in the polar representation,
then you won't need many conversions. In fact, I wrote polar_complex in
the first place in a case when I found myself very frequently calling abs,
arg, and polar using std::complex -- the problem didn't belong in the
cartesian representation.

But you are right that the relation between them needs careful
consideration; that was on my list of issues to address if I "boosted" it.

> There is another, perhaps deeper, problem to consider, with the
>representation of complex numbers (and others), as evidenced by a thread
>on comp.std.c++ a couple of years back: layout guaranties. People
>wanted, in essence, to be able to break encapsulation when it suited
>them (for efficiency in time-critical code). The discussion also
>encompassed thoughts about the polar representation (which, as you
>remarked, also has advantages). As far as I know, there has been no
>tangible result to that thread.
>
> In short, I am not sure a library for complex numbers in polar
>form would be worthwhile, but I certainly think that if you could make
>the above situation progress (as in: write a formal proposal), it would
>be a great win for us all.

Well, I needn't worry about polar_complex layout because there are no C or
FORTRAN equivalents to be layout compatible with. :)

Regarding std::complex, just about a month ago, I posted to
comp.lang.c++.moderated about these very issues. The current status is in
this paper:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1589.html The
layout guarantee seems certain, and it lays out possible solutions for the
second issue. I don't see much that I can add other than to give my
recommendation on the solution and prod them to pick one already. I've
already been doing both on clc++m; I suppose I could write Howard Hinnant
directly, but he's responded to several threads so he's probably read most
of my posts.

This is becoming a digression, but my opinion, for what it's
worth: std::complex should never have been encapsulated as it was in the
first place. And to justify this, in addition to the efficiency issues,
there is clarity of the code's intent. Consider:

1a) z = complex<double>(z.real(), 1);
2a) z.imag() = 1;

1b) z = complex<double>(z.real(), z.imag() + 1);
2b) z.imag() += 1;

I think it's clearly much easier to read the intent of the code in the
second cases.

Matt Austern wrote:
>But is it likely that you would want to use a package that provides
>complex numbers in polar form? My experience is that if you're
>working with complex numbers that you want to think of in the form
>r exp(i phi), then you'll probably just do the work of separating out
>the magnitude and phase ahead of time in your equations, and then
>compute with them separately. After all: if you're doing something
>where the magnitude-phase form is most convenient, it usually
>means that putting numbers in that form makes the equations
>simpler.

True to a degree, though such separation is not always possible, especially
in non-linear situations. And a similar argument is true for cartesian
complex: if you're doing something where the real-imaginary form is most
convenient, it also usually means that putting numbers in that form makes
the equations simpler. But it's usually easier to keep track of, for
instance, just
      vector< complex<double> > Numbers;
than
      vector<double> NumbersReal; vector<double> NumbersImag;
and there's usually no harm in combining them like that (unless of course
complex is over-encapsulated, but I digress again. . .).

Similarly,
      vector< polar_complex<double> > Wave;
is simpler to deal with than
      vector<double> WaveAmplitude; vector<double> WaveMagnitude;
so combine them if there's no harm in doing so.

This is appealing to the "development efficiency" (and code clarity)
argument I made above.

> Still, from the discussion on comp.std.cpp a few years back, I
>recall that there was a feeling that having multiple representations
>(say cartesian and polar) at the same time would have been nice. Of
>course, it would have been a bloated beast (at least in a naive
>implementation), and a slow one at that (to keep track of the various
>changes, in a naive implementation still). Furthermore it clashed with
>the desire to have some layout guaranty which played nice with C (that
>line of reasoning seems to have flown out in limbo, however).
>
> This illustration is merely there as a reminder that the wish for
>concurrent multiple points of views of a given abstract entity is not a
>futile one, if one that I fear can't be translated from mathematics to
>CS. We've had another illustration of that recently with the affine
>space/vector space discussion in a GUI thread a few days back here
>(which also happens to be relevant to this discussion as well, at least
>in two dimensions).

Well now you are touching on an entirely separate point. For a
mathematical primitive used in numerical work, making the same class hold
different representations is a terrible idea. Pick the representation you
need and stick with it, converting between representations only if/when you
must. If you find yourself converting constantly, then you have picked the
wrong algorithm or need another representation altogether. And how many
representations should you implement in one class? Why cartesian and polar
coordinates but not parabolic, elliptical, bipolar, etc.?

------------------------------------------------------------

Again, I appreciate all the comments, but the interest obviously isn't
there for developing the class further.

Thanks, Mickey Moore


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk