Spaces:
Sleeping
Sleeping
| from ..libmp.backend import xrange | |
| import warnings | |
| # TODO: interpret list as vectors (for multiplication) | |
| rowsep = '\n' | |
| colsep = ' ' | |
| class _matrix(object): | |
| """ | |
| Numerical matrix. | |
| Specify the dimensions or the data as a nested list. | |
| Elements default to zero. | |
| Use a flat list to create a column vector easily. | |
| The datatype of the context (mpf for mp, mpi for iv, and float for fp) is used to store the data. | |
| Creating matrices | |
| ----------------- | |
| Matrices in mpmath are implemented using dictionaries. Only non-zero values | |
| are stored, so it is cheap to represent sparse matrices. | |
| The most basic way to create one is to use the ``matrix`` class directly. | |
| You can create an empty matrix specifying the dimensions: | |
| >>> from mpmath import * | |
| >>> mp.dps = 15 | |
| >>> matrix(2) | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| >>> matrix(2, 3) | |
| matrix( | |
| [['0.0', '0.0', '0.0'], | |
| ['0.0', '0.0', '0.0']]) | |
| Calling ``matrix`` with one dimension will create a square matrix. | |
| To access the dimensions of a matrix, use the ``rows`` or ``cols`` keyword: | |
| >>> A = matrix(3, 2) | |
| >>> A | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| >>> A.rows | |
| 3 | |
| >>> A.cols | |
| 2 | |
| You can also change the dimension of an existing matrix. This will set the | |
| new elements to 0. If the new dimension is smaller than before, the | |
| concerning elements are discarded: | |
| >>> A.rows = 2 | |
| >>> A | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| Internally ``mpmathify`` is used every time an element is set. This | |
| is done using the syntax A[row,column], counting from 0: | |
| >>> A = matrix(2) | |
| >>> A[1,1] = 1 + 1j | |
| >>> A | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', mpc(real='1.0', imag='1.0')]]) | |
| A more comfortable way to create a matrix lets you use nested lists: | |
| >>> matrix([[1, 2], [3, 4]]) | |
| matrix( | |
| [['1.0', '2.0'], | |
| ['3.0', '4.0']]) | |
| Convenient advanced functions are available for creating various standard | |
| matrices, see ``zeros``, ``ones``, ``diag``, ``eye``, ``randmatrix`` and | |
| ``hilbert``. | |
| Vectors | |
| ....... | |
| Vectors may also be represented by the ``matrix`` class (with rows = 1 or cols = 1). | |
| For vectors there are some things which make life easier. A column vector can | |
| be created using a flat list, a row vectors using an almost flat nested list:: | |
| >>> matrix([1, 2, 3]) | |
| matrix( | |
| [['1.0'], | |
| ['2.0'], | |
| ['3.0']]) | |
| >>> matrix([[1, 2, 3]]) | |
| matrix( | |
| [['1.0', '2.0', '3.0']]) | |
| Optionally vectors can be accessed like lists, using only a single index:: | |
| >>> x = matrix([1, 2, 3]) | |
| >>> x[1] | |
| mpf('2.0') | |
| >>> x[1,0] | |
| mpf('2.0') | |
| Other | |
| ..... | |
| Like you probably expected, matrices can be printed:: | |
| >>> print randmatrix(3) # doctest:+SKIP | |
| [ 0.782963853573023 0.802057689719883 0.427895717335467] | |
| [0.0541876859348597 0.708243266653103 0.615134039977379] | |
| [ 0.856151514955773 0.544759264818486 0.686210904770947] | |
| Use ``nstr`` or ``nprint`` to specify the number of digits to print:: | |
| >>> nprint(randmatrix(5), 3) # doctest:+SKIP | |
| [2.07e-1 1.66e-1 5.06e-1 1.89e-1 8.29e-1] | |
| [6.62e-1 6.55e-1 4.47e-1 4.82e-1 2.06e-2] | |
| [4.33e-1 7.75e-1 6.93e-2 2.86e-1 5.71e-1] | |
| [1.01e-1 2.53e-1 6.13e-1 3.32e-1 2.59e-1] | |
| [1.56e-1 7.27e-2 6.05e-1 6.67e-2 2.79e-1] | |
| As matrices are mutable, you will need to copy them sometimes:: | |
| >>> A = matrix(2) | |
| >>> A | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| >>> B = A.copy() | |
| >>> B[0,0] = 1 | |
| >>> B | |
| matrix( | |
| [['1.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| >>> A | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| Finally, it is possible to convert a matrix to a nested list. This is very useful, | |
| as most Python libraries involving matrices or arrays (namely NumPy or SymPy) | |
| support this format:: | |
| >>> B.tolist() | |
| [[mpf('1.0'), mpf('0.0')], [mpf('0.0'), mpf('0.0')]] | |
| Matrix operations | |
| ----------------- | |
| You can add and subtract matrices of compatible dimensions:: | |
| >>> A = matrix([[1, 2], [3, 4]]) | |
| >>> B = matrix([[-2, 4], [5, 9]]) | |
| >>> A + B | |
| matrix( | |
| [['-1.0', '6.0'], | |
| ['8.0', '13.0']]) | |
| >>> A - B | |
| matrix( | |
| [['3.0', '-2.0'], | |
| ['-2.0', '-5.0']]) | |
| >>> A + ones(3) # doctest:+ELLIPSIS | |
| Traceback (most recent call last): | |
| ... | |
| ValueError: incompatible dimensions for addition | |
| It is possible to multiply or add matrices and scalars. In the latter case the | |
| operation will be done element-wise:: | |
| >>> A * 2 | |
| matrix( | |
| [['2.0', '4.0'], | |
| ['6.0', '8.0']]) | |
| >>> A / 4 | |
| matrix( | |
| [['0.25', '0.5'], | |
| ['0.75', '1.0']]) | |
| >>> A - 1 | |
| matrix( | |
| [['0.0', '1.0'], | |
| ['2.0', '3.0']]) | |
| Of course you can perform matrix multiplication, if the dimensions are | |
| compatible, using ``@`` (for Python >= 3.5) or ``*``. For clarity, ``@`` is | |
| recommended (`PEP 465 <https://www.python.org/dev/peps/pep-0465/>`), because | |
| the meaning of ``*`` is different in many other Python libraries such as NumPy. | |
| >>> A @ B # doctest:+SKIP | |
| matrix( | |
| [['8.0', '22.0'], | |
| ['14.0', '48.0']]) | |
| >>> A * B # same as A @ B | |
| matrix( | |
| [['8.0', '22.0'], | |
| ['14.0', '48.0']]) | |
| >>> matrix([[1, 2, 3]]) * matrix([[-6], [7], [-2]]) | |
| matrix( | |
| [['2.0']]) | |
| .. | |
| COMMENT: TODO: the above "doctest:+SKIP" may be removed as soon as we | |
| have dropped support for Python 3.5 and below. | |
| You can raise powers of square matrices:: | |
| >>> A**2 | |
| matrix( | |
| [['7.0', '10.0'], | |
| ['15.0', '22.0']]) | |
| Negative powers will calculate the inverse:: | |
| >>> A**-1 | |
| matrix( | |
| [['-2.0', '1.0'], | |
| ['1.5', '-0.5']]) | |
| >>> A * A**-1 | |
| matrix( | |
| [['1.0', '1.0842021724855e-19'], | |
| ['-2.16840434497101e-19', '1.0']]) | |
| Matrix transposition is straightforward:: | |
| >>> A = ones(2, 3) | |
| >>> A | |
| matrix( | |
| [['1.0', '1.0', '1.0'], | |
| ['1.0', '1.0', '1.0']]) | |
| >>> A.T | |
| matrix( | |
| [['1.0', '1.0'], | |
| ['1.0', '1.0'], | |
| ['1.0', '1.0']]) | |
| Norms | |
| ..... | |
| Sometimes you need to know how "large" a matrix or vector is. Due to their | |
| multidimensional nature it's not possible to compare them, but there are | |
| several functions to map a matrix or a vector to a positive real number, the | |
| so called norms. | |
| For vectors the p-norm is intended, usually the 1-, the 2- and the oo-norm are | |
| used. | |
| >>> x = matrix([-10, 2, 100]) | |
| >>> norm(x, 1) | |
| mpf('112.0') | |
| >>> norm(x, 2) | |
| mpf('100.5186549850325') | |
| >>> norm(x, inf) | |
| mpf('100.0') | |
| Please note that the 2-norm is the most used one, though it is more expensive | |
| to calculate than the 1- or oo-norm. | |
| It is possible to generalize some vector norms to matrix norm:: | |
| >>> A = matrix([[1, -1000], [100, 50]]) | |
| >>> mnorm(A, 1) | |
| mpf('1050.0') | |
| >>> mnorm(A, inf) | |
| mpf('1001.0') | |
| >>> mnorm(A, 'F') | |
| mpf('1006.2310867787777') | |
| The last norm (the "Frobenius-norm") is an approximation for the 2-norm, which | |
| is hard to calculate and not available. The Frobenius-norm lacks some | |
| mathematical properties you might expect from a norm. | |
| """ | |
| def __init__(self, *args, **kwargs): | |
| self.__data = {} | |
| # LU decompostion cache, this is useful when solving the same system | |
| # multiple times, when calculating the inverse and when calculating the | |
| # determinant | |
| self._LU = None | |
| if "force_type" in kwargs: | |
| warnings.warn("The force_type argument was removed, it did not work" | |
| " 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 isinstance(args[0], (list, tuple)): | |
| if isinstance(args[0][0], (list, tuple)): | |
| # interpret nested list as matrix | |
| A = args[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. | |
| self[i, j] = a | |
| else: | |
| # interpret list as row vector | |
| v = args[0] | |
| 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] | |
| else: | |
| if not isinstance(args[1], int): | |
| raise TypeError("expected int") | |
| 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 xrange(A.__rows): | |
| for j in xrange(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 | |
| else: | |
| raise TypeError('could not interpret given arguments') | |
| def apply(self, f): | |
| """ | |
| Return a copy of self with the function `f` applied elementwise. | |
| """ | |
| new = self.ctx.matrix(self.__rows, self.__cols) | |
| for i in xrange(self.__rows): | |
| for j in xrange(self.__cols): | |
| new[i,j] = f(self[i,j]) | |
| return new | |
| def __nstr__(self, n=None, **kwargs): | |
| # Build table of string representations of the elements | |
| res = [] | |
| # Track per-column max lengths for pretty alignment | |
| maxlen = [0] * self.cols | |
| for i in range(self.rows): | |
| res.append([]) | |
| for j in range(self.cols): | |
| if n: | |
| string = self.ctx.nstr(self[i,j], n, **kwargs) | |
| else: | |
| string = str(self[i,j]) | |
| res[-1].append(string) | |
| maxlen[j] = max(len(string), maxlen[j]) | |
| # Patch strings together | |
| 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) | |
| def __str__(self): | |
| return self.__nstr__() | |
| def _toliststr(self, avoid_type=False): | |
| """ | |
| Create a list string from a matrix. | |
| If avoid_type: avoid multiple 'mpf's. | |
| """ | |
| # XXX: should be something like self.ctx._types | |
| typ = self.ctx.mpf | |
| s = '[' | |
| for i in xrange(self.__rows): | |
| s += '[' | |
| for j in xrange(self.__cols): | |
| if not avoid_type or not isinstance(self[i,j], typ): | |
| a = repr(self[i,j]) | |
| else: | |
| a = "'" + str(self[i,j]) + "'" | |
| s += a + ', ' | |
| s = s[:-2] | |
| s += '],\n ' | |
| s = s[:-3] | |
| s += ']' | |
| return s | |
| def tolist(self): | |
| """ | |
| Convert the matrix to a nested list. | |
| """ | |
| 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) + ')' | |
| return s | |
| 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] | |
| else: | |
| return self.ctx.zero | |
| def __set_element(self, key, value): | |
| ''' | |
| Fast assignment of the i,j element in the matrix | |
| This function 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 | |
| 3. Does not check the value type | |
| 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] | |
| def __getitem__(self, key): | |
| ''' | |
| Getitem function for mp matrix class with slice index enabled | |
| it allows the following assingments | |
| scalar to a slice of the matrix | |
| B = A[:,2:6] | |
| ''' | |
| # Convert vector to matrix indexing | |
| if isinstance(key, int) or isinstance(key,slice): | |
| # only sufficent for vectors | |
| if self.__rows == 1: | |
| key = (0, key) | |
| elif self.__cols == 1: | |
| key = (key, 0) | |
| else: | |
| raise IndexError('insufficient indices for matrix') | |
| if isinstance(key[0],slice) or isinstance(key[1],slice): | |
| #Rows | |
| 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): | |
| # Generate indices | |
| rows = xrange(*key[0].indices(self.__rows)) | |
| else: | |
| raise IndexError('Row index out of bounds') | |
| else: | |
| # Single row | |
| rows = [key[0]] | |
| # Columns | |
| 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): | |
| # Generate indices | |
| columns = xrange(*key[1].indices(self.__cols)) | |
| else: | |
| raise IndexError('Column index out of bounds') | |
| else: | |
| # Single column | |
| columns = [key[1]] | |
| # Create matrix slice | |
| m = self.ctx.matrix(len(rows),len(columns)) | |
| # 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))) | |
| return m | |
| else: | |
| # single element extraction | |
| 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] | |
| else: | |
| return self.ctx.zero | |
| def __setitem__(self, key, value): | |
| # setitem function for mp matrix class with slice index enabled | |
| # it allows the following assingments | |
| # scalar to a slice of the matrix | |
| # A[:,2:6] = 2.5 | |
| # submatrix to matrix (the value matrix should be the same size as the slice size) | |
| # A[3,:] = B where A is n x m and B is n x 1 | |
| # Convert vector to matrix indexing | |
| if isinstance(key, int) or isinstance(key,slice): | |
| # only sufficent for vectors | |
| if self.__rows == 1: | |
| key = (0, key) | |
| elif self.__cols == 1: | |
| key = (key, 0) | |
| else: | |
| raise IndexError('insufficient indices for matrix') | |
| # Slice indexing | |
| if isinstance(key[0],slice) or isinstance(key[1],slice): | |
| # Rows | |
| 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): | |
| # generate row indices | |
| rows = xrange(*key[0].indices(self.__rows)) | |
| else: | |
| raise IndexError('Row index out of bounds') | |
| else: | |
| # Single row | |
| rows = [key[0]] | |
| # Columns | |
| 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): | |
| # Generate column indices | |
| columns = xrange(*key[1].indices(self.__cols)) | |
| else: | |
| raise IndexError('Column index out of bounds') | |
| else: | |
| # Single column | |
| columns = [key[1]] | |
| # Assign slice with a scalar | |
| if isinstance(value,self.ctx.matrix): | |
| # Assign elements to matrix if input and output dimensions match | |
| 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))) | |
| else: | |
| raise ValueError('Dimensions do not match') | |
| else: | |
| # Assign slice with scalars | |
| value = self.ctx.convert(value) | |
| for i in rows: | |
| for j in columns: | |
| self.__set_element((i,j), value) | |
| else: | |
| # Single element assingment | |
| # Check bounds | |
| 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] | |
| if self._LU: | |
| self._LU = None | |
| return | |
| def __iter__(self): | |
| for i in xrange(self.__rows): | |
| for j in xrange(self.__cols): | |
| yield self[i,j] | |
| def __mul__(self, other): | |
| if isinstance(other, self.ctx.matrix): | |
| # dot multiplication | |
| if self.__cols != other.__rows: | |
| raise ValueError('dimensions not compatible for multiplication') | |
| new = self.ctx.matrix(self.__rows, other.__cols) | |
| self_zero = self.ctx.zero | |
| self_get = self.__data.get | |
| other_zero = other.ctx.zero | |
| other_get = other.__data.get | |
| for i in xrange(self.__rows): | |
| for j in xrange(other.__cols): | |
| new[i, j] = self.ctx.fdot((self_get((i,k), self_zero), other_get((k,j), other_zero)) | |
| for k in xrange(other.__rows)) | |
| return new | |
| else: | |
| # try scalar multiplication | |
| new = self.ctx.matrix(self.__rows, self.__cols) | |
| for i in xrange(self.__rows): | |
| for j in xrange(self.__cols): | |
| new[i, j] = other * self[i, j] | |
| return new | |
| def __matmul__(self, other): | |
| return self.__mul__(other) | |
| def __rmul__(self, other): | |
| # assume other is scalar and thus commutative | |
| if isinstance(other, self.ctx.matrix): | |
| raise TypeError("other should not be type of ctx.matrix") | |
| return self.__mul__(other) | |
| def __pow__(self, other): | |
| # avoid cyclic import problems | |
| #from linalg import inverse | |
| if not isinstance(other, int): | |
| raise ValueError('only integer exponents are supported') | |
| 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) | |
| if n < 0: | |
| n = -n | |
| neg = True | |
| else: | |
| neg = False | |
| i = n | |
| y = 1 | |
| z = self.copy() | |
| while i != 0: | |
| if i % 2 == 1: | |
| y = y * z | |
| z = z*z | |
| i = i // 2 | |
| if neg: | |
| y = self.ctx.inverse(y) | |
| return y | |
| def __div__(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 xrange(self.__rows): | |
| for j in xrange(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): | |
| raise ValueError('incompatible dimensions for addition') | |
| new = self.ctx.matrix(self.__rows, self.__cols) | |
| for i in xrange(self.__rows): | |
| for j in xrange(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 xrange(self.__rows): | |
| for j in xrange(self.__cols): | |
| new[i,j] += self[i,j] + other | |
| return new | |
| def __radd__(self, other): | |
| 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): | |
| raise ValueError('incompatible dimensions for subtraction') | |
| return self.__add__(other * (-1)) | |
| def __pos__(self): | |
| """ | |
| +M returns a copy of M, rounded to current working precision. | |
| """ | |
| return (+1) * self | |
| def __neg__(self): | |
| return (-1) * self | |
| def __rsub__(self, other): | |
| return -self + other | |
| def __eq__(self, other): | |
| return self.__rows == other.__rows and self.__cols == other.__cols \ | |
| and self.__data == other.__data | |
| def __len__(self): | |
| if self.rows == 1: | |
| return self.cols | |
| elif self.cols == 1: | |
| return self.rows | |
| else: | |
| return self.rows # do it like numpy | |
| def __getrows(self): | |
| return self.__rows | |
| def __setrows(self, value): | |
| for key in self.__data.copy(): | |
| if key[0] >= value: | |
| del self.__data[key] | |
| self.__rows = value | |
| rows = property(__getrows, __setrows, doc='number of rows') | |
| def __getcols(self): | |
| return self.__cols | |
| def __setcols(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') | |
| def transpose(self): | |
| new = self.ctx.matrix(self.__cols, self.__rows) | |
| for i in xrange(self.__rows): | |
| for j in xrange(self.__cols): | |
| new[j,i] = self[i,j] | |
| return new | |
| T = property(transpose) | |
| def conjugate(self): | |
| return self.apply(self.ctx.conj) | |
| def transpose_conj(self): | |
| return self.conjugate().transpose() | |
| H = property(transpose_conj) | |
| def copy(self): | |
| new = self.ctx.matrix(self.__rows, self.__cols) | |
| new.__data = self.__data.copy() | |
| return new | |
| __copy__ = copy | |
| def column(self, n): | |
| m = self.ctx.matrix(self.rows, 1) | |
| for i in range(self.rows): | |
| m[i] = self[i,n] | |
| return m | |
| class MatrixMethods(object): | |
| def __init__(ctx): | |
| # XXX: subclass | |
| ctx.matrix = type('matrix', (_matrix,), {}) | |
| ctx.matrix.ctx = ctx | |
| ctx.matrix.convert = ctx.convert | |
| def eye(ctx, n, **kwargs): | |
| """ | |
| Create square identity matrix n x n. | |
| """ | |
| A = ctx.matrix(n, **kwargs) | |
| for i in xrange(n): | |
| A[i,i] = 1 | |
| return A | |
| def diag(ctx, diagonal, **kwargs): | |
| """ | |
| Create square diagonal matrix using given list. | |
| Example: | |
| >>> from mpmath import diag, mp | |
| >>> mp.pretty = False | |
| >>> diag([1, 2, 3]) | |
| matrix( | |
| [['1.0', '0.0', '0.0'], | |
| ['0.0', '2.0', '0.0'], | |
| ['0.0', '0.0', '3.0']]) | |
| """ | |
| A = ctx.matrix(len(diagonal), **kwargs) | |
| for i in xrange(len(diagonal)): | |
| A[i,i] = diagonal[i] | |
| return A | |
| def zeros(ctx, *args, **kwargs): | |
| """ | |
| Create matrix m x n filled with zeros. | |
| One given dimension will create square matrix n x n. | |
| Example: | |
| >>> from mpmath import zeros, mp | |
| >>> mp.pretty = False | |
| >>> zeros(2) | |
| matrix( | |
| [['0.0', '0.0'], | |
| ['0.0', '0.0']]) | |
| """ | |
| if len(args) == 1: | |
| m = n = args[0] | |
| elif len(args) == 2: | |
| m = args[0] | |
| n = args[1] | |
| else: | |
| raise TypeError('zeros expected at most 2 arguments, got %i' % len(args)) | |
| A = ctx.matrix(m, n, **kwargs) | |
| for i in xrange(m): | |
| for j in xrange(n): | |
| A[i,j] = 0 | |
| return A | |
| def ones(ctx, *args, **kwargs): | |
| """ | |
| Create matrix m x n filled with ones. | |
| One given dimension will create square matrix n x n. | |
| Example: | |
| >>> from mpmath import ones, mp | |
| >>> mp.pretty = False | |
| >>> ones(2) | |
| matrix( | |
| [['1.0', '1.0'], | |
| ['1.0', '1.0']]) | |
| """ | |
| if len(args) == 1: | |
| m = n = args[0] | |
| elif len(args) == 2: | |
| m = args[0] | |
| n = args[1] | |
| else: | |
| raise TypeError('ones expected at most 2 arguments, got %i' % len(args)) | |
| A = ctx.matrix(m, n, **kwargs) | |
| for i in xrange(m): | |
| for j in xrange(n): | |
| A[i,j] = 1 | |
| return A | |
| def hilbert(ctx, m, n=None): | |
| """ | |
| Create (pseudo) hilbert matrix m x n. | |
| One given dimension will create hilbert matrix n x n. | |
| The matrix is very ill-conditioned and symmetric, positive definite if | |
| square. | |
| """ | |
| if n is None: | |
| n = m | |
| A = ctx.matrix(m, n) | |
| for i in xrange(m): | |
| for j in xrange(n): | |
| A[i,j] = ctx.one / (i + j + 1) | |
| return A | |
| def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs): | |
| """ | |
| Create a random m x n matrix. | |
| All values are >= min and <max. | |
| n defaults to m. | |
| Example: | |
| >>> from mpmath import randmatrix | |
| >>> randmatrix(2) # doctest:+SKIP | |
| matrix( | |
| [['0.53491598236191806', '0.57195669543302752'], | |
| ['0.85589992269513615', '0.82444367501382143']]) | |
| """ | |
| if not n: | |
| n = m | |
| A = ctx.matrix(m, n, **kwargs) | |
| for i in xrange(m): | |
| for j in xrange(n): | |
| A[i,j] = ctx.rand() * (max - min) + min | |
| return A | |
| def swap_row(ctx, A, i, j): | |
| """ | |
| Swap row i with row j. | |
| """ | |
| if i == j: | |
| return | |
| if isinstance(A, ctx.matrix): | |
| for k in xrange(A.cols): | |
| A[i,k], A[j,k] = A[j,k], A[i,k] | |
| elif isinstance(A, list): | |
| A[i], A[j] = A[j], A[i] | |
| else: | |
| raise TypeError('could not interpret type') | |
| def extend(ctx, A, b): | |
| """ | |
| Extend matrix A with column b and return result. | |
| """ | |
| if not isinstance(A, ctx.matrix): | |
| raise TypeError("A should be a type of ctx.matrix") | |
| if A.rows != len(b): | |
| raise ValueError("Value should be equal to len(b)") | |
| A = A.copy() | |
| A.cols += 1 | |
| for i in xrange(A.rows): | |
| A[i, A.cols-1] = b[i] | |
| return A | |
| def norm(ctx, x, p=2): | |
| r""" | |
| Gives the entrywise `p`-norm of an iterable *x*, i.e. the vector norm | |
| `\left(\sum_k |x_k|^p\right)^{1/p}`, for any given `1 \le p \le \infty`. | |
| Special cases: | |
| If *x* is not iterable, this just returns ``absmax(x)``. | |
| ``p=1`` gives the sum of absolute values. | |
| ``p=2`` is the standard Euclidean vector norm. | |
| ``p=inf`` gives the magnitude of the largest element. | |
| For *x* a matrix, ``p=2`` is the Frobenius norm. | |
| For operator matrix norms, use :func:`~mpmath.mnorm` instead. | |
| You can use the string 'inf' as well as float('inf') or mpf('inf') | |
| to specify the infinity norm. | |
| **Examples** | |
| >>> from mpmath import * | |
| >>> mp.dps = 15; mp.pretty = False | |
| >>> x = matrix([-10, 2, 100]) | |
| >>> norm(x, 1) | |
| mpf('112.0') | |
| >>> norm(x, 2) | |
| mpf('100.5186549850325') | |
| >>> norm(x, inf) | |
| mpf('100.0') | |
| """ | |
| try: | |
| iter(x) | |
| except TypeError: | |
| return ctx.absmax(x) | |
| if type(p) is not int: | |
| p = ctx.convert(p) | |
| if p == ctx.inf: | |
| return max(ctx.absmax(i) for i in x) | |
| elif p == 1: | |
| return ctx.fsum(x, absolute=1) | |
| elif p == 2: | |
| return ctx.sqrt(ctx.fsum(x, absolute=1, squared=1)) | |
| elif p > 1: | |
| return ctx.nthroot(ctx.fsum(abs(i)**p for i in x), p) | |
| else: | |
| raise ValueError('p has to be >= 1') | |
| def mnorm(ctx, A, p=1): | |
| r""" | |
| Gives the matrix (operator) `p`-norm of A. Currently ``p=1`` and ``p=inf`` | |
| are supported: | |
| ``p=1`` gives the 1-norm (maximal column sum) | |
| ``p=inf`` gives the `\infty`-norm (maximal row sum). | |
| You can use the string 'inf' as well as float('inf') or mpf('inf') | |
| ``p=2`` (not implemented) for a square matrix is the usual spectral | |
| matrix norm, i.e. the largest singular value. | |
| ``p='f'`` (or 'F', 'fro', 'Frobenius, 'frobenius') gives the | |
| Frobenius norm, which is the elementwise 2-norm. The Frobenius norm is an | |
| approximation of the spectral norm and satisfies | |
| .. math :: | |
| \frac{1}{\sqrt{\mathrm{rank}(A)}} \|A\|_F \le \|A\|_2 \le \|A\|_F | |
| The Frobenius norm lacks some mathematical properties that might | |
| be expected of a norm. | |
| For general elementwise `p`-norms, use :func:`~mpmath.norm` instead. | |
| **Examples** | |
| >>> from mpmath import * | |
| >>> mp.dps = 15; mp.pretty = False | |
| >>> A = matrix([[1, -1000], [100, 50]]) | |
| >>> mnorm(A, 1) | |
| mpf('1050.0') | |
| >>> mnorm(A, inf) | |
| mpf('1001.0') | |
| >>> mnorm(A, 'F') | |
| mpf('1006.2310867787777') | |
| """ | |
| A = ctx.matrix(A) | |
| if type(p) is not int: | |
| if type(p) is str and 'frobenius'.startswith(p.lower()): | |
| return ctx.norm(A, 2) | |
| p = ctx.convert(p) | |
| m, n = A.rows, A.cols | |
| if p == 1: | |
| return max(ctx.fsum((A[i,j] for i in xrange(m)), absolute=1) for j in xrange(n)) | |
| elif p == ctx.inf: | |
| return max(ctx.fsum((A[i,j] for j in xrange(n)), absolute=1) for i in xrange(m)) | |
| else: | |
| raise NotImplementedError("matrix p-norm for arbitrary p") | |
| if __name__ == '__main__': | |
| import doctest | |
| doctest.testmod() | |