Arbitrary Precision Complex Intervals

This is a simple complex interval package, using intervals which are axis-aligned rectangles in the complex plane. It has very few special functions, and it does not use any special tricks to keep the size of the intervals down.

AUTHORS:

These authors wrote complex_mpfr.pyx (renamed from complex_number.pyx):

- William Stein (2006-01-26): complete rewrite
- Joel B. Mohler (2006-12-16): naive rewrite into pyrex
- William Stein(2007-01): rewrite of Mohler's rewrite

Then complex_number.pyx was copied to complex_interval.pyx and heavily modified:

  • Carl Witty (2007-10-24): rewrite to become a complex interval package

  • Travis Scrimshaw (2012-10-18): Added documentation to get full coverage.

Warning

Mixing symbolic expressions with intervals (in particular, converting constant symbolic expressions to intervals), can lead to incorrect results:

sage: ref = ComplexIntervalField(100)(ComplexBallField(100).one().airy_ai())
sage: ref
0.135292416312881415524147423515?
sage: val = CIF(airy_ai(1)); val # known bug
0.13529241631288142?
sage: val.overlaps(ref)          # known bug
False

Todo

Implement ComplexIntervalFieldElement multiplicative order similar to ComplexNumber multiplicative order with _set_multiplicative_order(n) and ComplexNumber.multiplicative_order() methods.

class sage.rings.complex_interval.ComplexIntervalFieldElement

Bases: sage.structure.element.FieldElement

A complex interval.

EXAMPLES:

sage: I = CIF.gen()
sage: b = 3/2 + 5/2*I
sage: TestSuite(b).run()
arg()

Same as argument().

EXAMPLES:

sage: i = CIF.0
sage: (i^2).arg()
3.141592653589794?
argument()

The argument (angle) of the complex number, normalized so that \(-\pi < \theta.lower() \leq \pi\).

We raise a ValueError if the interval strictly contains 0, or if the interval contains only 0.

Warning

We do not always use the standard branch cut for argument! If the interval crosses the negative real axis, then the argument will be an interval whose lower bound is less than \(\pi\) and whose upper bound is more than \(\pi\); in effect, we move the branch cut away from the interval.

EXAMPLES:

sage: i = CIF.0
sage: (i^2).argument()
3.141592653589794?
sage: (1+i).argument()
0.785398163397449?
sage: i.argument()
1.570796326794897?
sage: (-i).argument()
-1.570796326794897?
sage: (-1/1000 - i).argument()
-1.571796326461564?
sage: CIF(2).argument()
0
sage: CIF(-2).argument()
3.141592653589794?

Here we see that if the interval crosses the negative real axis, then the argument can exceed \(\pi\), and we we violate the standard interval guarantees in the process:

sage: CIF(-2, RIF(-0.1, 0.1)).argument().str(style='brackets')
'[3.0916342578678501 .. 3.1915510493117365]'
sage: CIF(-2, -0.1).argument()
-3.091634257867851?
bisection()

Return the bisection of self into four intervals whose union is self and intersection is center().

EXAMPLES:

sage: z = CIF(RIF(2, 3), RIF(-5, -4))
sage: z.bisection()
(3.? - 5.?*I, 3.? - 5.?*I, 3.? - 5.?*I, 3.? - 5.?*I)
sage: for z in z.bisection():
....:     print(z.real().endpoints())
....:     print(z.imag().endpoints())
(2.00000000000000, 2.50000000000000)
(-5.00000000000000, -4.50000000000000)
(2.50000000000000, 3.00000000000000)
(-5.00000000000000, -4.50000000000000)
(2.00000000000000, 2.50000000000000)
(-4.50000000000000, -4.00000000000000)
(2.50000000000000, 3.00000000000000)
(-4.50000000000000, -4.00000000000000)

sage: z = CIF(RIF(sqrt(2), sqrt(3)), RIF(e, pi))
sage: a, b, c, d = z.bisection()
sage: a.intersection(b).intersection(c).intersection(d) == CIF(z.center())
True

sage: zz = a.union(b).union(c).union(c)
sage: zz.real().endpoints() == z.real().endpoints()
True
sage: zz.imag().endpoints() == z.imag().endpoints()
True
center()

Return the closest floating-point approximation to the center of the interval.

EXAMPLES:

sage: CIF(RIF(1, 2), RIF(3, 4)).center()
1.50000000000000 + 3.50000000000000*I
conjugate()

