Complex numbers

Is there a complex number implementation in the Scala (or Java) standard library? If not, is there a recommended implementation out there somewhere? Thanks.

I suspect that breeze and spire are the most popular scala math libraries that include complex numbers. I don’t have much experience with either.

1 Like

Thanks. It seems to me that there should be a basic complex number implementation in the standard library.

I have occasion to solve cubic equations, so I implemented some formulas from the web. However, I discovered that I need to use complex numbers even to find a real root in some cases (because the formulas take the square root of a number that can be negative).

I prefer to avoid external library dependencies for a single use case.

Spire (and probably Breeze too) also has a Polynomial type with a function for finding all real roots.

It really isn’t appropriate for the standard library. Stdlib tries very hard to stick to very broadly-applicable data structures, with fairly universal applicability – while complex numbers are useful in a bunch of scientific / simulation situations, that’s still a fairly specific domain in the grand scheme of things.

In general, the trend is to remove things from stdlib, not add them, and to leave these topics to specialist libraries that care about doing them as well as possible. External open-source libraries are what makes the Scala ecosystem run…

2 Likes

If you want something added to the standard library, publish what you want as a library and once many people use it, there may be a case for it to be added.

I’d have to agree with @Justin du coeur, though, that complex numbers are too specific to be in the standard library. Their relevance is limited to certain kinds of math, engineering and science applications. Keep in mind that Scala, like Java, does not have much math support even for plain old Double, not even an error function, so most people will reach for a math library long before they consider something as fancy as complex numbers.

If you need complex numbers, roll your own or use a math library.

I ended up writing my own Complex class. I didn’t realize it initially, but a generic Complex class would not be sufficient for my purposes. I need one that can take Scalar arguments. My Scalar class represents scalars with physical units. For my purposes of solving cubic polynomials, my Complex class needs the real and imaginary parts to be in terms of such Scalars rather than just plain real numbers (i.e., so-called “Doubles”).

I got side-tracked trying to make my Complex class elegant. Not that it’s critical, but it would be nice to be able to write

val x = 3 * Complex(4,2)
val y = 3.0 * Complex(4,2)

But that requires implicit conversions from Int and Double to Complex. Unfortunately, when I tried to add such an implicit conversion it conflicted with implicit conversions that I already had from Int and Double to Scalar, so I had to abandon it.

For example, I want to be able to write

val x = 4 * m/s

which requires an implicit conversion from Int to Scalar. But then apparently I cannot also have an implicit conversion from Int to Complex in the same scope. Or can I? It seems that the compiler should be able to determine which implicit conversion is needed based on the type of the arguments. No?

In a similar situation (probability distributions) I defined methods like *: so one can have statements like

val x = 3 *: Complex(4,2)
val y = 3.0 *: Complex(4,2)

Less elegant than just * but with clean code.

regards,
Siddhartha

You need complex numbers with different units on the real and imaginary axes?! Or you just need the whole thing to have physical units? (So it is Scalar-like, but not a pair of Scalars.)

I’d just define a typeclass:

trait MultipliesOnLeft[A] {
  def leftMult(i: Int, a: A): A
  def leftMult(d: Double, a: A): A
}

Create implicit instances for Complex and Scalar.

Then you enrich Int and Double with

implicit class LeftMultiplyDouble(value: Double) extends AnyVal {
  def *[A: MultipliesOnLeft](a: A) =
    implicitly[MultipliesOnLeft[A]].leftMult(value, a)
}

Then you get the proper type inference. Generally the JVM will manage to optimize the extra code away without performance penalty.