12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001 |
- 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.4 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 TODO: use Strassen's method?
- if self.__cols != other.__rows:
- raise ValueError('dimensions not compatible for multiplication')
- new = self.ctx.matrix(self.__rows, other.__cols)
- for i in xrange(self.__rows):
- for j in xrange(other.__cols):
- new[i, j] = self.ctx.fdot((self[i,k], other[k,j])
- 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()
|