Source code for calcium

"""
Calcium includes a simple Python interface implemented using ``ctypes``.

Introduction
------------------------------------------------------------------------

Setup and usage
..................

Make sure the Calcium library and its dependencies are built
and in the path of the system's dynamic library loader.
Then make sure that ``<calcium_source_dir>/pycalcium`` is in the
Python path, for example by adding it to ``PYTHONPATH``,
adding it to ``sys.path``,
or simply starting Python inside the ``pycalcium`` directory.

Import the module and run perform a calculation:

    >>> import calcium
    >>> calcium.ca(1) / 3
    0.333333 {1/3}
    >>> calcium.exp(calcium.pi * calcium.i / 2)
    1.00000*I {a where a = I [a^2+1=0]}

If you don't mind polluting the global namespace, import everything:

    >>> from calcium import *
    >>> exp(pi*i/2) + ca(1)/3
    0.333333 + 1.00000*I {(3*a+1)/3 where a = I [a^2+1=0]}

Current limitations
.....................

* Does not support creating new context objects or modifying
  the settings of a context object.
* Leaks memory (for example, when printing).
* Because ``ctypes`` is used, this is not as efficient as a
  Cython wrapper. This interface should be used for testing
  and not for absolute performance.
* Does not wrap various functions.

API documentation
------------------------------------------------------------------------

Functions are available both as regular functions and as methods
on the ``ca`` class.

"""

import ctypes
import ctypes.util
import sys

libcalcium_path = ctypes.util.find_library('calcium')
libcalcium = ctypes.CDLL(libcalcium_path)

libarb_path = ctypes.util.find_library('arb')
libarb = ctypes.CDLL(libarb_path)

libflint_path = ctypes.util.find_library('flint')
libflint = ctypes.CDLL(libflint_path)

T_TRUE = 0
T_FALSE = 1
T_UNKNOWN = 2

class _fmpz_struct(ctypes.Structure):
    _fields_ = [('val', ctypes.c_long)]

# ctypes.cast(x, ctypes.POINTER(ctypes.c_ulong))
# ctypes.POINTER(ctypes.c_ulong)