Return the complex conjugate of this complex number.

EXAMPLES:

sage: i = CIF.0
sage: (1+i).conjugate()
1 - 1*I
contains_zero()

Return True if self is an interval containing zero.

EXAMPLES:

sage: CIF(0).contains_zero()
True
sage: CIF(RIF(-1, 1), 1).contains_zero()
False
cos()

Compute the cosine of this complex interval.

EXAMPLES:

sage: CIF(1,1).cos()
0.833730025131149? - 0.988897705762865?*I
sage: CIF(3).cos()
-0.9899924966004455?
sage: CIF(0,2).cos()
3.762195691083632?

Check that trac ticket #17285 is fixed:

sage: CIF(cos(2/3))
0.7858872607769480?

ALGORITHM:

The implementation uses the following trigonometric identity

\[\cos(x + iy) = \cos(x) \cosh(y) - i \sin(x) \sinh(y)\]
cosh()

Return the hyperbolic cosine of this complex interval.

EXAMPLES:

sage: CIF(1,1).cosh()
0.833730025131149? + 0.988897705762865?*I
sage: CIF(2).cosh()
3.762195691083632?
sage: CIF(0,2).cosh()
-0.4161468365471424?

ALGORITHM:

The implementation uses the following trigonometric identity

\[\cosh(x+iy) = \cos(y) \cosh(x) + i \sin(y) \sinh(x)\]
crosses_log_branch_cut()

Return True if this interval crosses the standard branch cut for log() (and hence for exponentiation) and for argument. (Recall that this branch cut is infinitesimally below the negative portion of the real axis.)

EXAMPLES:

sage: z = CIF(1.5, 2.5) - CIF(0, 2.50000000000000001); z
1.5000000000000000? + -1.?e-15*I
sage: z.crosses_log_branch_cut()
False
sage: CIF(-2, RIF(-0.1, 0.1)).crosses_log_branch_cut()
True
diameter()

Return a somewhat-arbitrarily defined “diameter” for this interval.

The diameter of an interval is the maximum of the diameter of the real and imaginary components, where diameter on a real interval is defined as absolute diameter if the interval contains zero, and relative diameter otherwise.

EXAMPLES:

sage: CIF(RIF(-1, 1), RIF(13, 17)).diameter()
2.00000000000000
sage: CIF(RIF(-0.1, 0.1), RIF(13, 17)).diameter()
0.266666666666667
sage: CIF(RIF(-1, 1), 15).diameter()
2.00000000000000
edges()

Return the 4 edges of the rectangle in the complex plane defined by this interval as intervals.

OUTPUT: a 4-tuple of complex intervals (left edge, right edge, lower edge, upper edge)

See also

endpoints() which returns the 4 corners of the rectangle.

EXAMPLES:

sage: CIF(RIF(1,2), RIF(3,4)).edges()
(1 + 4.?*I, 2 + 4.?*I, 2.? + 3*I, 2.? + 4*I)
sage: ComplexIntervalField(20)(-2).log().edges()
(0.69314671? + 3.14160?*I,
 0.69314766? + 3.14160?*I,
 0.693147? + 3.1415902?*I,
 0.693147? + 3.1415940?*I)
endpoints()

Return the 4 corners of the rectangle in the complex plane defined by this interval.

OUTPUT: a 4-tuple of complex numbers (lower left, upper right, upper left, lower right)

See also

edges() which returns the 4 edges of the rectangle.

EXAMPLES:

sage: CIF(RIF(1,2), RIF(3,4)).endpoints()
(1.00000000000000 + 3.00000000000000*I,
 2.00000000000000 + 4.00000000000000*I,
 1.00000000000000 + 4.00000000000000*I,
 2.00000000000000 + 3.00000000000000*I)
sage: ComplexIntervalField(20)(-2).log().endpoints()
(0.69315 + 3.1416*I,
 0.69315 + 3.1416*I,
 0.69315 + 3.1416*I,
 0.69315 + 3.1416*I)
exp()

Compute \(e^z\) or \(\exp(z)\) where \(z\) is the complex number self.

EXAMPLES:

sage: i = ComplexIntervalField(300).0
sage: z = 1 + i
sage: z.exp()
1.46869393991588515713896759732660426132695673662900872279767567631093696585951213872272450? + 2.28735528717884239120817190670050180895558625666835568093865811410364716018934540926734485?*I
imag()

