num-complex icon indicating copy to clipboard operation
num-complex copied to clipboard

Proposal: Add complex cis function

Open Expander opened this issue 1 year ago • 8 comments

I propose to add the complex variant of the real $\mathrm{cis}$ function. It would be defined as $\mathrm{cis}\colon\mathbb{C}\to\mathbb{C}$ with

$$\mathrm{cis}(z) = \exp(iz) = \exp(-\mathrm{Im}(z))\mathrm{cis}(\mathrm{Re}(z))$$

This complex $\mathrm{cis}$ function has applications in physics and numerical mathematics.

Expander avatar Nov 30 '24 10:11 Expander

Since there's already fn cis(T) -> Complex<T>, maybe this could be fn into_cis(self) -> Self? Is there any naming precedent from other languages for this form?

cuviper avatar Dec 01 '24 00:12 cuviper

Implementations in other languages that I found so far include:

  • Intel's C++ compiler has only double _Complex cis(double). (I guess in C's naming convention one would have cis for a function with a real and ccis for a function with a complex argument.)
  • Common Lisp has only cis real => complex
  • julia has cis(z::Real) and cis(z::Complex) via function overloading
  • D has Complex!real expi(real y)

So, it could also be named expi or ccis.

Expander avatar Dec 01 '24 13:12 Expander

There is a technical problem in the implementation. num::Complex::cis() has the following signature: pub fn cis(phase: T) -> Complex<T>

This is not correct deduction of types. I suggest changing the signature to something that decouples phase type from number type.

let y: Complex<i32> = num::Complex::cis(1)

The above example shows why this type sharing is wrong. changing the following phase to 1.0 changes the Complex number completely.

a-samea avatar Mar 31 '25 18:03 a-samea

@a-samea that's a strange example -- $cis(1) \approx 0.54 + 0.84i$, so it would have to round or truncate badly for Complex<i32>.

cuviper avatar Mar 31 '25 18:03 cuviper

Yes, this is an strange example. The point was that phase data type has nothing to do with the number data type. I am also sorry to bring it into this discussion. I later found out that it was irrelevant to the cis function I was using. I did not know how these were implemented but i sensed a potential bug.

a-samea avatar Mar 31 '25 18:03 a-samea

I would rather propose to add

impl<T: Neg<Output = T>> Complex<T> {
    pub fn mul_i(self) -> Self {
        Self {
            re: -self.im,
            im: self.re,
        }
    }
}

so you can write your cis function as z.mul_i().exp(). Seems like a much cleaner solution.

redweasel avatar Aug 19 '25 15:08 redweasel

Multiplication of i and z are automatically handled via operator overloading.

Complex::I * z

This the output of your function in practice.

a-samea avatar Aug 19 '25 15:08 a-samea

@a-samea your solution would have the same performance for floats due to the compiler optimizing the multiplication with 0,1 out, I assume, but I like that explicit, as for types like BigRational one can not guarantee that optimization to take place.

redweasel avatar Aug 19 '25 15:08 redweasel