[docs]class ca_struct(ctypes.Structure): """Low-level wrapper for ca_struct, for internal use by ctypes.""" _fields_ = [('data', ctypes.c_long * 5)]
[docs]class ca_ctx_struct(ctypes.Structure): """Low-level wrapper for ca_ctx_struct, for internal use by ctypes.""" # todo: use the real size _fields_ = [('content', ctypes.c_ulong * 64)]
[docs]class ca_mat_struct(ctypes.Structure): """Low-level wrapper for ca_mat_struct, for internal use by ctypes.""" _fields_ = [('entries', ctypes.c_void_p), ('r', ctypes.c_long), ('c', ctypes.c_long), ('rows', ctypes.c_void_p)]
[docs]class ca_vec_struct(ctypes.Structure): """Low-level wrapper for ca_vec_struct, for internal use by ctypes.""" _fields_ = [('entries', ctypes.c_void_p), ('length', ctypes.c_long), ('alloc', ctypes.c_long)]
[docs]class ca_poly_struct(ctypes.Structure): """Low-level wrapper for ca_poly_struct, for internal use by ctypes.""" _fields_ = [('coeffs', ctypes.c_void_p), ('length', ctypes.c_long), ('alloc', ctypes.c_long)]
[docs]class ca_poly_vec_struct(ctypes.Structure): """Low-level wrapper for ca_poly_vec_struct, for internal use by ctypes.""" _fields_ = [('entries', ctypes.POINTER(ca_poly_struct)), ('length', ctypes.c_long), ('alloc', ctypes.c_long)]
[docs]class ca_ctx: """ Python class wrapping the ca_ctx_t context object. Currently only supports a global instance. """
[docs] def __init__(self): self._data = ca_ctx_struct() self._ref = ctypes.byref(self._data) libcalcium.ca_ctx_init(self._ref)
def __del__(self): # Python calls ctx_default.__del__ before all ca instances are # destroyed, despite the fact that the ca instances contain # references to ctx_default. # WHY?????????????? # libcalcium.ca_ctx_clear(self._ref) pass @property def _as_parameter_(self): return self._ref
[docs] @staticmethod def from_param(arg): return arg
ctx_default = ca_ctx()
[docs]class ca: """ Python class wrapping the ca_t type for numbers. Examples:: >>> ca(1) 1 >>> ca() 0 >>> ca(0) 0 >>> ca(-5) -5 >>> ca(2.25) 2.25000 {9/4} >>> ca(1) / 3 0.333333 {1/3} >>> (-1) ** ca(0.5) 1.00000*I {a where a = I [a^2+1=0]} >>> ca(1-2j) 1.00000 - 2.00000*I {-2*a+1 where a = I [a^2+1=0]} >>> ca(0.1) # be careful with float input! 0.100000 {3602879701896397/36028797018963968} >>> ca(float("inf")) +Infinity >>> ca(float("nan")) Unknown >>> 3 < pi < ca(22)/7 True """
[docs] def __init__(self, val=0): self._ctx_python = ctx_default self._ctx = self._ctx_python._ref self._data = ca_struct() self._ref = ctypes.byref(self._data) libcalcium.ca_init(self, self._ctx) if val is not 0: typ = type(val) if typ is int: b = sys.maxsize if -b <= val <= b: libcalcium.ca_set_si(self, val, self._ctx) else: n = _fmpz_struct() nref = ctypes.byref(n) libflint.fmpz_init(nref) libflint.fmpz_set_str(nref, ctypes.c_char_p(str(val).encode('ascii')), 10) libcalcium.ca_set_fmpz(self, nref, self._ctx) libflint.fmpz_clear(nref) elif typ is ca: libcalcium.ca_set(self, val, self._ctx) elif typ is float: libcalcium.ca_set_d(self, val, self._ctx) elif typ is complex: libcalcium.ca_set_d_d(self, val.real, val.imag, self._ctx) else: raise TypeError
def __del__(self): libcalcium.ca_clear(self, self._ctx) @property def _as_parameter_(self): return self._ref
[docs] @staticmethod def from_param(arg): return arg
[docs] @staticmethod def inf(): """ The special value positive infinity. Examples:: >>> inf == ca.inf() # alias for the method True >>> inf +Infinity >>> -inf -Infinity >>> abs(-inf) +Infinity >>> inf + inf +Infinity >>> (-inf) + (-inf) -Infinity >>> -inf * inf -Infinity >>> inf / inf Undefined >>> 1 / inf 0 >>> re(inf) Undefined >>> im(inf) Undefined >>> sign(inf) 1 >>> sign((1+i)*inf) == (1+i)/sqrt(2) True """ res = ca() libcalcium.ca_pos_inf(res, res._ctx) return res
[docs] @staticmethod def uinf(): """ The special value unsigned infinity. Examples:: >>> uinf == ca.uinf() # alias for the method True >>> uinf UnsignedInfinity >>> abs(uinf) +Infinity >>> -3 * uinf UnsignedInfinity >>> 1/uinf 0 >>> sign(uinf) Undefined >>> uinf * uinf UnsignedInfinity >>> uinf / uinf Undefined >>> uinf + uinf Undefined >>> re(uinf) Undefined >>> im(uinf) Undefined """ res = ca() libcalcium.ca_uinf(res, res._ctx) return res
[docs] @staticmethod def undefined(): """ The special value undefined. Examples:: >>> undefined == ca.undefined() # alias for the method True >>> undefined Undefined >>> undefined == undefined True >>> undefined * 0 Undefined """ res = ca() libcalcium.ca_undefined(res, res._ctx) return res
[docs] @staticmethod def unknown(): """ The special meta-value unknown. This meta-value is not comparable. Examples:: >>> unknown == unknown Traceback (most recent call last): ... ValueError: unable to decide predicate: equal >>> unknown == 0 Traceback (most recent call last): ... ValueError: unable to decide predicate: equal >>> unknown == undefined Traceback (most recent call last): ... ValueError: unable to decide predicate: equal >>> unknown + unknown Unknown >>> unknown + 3 Unknown >>> unknown + undefined Undefined """ res = ca() libcalcium.ca_unknown(res, res._ctx) return res
[docs] @staticmethod def pi(): """ The constant pi. Examples:: >>> pi == ca.pi() # alias for the method True >>> pi 3.14159 {a where a = 3.14159 [Pi]} >>> sin(pi/6) 0.500000 {1/2} >>> (pi - 3) ** 3 0.00283872 {a^3-9*a^2+27*a-27 where a = 3.14159 [Pi]} """ res = ca() libcalcium.ca_pi(res, res._ctx) return res
[docs] @staticmethod def euler(): """ Euler's constant (gamma). Examples:: >>> euler == ca.euler() # alias for the method True >>> euler 0.577216 {a where a = 0.577216 [Euler]} >>> exp(euler) 1.78107 {a where a = 1.78107 [Exp(0.577216 {b})], b = 0.577216 [Euler]} """ res = ca() libcalcium.ca_euler(res, res._ctx) return res
[docs] @staticmethod def i(): """ The imaginary unit. Examples:: >>> i == ca.i() # alias for the method True >>> i == I == j # extra aliases for convenience True >>> i 1.00000*I {a where a = I [a^2+1=0]} >>> i**2 -1 >>> i**3 -1.00000*I {-a where a = I [a^2+1=0]} >>> abs(i) 1 >>> sign(i) 1.00000*I {a where a = I [a^2+1=0]} >>> abs(sqrt(1+i) / sqrt(1-i)) 1 >>> re(i), im(i) (0, 1) """ res = ca() libcalcium.ca_i(res, res._ctx) return res
def __repr__(self): s = libcalcium.ca_get_str(self, self._ctx) res = str(s, 'ascii') #libflint.flint_free(s) return res def __str__(self): return self.__repr__()
[docs] def __bool__(self): t = libcalcium.ca_check_is_zero(self, self._ctx) if t == T_TRUE: return False if t == T_FALSE: return True raise ValueError("unable to decide predicate: is_zero")
def __eq__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented t = libcalcium.ca_check_equal(self, other, self._ctx) if t == T_TRUE: return True if t == T_FALSE: return False raise ValueError("unable to decide predicate: equal") def __ne__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented t = libcalcium.ca_check_equal(self, other, self._ctx) if t == T_TRUE: return False if t == T_FALSE: return True raise ValueError("unable to decide predicate: equal") def __le__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented t = libcalcium.ca_check_le(self, other, self._ctx) if t == T_TRUE: return True if t == T_FALSE: return False raise ValueError("unable to decide predicate: le") def __lt__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented t = libcalcium.ca_check_lt(self, other, self._ctx) if t == T_TRUE: return True if t == T_FALSE: return False raise ValueError("unable to decide predicate: lt") def __ge__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented t = libcalcium.ca_check_ge(self, other, self._ctx) if t == T_TRUE: return True if t == T_FALSE: return False raise ValueError("unable to decide predicate: ge") def __gt__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented t = libcalcium.ca_check_gt(self, other, self._ctx) if t == T_TRUE: return True if t == T_FALSE: return False raise ValueError("unable to decide predicate: gt") def __add__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_add(res, self, other, self._ctx) return res __radd__ = __add__ def __sub__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_sub(res, self, other, self._ctx) return res def __rsub__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_sub(res, other, self, self._ctx) return res def __mul__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_mul(res, self, other, self._ctx) return res __rmul__ = __mul__ def __truediv__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_div(res, self, other, self._ctx) return res def __rtruediv__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_div(res, other, self, self._ctx) return res def __floordiv__(self, other): return (self / other).floor() def __rfloordiv__(self, other): return (other / self).floor() def __pow__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_pow(res, self, other, self._ctx) return res def __rpow__(self, other): if type(self) is not type(other): try: other = ca(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_pow(res, other, self, self._ctx) return res def __abs__(self): res = ca() libcalcium.ca_abs(res, self, self._ctx) return res def __neg__(self): res = ca() libcalcium.ca_neg(res, self, self._ctx) return res def __pos__(self): res = ca() libcalcium.ca_set(res, self, self._ctx) return res
[docs] def re(self): """ Real part. Examples:: >>> re(2+3j) == ca(2+3j).re() True >>> re(2+3*i) 2 """ res = ca() libcalcium.ca_re(res, self, self._ctx) return res
[docs] def im(self): """ Imaginary part. Examples:: >>> im(2+3j) == ca(2+3j).im() # alias for the method True >>> im(2+3*i) 3 """ res = ca() libcalcium.ca_im(res, self, self._ctx) return res
[docs] def conj(self): """ Complex conjugate. Examples:: >>> conj(1j) == conjugate(1j) == ca(1j).conj() == ca(1j).conjugate() # alias for the method True >>> conj(2+i) 2.00000 - 1.00000*I {-a+2 where a = I [a^2+1=0]} >>> conj(pi) 3.14159 {a where a = 3.14159 [Pi]} """ res = ca() libcalcium.ca_conjugate(res, self, self._ctx) return res
conjugate = conj
[docs] def floor(self): """ Floor function. Examples:: >>> floor(3) == ca(3).floor() # alias for the method True >>> floor(pi) 3 >>> floor(-pi) -4 """ res = ca() libcalcium.ca_floor(res, self, self._ctx) return res
[docs] def ceil(self): """ Ceiling function. Examples:: >>> ceil(3) == ca(3).ceil() # alias for the method True >>> ceil(pi) 4 >>> ceil(-pi) -3 """ res = ca() libcalcium.ca_ceil(res, self, self._ctx) return res
[docs] def sgn(self): """ Sign function. Examples:: >>> sgn(2) == sign(2) == ca(2).sgn() # aliases for the method True >>> sign(0) 0 >>> sign(sqrt(2)) 1 >>> sign(-sqrt(2)) -1 >>> sign(-sqrt(-2)) -1.00000*I {-a where a = I [a^2+1=0]} """ res = ca() libcalcium.ca_sgn(res, self, self._ctx) return res
sign = sgn
[docs] def sqrt(self): """ Principal square root. Examples:: >>> sqrt(2) == ca(2).sqrt() # alias for the method True >>> sqrt(0) 0 >>> sqrt(1) 1 >>> sqrt(2) 1.41421 {a where a = 1.41421 [a^2-2=0]} >>> sqrt(-1) 1.00000*I {a where a = I [a^2+1=0]} >>> sqrt(inf) +Infinity >>> sqrt(-inf) +I * Infinity >>> sqrt(uinf) UnsignedInfinity >>> sqrt(undefined) Undefined >>> sqrt(unknown) Unknown """ res = ca() libcalcium.ca_sqrt(res, self, self._ctx) return res
[docs] def log(self): """ Natural logarithm. Examples:: >>> log(2) == ca(2).log() # alias for the method True >>> log(1) 0 >>> log(exp(2)) 2 >>> log(2) 0.693147 {a where a = 0.693147 [Log(2)]} >>> log(4) == 2*log(2) True >>> log(1+sqrt(2)) / log(3+2*sqrt(2)) 0.500000 {1/2} >>> log(ca(10)**(10**30)) / log(10) 1.00000e+30 {1000000000000000000000000000000} >>> log(-1) 3.14159*I {a where a = 3.14159*I [Log(-1)]} >>> log(i) 1.57080*I {(a*b)/2 where a = 3.14159 [Pi], b = I [b^2+1=0]} >>> log(0) -Infinity >>> log(inf) +Infinity >>> log(-inf) +Infinity >>> log(uinf) +Infinity >>> log(undefined) Undefined >>> log(unknown) Unknown """ res = ca() libcalcium.ca_log(res, self, self._ctx) return res
[docs] def exp(self): """ Exponential function. Examples:: >>> exp(0) 1 >>> exp(1) 2.71828 {a where a = 2.71828 [Exp(1)]} >>> exp(-1) 0.367879 {a where a = 0.367879 [Exp(-1)]} >>> exp(-1) * exp(1) 1 >>> exp(7*pi*i/2) -1.00000*I {-a where a = I [a^2+1=0]} >>> exp(inf) +Infinity >>> exp(-inf) 0 >>> exp(uinf) Undefined >>> exp(undefined) Undefined >>> exp(unknown) Unknown """ res = ca() libcalcium.ca_exp(res, self, self._ctx) return res
[docs] def erf(self): """ Error function. Examples:: >>> erf(0) 0 >>> erf(1) 0.842701 {a where a = 0.842701 [Erf(1)]} >>> erf(inf) 1 >>> erf(-inf) -1 >>> erf(i*inf) +I * Infinity >>> erf(-i*inf) -I * Infinity >>> erf(uinf) Undefined """ res = ca() libcalcium.ca_erf(res, self, self._ctx) return res
[docs] def erfc(self): """ Complementary error function. Examples:: >>> erfc(inf) 0 >>> erfc(-inf) 2 >>> erfc(1000) 1.86004e-434298 {a where a = 1.86004e-434298 [Erfc(1000)]} >>> erfc(i*inf) -I * Infinity >>> erfc(-i*inf) +I * Infinity >>> erfc(sqrt(2)) + erf(sqrt(2)) 1 >>> erfc(uinf) Undefined """ res = ca() libcalcium.ca_erfc(res, self, self._ctx) return res
[docs] def erfi(self): """ Imaginary error function. Examples:: >>> erfi(0) 0 >>> erfi(1) 1.65043 {a where a = 1.65043 [Erfi(1)]} >>> erfi(inf) +Infinity >>> erfi(-inf) -Infinity >>> erfi(i*inf) 1.00000*I {a where a = I [a^2+1=0]} >>> erfi(-i*inf) -1.00000*I {-a where a = I [a^2+1=0]} >>> erf(2)**2 + erfi(i*2)**2 0 """ res = ca() libcalcium.ca_erfi(res, self, self._ctx) return res
[docs] def gamma(self): """ Gamma function. Examples:: >>> [gamma(n) for n in range(1,11)] [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] >>> gamma(0) UnsignedInfinity >>> 1 / gamma(0) 0 >>> gamma(0.5) 1.77245 {a where a = 1.77245 [Sqrt(3.14159 {b})], b = 3.14159 [Pi]} >>> gamma(0.5)**2 == pi True >>> pi * gamma(pi) / gamma(pi+1) 1 """ res = ca() libcalcium.ca_gamma(res, self, self._ctx) return res
[docs]class ca_mat: """ Python class wrapping the ca_mat_t type for matrices. Examples:: >>> ca_mat(2,3) ca_mat of size 2 x 3 [0, 0, 0] [0, 0, 0] >>> ca_mat([[1,2],[3,4],[5,6]]) ca_mat of size 3 x 2 [1, 2] [3, 4] [5, 6] >>> ca_mat(2, 5, range(10)) ca_mat of size 2 x 5 [0, 1, 2, 3, 4] [5, 6, 7, 8, 9] >>> ca_mat([[1,-2],[2,1]]) * ca_mat([[1,pi],[1,2]]) ca_mat of size 2 x 2 [-1, -0.858407 {a-4 where a = 3.14159 [Pi]}] [ 3, 8.28319 {2*a+2 where a = 3.14159 [Pi]}] A nontrivial calculation:: >>> H = ca_mat([[ca(1)/(i+j+1) for i in range(5)] for j in range(5)]) >>> H ca_mat of size 5 x 5 [ 1, 0.500000 {1/2}, 0.333333 {1/3}, 0.250000 {1/4}, 0.200000 {1/5}] [0.500000 {1/2}, 0.333333 {1/3}, 0.250000 {1/4}, 0.200000 {1/5}, 0.166667 {1/6}] [0.333333 {1/3}, 0.250000 {1/4}, 0.200000 {1/5}, 0.166667 {1/6}, 0.142857 {1/7}] [0.250000 {1/4}, 0.200000 {1/5}, 0.166667 {1/6}, 0.142857 {1/7}, 0.125000 {1/8}] [0.200000 {1/5}, 0.166667 {1/6}, 0.142857 {1/7}, 0.125000 {1/8}, 0.111111 {1/9}] >>> H.trace() 1.78730 {563/315} >>> sum(c*multiplicity for (c, multiplicity) in H.eigenvalues()) 1.78730 {563/315} >>> H.det() 3.74930e-12 {1/266716800000} >>> prod(c**multiplicity for (c, multiplicity) in H.eigenvalues()) 3.74930e-12 {1/266716800000} """
[docs] def __init__(self, *args): self._ctx_python = ctx_default self._ctx = self._ctx_python._ref self._data = ca_mat_struct() self._ref = ctypes.byref(self._data) if len(args) == 1: val = args[0] if isinstance(val, ca_mat): m = val.nrows() n = val.ncols() libcalcium.ca_mat_init(self, m, n, self._ctx) libcalcium.ca_mat_set(self, val, self._ctx) elif isinstance(val, (list, tuple)): m = len(val) n = 0 if m != 0: if not isinstance(val[0], (list, tuple)): raise TypeError("single input to ca_mat must be a list of lists") n = len(val[0]) for i in range(1, m): if len(val[i]) != n: raise ValueError("input rows have different lengths") libcalcium.ca_mat_init(self, m, n, self._ctx) for i in range(m): row = val[i] for j in range(n): x = ca(row[j]) libcalcium.ca_set(libcalcium.ca_mat_entry_ptr(self, i, j, self._ctx), x, self._ctx) else: raise TypeError("cannot create ca_mat from input of type %s" % type(val)) elif len(args) == 2: m, n = args assert m >= 0 assert n >= 0 libcalcium.ca_mat_init(self, m, n, self._ctx) elif len(args) == 3: m, n, entries = args assert m >= 0 assert n >= 0 libcalcium.ca_mat_init(self, m, n, self._ctx) entries = list(entries) if len(entries) != m*n: raise ValueError("list of entries has the wrong length") for i in range(m): for j in range(n): x = ca(entries[i*n + j]) libcalcium.ca_set(libcalcium.ca_mat_entry_ptr(self, i, j, self._ctx), x, self._ctx) else: raise ValueError("ca_mat: expected 1-3 arguments")
def __del__(self): libcalcium.ca_mat_clear(self, self._ctx) @property def _as_parameter_(self): return self._ref
[docs] @staticmethod def from_param(arg): return arg
[docs] def __bool__(self): t = libcalcium.ca_mat_check_is_zero(self, self._ctx) if t == T_TRUE: return False if t == T_FALSE: return True raise ValueError("unable to decide predicate: is_zero")
def __eq__(self, other): """ Examples:: >>> ca_mat([[1,1],[0,1]]) ** 2 == ca_mat([[1,2],[0,1]]) True """ if type(self) is not type(other): try: other = ca_mat(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = libcalcium.ca_mat_check_equal(self, other, self._ctx) if res == T_TRUE: return True if res == T_FALSE: return False raise ValueError("unable to decide equality") def __ne__(self, other): """ Examples:: >>> ca_mat([[1,1],[0,1]]) ** 2 != ca_mat([[1,3],[0,1]]) True """ if type(self) is not type(other): try: other = ca_mat(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = libcalcium.ca_mat_check_equal(self, other, self._ctx) if res == T_TRUE: return False if res == T_FALSE: return True raise ValueError("unable to decide equality")
[docs] def nrows(self): return self._data.r
[docs] def ncols(self): return self._data.c
[docs] def entries(self): m = self.nrows() n = self.ncols() L = [None] * (m * n) for i in range(m): for j in range(n): L[i*n + j] = self[i, j] return L
def __iter__(self): m = self.nrows() n = self.ncols() for i in range(m): for j in range(n): yield self[i, j]
[docs] def table(self): m = self.nrows() n = self.ncols() L = self.entries() return [L[i*n : (i+1)*n] for i in range(m)]
# supports mpmath conversions tolist = table def __repr__(self): s = "ca_mat of size %i x %i\n" % (self.nrows(), self.ncols()) nrows = self.nrows() ncols = self.ncols() def matrix_to_str(tab): if len(tab) == 0 or len(tab[0]) == 0: return "[]" tab = [[str(c) for c in row] for row in tab] widths = [] for i in range(len(tab[0])): w = max([len(row[i]) for row in tab]) widths.append(w) for i in range(len(tab)): tab[i] = [s.rjust(widths[j]) for j, s in enumerate(tab[i])] tab[i] = "[" + (", ".join(tab[i])) + "]" return "\n".join(tab) return s + matrix_to_str(self.table()) __str__ = __repr__ def __getitem__(self, ij): i, j = ij nrows = self.nrows() ncols = self.ncols() assert 0 <= i < nrows assert 0 <= j < ncols x = ca() libcalcium.ca_set(x, libcalcium.ca_mat_entry_ptr(self, i, j, self._ctx), self._ctx) return x def __setitem__(self, ij, x): i, j = ij nrows = self.nrows() ncols = self.ncols() assert 0 <= i < nrows assert 0 <= j < ncols x = ca(x) libcalcium.ca_set(libcalcium.ca_mat_entry_ptr(self, i, j, self._ctx), x, self._ctx)
[docs] def trace(self): """ The trace of this matrix. Examples:: >>> ca_mat([[1,2],[3,pi]]).trace() 4.14159 {a+1 where a = 3.14159 [Pi]} """ nrows = self.nrows() ncols = self.ncols() if nrows != ncols: raise ValueError("a square matrix is required") res = ca() libcalcium.ca_mat_trace(res, self, self._ctx) return res
[docs] def det(self): """ The determinant of this matrix. Examples:: >>> ca_mat([[1,1-i*pi],[1+i*pi,1]]).det() -9.86960 {-a^2 where a = 3.14159 [Pi], b = I [b^2+1=0]} """ nrows = self.nrows() ncols = self.ncols() if nrows != ncols: raise ValueError("a square matrix is required") res = ca() libcalcium.ca_mat_det(res, self, self._ctx) return res
def __neg__(self): res = ca_mat(self.nrows(), self.ncols()) libcalcium.ca_mat_neg(res, self, self._ctx) return res def __pos__(self): res = ca_mat(self.nrows(), self.ncols()) libcalcium.ca_mat_set(res, self, self._ctx) return res def __add__(self, other): if type(self) is not type(other): try: other = ca_mat(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") m = self.nrows() n = self.ncols() if m != other.nrows() or n != other.ncols(): raise ValueError("incompatible matrix shapes") res = ca_mat(m, n) libcalcium.ca_mat_add(res, self, other, self._ctx) return res def __sub__(self, other): if type(self) is not type(other): try: other = ca_mat(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") m = self.nrows() n = self.ncols() if m != other.nrows() or n != other.ncols(): raise ValueError("incompatible matrix shapes") res = ca_mat(m, n) libcalcium.ca_mat_sub(res, self, other, self._ctx) return res def __mul__(self, other): if type(self) is not type(other): try: other = ca(other) if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") m = self.nrows() n = self.ncols() res = ca_mat(m, n) libcalcium.ca_mat_mul_ca(res, self, other, self._ctx) return res except TypeError: pass try: other = ca_mat(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") m = self.nrows() n = self.ncols() k = other.ncols() if n != other.nrows(): raise ValueError("incompatible matrix shapes") res = ca_mat(m, k) libcalcium.ca_mat_mul(res, self, other, self._ctx) return res def __rmul__(self, other): try: other = ca(other) if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") m = self.nrows() n = self.ncols() res = ca_mat(m, n) libcalcium.ca_mat_mul_ca(res, self, other, self._ctx) return res except TypeError: pass return NotImplemented def __truediv__(self, other): try: other = ca(other) if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") m = self.nrows() n = self.ncols() res = ca_mat(m, n) libcalcium.ca_mat_div_ca(res, self, other, self._ctx) return res except TypeError: pass return NotImplemented def __pow__(self, other): m = self.nrows() n = self.ncols() assert m == n e = int(other) assert 0 <= e <= sys.maxsize res = ca_mat(m, m) libcalcium.ca_mat_pow_ui_binexp(res, self, e, self._ctx) return res
[docs] def conjugate(self): """ Entrywise complex conjugate. >>> ca_mat([[5,1-i]]).conjugate() ca_mat of size 1 x 2 [5, 1.00000 + 1.00000*I {a+1 where a = I [a^2+1=0]}] """ res = ca_mat(self.nrows(), self.ncols()) libcalcium.ca_mat_conjugate(res, self, self._ctx) return res
conj = conjugate
[docs] def transpose(self): """ Matrix transpose. >>> ca_mat([[5,1-i]]).transpose() ca_mat of size 2 x 1 [ 5] [1.00000 - 1.00000*I {-a+1 where a = I [a^2+1=0]}] """ res = ca_mat(self.ncols(), self.nrows()) libcalcium.ca_mat_transpose(res, self, self._ctx) return res
[docs] def conjugate_transpose(self): """ Conjugate matrix transpose. >>> ca_mat([[5,1-i]]).conjugate_transpose() ca_mat of size 2 x 1 [ 5] [1.00000 + 1.00000*I {a+1 where a = I [a^2+1=0]}] """ res = ca_mat(self.ncols(), self.nrows()) libcalcium.ca_mat_conjugate_transpose(res, self, self._ctx) return res
[docs] def charpoly(self): """ Characteristic polynomial of this matrix. >>> ca_mat([[5,pi],[1,-1]]).charpoly() ca_poly of length 3 [-8.14159 {-a-5 where a = 3.14159 [Pi]}, -4, 1] """ m = self.nrows() n = self.ncols() assert m == n res = ca_poly() libcalcium.ca_mat_charpoly(res, self, self._ctx) return res
[docs] def eigenvalues(self): """ Eigenvalues of this matrix. Returns a list of (value, multiplicity) pairs. >>> ca_mat(4, 4, range(16)).eigenvalues() [(-2.46425 {-a+15 where a = 17.4642 [a^2-305=0]}, 1), (32.4642 {a+15 where a = 17.4642 [a^2-305=0]}, 1), (0, 2)] >>> ca_mat([[1,pi],[-pi,1]]).eigenvalues()[0] (1.00000 + 3.14159*I {(a+2)/2 where a = 6.28319*I [Sqrt(-39.4784 {-4*b^2})], b = 3.14159 [Pi]}, 1) """ m = self.nrows() n = self.ncols() assert m == n lamda = ca_vec() exp = ctypes.cast(libflint.flint_malloc(ctypes.sizeof(ctypes.c_ulong) * n), ctypes.POINTER(ctypes.c_ulong)) success = libcalcium.ca_mat_eigenvalues(lamda, exp, self, self._ctx) if not success: libflint.flint_free(exp) raise ValueError("failed to compute eigenvalues") else: res = [(lamda[i], exp[i]) for i in range(len(lamda))] libflint.flint_free(exp) return res
[docs]class ca_vec: """ Python class wrapping the ca_vec_t type for vectors. """
[docs] def __init__(self, n=0): self._ctx_python = ctx_default self._ctx = self._ctx_python._ref self._data = ca_vec_struct() self._ref = ctypes.byref(self._data) n = int(n) assert n >= 0 libcalcium.ca_vec_init(self, n, self._ctx)
def __del__(self): libcalcium.ca_vec_clear(self, self._ctx) @property def _as_parameter_(self): return self._ref
[docs] @staticmethod def from_param(arg): return arg
def __len__(self): return self._data.length def __getitem__(self, i): n = len(self) assert 0 <= i < n x = ca() libcalcium.ca_set(x, libcalcium.ca_vec_entry_ptr(self, i, self._ctx), self._ctx) return x
[docs]class ca_poly_vec:
[docs] def __init__(self, n=0): self._ctx_python = ctx_default self._ctx = self._ctx_python._ref self._data = ca_poly_vec_struct() self._ref = ctypes.byref(self._data) n = int(n) assert n >= 0 libcalcium.ca_poly_vec_init(self, n, self._ctx)
def __del__(self): libcalcium.ca_poly_vec_clear(self, self._ctx) @property def _as_parameter_(self): return self._ref
[docs] @staticmethod def from_param(arg): return arg
def __len__(self): return self._data.length def __getitem__(self, i): n = len(self) assert 0 <= i < n x = ca_poly() libcalcium.ca_poly_set(x, ctypes.byref(self._data.entries[i]), self._ctx) return x
[docs]class ca_poly: """ Python class wrapping the ca_poly_t type for polynomials. """
[docs] def __init__(self, val=0): self._ctx_python = ctx_default self._ctx = self._ctx_python._ref self._data = ca_poly_struct() self._ref = ctypes.byref(self._data) libcalcium.ca_poly_init(self, self._ctx) # todo: check conext objects if type(val) is ca_poly: libcalcium.ca_poly_set(self, val, self._ctx) elif val: try: val = [ca(c) for c in val] for i in range(len(val)): libcalcium.ca_poly_set_coeff_ca(self, i, val[i], self._ctx) except TypeError: val = ca(val) libcalcium.ca_poly_set_ca(self, val, self._ctx)
def __del__(self): libcalcium.ca_poly_clear(self, self._ctx) @property def _as_parameter_(self): return self._ref
[docs] @staticmethod def from_param(arg): return arg
[docs] def __bool__(self): t = libcalcium.ca_poly_check_is_zero(self, self._ctx) if t == T_TRUE: return False if t == T_FALSE: return True raise ValueError("unable to decide predicate: is_zero")
def __eq__(self, other): """ Examples:: >>> ca_poly([1,1]) ** 2 == ca_poly([1,2,1]) True """ if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = libcalcium.ca_poly_check_equal(self, other, self._ctx) if res == T_TRUE: return True if res == T_FALSE: return False raise ValueError("unable to decide equality") def __ne__(self, other): """ Examples:: >>> ca_poly([1,1]) ** 2 != ca_poly([1,3,1]) True """ if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = libcalcium.ca_poly_check_equal(self, other, self._ctx) if res == T_TRUE: return False if res == T_FALSE: return True raise ValueError("unable to decide equality") def __len__(self): return self._data.length def __repr__(self): n = len(self) s = "ca_poly of length %i\n" % n s += str([self[i] for i in range(n)]) return s __str__ = __repr__ def __getitem__(self, i): n = len(self) assert 0 <= i < n x = ca() libcalcium.ca_set(x, libcalcium.ca_poly_coeff_ptr(self, i, self._ctx), self._ctx) return x def __neg__(self): res = ca_poly() libcalcium.ca_poly_neg(res, self, self._ctx) return res def __pos__(self): res = ca_poly() libcalcium.ca_poly_set(res, self, self._ctx) return res def __add__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() libcalcium.ca_poly_add(res, self, other, self._ctx) return res __radd__ = __add__ def __sub__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() libcalcium.ca_poly_sub(res, self, other, self._ctx) return res def __rsub__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() libcalcium.ca_poly_sub(res, other, self, self._ctx) return res def __mul__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() libcalcium.ca_poly_mul(res, self, other, self._ctx) return res __rmul__ = __mul__ def __truediv__(self, other): try: other = ca(other) if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() libcalcium.ca_poly_div_ca(res, self, other, self._ctx) return res except TypeError: pass return NotImplemented def __floordiv__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() if not libcalcium.ca_poly_div(res, self, other, self._ctx): raise ValueError("failed polynomial division: unable to prove leading coefficient nonzero") return res def __mod__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() if not libcalcium.ca_poly_rem(res, self, other, self._ctx): raise ValueError("failed polynomial division: unable to prove leading coefficient nonzero") return res def __divmod__(self, other): if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res1 = ca_poly() res2 = ca_poly() if not libcalcium.ca_poly_divrem(res1, res2, self, other, self._ctx): raise ValueError("failed polynomial division: unable to prove leading coefficient nonzero") return res1, res2 def __pow__(self, other): e = int(other) assert e >= 0 and e * len(self) <= sys.maxsize res = ca_poly() libcalcium.ca_poly_pow_ui(res, self, e, self._ctx) return res def __call__(self, other): """ Evaluation or composition. >>> ca_poly([1,2,3])(pi) 36.8920 {3*a^2+2*a+1 where a = 3.14159 [Pi]} >>> ca_poly([1,2,3])(ca_poly([3,2,1])) ca_poly of length 5 [34, 40, 32, 12, 3] """ try: other = ca(other) if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca() libcalcium.ca_poly_evaluate(res, self, other, self._ctx) return res except TypeError: pass other = ca_poly(other) if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() libcalcium.ca_poly_compose(res, self, other, self._ctx) return res
[docs] def gcd(self, other): """ Polynomial GCD. Examples:: >>> x = ca_poly([0,1]); (x+1).gcd(x-1) ca_poly of length 1 [1] >>> x = ca_poly([0,1]); (x**2 + pi**2).gcd(x+i*pi) ca_poly of length 2 [3.14159*I {a*b where a = 3.14159 [Pi], b = I [b^2+1=0]}, 1] """ if type(self) is not type(other): try: other = ca_poly(other) except TypeError: return NotImplemented if self._ctx_python is not self._ctx_python: raise ValueError("different context objects!") res = ca_poly() if not libcalcium.ca_poly_gcd(res, self, other, self._ctx): raise ValueError("failed polynomial gcd") return res
[docs] def roots(self): """ Roots of this polynomial. Returns a list of (root, multiplicity) pairs. >>> ca_poly([2,11,20,12]).roots() [(-0.666667 {-2/3}, 1), (-0.500000 {-1/2}, 2)] """ n = len(self) roots = ca_vec() exp = ctypes.cast(libflint.flint_malloc(ctypes.sizeof(ctypes.c_ulong) * n), ctypes.POINTER(ctypes.c_ulong)) success = libcalcium.ca_poly_roots(roots, exp, self, self._ctx) if not success: libflint.flint_free(exp) raise ValueError("failed to compute roots") else: res = [(roots[i], exp[i]) for i in range(len(roots))] libflint.flint_free(exp) return res
[docs] def factor_squarefree(self): """ Squarefree factorization of this polynomial Returns (lc, L) where L is a list of (factor, multiplicity) pairs. >>> ca_poly([9,6,7,-28,12]).factor_squarefree() (12, [(ca_poly of length 3 [0.333333 {1/3}, 0.666667 {2/3}, 1], 1), (ca_poly of length 2 [-1.50000 {-3/2}, 1], 2)]) """ n = len(self) lc = ca() fac = ca_poly_vec() exp = ctypes.cast(libflint.flint_malloc(ctypes.sizeof(ctypes.c_ulong) * n), ctypes.POINTER(ctypes.c_ulong)) success = libcalcium.ca_poly_factor_squarefree(lc, fac, exp, self, self._ctx) if not success: libflint.flint_free(exp) raise ValueError("failed to compute factors") else: res = [(fac[i], exp[i]) for i in range(len(fac))] libflint.flint_free(exp) return lc, res
[docs] def squarefree_part(self): """ Squarefree part of this polynomial. >>> ca_poly([9,6,7,-28,12]).squarefree_part() ca_poly of length 4 [-0.500000 {-1/2}, -0.666667 {-2/3}, -0.833333 {-5/6}, 1] """ res = ca_poly() if not libcalcium.ca_poly_squarefree_part(res, self, self._ctx): raise ValueError("failed to compute squarefree part") return res
[docs] def integral(self): """ Integral of this polynomial. >>> ca_poly([1,1,1,1]).integral() ca_poly of length 5 [0, 1, 0.500000 {1/2}, 0.333333 {1/3}, 0.250000 {1/4}] """ res = ca_poly() libcalcium.ca_poly_integral(res, self, self._ctx) return res
[docs] def derivative(self): """ Derivative of this polynomial. >>> ca_poly([1,1,1,1]).derivative() ca_poly of length 3 [1, 2, 3] """ res = ca_poly() libcalcium.ca_poly_derivative(res, self, self._ctx) return res
[docs] def monic(self): """ Make this polynomial monic. >>> ca_poly([1,2,3]).monic() ca_poly of length 3 [0.333333 {1/3}, 0.666667 {2/3}, 1] >>> ca_poly().monic() Traceback (most recent call last): ... ValueError: failed to make monic """ res = ca_poly() if not libcalcium.ca_poly_make_monic(res, self, self._ctx): raise ValueError("failed to make monic") return res
[docs] def is_proper(self): """ Checks if this polynomial definitely has finite coefficients and that the leading coefficient is provably nonzero. >>> ca_poly([]).is_proper() True >>> ca_poly([1,2,3]).is_proper() True >>> ca_poly([1,2,1-exp(ca(2)**-10000)]).is_proper() False >>> ca_poly([inf]).is_proper() False """ res = ca_poly() if libcalcium.ca_poly_is_proper(self, self._ctx): return True return False
[docs] def degree(self): """ Degree of this polynomial. >>> ca_poly([1,2,3]).degree() 2 >>> ca_poly().degree() -1 >>> ca_poly([1,2,1-exp(ca(2)**-10000)]).degree() Traceback (most recent call last): ... ValueError: unable to determine degree """ if self.is_proper(): return len(self) - 1 raise ValueError("unable to determine degree")
# todo: in functions, don't create copies of the input
[docs]def re(x): return ca(x).re()
[docs]def im(x): return ca(x).im()
[docs]def sgn(x): return ca(x).sgn()
[docs]def sign(x): return ca(x).sign()
[docs]def floor(x): return ca(x).floor()
[docs]def ceil(x): return ca(x).ceil()
[docs]def conj(x): return ca(x).conj()
[docs]def conjugate(x): return ca(x).conjugate()
[docs]def sqrt(x): return ca(x).sqrt()
[docs]def log(x): return ca(x).log()
[docs]def exp(x): return ca(x).exp()
[docs]def erf(x): return ca(x).erf()
[docs]def erfc(x): return ca(x).erfc()
[docs]def erfi(x): return ca(x).erfi()
[docs]def gamma(x): return ca(x).gamma()
[docs]def fac(x): """ Alias for gamma(x+1). Examples:: >>> fac(10) 3.62880e+6 {3628800} """ return (ca(x)+1).gamma()
[docs]def cos(x): """ The cosine function is not yet implemented in Calcium. This placeholder function evaluates the cosine function using complex exponentials. Examples:: >>> cos(0) 1 >>> cos(pi) -1 >>> cos(pi/2) 0 >>> cos(pi/3) 0.500000 {1/2} >>> cos(pi/6)**2 0.750000 {3/4} >>> cos(1)**2 + sin(1)**2 1 """ ix = ca(x)*i y = exp(ix) return (y + 1/y)/2
[docs]def sin(x): """ The sine function is not yet implemented in Calcium. This placeholder function evaluates the sine function using complex exponentials. Examples:: >>> sin(0) 0 >>> sin(pi) 0 >>> sin(pi/2) 1 >>> sin(pi/6) 0.500000 {1/2} >>> sin(sqrt(2))**2 + cos(sqrt(2))**2 1 >>> sin(3 + pi) + sin(3) 0 """ ix = ca(x)*i y = exp(ix) return (y - 1/y)/(2*i)
[docs]def tan(x): """ The tangent function is not yet implemented in Calcium. This placeholder function evaluates the tangent function using complex exponentials. """ return sin(x)/cos(x)
[docs]def atan(x): """ The inverse tangent function is not yet implemented in Calcium. This placeholder function evaluates the inverse tangent function using complex logarithms. Examples:: >>> atan(0) 0 >>> 4 * atan(1) == pi True """ return (-i/2)*log((i-x)/(i+x))
[docs]def cosh(x): """ The hyperbolic cosine function is not yet implemented in Calcium. This placeholder function evaluates the hyperbolic cosine function using exponentials. Examples:: >>> cosh(1) 1.54308 {(a^2+1)/(2*a) where a = 2.71828 [Exp(1)]} """ y = exp(x) return (y + 1/y)/2
[docs]def sinh(x): """ The hyperbolic sine function is not yet implemented in Calcium. This placeholder function evaluates the hyperbolic sine function using exponentials. Examples:: >>> sinh(1) 1.17520 {(a^2-1)/(2*a) where a = 2.71828 [Exp(1)]} """ y = exp(x) return (y - 1/y)/2
[docs]def tanh(x): """ The hyperbolic tangent function is not yet implemented in Calcium. This placeholder function evaluates the hyperbolic tangent function using exponentials. Examples:: >>> tanh(1) 0.761594 {(a^2-1)/(a^2+1) where a = 2.71828 [Exp(1)]} """ return sinh(x)/cosh(x)
#class allocated_c_char_p(ctypes.c_char_p): # def __del__(self): # libflint.flint_free(self) libcalcium.ca_set_si.argtypes = ca, ctypes.c_long, ca_ctx libcalcium.ca_set_d.argtypes = ca, ctypes.c_double, ca_ctx libcalcium.ca_set_d_d.argtypes = ca, ctypes.c_double, ctypes.c_double, ca_ctx libcalcium.ca_get_str.argtypes = ca, ca_ctx libcalcium.ca_get_str.restype = ctypes.c_char_p libflint.fmpz_set_str.argtypes = ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int i = j = I = ca.i() pi = ca.pi() euler = ca.euler() e = E = ca(1).exp() inf = ca.inf() uinf = ca.uinf() undefined = ca.undefined() unknown = ca.unknown()
[docs]def prod(s): res = ca(1) for x in s: res *= x return res
[docs]def test_floor_ceil(): assert floor(sqrt(2)) == 1 assert ceil(sqrt(2)) == 2
[docs]def test_power_identities(): assert (sqrt(2)**sqrt(2))**sqrt(2) == 2 assert (sqrt(-2)**sqrt(2))**sqrt(2) == -2 assert (sqrt(3)**sqrt(3))**sqrt(3) == 3*sqrt(3) assert sqrt(-pi)**2 == -pi
[docs]def test_log(): assert log(1+pi) - log(pi) - log(1+1/pi) == 0 assert log(log(-log(log(exp(exp(-exp(exp(3)))))))) == 3
[docs]def test_exp(): assert exp(pi*i) + 1 == 0 assert exp(pi*i) == -1 assert exp(log(2)*log(3)) > 2 assert e**2 == exp(2)
[docs]def test_erf(): assert erf(2*log(sqrt(ca(1)/2-sqrt(2)/4))+log(4)) - erf(log(2-sqrt(2))) == 0 assert 1-erf(pi)-erfc(pi) == 0 assert erf(sqrt(2))**2 + erfi(sqrt(-2))**2 == 0
[docs]def test_gudermannian(): def gd(x): return 2*atan(exp(x))-pi/2 assert sin(gd(1)) == tanh(1) assert tan(gd(1)) == sinh(1) assert sin(gd(sqrt(2))) == tanh(sqrt(2)) assert tan(gd(1)/2) - tanh(ca(1)/2) == 0
[docs]def test_gamma(): assert gamma(1) == 1 assert gamma(0.5) == sqrt(pi) assert 1/gamma(0) == 0 assert gamma(sqrt(2)*sqrt(3)) == gamma(sqrt(6)) #assert gamma(pi+1)/gamma(pi) == pi assert gamma(pi)/gamma(pi-1) == pi-1
[docs]def test_xfail(): # Test some simplifications that are known not to work yet. # When a case starts working, we will get a test failure so we can # catch it and add it to the working tests def gd(x): return 2*atan(exp(x))-pi/2 def expect_error(f): try: f() except ValueError: return raise AssertionError
# expect_error(lambda: tan(gd(1)/2) - tanh(ca(1)/2) == 0) if __name__ == "__main__": from time import time print("Testing pycalcium_ctypes") print("----------------------------------------------------------") for fname in dir(): if fname.startswith("test_"): print(fname + "...", end="") t1 = time() globals()[fname]() t2 = time() print("PASS", end=" ") print("%.2f" % (t2-t1)) print("----------------------------------------------------------") import doctest doctest.testmod(verbose=True) print("----------------------------------------------------------")