+25
-18
@@ -6,7 +6,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [3.8, 3.9, '3.10', 3.11, 3.12, pypy3.8, pypy3.9, pypy3.10]
|
||||
python-version: [3.8, 3.9, '3.10', 3.11, 3.12, 3.13, pypy3.8, pypy3.9, pypy3.10]
|
||||
coverage: [false]
|
||||
nogmpy: [false]
|
||||
default: [false]
|
||||
include:
|
||||
- python-version: 3.11
|
||||
coverage: true
|
||||
nogmpy: true
|
||||
- python-version: 3.12
|
||||
coverage: true
|
||||
default: true
|
||||
env:
|
||||
PYTEST_ADDOPTS: -n auto
|
||||
steps:
|
||||
@@ -14,7 +24,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
@@ -26,46 +36,43 @@ jobs:
|
||||
run: |
|
||||
pip install --upgrade setuptools pip
|
||||
pip install --upgrade .[develop,gmpy,docs,ci]
|
||||
- name: Remove gmpy (for coverage tests)
|
||||
if: matrix.nogmpy
|
||||
run: pip uninstall -y gmpy2
|
||||
- name: Linting with flake8, etc
|
||||
if: matrix.python-version >= 3.9
|
||||
if: matrix.default
|
||||
run: |
|
||||
python -We:invalid -m compileall -f mpmath -q
|
||||
flake518
|
||||
- name: Tests
|
||||
if: matrix.python-version != 3.10 && matrix.python-version != 3.11
|
||||
run: |
|
||||
pytest
|
||||
MPMATH_STRICT=Y pytest mpmath/tests/test_basic_ops.py
|
||||
- name: Remove gmpy on 3.10
|
||||
if: matrix.python-version == 3.10
|
||||
run: pip uninstall -y gmpy2
|
||||
if: ${{ ! matrix.coverage }}
|
||||
run: pytest
|
||||
- name: Run coverage tests
|
||||
env:
|
||||
PYTEST_ADDOPTS: --cov mpmath --cov-append -n auto
|
||||
if: matrix.python-version == 3.10 || matrix.python-version == 3.11
|
||||
if: matrix.coverage
|
||||
run: |
|
||||
pytest
|
||||
MPMATH_STRICT=Y pytest mpmath/tests/test_basic_ops.py
|
||||
coverage html
|
||||
coverage xml
|
||||
- name: Upload coverage data
|
||||
if: matrix.python-version == 3.10 || matrix.python-version == 3.11
|
||||
if: matrix.coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
fail_ci_if_error: true
|
||||
- name: Building docs
|
||||
if: matrix.python-version == 3.11
|
||||
if: matrix.default
|
||||
run: |
|
||||
sphinx-build --color -W --keep-going -b html docs build/sphinx/html
|
||||
sphinx-build --color -W --keep-going -b latex docs build/sphinx/latex
|
||||
make -C build/sphinx/latex all-pdf
|
||||
- name: Make packages
|
||||
if: matrix.python-version == 3.11
|
||||
if: matrix.default
|
||||
run: python -m build
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
if: matrix.python-version == 3.11
|
||||
uses: actions/upload-artifact@v4
|
||||
if: matrix.default
|
||||
with:
|
||||
path: |
|
||||
dist/
|
||||
@@ -74,7 +81,7 @@ jobs:
|
||||
coverage.xml
|
||||
build/coverage/html/
|
||||
- name: Publish package on PyPI
|
||||
if: matrix.python-version == 3.11 && github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
||||
if: matrix.default && github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
|
||||
@@ -5,7 +5,6 @@ to improve accuracy at extremely high zoom levels.
|
||||
"""
|
||||
|
||||
import mpmath
|
||||
import cmath
|
||||
|
||||
ctx = mpmath.fp
|
||||
# ctx = mpmath.mp
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ def interactive():
|
||||
if tofile:
|
||||
tofile = open(tofile, "w")
|
||||
|
||||
calculateit(base, digits, tofile)
|
||||
calculateit(int(base), int(digits), tofile)
|
||||
input("\nPress enter to close this script.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -127,8 +127,6 @@ Developers may run tests from the source tree with::
|
||||
If any test fails, please send a detailed bug report to the `mpmath issue
|
||||
tracker <https://github.com/mpmath/mpmath/issues>`_.
|
||||
|
||||
To enable extra diagnostics, use, set ``MPMATH_STRICT`` environment variable.
|
||||
|
||||
Compiling the documentation
|
||||
---------------------------
|
||||
|
||||
|
||||
+1
-3
@@ -274,11 +274,9 @@ def _binary_op(f_real, f_complex):
|
||||
ivmpf.__add__, ivmpf.__radd__, ivmpc.__add__, ivmpc.__radd__ = _binary_op(mpi_add, mpci_add)
|
||||
ivmpf.__sub__, ivmpf.__rsub__, ivmpc.__sub__, ivmpc.__rsub__ = _binary_op(mpi_sub, mpci_sub)
|
||||
ivmpf.__mul__, ivmpf.__rmul__, ivmpc.__mul__, ivmpc.__rmul__ = _binary_op(mpi_mul, mpci_mul)
|
||||
ivmpf.__div__, ivmpf.__rdiv__, ivmpc.__div__, ivmpc.__rdiv__ = _binary_op(mpi_div, mpci_div)
|
||||
ivmpf.__pow__, ivmpf.__rpow__, ivmpc.__pow__, ivmpc.__rpow__ = _binary_op(mpi_pow, mpci_pow)
|
||||
|
||||
ivmpf.__truediv__ = ivmpf.__div__; ivmpf.__rtruediv__ = ivmpf.__rdiv__
|
||||
ivmpc.__truediv__ = ivmpc.__div__; ivmpc.__rtruediv__ = ivmpc.__rdiv__
|
||||
ivmpf.__truediv__, ivmpf.__rtruediv__, ivmpc.__truediv__, ivmpc.__rtruediv__ = _binary_op(mpi_div, mpci_div)
|
||||
|
||||
class ivmpf_constant(ivmpf):
|
||||
def __new__(cls, f):
|
||||
|
||||
@@ -458,7 +458,7 @@ def polylog_general(ctx, s, z):
|
||||
k = 0
|
||||
while 1:
|
||||
term = ctx.zeta(s-k) * t
|
||||
if abs(term) < ctx.eps:
|
||||
if not abs(term) >= ctx.eps:
|
||||
break
|
||||
v += term
|
||||
k += 1
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from .backend import (BACKEND, HASH_BITS, HASH_MODULUS, MPQ, MPZ, MPZ_FIVE,
|
||||
MPZ_ONE, MPZ_THREE, MPZ_TWO, MPZ_ZERO, STRICT, gmpy,
|
||||
int_types)
|
||||
MPZ_ONE, MPZ_THREE, MPZ_TWO, MPZ_ZERO, gmpy, int_types)
|
||||
from .gammazeta import (apery_fixed, bernfrac, catalan_fixed, euler_fixed,
|
||||
glaisher_fixed, khinchin_fixed, mertens_fixed,
|
||||
mpc_altzeta, mpc_factorial, mpc_gamma, mpc_harmonic,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from fractions import Fraction
|
||||
import os
|
||||
import sys
|
||||
from fractions import Fraction
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Support GMPY for high-speed large integer arithmetic. #
|
||||
@@ -36,11 +37,6 @@ if 'MPMATH_NOGMPY' not in os.environ:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if 'MPMATH_STRICT' in os.environ:
|
||||
STRICT = True
|
||||
else:
|
||||
STRICT = False
|
||||
|
||||
MPZ_ZERO = MPZ(0)
|
||||
MPZ_ONE = MPZ(1)
|
||||
MPZ_TWO = MPZ(2)
|
||||
|
||||
+5
-12
@@ -12,7 +12,7 @@ from bisect import bisect
|
||||
getrandbits = None
|
||||
|
||||
from .backend import (BACKEND, HASH_BITS, HASH_MODULUS, MPZ, MPZ_FIVE, MPZ_ONE,
|
||||
MPZ_TWO, MPZ_ZERO, STRICT, gmpy)
|
||||
MPZ_TWO, MPZ_ZERO, gmpy)
|
||||
from .libintmath import (bctable, bin_to_radix, giant_steps, isqrt, isqrt_fast,
|
||||
lshift, numeral, rshift, sqrt_fixed, sqrtrem,
|
||||
stddigits, trailing, trailtable)
|
||||
@@ -179,9 +179,10 @@ def _normalize(sign, man, exp, bc, prec, rnd):
|
||||
|
||||
_exp_types = (int,)
|
||||
|
||||
def strict_normalize(sign, man, exp, bc, prec, rnd):
|
||||
"""Additional checks on the components of an mpf. Enable tests by setting
|
||||
the environment variable MPMATH_STRICT to Y."""
|
||||
if BACKEND == 'gmpy':
|
||||
_normalize = gmpy._mpmath_normalize
|
||||
|
||||
def normalize(sign, man, exp, bc, prec, rnd):
|
||||
assert type(man) == MPZ
|
||||
assert type(bc) in _exp_types
|
||||
assert type(exp) in _exp_types
|
||||
@@ -189,14 +190,6 @@ def strict_normalize(sign, man, exp, bc, prec, rnd):
|
||||
assert man >= 0
|
||||
return _normalize(sign, man, exp, bc, prec, rnd)
|
||||
|
||||
if BACKEND == 'gmpy':
|
||||
_normalize = gmpy._mpmath_normalize
|
||||
|
||||
if STRICT:
|
||||
normalize = strict_normalize
|
||||
else:
|
||||
normalize = _normalize
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Conversion functions #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
@@ -146,7 +146,7 @@ class LinearAlgebraMethods:
|
||||
A[i,j] /= A[j,j]
|
||||
for k in range(j + 1, n):
|
||||
A[i,k] -= A[i,j]*A[j,k]
|
||||
if ctx.absmin(A[n - 1,n - 1]) <= tol:
|
||||
if p and ctx.absmin(A[n - 1,n - 1]) <= tol:
|
||||
raise ZeroDivisionError('matrix is numerically singular')
|
||||
# cache decomposition
|
||||
if not overwrite and isinstance(orig, ctx.matrix):
|
||||
@@ -197,9 +197,6 @@ class LinearAlgebraMethods:
|
||||
(especially for overdetermined systems), but it's twice as efficient.
|
||||
Use qr_solve if you want more precision or have to solve a very ill-
|
||||
conditioned system.
|
||||
|
||||
If you specify real=True, it does not check for overdeterminded complex
|
||||
systems.
|
||||
"""
|
||||
prec = ctx.prec
|
||||
try:
|
||||
@@ -214,12 +211,7 @@ class LinearAlgebraMethods:
|
||||
AH = A.H
|
||||
A = AH * A
|
||||
b = AH * b
|
||||
if (kwargs.get('real', False) or
|
||||
not sum(type(i) is ctx.mpc for i in A)):
|
||||
# TODO: necessary to check also b?
|
||||
x = ctx.cholesky_solve(A, b)
|
||||
else:
|
||||
x = ctx.lu_solve(A, b)
|
||||
x = ctx.cholesky_solve(A, b)
|
||||
else:
|
||||
# LU factorization
|
||||
A, p = ctx.LU_decomp(A)
|
||||
@@ -551,6 +543,12 @@ class LinearAlgebraMethods:
|
||||
>>> print(det(A))
|
||||
1.0
|
||||
|
||||
The determinant of a 0 by 0 matrix is 1 as the product of no factors
|
||||
is by convention the multiplicative identity.
|
||||
>>> A = matrix(0, 0)
|
||||
>>> print(det(A))
|
||||
1
|
||||
|
||||
But in general a matrix can have any number as its determinant.
|
||||
|
||||
>>> A = matrix([[2, 6, 4],[3, 8, 6],[1, 1, 2]])
|
||||
|
||||
+121
-116
@@ -2,9 +2,6 @@ import warnings
|
||||
|
||||
# TODO: interpret list as vectors (for multiplication)
|
||||
|
||||
rowsep = '\n'
|
||||
colsep = ' '
|
||||
|
||||
class _matrix:
|
||||
"""
|
||||
Numerical matrix.
|
||||
@@ -275,7 +272,7 @@ class _matrix:
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.__data = {}
|
||||
self._data = {}
|
||||
# LU decompostion cache, this is useful when solving the same system
|
||||
# multiple times, when calculating the inverse and when calculating the
|
||||
# determinant
|
||||
@@ -285,13 +282,17 @@ class _matrix:
|
||||
" properly anyway. If you want to force floating-point or"
|
||||
" interval computations, use the respective methods from `fp`"
|
||||
" or `mp` instead, e.g., `fp.matrix()` or `iv.matrix()`."
|
||||
" If you want to truncate values to integer, use .apply(int) instead.")
|
||||
" If you want to truncate values to integer, use .apply(int) instead.",
|
||||
DeprecationWarning)
|
||||
if isinstance(args[0], (list, tuple)):
|
||||
if isinstance(args[0][0], (list, tuple)):
|
||||
if not args[0]:
|
||||
self._rows = 0
|
||||
self._cols = 0
|
||||
elif isinstance(args[0][0], (list, tuple)):
|
||||
# interpret nested list as matrix
|
||||
A = args[0]
|
||||
self.__rows = len(A)
|
||||
self.__cols = len(A[0])
|
||||
self._rows = len(A)
|
||||
self._cols = len(A[0])
|
||||
for i, row in enumerate(A):
|
||||
for j, a in enumerate(row):
|
||||
# note: this will call __setitem__ which will call self.ctx.convert() to convert the datatype.
|
||||
@@ -299,31 +300,31 @@ class _matrix:
|
||||
else:
|
||||
# interpret list as row vector
|
||||
v = args[0]
|
||||
self.__rows = len(v)
|
||||
self.__cols = 1
|
||||
self._rows = len(v)
|
||||
self._cols = 1
|
||||
for i, e in enumerate(v):
|
||||
self[i, 0] = e
|
||||
elif isinstance(args[0], int):
|
||||
# create empty matrix of given dimensions
|
||||
if len(args) == 1:
|
||||
self.__rows = self.__cols = args[0]
|
||||
self._rows = self._cols = args[0]
|
||||
else:
|
||||
if not isinstance(args[1], int):
|
||||
raise TypeError("expected int")
|
||||
self.__rows = args[0]
|
||||
self.__cols = args[1]
|
||||
self._rows = args[0]
|
||||
self._cols = args[1]
|
||||
elif isinstance(args[0], _matrix):
|
||||
A = args[0]
|
||||
self.__rows = A._matrix__rows
|
||||
self.__cols = A._matrix__cols
|
||||
for i in range(A.__rows):
|
||||
for j in range(A.__cols):
|
||||
self._rows = A._rows
|
||||
self._cols = A._cols
|
||||
for i in range(A._rows):
|
||||
for j in range(A._cols):
|
||||
self[i, j] = A[i, j]
|
||||
elif hasattr(args[0], 'tolist'):
|
||||
A = self.ctx.matrix(args[0].tolist())
|
||||
self.__data = A._matrix__data
|
||||
self.__rows = A._matrix__rows
|
||||
self.__cols = A._matrix__cols
|
||||
self._data = A._data
|
||||
self._rows = A._rows
|
||||
self._cols = A._cols
|
||||
else:
|
||||
raise TypeError('could not interpret given arguments')
|
||||
|
||||
@@ -331,9 +332,9 @@ class _matrix:
|
||||
"""
|
||||
Return a copy of self with the function `f` applied elementwise.
|
||||
"""
|
||||
new = self.ctx.matrix(self.__rows, self.__cols)
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
new = self.ctx.matrix(self._rows, self._cols)
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
new[i,j] = f(self[i,j])
|
||||
return new
|
||||
|
||||
@@ -352,17 +353,19 @@ class _matrix:
|
||||
res[-1].append(string)
|
||||
maxlen[j] = max(len(string), maxlen[j])
|
||||
# Patch strings together
|
||||
rowsep = '\n'
|
||||
colsep = ' '
|
||||
for i, row in enumerate(res):
|
||||
for j, elem in enumerate(row):
|
||||
# Pad each element up to maxlen so the columns line up
|
||||
row[j] = elem.rjust(maxlen[j])
|
||||
res[i] = "[" + colsep.join(row) + "]"
|
||||
return rowsep.join(res)
|
||||
return rowsep.join(res) if self.rows or self.cols else ''
|
||||
|
||||
def __str__(self):
|
||||
return self.__nstr__()
|
||||
|
||||
def _toliststr(self, avoid_type=False):
|
||||
def _toliststr(self):
|
||||
"""
|
||||
Create a list string from a matrix.
|
||||
|
||||
@@ -371,17 +374,19 @@ class _matrix:
|
||||
# XXX: should be something like self.ctx._types
|
||||
typ = self.ctx.mpf
|
||||
s = '['
|
||||
for i in range(self.__rows):
|
||||
for i in range(self._rows):
|
||||
s += '['
|
||||
for j in range(self.__cols):
|
||||
if not avoid_type or not isinstance(self[i,j], typ):
|
||||
for j in range(self._cols):
|
||||
if not isinstance(self[i,j], typ):
|
||||
a = repr(self[i,j])
|
||||
else:
|
||||
a = "'" + str(self[i,j]) + "'"
|
||||
s += a + ', '
|
||||
s = s[:-2]
|
||||
if s[-1] != '[':
|
||||
s = s[:-2]
|
||||
s += '],\n '
|
||||
s = s[:-3]
|
||||
if s[-1] != '[':
|
||||
s = s[:-3]
|
||||
s += ']'
|
||||
return s
|
||||
|
||||
@@ -389,28 +394,28 @@ class _matrix:
|
||||
"""
|
||||
Convert the matrix to a nested list.
|
||||
"""
|
||||
return [[self[i,j] for j in range(self.__cols)] for i in range(self.__rows)]
|
||||
return [[self[i,j] for j in range(self._cols)] for i in range(self._rows)]
|
||||
|
||||
def __repr__(self):
|
||||
if self.ctx.pretty:
|
||||
return self.__str__()
|
||||
s = 'matrix(\n'
|
||||
s += self._toliststr(avoid_type=True) + ')'
|
||||
s += self._toliststr() + ')'
|
||||
return s
|
||||
|
||||
def __get_element(self, key):
|
||||
def _get_element(self, key):
|
||||
'''
|
||||
Fast extraction of the i,j element from the matrix
|
||||
This function is for private use only because is unsafe:
|
||||
1. Does not check on the value of key it expects key to be a integer tuple (i,j)
|
||||
2. Does not check bounds
|
||||
'''
|
||||
if key in self.__data:
|
||||
return self.__data[key]
|
||||
if key in self._data:
|
||||
return self._data[key]
|
||||
else:
|
||||
return self.ctx.zero
|
||||
|
||||
def __set_element(self, key, value):
|
||||
def _set_element(self, key, value):
|
||||
'''
|
||||
Fast assignment of the i,j element in the matrix
|
||||
This function is unsafe:
|
||||
@@ -420,9 +425,9 @@ class _matrix:
|
||||
4. Does not reset the LU cache
|
||||
'''
|
||||
if value: # only store non-zeros
|
||||
self.__data[key] = value
|
||||
elif key in self.__data:
|
||||
del self.__data[key]
|
||||
self._data[key] = value
|
||||
elif key in self._data:
|
||||
del self._data[key]
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
@@ -435,9 +440,9 @@ class _matrix:
|
||||
# Convert vector to matrix indexing
|
||||
if isinstance(key, int) or isinstance(key,slice):
|
||||
# only sufficent for vectors
|
||||
if self.__rows == 1:
|
||||
if self._rows == 1:
|
||||
key = (0, key)
|
||||
elif self.__cols == 1:
|
||||
elif self._cols == 1:
|
||||
key = (key, 0)
|
||||
else:
|
||||
raise IndexError('insufficient indices for matrix')
|
||||
@@ -448,14 +453,14 @@ class _matrix:
|
||||
if isinstance(key[0],slice):
|
||||
#Check bounds
|
||||
if (key[0].start is None or key[0].start >= 0) and \
|
||||
(key[0].stop is None or key[0].stop <= self.__rows+1):
|
||||
(key[0].stop is None or key[0].stop <= self._rows+1):
|
||||
# Generate indices
|
||||
rows = range(*key[0].indices(self.__rows))
|
||||
rows = range(*key[0].indices(self._rows))
|
||||
else:
|
||||
raise IndexError('Row index out of bounds')
|
||||
else:
|
||||
# Single row
|
||||
if key[0] >= self.__rows:
|
||||
if key[0] >= self._rows:
|
||||
raise IndexError('Row index out of bounds')
|
||||
rows = [key[0]]
|
||||
|
||||
@@ -463,15 +468,15 @@ class _matrix:
|
||||
if isinstance(key[1],slice):
|
||||
# Check bounds
|
||||
if (key[1].start is None or key[1].start >= 0) and \
|
||||
(key[1].stop is None or key[1].stop <= self.__cols+1):
|
||||
(key[1].stop is None or key[1].stop <= self._cols+1):
|
||||
# Generate indices
|
||||
columns = range(*key[1].indices(self.__cols))
|
||||
columns = range(*key[1].indices(self._cols))
|
||||
else:
|
||||
raise IndexError('Column index out of bounds')
|
||||
|
||||
else:
|
||||
# Single column
|
||||
if key[1] >= self.__cols:
|
||||
if key[1] >= self._cols:
|
||||
raise IndexError('Column index out of bounds')
|
||||
columns = [key[1]]
|
||||
|
||||
@@ -481,16 +486,16 @@ class _matrix:
|
||||
# Assign elements to the output matrix
|
||||
for i,x in enumerate(rows):
|
||||
for j,y in enumerate(columns):
|
||||
m.__set_element((i,j),self.__get_element((x,y)))
|
||||
m._set_element((i,j),self._get_element((x,y)))
|
||||
|
||||
return m
|
||||
|
||||
else:
|
||||
# single element extraction
|
||||
if key[0] >= self.__rows or key[1] >= self.__cols:
|
||||
if key[0] >= self._rows or key[1] >= self._cols:
|
||||
raise IndexError('matrix index out of range')
|
||||
if key in self.__data:
|
||||
return self.__data[key]
|
||||
if key in self._data:
|
||||
return self._data[key]
|
||||
else:
|
||||
return self.ctx.zero
|
||||
|
||||
@@ -504,9 +509,9 @@ class _matrix:
|
||||
# Convert vector to matrix indexing
|
||||
if isinstance(key, int) or isinstance(key,slice):
|
||||
# only sufficent for vectors
|
||||
if self.__rows == 1:
|
||||
if self._rows == 1:
|
||||
key = (0, key)
|
||||
elif self.__cols == 1:
|
||||
elif self._cols == 1:
|
||||
key = (key, 0)
|
||||
else:
|
||||
raise IndexError('insufficient indices for matrix')
|
||||
@@ -516,9 +521,9 @@ class _matrix:
|
||||
if isinstance(key[0],slice):
|
||||
# Check bounds
|
||||
if (key[0].start is None or key[0].start >= 0) and \
|
||||
(key[0].stop is None or key[0].stop <= self.__rows+1):
|
||||
(key[0].stop is None or key[0].stop <= self._rows+1):
|
||||
# generate row indices
|
||||
rows = range(*key[0].indices(self.__rows))
|
||||
rows = range(*key[0].indices(self._rows))
|
||||
else:
|
||||
raise IndexError('Row index out of bounds')
|
||||
else:
|
||||
@@ -528,9 +533,9 @@ class _matrix:
|
||||
if isinstance(key[1],slice):
|
||||
# Check bounds
|
||||
if (key[1].start is None or key[1].start >= 0) and \
|
||||
(key[1].stop is None or key[1].stop <= self.__cols+1):
|
||||
(key[1].stop is None or key[1].stop <= self._cols+1):
|
||||
# Generate column indices
|
||||
columns = range(*key[1].indices(self.__cols))
|
||||
columns = range(*key[1].indices(self._cols))
|
||||
else:
|
||||
raise IndexError('Column index out of bounds')
|
||||
else:
|
||||
@@ -542,7 +547,7 @@ class _matrix:
|
||||
if len(rows) == value.rows and len(columns) == value.cols:
|
||||
for i,x in enumerate(rows):
|
||||
for j,y in enumerate(columns):
|
||||
self.__set_element((x,y), value.__get_element((i,j)))
|
||||
self._set_element((x,y), value._get_element((i,j)))
|
||||
else:
|
||||
raise ValueError('Dimensions do not match')
|
||||
else:
|
||||
@@ -550,44 +555,44 @@ class _matrix:
|
||||
value = self.ctx.convert(value)
|
||||
for i in rows:
|
||||
for j in columns:
|
||||
self.__set_element((i,j), value)
|
||||
self._set_element((i,j), value)
|
||||
else:
|
||||
# Single element assingment
|
||||
# Check bounds
|
||||
if key[0] >= self.__rows or key[1] >= self.__cols:
|
||||
if key[0] >= self._rows or key[1] >= self._cols:
|
||||
raise IndexError('matrix index out of range')
|
||||
# Convert and store value
|
||||
value = self.ctx.convert(value)
|
||||
if value: # only store non-zeros
|
||||
self.__data[key] = value
|
||||
elif key in self.__data:
|
||||
del self.__data[key]
|
||||
self._data[key] = value
|
||||
elif key in self._data:
|
||||
del self._data[key]
|
||||
|
||||
if self._LU:
|
||||
self._LU = None
|
||||
return
|
||||
|
||||
def __iter__(self):
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
yield self[i,j]
|
||||
|
||||
def __mul__(self, other):
|
||||
if isinstance(other, self.ctx.matrix):
|
||||
# dot multiplication
|
||||
if self.__cols != other.__rows:
|
||||
if self._cols != other._rows:
|
||||
raise ValueError('dimensions not compatible for multiplication')
|
||||
new = self.ctx.matrix(self.__rows, other.__cols)
|
||||
for i in range(self.__rows):
|
||||
for j in range(other.__cols):
|
||||
new[i, j] = self.ctx.fdot((self.__data[i,k], other.__data[k,j])
|
||||
for k in range(other.__rows) if (i,k) in self.__data and (k,j) in other.__data)
|
||||
new = self.ctx.matrix(self._rows, other._cols)
|
||||
for i in range(self._rows):
|
||||
for j in range(other._cols):
|
||||
new[i, j] = self.ctx.fdot((self._data[i,k], other._data[k,j])
|
||||
for k in range(other._rows) if (i,k) in self._data and (k,j) in other._data)
|
||||
return new
|
||||
else:
|
||||
# try scalar multiplication
|
||||
new = self.ctx.matrix(self.__rows, self.__cols)
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
new = self.ctx.matrix(self._rows, self._cols)
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
new[i, j] = other * self[i, j]
|
||||
return new
|
||||
|
||||
@@ -605,11 +610,11 @@ class _matrix:
|
||||
#from linalg import inverse
|
||||
if not isinstance(other, int):
|
||||
raise ValueError('only integer exponents are supported')
|
||||
if not self.__rows == self.__cols:
|
||||
if not self._rows == self._cols:
|
||||
raise ValueError('only powers of square matrices are defined')
|
||||
n = other
|
||||
if n == 0:
|
||||
return self.ctx.eye(self.__rows)
|
||||
return self.ctx.eye(self._rows)
|
||||
if n < 0:
|
||||
n = -n
|
||||
neg = True
|
||||
@@ -627,31 +632,29 @@ class _matrix:
|
||||
y = self.ctx.inverse(y)
|
||||
return y
|
||||
|
||||
def __div__(self, other):
|
||||
def __truediv__(self, other):
|
||||
# assume other is scalar and do element-wise divison
|
||||
assert not isinstance(other, self.ctx.matrix)
|
||||
new = self.ctx.matrix(self.__rows, self.__cols)
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
new = self.ctx.matrix(self._rows, self._cols)
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
new[i,j] = self[i,j] / other
|
||||
return new
|
||||
|
||||
__truediv__ = __div__
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, self.ctx.matrix):
|
||||
if not (self.__rows == other.__rows and self.__cols == other.__cols):
|
||||
if not (self._rows == other._rows and self._cols == other._cols):
|
||||
raise ValueError('incompatible dimensions for addition')
|
||||
new = self.ctx.matrix(self.__rows, self.__cols)
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
new = self.ctx.matrix(self._rows, self._cols)
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
new[i,j] = self[i,j] + other[i,j]
|
||||
return new
|
||||
else:
|
||||
# assume other is scalar and add element-wise
|
||||
new = self.ctx.matrix(self.__rows, self.__cols)
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
new = self.ctx.matrix(self._rows, self._cols)
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
new[i,j] += self[i,j] + other
|
||||
return new
|
||||
|
||||
@@ -659,8 +662,8 @@ class _matrix:
|
||||
return self.__add__(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, self.ctx.matrix) and not (self.__rows == other.__rows
|
||||
and self.__cols == other.__cols):
|
||||
if isinstance(other, self.ctx.matrix) and not (self._rows == other._rows
|
||||
and self._cols == other._cols):
|
||||
raise ValueError('incompatible dimensions for subtraction')
|
||||
return self.__add__(other * (-1))
|
||||
|
||||
@@ -678,8 +681,8 @@ class _matrix:
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
return (self.__rows == other.__rows and self.__cols == other.__cols
|
||||
and self.__data == other.__data)
|
||||
return (self._rows == other._rows and self._cols == other._cols
|
||||
and self._data == other._data)
|
||||
except AttributeError:
|
||||
return NotImplemented
|
||||
|
||||
@@ -691,32 +694,34 @@ class _matrix:
|
||||
else:
|
||||
return self.rows # do it like numpy
|
||||
|
||||
def __getrows(self):
|
||||
return self.__rows
|
||||
@property
|
||||
def rows(self):
|
||||
"""Number of rows."""
|
||||
return self._rows
|
||||
|
||||
def __setrows(self, value):
|
||||
for key in self.__data.copy():
|
||||
@rows.setter
|
||||
def rows(self, value):
|
||||
for key in self._data.copy():
|
||||
if key[0] >= value:
|
||||
del self.__data[key]
|
||||
self.__rows = value
|
||||
del self._data[key]
|
||||
self._rows = value
|
||||
|
||||
rows = property(__getrows, __setrows, doc='number of rows')
|
||||
@property
|
||||
def cols(self):
|
||||
"""Number of columns."""
|
||||
return self._cols
|
||||
|
||||
def __getcols(self):
|
||||
return self.__cols
|
||||
|
||||
def __setcols(self, value):
|
||||
for key in self.__data.copy():
|
||||
@cols.setter
|
||||
def cols(self, value):
|
||||
for key in self._data.copy():
|
||||
if key[1] >= value:
|
||||
del self.__data[key]
|
||||
self.__cols = value
|
||||
|
||||
cols = property(__getcols, __setcols, doc='number of columns')
|
||||
del self._data[key]
|
||||
self._cols = value
|
||||
|
||||
def transpose(self):
|
||||
new = self.ctx.matrix(self.__cols, self.__rows)
|
||||
for i in range(self.__rows):
|
||||
for j in range(self.__cols):
|
||||
new = self.ctx.matrix(self._cols, self._rows)
|
||||
for i in range(self._rows):
|
||||
for j in range(self._cols):
|
||||
new[j,i] = self[i,j]
|
||||
return new
|
||||
|
||||
@@ -731,8 +736,8 @@ class _matrix:
|
||||
H = property(transpose_conj)
|
||||
|
||||
def copy(self):
|
||||
new = self.ctx.matrix(self.__rows, self.__cols)
|
||||
new.__data = self.__data.copy()
|
||||
new = self.ctx.matrix(self._rows, self._cols)
|
||||
new._data = self._data.copy()
|
||||
return new
|
||||
|
||||
__copy__ = copy
|
||||
@@ -988,8 +993,8 @@ class MatrixMethods:
|
||||
p = ctx.convert(p)
|
||||
m, n = A.rows, A.cols
|
||||
if p == 1:
|
||||
return max(ctx.fsum((A[i,j] for i in range(m)), absolute=1) for j in range(n))
|
||||
return max((ctx.fsum((A[i,j] for i in range(m)), absolute=1) for j in range(n)), default=0)
|
||||
elif p == ctx.inf:
|
||||
return max(ctx.fsum((A[i,j] for j in range(n)), absolute=1) for i in range(m))
|
||||
return max((ctx.fsum((A[i,j] for j in range(n)), absolute=1) for i in range(m)), default=0)
|
||||
else:
|
||||
raise NotImplementedError("matrix p-norm for arbitrary p")
|
||||
|
||||
@@ -15,9 +15,9 @@ from mpmath import (acos, acosh, acot, acoth, acsc, acsch, arange, arg, asec,
|
||||
phi, pi, power, powm1, radians, rand, re, root, sec, sech,
|
||||
sign, sin, sinc, sincpi, sinh, sinpi, sqrt, tan, tanh,
|
||||
twinprime, unitroots)
|
||||
from mpmath.libmp import (ComplexResult, from_int, mpf_gt, mpf_lt, mpf_mul,
|
||||
mpf_pow_int, mpf_rand, mpf_sqrt, round_ceiling,
|
||||
round_down, round_nearest, round_up)
|
||||
from mpmath.libmp import (MPZ, ComplexResult, from_int, mpf_gt, mpf_lt,
|
||||
mpf_mul, mpf_pow_int, mpf_rand, mpf_sqrt,
|
||||
round_ceiling, round_down, round_nearest, round_up)
|
||||
|
||||
|
||||
def mpc_ae(a, b, eps=eps):
|
||||
@@ -162,10 +162,10 @@ def test_exp():
|
||||
assert exp(0) == 1
|
||||
assert exp(10000).ae(mpf('8.8068182256629215873e4342'))
|
||||
assert exp(-10000).ae(mpf('1.1354838653147360985e-4343'))
|
||||
a = exp(mpf((1, 8198646019315405, -53, 53)))
|
||||
a = exp(mpf((1, MPZ(8198646019315405), -53, 53)))
|
||||
assert a.bc == a.man.bit_length()
|
||||
mp.prec = 67
|
||||
a = exp(mpf((1, 1781864658064754565, -60, 61)))
|
||||
a = exp(mpf((1, MPZ(1781864658064754565), -60, 61)))
|
||||
assert a.bc == a.man.bit_length()
|
||||
mp.prec = 53
|
||||
assert exp(ln2 * 10).ae(1024)
|
||||
|
||||
@@ -2388,3 +2388,8 @@ def test_issue_251():
|
||||
assert lerchphi(1.00000001, 4.1+1j,
|
||||
1.0).ae(1.0497861498996701 - 0.053190919646660638j)
|
||||
assert zeta(4.1+1j, 1.0).ae(1.0497861493928464 - 0.053190918836910267j)
|
||||
|
||||
def test_issue_505():
|
||||
assert mp.isnan(mp.polylog(mp.inf, 2.2))
|
||||
assert mp.isnan(mp.polylog(mp.ninf, 2.2))
|
||||
assert mp.isnan(mp.polylog(mp.nan, 2.2))
|
||||
|
||||
@@ -82,6 +82,8 @@ A13 = matrix([[2, 6, 4],
|
||||
[3, 8, 6],
|
||||
[1, 1, 2]])
|
||||
|
||||
A14 = matrix(0, 0)
|
||||
|
||||
def test_LU_decomp():
|
||||
A = A3.copy()
|
||||
b = b3
|
||||
@@ -213,6 +215,7 @@ def test_det():
|
||||
assert det(zeros(3)) == 0
|
||||
assert det(A11) == 0
|
||||
assert absmin(det(A12*1e-30) - 1e-30) < eps
|
||||
assert det(A14) == 1
|
||||
|
||||
def test_cond():
|
||||
A = matrix([[1.2969, 0.8648], [0.2161, 0.1441]])
|
||||
|
||||
@@ -12,11 +12,11 @@ def test_matrix_basic():
|
||||
assert A1 == eye(3)
|
||||
assert A1 == matrix(A1)
|
||||
A2 = matrix(3, 2)
|
||||
assert not A2._matrix__data
|
||||
assert not A2._data
|
||||
A3 = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
||||
assert list(A3) == list(range(1, 10))
|
||||
A3[1,1] = 0
|
||||
assert (1, 1) not in A3._matrix__data
|
||||
assert (1, 1) not in A3._data
|
||||
A4 = matrix([[1, 2, 3], [4, 5, 6]])
|
||||
A5 = matrix([[6, -1], [3, 2], [0, -3]])
|
||||
assert A4 * A5 == matrix([[12, -6], [39, -12]])
|
||||
@@ -34,7 +34,7 @@ def test_matrix_basic():
|
||||
assert A2.cols == 2
|
||||
A3.rows = 2
|
||||
A3.cols = 2
|
||||
assert len(A3._matrix__data) == 3
|
||||
assert len(A3._data) == 3
|
||||
assert A4 + A4 == 2*A4
|
||||
pytest.raises(ValueError, lambda: A4 + A2)
|
||||
assert sum(A1 - A1) == 0
|
||||
@@ -172,7 +172,7 @@ def test_vector():
|
||||
x = matrix([0, 1, 2, 3, 4])
|
||||
assert x == matrix([[0], [1], [2], [3], [4]])
|
||||
assert x[3] == 3
|
||||
assert len(x._matrix__data) == 4
|
||||
assert len(x._data) == 4
|
||||
assert list(x) == list(range(5))
|
||||
x[0] = -10
|
||||
x[4] = 0
|
||||
@@ -251,3 +251,7 @@ def test_interval_matrix_mult_bug():
|
||||
assert mp.mpf('1.00000000000001998401444325291756783368705994138804689654') in C[0, 0]
|
||||
# the following caused an error before the bug was fixed
|
||||
assert iv.matrix(mp.eye(2)) * (iv.ones(2) + mpi(1, 2)) == iv.matrix([[mpi(2, 3), mpi(2, 3)], [mpi(2, 3), mpi(2, 3)]])
|
||||
|
||||
def test_issue_156():
|
||||
with pytest.deprecated_call():
|
||||
matrix([[1, 2], [3, 4]], force_type=float)
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
from mpmath import inf, matrix, nstr
|
||||
|
||||
|
||||
A1 = matrix([])
|
||||
A2 = matrix([[]])
|
||||
A3 = matrix(2)
|
||||
A4 = matrix([1, 2, 3])
|
||||
|
||||
|
||||
def test_nstr():
|
||||
m = matrix([[0.75, 0.190940654, -0.0299195971],
|
||||
[0.190940654, 0.65625, 0.205663228],
|
||||
@@ -13,3 +19,31 @@ def test_nstr():
|
||||
'''[ 0.75 0.1909 -0.02992]
|
||||
[ 0.1909 0.6563 0.2057]
|
||||
[-0.02992 0.2057 6.445e-21]'''
|
||||
|
||||
def test_matrix_repr():
|
||||
assert repr(A1) == \
|
||||
'''matrix(
|
||||
[])'''
|
||||
assert repr(A2) == \
|
||||
'''matrix(
|
||||
[[]])'''
|
||||
assert repr(A3) == \
|
||||
'''matrix(
|
||||
[['0.0', '0.0'],
|
||||
['0.0', '0.0']])'''
|
||||
assert repr(A4) == \
|
||||
'''matrix(
|
||||
[['1.0'],
|
||||
['2.0'],
|
||||
['3.0']])'''
|
||||
|
||||
def test_matrix_str():
|
||||
assert str(A1) == ''
|
||||
assert str(A2) == '[]'
|
||||
assert str(A3) == \
|
||||
'''[0.0 0.0]
|
||||
[0.0 0.0]'''
|
||||
assert str(A4) == \
|
||||
'''[1.0]
|
||||
[2.0]
|
||||
[3.0]'''
|
||||
|
||||
@@ -4,9 +4,12 @@ sure that passing custom Axes works.
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from mpmath import fp, mp
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:datetime.datetime.utc.*:DeprecationWarning")
|
||||
def test_axes():
|
||||
try:
|
||||
import matplotlib
|
||||
|
||||
+3
-2
@@ -30,11 +30,12 @@ Homepage = 'https://mpmath.org/'
|
||||
'Bug Tracker' = 'https://github.com/mpmath/mpmath/issues'
|
||||
Documentation = 'http://mpmath.org/doc/current/'
|
||||
[project.optional-dependencies]
|
||||
tests = ['pytest>=6', 'numpy<=1.25.2; python_version<"3.12"']
|
||||
tests = ['pytest>=6', 'numpy; python_version<"3.13"',
|
||||
'matplotlib; python_version<"3.13"']
|
||||
develop = ['mpmath[tests]', 'flake518>=1.5; python_version>="3.9"',
|
||||
'pytest-cov', 'wheel', 'build']
|
||||
gmpy = ['gmpy2>=2.1.0a4; platform_python_implementation!="PyPy" and python_version<"3.12"',
|
||||
'gmpy2>=2.2.0a1; platform_python_implementation!="PyPy" and python_version>="3.12"']
|
||||
'gmpy2>=2.2.0a1; platform_python_implementation!="PyPy" and python_version>="3.12" and python_version<"3.13"']
|
||||
docs = ['sphinx']
|
||||
ci = ['pytest-xdist']
|
||||
[tool.setuptools]
|
||||
|
||||
Reference in New Issue
Block a user