| | from ..libmp.backend import xrange |
| | import warnings |
| |
|
| | |
| |
|
| | 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 = {} |
| | |
| | |
| | |
| | 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)): |
| | |
| | A = args[0] |
| | self.__rows = len(A) |
| | self.__cols = len(A[0]) |
| | for i, row in enumerate(A): |
| | for j, a in enumerate(row): |
| | |
| | self[i, j] = a |
| | else: |
| | |
| | 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): |
| | |
| | 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): |
| | |
| | res = [] |
| | |
| | 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]) |
| | |
| | for i, row in enumerate(res): |
| | for j, elem in enumerate(row): |
| | |
| | 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. |
| | """ |
| | |
| | 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: |
| | 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] |
| | ''' |
| | |
| | if isinstance(key, int) or isinstance(key,slice): |
| | |
| | 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): |
| |
|
| | |
| | if isinstance(key[0],slice): |
| | |
| | if (key[0].start is None or key[0].start >= 0) and \ |
| | (key[0].stop is None or key[0].stop <= self.__rows+1): |
| | |
| | rows = xrange(*key[0].indices(self.__rows)) |
| | else: |
| | raise IndexError('Row index out of bounds') |
| | else: |
| | |
| | rows = [key[0]] |
| |
|
| | |
| | if isinstance(key[1],slice): |
| | |
| | if (key[1].start is None or key[1].start >= 0) and \ |
| | (key[1].stop is None or key[1].stop <= self.__cols+1): |
| | |
| | columns = xrange(*key[1].indices(self.__cols)) |
| | else: |
| | raise IndexError('Column index out of bounds') |
| |
|
| | else: |
| | |
| | columns = [key[1]] |
| |
|
| | |
| | m = self.ctx.matrix(len(rows),len(columns)) |
| |
|
| | |
| | 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: |
| | |
| | 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): |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if isinstance(key, int) or isinstance(key,slice): |
| | |
| | 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): |
| | |
| | if isinstance(key[0],slice): |
| | |
| | if (key[0].start is None or key[0].start >= 0) and \ |
| | (key[0].stop is None or key[0].stop <= self.__rows+1): |
| | |
| | rows = xrange(*key[0].indices(self.__rows)) |
| | else: |
| | raise IndexError('Row index out of bounds') |
| | else: |
| | |
| | rows = [key[0]] |
| | |
| | if isinstance(key[1],slice): |
| | |
| | if (key[1].start is None or key[1].start >= 0) and \ |
| | (key[1].stop is None or key[1].stop <= self.__cols+1): |
| | |
| | columns = xrange(*key[1].indices(self.__cols)) |
| | else: |
| | raise IndexError('Column index out of bounds') |
| | else: |
| | |
| | columns = [key[1]] |
| | |
| | if isinstance(value,self.ctx.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))) |
| | else: |
| | raise ValueError('Dimensions do not match') |
| | else: |
| | |
| | value = self.ctx.convert(value) |
| | for i in rows: |
| | for j in columns: |
| | self.__set_element((i,j), value) |
| | else: |
| | |
| | |
| | if key[0] >= self.__rows or key[1] >= self.__cols: |
| | raise IndexError('matrix index out of range') |
| | |
| | value = self.ctx.convert(value) |
| | if value: |
| | 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): |
| | |
| | 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: |
| | |
| | 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): |
| | |
| | if isinstance(other, self.ctx.matrix): |
| | raise TypeError("other should not be type of ctx.matrix") |
| | return self.__mul__(other) |
| |
|
| | def __pow__(self, other): |
| | |
| | |
| | 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): |
| | |
| | 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: |
| | |
| | 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 |
| |
|
| | 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): |
| | |
| | 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() |
| |
|