Return imaginary part of self.

EXAMPLES:

sage: i = ComplexIntervalField(100).0
sage: z = 2 + 3*i
sage: x = z.imag(); x
3
sage: x.parent()
Real Interval Field with 100 bits of precision
intersection(other)

Return the intersection of the two complex intervals self and other.

EXAMPLES:

sage: CIF(RIF(1, 3), RIF(1, 3)).intersection(CIF(RIF(2, 4), RIF(2, 4))).str(style='brackets')
'[2.0000000000000000 .. 3.0000000000000000] + [2.0000000000000000 .. 3.0000000000000000]*I'
sage: CIF(RIF(1, 2), RIF(1, 3)).intersection(CIF(RIF(3, 4), RIF(2, 4)))
Traceback (most recent call last):
...
ValueError: intersection of non-overlapping intervals
is_NaN()

Return True if this is not-a-number.

EXAMPLES:

sage: CIF(2, 1).is_NaN()
False
sage: CIF(NaN).is_NaN()
True
sage: (1 / CIF(0, 0)).is_NaN()
True
is_exact()

Return whether this complex interval is exact (i.e. contains exactly one complex value).

EXAMPLES:

sage: CIF(3).is_exact()
True
sage: CIF(0, 2).is_exact()
True
sage: CIF(-4, 0).sqrt().is_exact()
True
sage: CIF(-5, 0).sqrt().is_exact()
False
sage: CIF(0, 2*pi).is_exact()
False
sage: CIF(e).is_exact()
False
sage: CIF(1e100).is_exact()
True
sage: (CIF(1e100) + 1).is_exact()
False
is_square()

Return True as \(\CC\) is algebraically closed.

EXAMPLES:

sage: CIF(2, 1).is_square()
True
lexico_cmp(left, right)

Intervals are compared lexicographically on the 4-tuple: (x.real().lower(), x.real().upper(), x.imag().lower(), x.imag().upper())

EXAMPLES:

sage: a = CIF(RIF(0,1), RIF(0,1))
sage: b = CIF(RIF(0,1), RIF(0,2))
sage: c = CIF(RIF(0,2), RIF(0,2))
sage: a.lexico_cmp(b)
-1
sage: b.lexico_cmp(c)
-1
sage: a.lexico_cmp(c)
-1
sage: a.lexico_cmp(a)
0
sage: b.lexico_cmp(a)
1
log(base=None)

Complex logarithm of \(z\).

Warning

This does always not use the standard branch cut for complex log! See the docstring for argument() to see what we do instead.

EXAMPLES:

sage: a = CIF(RIF(3, 4), RIF(13, 14))
sage: a.log().str(style='brackets')
'[2.5908917751460420 .. 2.6782931373360067] + [1.2722973952087170 .. 1.3597029935721503]*I'
sage: a.log().exp().str(style='brackets')
'[2.7954667135098274 .. 4.2819545928390213] + [12.751682453911920 .. 14.237018048974635]*I'
sage: a in a.log().exp()
True

If the interval crosses the negative real axis, then we don’t use the standard branch cut (and we violate the interval guarantees):

sage: CIF(-3, RIF(-1/4, 1/4)).log().str(style='brackets')
'[1.0986122886681095 .. 1.1020725100903968] + [3.0584514217013518 .. 3.2247338854782349]*I'
sage: CIF(-3, -1/4).log()
1.102072510090397? - 3.058451421701352?*I

Usually if an interval contains zero, we raise an exception:

sage: CIF(RIF(-1,1),RIF(-1,1)).log()
Traceback (most recent call last):
...
ValueError: Can...t take the argument of interval strictly containing zero

But we allow the exact input zero:

sage: CIF(0).log()
[-infinity .. -infinity]

If a base is passed from another function, we can accommodate this:

sage: CIF(-1,1).log(2)
0.500000000000000? + 3.39927010637040?*I
magnitude()

The largest absolute value of the elements of the interval, rounded away from zero.

OUTPUT: a real number with rounding mode RNDU

EXAMPLES:

sage: CIF(RIF(-1,1), RIF(-1,1)).magnitude()
1.41421356237310
sage: CIF(RIF(1,2), RIF(3,4)).magnitude()
4.47213595499958
sage: parent(CIF(1).magnitude())
Real Field with 53 bits of precision and rounding RNDU
mignitude()

