Merge pull request #759 from skirpichev/misc

Misc fixes
This commit is contained in:
Sergey B Kirpichev
2024-02-28 07:55:40 +03:00
committed by GitHub
18 changed files with 226 additions and 183 deletions
+25 -18
View File
@@ -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__
-1
View File
@@ -5,7 +5,6 @@ to improve accuracy at extremely high zoom levels.
"""
import mpmath
import cmath
ctx = mpmath.fp
# ctx = mpmath.mp
+1 -1
View File
@@ -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__":
-2
View File
@@ -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
View File
@@ -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):
+1 -1
View File
@@ -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 -2
View File
@@ -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,
+2 -6
View File
@@ -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
View File
@@ -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 #
#----------------------------------------------------------------------------#
+8 -10
View File
@@ -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
View File
@@ -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")
+5 -5
View File
@@ -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)
+5
View File
@@ -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))
+3
View File
@@ -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]])
+8 -4
View File
@@ -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)
+34
View File
@@ -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]'''
+3
View File
@@ -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
View File
@@ -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]