Source code for ComplexMath
"""The WaveBlocks Project
Some selected functions for complex math.
@author: R. Bourquin
@copyright: Copyright (C) 2011, 2012, 2015 R. Bourquin
@license: Modified BSD License
"""
from numpy import array, hstack, cumsum, diff, around, abs, angle, exp, sqrt, pi, squeeze
__all__ = ["continuate", "cont_angle", "cont_sqrt"]
[docs]def continuate(data, jump=2.0 * pi, reference=0.0):
r"""Make the given data continuous by removing all jumps of size :math:`k\cdot\text{jump}`
but not touching jumps of any other size. This can be used to overcome issues with
the branch cut along the negative axis. There may be arise problems with jumps that
are of size nearly :math:`k\cdot\text{jump}`.
:param data: An array with the input data.
:param jump: The basic size of jumps which will be removed. Default is :math:`2 \pi`.
:param reference: This value allows the specify the starting point for continuation
explicitly. It can be used together with ``data``.
:type reference: A single float number.
"""
# TODO: Maybe replace with 'unwrap' from numpy?
assert squeeze(reference).ndim == 0, "The 'reference' must be a scalar value."
data = hstack([array(reference), array(data).reshape(-1)])
offsets = cumsum(around(diff(data) / (1.0 * jump)))
return (data - jump * hstack([0.0, offsets]))[1:]
[docs]def cont_angle(data, reference=None):
r"""Compute the angle of a complex number *not* constrained to
the principal value and avoiding discontinuities at the branch cut.
This function just applies 'continuate(.)' to the complex phase.
:param data: An array with the input data.
:param reference: This value allows the specify the starting point for
continuation explicitly. It can be used together with ``data``.
:type reference: A single float number.
"""
if reference is None:
# Return just cont_f(x)
return continuate(angle(data))
else:
# Return a 2-tuple (cont_f(x), new_reference)
result = continuate(angle(data), reference=reference)
reference = result
return result, reference
[docs]def cont_sqrt(data, reference=None):
r"""Compute the complex square root (following the Riemann surface)
yields a result *not* constrained to the principal value and avoiding
discontinuities at the branch cut. This function applies 'continuate(.)'
to the complex phase and computes for
:math:`z = r \exp \left(i \phi \right)` the complex square root
its square root according to the formula
:math:`\sqrt{z} = \sqrt{r} \exp \left(i \frac{\phi}{2} \right)`.
:param data: An array with the input data.
:param reference: This value allows the specify the starting point for
continuation explicitly. It can be used together with ``data``.
:type reference: A single float number.
"""
if reference is None:
# Return just cont_f(x)
return sqrt(abs(data)) * exp(0.5j * continuate(angle(data)))
else:
# Return a 2-tuple (cont_f(x), new_reference)
phi = continuate(angle(data), reference=reference)
result = sqrt(abs(data)) * exp(0.5j * phi)
reference = phi[0]
# TODO: Rethink what 'reference' to return for an array of input values.
return result, reference
class ContinuousSqrt(object):
r"""Class for computing continuous square roots.
All implementation details about referencing is
hidden from the user. The class is side-effect free.
"""
def __init__(self, reference=0.0j):
self.set(reference)
def clone(self):
return ContinuousSqrt(reference=self._reference)
def __call__(self, radicand):
# Updating the reference is idempotent for identical radicands.
root, reference = cont_sqrt(radicand, reference=self._reference)
self._reference = reference
return root
def set(self, reference):
assert squeeze(reference).ndim == 0, "The 'reference' must be a scalar value."
self._reference = array(reference)
def get(self):
return self._reference.copy()