The smallest absolute value of the elements of the interval, rounded towards zero.

OUTPUT: a real number with rounding mode RNDD

EXAMPLES:

sage: CIF(RIF(-1,1), RIF(-1,1)).mignitude()
0.000000000000000
sage: CIF(RIF(1,2), RIF(3,4)).mignitude()
3.16227766016837
sage: parent(CIF(1).mignitude())
Real Field with 53 bits of precision and rounding RNDD
norm()

Return the norm of this complex number.

If \(c = a + bi\) is a complex number, then the norm of \(c\) is defined as the product of \(c\) and its complex conjugate:

\[ ext{norm}(c) = ext{norm}(a + bi) = c \cdot \overline{c} = a^2 + b^2.\]

The norm of a complex number is different from its absolute value. The absolute value of a complex number is defined to be the square root of its norm. A typical use of the complex norm is in the integral domain \(\ZZ[i]\) of Gaussian integers, where the norm of each Gaussian integer \(c = a + bi\) is defined as its complex norm.

EXAMPLES:

sage: CIF(2, 1).norm()
5
sage: CIF(1, -2).norm()
5
overlaps(other)

Return True if self and other are intervals with at least one value in common.

EXAMPLES:

sage: CIF(0).overlaps(CIF(RIF(0, 1), RIF(-1, 0)))
True
sage: CIF(1).overlaps(CIF(1, 1))
False
plot(pointsize=10, **kwds)

Plot a complex interval as a rectangle.

EXAMPLES:

sage: sum(plot(CIF(RIF(1/k, 1/k), RIF(-k, k))) for k in [1..10])
Graphics object consisting of 20 graphics primitives

Exact and nearly exact points are still visible:

sage: plot(CIF(pi, 1), color='red') + plot(CIF(1, e), color='purple') + plot(CIF(-1, -1))
Graphics object consisting of 6 graphics primitives

A demonstration that \(z \mapsto z^2\) acts chaotically on \(|z|=1\):

sage: z = CIF(0, 2*pi/1000).exp()
sage: g = Graphics()
sage: for i in range(40):
....:     z = z^2
....:     g += z.plot(color=(1./(40-i), 0, 1))
...
sage: g
Graphics object consisting of 80 graphics primitives
prec()

Return precision of this complex number.

EXAMPLES:

sage: i = ComplexIntervalField(2000).0
sage: i.prec()
2000
real()

Return real part of self.

EXAMPLES:

sage: i = ComplexIntervalField(100).0
sage: z = 2 + 3*i
sage: x = z.real(); x
2
sage: x.parent()
Real Interval Field with 100 bits of precision
sin()

Compute the sine of this complex interval.

EXAMPLES:

sage: CIF(1,1).sin()
1.298457581415978? + 0.634963914784736?*I
sage: CIF(2).sin()
0.909297426825682?
sage: CIF(0,2).sin()
3.626860407847019?*I

Check that trac ticket #17825 is fixed:

sage: CIF(sin(2/3))
0.618369803069737?

ALGORITHM:

The implementation uses the following trigonometric identity

\[\sin(x + iy) = \sin(x) \cosh(y) + i \cos (x) \sinh(y)\]
sinh()

Return the hyperbolic sine of this complex interval.

EXAMPLES:

sage: CIF(1,1).sinh()
0.634963914784736? + 1.298457581415978?*I
sage: CIF(2).sinh()
3.626860407847019?
sage: CIF(0,2).sinh()
0.909297426825682?*I

ALGORITHM:

The implementation uses the following trigonometric identity

\[\sinh(x+iy) = \cos(y) \sinh(x) + i \sin(y) \cosh(x)\]
sqrt(all=False, **kwds)

The square root function.

Warning

We approximate the standard branch cut along the negative real axis, with sqrt(-r^2) = i*r for positive real r; but if the interval crosses the negative real axis, we pick the root with positive imaginary component for the entire interval.

INPUT:

  • all – bool (default: False); if True, return a list of all square roots.

EXAMPLES:

sage: CIF(-1).sqrt()^2
-1
sage: sqrt(CIF(2))
1.414213562373095?
sage: sqrt(CIF(-1))
1*I
sage: sqrt(CIF(2-I))^2
2.00000000000000? - 1.00000000000000?*I
sage: CC(-2-I).sqrt()^2
-2.00000000000000 - 1.00000000000000*I

