Copyright | (c) The University of Glasgow 2001 |
---|---|
License | BSD-style (see the file libraries/base/LICENSE) |
Maintainer | libraries@haskell.org |
Stability | provisional |
Portability | portable |
Safe Haskell | Trustworthy |
Complex numbers.
Synopsis
- data Complex a = !a :+ !a
- realPart :: Complex a -> a
- imagPart :: Complex a -> a
- mkPolar :: Floating a => a -> a -> Complex a
- cis :: Floating a => a -> Complex a
- polar :: RealFloat a => Complex a -> (a, a)
- magnitude :: RealFloat a => Complex a -> a
- phase :: RealFloat a => Complex a -> a
- conjugate :: Num a => Complex a -> Complex a
- iTimes :: RealFloat a => Complex a -> Complex a
- (+:) :: RealFloat a => Complex a -> a -> Complex a
- (-:) :: RealFloat a => Complex a -> a -> Complex a
- (*:) :: RealFloat a => Complex a -> a -> Complex a
- (/:) :: RealFloat a => Complex a -> a -> Complex a
Rectangular form
Complex numbers are an algebraic type.
For a complex number z
,
is a number with the magnitude of abs
zz
,
but oriented in the positive real direction, whereas
has the phase of signum
zz
, but unit magnitude.
The Foldable
and Traversable
instances traverse the real part first.
Note that Complex
's instances inherit the deficiencies from the type
parameter's. For example, Complex Float
's Ord
instance has similar
problems to Float
's.
A number of functions have branch cuts and principal values.
!a :+ !a infix 6 | forms a complex number from its real and imaginary rectangular components. |
Instances
Monad Complex # | Since: 4.9.0.0 |
Functor Complex # | Since: 4.9.0.0 |
MonadFix Complex # | Since: 4.15.0.0 |
Defined in Data.Complex | |
Applicative Complex # | Since: 4.9.0.0 |
Foldable Complex # | Since: 4.9.0.0 |
Defined in Data.Complex fold :: Monoid m => Complex m -> m foldMap :: Monoid m => (a -> m) -> Complex a -> m foldMap' :: Monoid m => (a -> m) -> Complex a -> m foldr :: (a -> b -> b) -> b -> Complex a -> b foldr' :: (a -> b -> b) -> b -> Complex a -> b foldl :: (b -> a -> b) -> b -> Complex a -> b foldl' :: (b -> a -> b) -> b -> Complex a -> b foldr1 :: (a -> a -> a) -> Complex a -> a foldl1 :: (a -> a -> a) -> Complex a -> a elem :: Eq a => a -> Complex a -> Bool maximum :: Ord a => Complex a -> a minimum :: Ord a => Complex a -> a | |
Traversable Complex # | Since: 4.9.0.0 |
MonadZip Complex # | Since: 4.15.0.0 |
Eq a => Eq (Complex a) # | Since: 2.1 |
RealFloat a => Floating (Complex a) # | Since: 2.1 |
Defined in Data.Complex sqrt :: Complex a -> Complex a (**) :: Complex a -> Complex a -> Complex a logBase :: Complex a -> Complex a -> Complex a asin :: Complex a -> Complex a acos :: Complex a -> Complex a atan :: Complex a -> Complex a sinh :: Complex a -> Complex a cosh :: Complex a -> Complex a tanh :: Complex a -> Complex a asinh :: Complex a -> Complex a acosh :: Complex a -> Complex a atanh :: Complex a -> Complex a log1p :: Complex a -> Complex a expm1 :: Complex a -> Complex a | |
RealFloat a => Fractional (Complex a) # | Since: 2.1 |
Defined in Data.Complex | |
Data a => Data (Complex a) # | Since: 2.1 |
Defined in Data.Complex gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Complex a -> c (Complex a) gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (Complex a) toConstr :: Complex a -> Constr dataTypeOf :: Complex a -> DataType dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c (Complex a)) dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c (Complex a)) gmapT :: (forall b. Data b => b -> b) -> Complex a -> Complex a gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Complex a -> r gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Complex a -> r gmapQ :: (forall d. Data d => d -> u) -> Complex a -> [u] gmapQi :: Int -> (forall d. Data d => d -> u) -> Complex a -> u gmapM :: Monad m => (forall d. Data d => d -> m d) -> Complex a -> m (Complex a) gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Complex a -> m (Complex a) gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Complex a -> m (Complex a) | |
RealFloat a => Num (Complex a) # | Since: 2.1 |
Read a => Read (Complex a) # | Since: 2.1 |
Defined in Data.Complex | |
Show a => Show (Complex a) # | Since: 2.1 |
Generic (Complex a) # | Since: 4.9.0.0 |
Storable a => Storable (Complex a) # | Since: 4.8.0.0 |
Defined in Data.Complex peekElemOff :: Ptr (Complex a) -> Int -> IO (Complex a) pokeElemOff :: Ptr (Complex a) -> Int -> Complex a -> IO () peekByteOff :: Ptr b -> Int -> IO (Complex a) pokeByteOff :: Ptr b -> Int -> Complex a -> IO () | |
Generic1 Complex # | Since: 4.9.0.0 |
type Rep (Complex a) # | |
Defined in Data.Complex type Rep (Complex a) = D1 ('MetaData "Complex" "Data.Complex" "main" 'False) (C1 ('MetaCons ":+" ('InfixI 'NotAssociative 6) 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 a) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 a))) | |
type Rep1 Complex # | |
Defined in Data.Complex type Rep1 Complex = D1 ('MetaData "Complex" "Data.Complex" "main" 'False) (C1 ('MetaCons ":+" ('InfixI 'NotAssociative 6) 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) Par1 :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) Par1)) |
Polar form
mkPolar :: Floating a => a -> a -> Complex a #
Form a complex number from polar components of magnitude and phase.
cis :: Floating a => a -> Complex a #
is a complex value with magnitude cis
t1
and phase t
(modulo 2*
).pi
polar :: RealFloat a => Complex a -> (a, a) #
The function polar
takes a complex number and
returns a (magnitude, phase) pair:
the magnitude is nonnegative, and the phase in the range [-
.pi
, pi
]
phase :: RealFloat a => Complex a -> a #
The phase of a complex number, in the range [-
.
If the number has a negative real part, and +/- 0 imaginary part
the result will be +/- pi.
The phase of 0:+0 is 0; of 0:+(-0) is -0; of (-0):+0 is pi; of (-0):+(-0) is -pi.pi
, pi
]
Other operations
iTimes :: RealFloat a => Complex a -> Complex a #
iTimes z == z * (0:+1)
, but without any rounding issues or loss of negative zero. E.g.
iTimes((-0):+0) == (-0):+(-0)
whereas
((-0):+0)*(0:+1) == (-0):+0
Operations with complex and real numbers
These operators combine complex and real numbers, without converting the real numbers to complex first. E.g.
(3:+(-0)) +: 1 == 4:+(-0)
whereas
(3:+(-0)) + 1 == (3:+(-0)) + (1:+0) == 4:+0
Branch Cuts and Principal Values
The "inverse" complex functions have branch cuts and principal values in ranges as follows:
(inverse) function | branch cut(s) | range x or (x:+y) where |
---|---|---|
phase | [-∞, -0] | -π <= x <= π |
sqrt | [-∞, -0) | x >= +0.0 |
log | [-∞, -0] | -π <= y <= π |
asin | [-∞, -1) and (1, ∞] | -π/2 <= x <= π/2 |
acos | [-∞, -1) and (1, ∞] | +0.0 <= x <= π |
atan | [-∞i, -i) and (i, ∞i] | -π/2 <= x <= π/2 |
asinh | [-∞i, -i) and (i, ∞i] | -π/2 <= y <= π/2 |
acosh | [-∞, 1) | x >= +0.0 and -π <= y <= π |
atanh | [-∞, -1) and (1, ∞] | -π/2 <= y <= π/2 |
Note that phase
maps complex numbers to real numbers
and the other functions map complex numbers to complex numbers.
All of the branch cuts fall on axes.
In this table, the expression x >= +0.0
and similar excludes x
where isNegativeZero x
is True
.
The behaviour of types Complex a
for a value on a branch cut is different,
depending on whether floating type a
supports signed zeros.
For types that support signed zeros (i.e. IEEE 754 types),
the branch cuts can be considered to be "between" 0.0
and -0.0
.
The mappings of 0.0
will be continuous with those of values greater than 0.0
, and
the mappings of -0.0
will be continuous with those of values less than 0.0
.
For types that don't support signed zeros, the mappings of points on a branch cut are continuous with mappings of points in one of the adjacent quadrants as indicated in the table below. In addition, the range of the functions excludes certain values, also indicated in the table.
(inverse) function | branch cut(s) continuous with | range excludes |
---|---|---|
phase | QII | -π |
sqrt | QII | (0, -∞i] |
log | QII | [-∞-πi, ∞-πi] |
asin | QII and QIV | (-π/2, -π/2-∞i], (π/2, π/2+∞i] |
acos | QII and QIV | (0, -∞i], (π, π+∞i] |
atan | QIII and QI | [-π/2, -π/2+∞i], [π/2, π/2-∞i] |
asinh | QIII and QI | (-π/2i, ∞-π/2i], (π/2i, -∞+π/2i] |
acosh | QII (x<0), QI (x>0) | (0, -πi], [-πi, ∞-πi] |
atanh | QII and QIV | [-π/2i, -∞-π/2i], [π/2i, ∞+π/2i] |
where the quadrants are labelled:
negative real | positive real | |
positive imaginary | QII | QI |
negative imaginary | QIII | QIV |
Some (inverse) functions also have branch points as follows:
(inverse) function | branch point | value at branch point |
---|---|---|
log | 0.0 :+ 0.0 | (-Infinity) :+ 0.0 |
atan | 0.0 :+ 1.0 | 0.0 :+ Infinity |
atan | 0.0 :+ (-1.0) | 0.0 :+ (-Infinity) |
atanh | 1.0 :+ 0.0 | Infinity :+ 0.0 |
atanh | (-1.0) :+ 0.0 | (-Infinity) :+ 0.0 |
For log
, branch points with a negative zero real part are included in
the branch cut, giving different results for
branch points with positive and negative zero in the imaginary part.
For other branch points, a negative zero input will be reflected in the output.
Haskell's branch cuts, continuities, ranges and branch points follow the recommendations by Kahan (Branch Cuts for Complex Elementary Functions or Much Ado About Nothing's Sign Bit, 1987), and are consitant with those in some other languages such as Common Lisp.
Explanation
Branch Cuts
This is an explanation of the branch cuts for the function sqrt
.
The same principles apply to the other functions.
Consider sqrt
of real numbers.
Both 2.0^2
and (-2.0)^2
have the value 4.0
,
but sqrt 4.0
returns only 2.0
.
In general sqrt
returns only the non-negative square root,
a standard widely adopted.
In other words the range of sqrt
, over real numbers, is [0, ∞]:
half of the real number line.
sqrt
over complex numbers has analagous behaviour.
The square of both 1:+2
and its negative (-1):+(-2)
is the same value (-3):+4
.
sqrt
maps (-3):+4
back to only one of these: 1:+2
,
the principal value.
The standard principal values for sqrt
are those x:+y
where x >= 0
:
half of the complex plane.
The table below shows the square of two sequences of complex numbers:
v | w = -v | z = v^2 (== w^2 ) |
---|---|---|
( 0.050):+2.0 | (-0.050):+(-2.0) | (-4):+( 0.2) |
( 0.025):+2.0 | (-0.025):+(-2.0) | (-4):+( 0.1) |
( 0.000):+2.0 | ( 0.000):+(-2.0) | (-4):+( 0.0) |
(-0.025):+2.0 | ( 0.025):+(-2.0) | (-4):+(-0.1) |
(-0.050):+2.0 | ( 0.050):+(-2.0) | (-4):+(-0.2) |
Each corresponding v
and w
has the same square z
.
sqrt
maps each of these z
values back to the principal value
(whichever of v
or w
has a non-negative real part, as highlighted in bold).
Hence, although the sequence z
is continuous, sqrt z
is discontinuous,
with the results jumping from a imaginary 2.0
to imaginary -2.0
as the imaginary part of z
crosses the real axis.
The discontinuity in sqrt
illustrated here happens at -4 on the real axis.
There would be similar discontinuities at all other points on the negative real axis.
Hence the negative real axis [-∞, -0)
forms a line of discontinuity, called a branch cut.
The location of the branch cuts and the range of the function are related.
In the case of sqrt $ (-4):+( 0.0)
, either v
or w
could be chosen, but:
- When dealing with floating point numbers that support negative zeros,
the result is chosen so that
sqrt $ (-4):+( 0.0)
is continuous with pointssqrt $ (-4):+y
wherey>0
, hence( 0.000):+2.0
, andsqrt $ (-4):+(-0.0)
is continuous with pointssqrt $ (-4):+y
wherey<0
, hence( 0.000):(-2.0)
. It is helpful to think of the branch cut as being "between"0.0
and-0.0
. When dealing with floating point numbers that don't support negative zeros, an arbitrary but standard choice is made. The standard choice in this case is that
sqrt $ (-4):+( 0.0)
is( 0.000):+2.0
. Hencesqrt
of points on the branch cut are continuous withsqrt
of points in QII on the complex plane. In addition,sqrt
will not map anything to( 0.000):+(-2.0)
, or any other point on the negative imaginary axis (0, -∞i], which is excluded from the range.Note that "continous with" doesn't mean "maps into" or "maps towards". For example, the first branch cut listed for
cos
is marked as "continuous with QII". Hence both(-2) :+ 0
(a point on this branch cut) and(-2) :+ 0.1
(a nearby point in QII) are both mapped bycos
to locations near each other, but in QIV.
The result for (unsigned) 0.0
(for types that don't support signed zeros)
is not necessarily the same as that for "positive" 0.0
(for those that types that do).
For example:
>>>
asin $ 2.0 :+ 0.0 :: Complex SomeNonIEEEFloatingType
1.570 :+ (-1.316)>>>
asin $ 2.0 :+ 0.0 :: Complex SomeIEEEFloatingType
1.570 :+ 1.316
Branch Points
This is an explanation of the branch point for the function log
.
The same principles apply to the other functions.
Mathematically \(e^x\to0\) as \(x\to-\infty\) and
\(\log 0\) is undefined.
Computationally it is usually helpful
(and is required by IEEE 754) to give -Infinity
:
>>>
log 0.0
-Infinity
For complex numbers, exp (x:+y) == mkPolar (exp x) y
,
i.e. a point distance exp x
from the origin, at angle y
.
As x
tends to -Infinity
, this point tends to 0.0 :+ 0.0
,
but from different angles depending on y
.
Hence exp $ (-1/0.0) :+ y
gives 0.0 :+ 0.0
for any y
.
The inverse function log
could map 0.0 :+ 0.0
to any -Infinity :+ y
.
The point 0.0 :+ 0.0
is called a branch point of log
.
An arbirary, but standard, choice (which must be within the principle value range)
is made for the result, as shown in the table above:
>>>
log $ 0.0 :+ 0.0
(-Infinity) :+ 0.0