Here, we select a non-principal root for part of the interval, and violate the standard interval guarantees:

sage: CIF(-5, RIF(-1, 1)).sqrt().str(style='brackets')
'[-0.22250788030178321 .. 0.22250788030178296] + [2.2251857651053086 .. 2.2581008643532262]*I'
sage: CIF(-5, -1).sqrt()
0.222507880301783? - 2.247111425095870?*I
str(base=10, style=None)

Return a string representation of self.

EXAMPLES:

sage: CIF(1.5).str()
'1.5000000000000000?'
sage: CIF(1.5, 2.5).str()
'1.5000000000000000? + 2.5000000000000000?*I'
sage: CIF(1.5, -2.5).str()
'1.5000000000000000? - 2.5000000000000000?*I'
sage: CIF(0, -2.5).str()
'-2.5000000000000000?*I'
sage: CIF(1.5).str(base=3)
'1.1111111111111111111111111111111112?'
sage: CIF(1, pi).str(style='brackets')
'[1.0000000000000000 .. 1.0000000000000000] + [3.1415926535897931 .. 3.1415926535897936]*I'

See also

  • RealIntervalFieldElement.str()

tan()

Return the tangent of this complex interval.

EXAMPLES:

sage: CIF(1,1).tan()
0.27175258531952? + 1.08392332733870?*I
sage: CIF(2).tan()
-2.185039863261519?
sage: CIF(0,2).tan()
0.964027580075817?*I
tanh()

Return the hyperbolic tangent of this complex interval.

EXAMPLES:

sage: CIF(1,1).tanh()
1.08392332733870? + 0.27175258531952?*I
sage: CIF(2).tanh()
0.964027580075817?
sage: CIF(0,2).tanh()
-2.185039863261519?*I
union(other)

Return the smallest complex interval including the two complex intervals self and other.

EXAMPLES:

sage: CIF(0).union(CIF(5, 5)).str(style='brackets')
'[0.0000000000000000 .. 5.0000000000000000] + [0.0000000000000000 .. 5.0000000000000000]*I'
zeta(a=None)

Return the image of this interval by the Hurwitz zeta function.

For a = 1 (or a = None), this computes the Riemann zeta function.

EXAMPLES:

sage: zeta(CIF(2, 3))
0.7980219851462757? - 0.1137443080529385?*I
sage: _.parent()
Complex Interval Field with 53 bits of precision
sage: CIF(2, 3).zeta(1/2)
-1.955171567161496? + 3.123301509220897?*I
sage.rings.complex_interval.create_ComplexIntervalFieldElement(s_real, s_imag=None, pad=0, min_prec=53)

Return the complex number defined by the strings s_real and s_imag as an element of ComplexIntervalField(prec=n), where \(n\) potentially has slightly more (controlled by pad) bits than given by \(s\).

INPUT:

  • s_real – a string that defines a real number (or something whose string representation defines a number)

  • s_imag – a string that defines a real number (or something whose string representation defines a number)

  • pad – an integer at least 0.

  • min_prec – number will have at least this many bits of precision, no matter what.

EXAMPLES:

sage: ComplexIntervalFieldElement('2.3')
2.300000000000000?
sage: ComplexIntervalFieldElement('2.3','1.1')
2.300000000000000? + 1.100000000000000?*I
sage: ComplexIntervalFieldElement(10)
10
sage: ComplexIntervalFieldElement(10,10)
10 + 10*I
sage: ComplexIntervalFieldElement(1.000000000000000000000000000,2)
1 + 2*I
sage: ComplexIntervalFieldElement(1,2.000000000000000000000)
1 + 2*I
sage: ComplexIntervalFieldElement(1.234567890123456789012345, 5.4321098654321987654321)
1.234567890123456789012350? + 5.432109865432198765432000?*I
sage.rings.complex_interval.is_ComplexIntervalFieldElement(x)

Check if x is a ComplexIntervalFieldElement.

EXAMPLES:

sage: from sage.rings.complex_interval import is_ComplexIntervalFieldElement as is_CIFE
sage: is_CIFE(CIF(2))
True
sage: is_CIFE(CC(2))
False
sage.rings.complex_interval.make_ComplexIntervalFieldElement0(fld, re, im)

Construct a ComplexIntervalFieldElement for pickling.