Coverage for /usr/lib/python3/dist-packages/mpmath/matrices/matrices.py: 15%
397 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from ..libmp.backend import xrange
2import warnings
4# TODO: interpret list as vectors (for multiplication)
6rowsep = '\n'
7colsep = ' '
9class _matrix(object):
10 """
11 Numerical matrix.
13 Specify the dimensions or the data as a nested list.
14 Elements default to zero.
15 Use a flat list to create a column vector easily.
17 The datatype of the context (mpf for mp, mpi for iv, and float for fp) is used to store the data.
19 Creating matrices
20 -----------------
22 Matrices in mpmath are implemented using dictionaries. Only non-zero values
23 are stored, so it is cheap to represent sparse matrices.
25 The most basic way to create one is to use the ``matrix`` class directly.
26 You can create an empty matrix specifying the dimensions:
28 >>> from mpmath import *
29 >>> mp.dps = 15
30 >>> matrix(2)
31 matrix(
32 [['0.0', '0.0'],
33 ['0.0', '0.0']])
34 >>> matrix(2, 3)
35 matrix(
36 [['0.0', '0.0', '0.0'],
37 ['0.0', '0.0', '0.0']])
39 Calling ``matrix`` with one dimension will create a square matrix.
41 To access the dimensions of a matrix, use the ``rows`` or ``cols`` keyword:
43 >>> A = matrix(3, 2)
44 >>> A
45 matrix(
46 [['0.0', '0.0'],
47 ['0.0', '0.0'],
48 ['0.0', '0.0']])
49 >>> A.rows
50 3
51 >>> A.cols
52 2
54 You can also change the dimension of an existing matrix. This will set the
55 new elements to 0. If the new dimension is smaller than before, the
56 concerning elements are discarded:
58 >>> A.rows = 2
59 >>> A
60 matrix(
61 [['0.0', '0.0'],
62 ['0.0', '0.0']])
64 Internally ``mpmathify`` is used every time an element is set. This
65 is done using the syntax A[row,column], counting from 0:
67 >>> A = matrix(2)
68 >>> A[1,1] = 1 + 1j
69 >>> A
70 matrix(
71 [['0.0', '0.0'],
72 ['0.0', mpc(real='1.0', imag='1.0')]])
74 A more comfortable way to create a matrix lets you use nested lists:
76 >>> matrix([[1, 2], [3, 4]])
77 matrix(
78 [['1.0', '2.0'],
79 ['3.0', '4.0']])
81 Convenient advanced functions are available for creating various standard
82 matrices, see ``zeros``, ``ones``, ``diag``, ``eye``, ``randmatrix`` and
83 ``hilbert``.
85 Vectors
86 .......
88 Vectors may also be represented by the ``matrix`` class (with rows = 1 or cols = 1).
89 For vectors there are some things which make life easier. A column vector can
90 be created using a flat list, a row vectors using an almost flat nested list::
92 >>> matrix([1, 2, 3])
93 matrix(
94 [['1.0'],
95 ['2.0'],
96 ['3.0']])
97 >>> matrix([[1, 2, 3]])
98 matrix(
99 [['1.0', '2.0', '3.0']])
101 Optionally vectors can be accessed like lists, using only a single index::
103 >>> x = matrix([1, 2, 3])
104 >>> x[1]
105 mpf('2.0')
106 >>> x[1,0]
107 mpf('2.0')
109 Other
110 .....
112 Like you probably expected, matrices can be printed::
114 >>> print randmatrix(3) # doctest:+SKIP
115 [ 0.782963853573023 0.802057689719883 0.427895717335467]
116 [0.0541876859348597 0.708243266653103 0.615134039977379]
117 [ 0.856151514955773 0.544759264818486 0.686210904770947]
119 Use ``nstr`` or ``nprint`` to specify the number of digits to print::
121 >>> nprint(randmatrix(5), 3) # doctest:+SKIP
122 [2.07e-1 1.66e-1 5.06e-1 1.89e-1 8.29e-1]
123 [6.62e-1 6.55e-1 4.47e-1 4.82e-1 2.06e-2]
124 [4.33e-1 7.75e-1 6.93e-2 2.86e-1 5.71e-1]
125 [1.01e-1 2.53e-1 6.13e-1 3.32e-1 2.59e-1]
126 [1.56e-1 7.27e-2 6.05e-1 6.67e-2 2.79e-1]
128 As matrices are mutable, you will need to copy them sometimes::
130 >>> A = matrix(2)
131 >>> A
132 matrix(
133 [['0.0', '0.0'],
134 ['0.0', '0.0']])
135 >>> B = A.copy()
136 >>> B[0,0] = 1
137 >>> B
138 matrix(
139 [['1.0', '0.0'],
140 ['0.0', '0.0']])
141 >>> A
142 matrix(
143 [['0.0', '0.0'],
144 ['0.0', '0.0']])
146 Finally, it is possible to convert a matrix to a nested list. This is very useful,
147 as most Python libraries involving matrices or arrays (namely NumPy or SymPy)
148 support this format::
150 >>> B.tolist()
151 [[mpf('1.0'), mpf('0.0')], [mpf('0.0'), mpf('0.0')]]
154 Matrix operations
155 -----------------
157 You can add and subtract matrices of compatible dimensions::
159 >>> A = matrix([[1, 2], [3, 4]])
160 >>> B = matrix([[-2, 4], [5, 9]])
161 >>> A + B
162 matrix(
163 [['-1.0', '6.0'],
164 ['8.0', '13.0']])
165 >>> A - B
166 matrix(
167 [['3.0', '-2.0'],
168 ['-2.0', '-5.0']])
169 >>> A + ones(3) # doctest:+ELLIPSIS
170 Traceback (most recent call last):
171 ...
172 ValueError: incompatible dimensions for addition
174 It is possible to multiply or add matrices and scalars. In the latter case the
175 operation will be done element-wise::
177 >>> A * 2
178 matrix(
179 [['2.0', '4.0'],
180 ['6.0', '8.0']])
181 >>> A / 4
182 matrix(
183 [['0.25', '0.5'],
184 ['0.75', '1.0']])
185 >>> A - 1
186 matrix(
187 [['0.0', '1.0'],
188 ['2.0', '3.0']])
190 Of course you can perform matrix multiplication, if the dimensions are
191 compatible, using ``@`` (for Python >= 3.5) or ``*``. For clarity, ``@`` is
192 recommended (`PEP 465 <https://www.python.org/dev/peps/pep-0465/>`), because
193 the meaning of ``*`` is different in many other Python libraries such as NumPy.
195 >>> A @ B # doctest:+SKIP
196 matrix(
197 [['8.0', '22.0'],
198 ['14.0', '48.0']])
199 >>> A * B # same as A @ B
200 matrix(
201 [['8.0', '22.0'],
202 ['14.0', '48.0']])
203 >>> matrix([[1, 2, 3]]) * matrix([[-6], [7], [-2]])
204 matrix(
205 [['2.0']])
207 ..
208 COMMENT: TODO: the above "doctest:+SKIP" may be removed as soon as we
209 have dropped support for Python 3.4 and below.
211 You can raise powers of square matrices::
213 >>> A**2
214 matrix(
215 [['7.0', '10.0'],
216 ['15.0', '22.0']])
218 Negative powers will calculate the inverse::
220 >>> A**-1
221 matrix(
222 [['-2.0', '1.0'],
223 ['1.5', '-0.5']])
224 >>> A * A**-1
225 matrix(
226 [['1.0', '1.0842021724855e-19'],
227 ['-2.16840434497101e-19', '1.0']])
231 Matrix transposition is straightforward::
233 >>> A = ones(2, 3)
234 >>> A
235 matrix(
236 [['1.0', '1.0', '1.0'],
237 ['1.0', '1.0', '1.0']])
238 >>> A.T
239 matrix(
240 [['1.0', '1.0'],
241 ['1.0', '1.0'],
242 ['1.0', '1.0']])
244 Norms
245 .....
247 Sometimes you need to know how "large" a matrix or vector is. Due to their
248 multidimensional nature it's not possible to compare them, but there are
249 several functions to map a matrix or a vector to a positive real number, the
250 so called norms.
252 For vectors the p-norm is intended, usually the 1-, the 2- and the oo-norm are
253 used.
255 >>> x = matrix([-10, 2, 100])
256 >>> norm(x, 1)
257 mpf('112.0')
258 >>> norm(x, 2)
259 mpf('100.5186549850325')
260 >>> norm(x, inf)
261 mpf('100.0')
263 Please note that the 2-norm is the most used one, though it is more expensive
264 to calculate than the 1- or oo-norm.
266 It is possible to generalize some vector norms to matrix norm::
268 >>> A = matrix([[1, -1000], [100, 50]])
269 >>> mnorm(A, 1)
270 mpf('1050.0')
271 >>> mnorm(A, inf)
272 mpf('1001.0')
273 >>> mnorm(A, 'F')
274 mpf('1006.2310867787777')
276 The last norm (the "Frobenius-norm") is an approximation for the 2-norm, which
277 is hard to calculate and not available. The Frobenius-norm lacks some
278 mathematical properties you might expect from a norm.
279 """
281 def __init__(self, *args, **kwargs):
282 self.__data = {}
283 # LU decompostion cache, this is useful when solving the same system
284 # multiple times, when calculating the inverse and when calculating the
285 # determinant
286 self._LU = None
287 if "force_type" in kwargs:
288 warnings.warn("The force_type argument was removed, it did not work"
289 " properly anyway. If you want to force floating-point or"
290 " interval computations, use the respective methods from `fp`"
291 " or `mp` instead, e.g., `fp.matrix()` or `iv.matrix()`."
292 " If you want to truncate values to integer, use .apply(int) instead.")
293 if isinstance(args[0], (list, tuple)):
294 if isinstance(args[0][0], (list, tuple)):
295 # interpret nested list as matrix
296 A = args[0]
297 self.__rows = len(A)
298 self.__cols = len(A[0])
299 for i, row in enumerate(A):
300 for j, a in enumerate(row):
301 # note: this will call __setitem__ which will call self.ctx.convert() to convert the datatype.
302 self[i, j] = a
303 else:
304 # interpret list as row vector
305 v = args[0]
306 self.__rows = len(v)
307 self.__cols = 1
308 for i, e in enumerate(v):
309 self[i, 0] = e
310 elif isinstance(args[0], int):
311 # create empty matrix of given dimensions
312 if len(args) == 1:
313 self.__rows = self.__cols = args[0]
314 else:
315 if not isinstance(args[1], int):
316 raise TypeError("expected int")
317 self.__rows = args[0]
318 self.__cols = args[1]
319 elif isinstance(args[0], _matrix):
320 A = args[0]
321 self.__rows = A._matrix__rows
322 self.__cols = A._matrix__cols
323 for i in xrange(A.__rows):
324 for j in xrange(A.__cols):
325 self[i, j] = A[i, j]
326 elif hasattr(args[0], 'tolist'):
327 A = self.ctx.matrix(args[0].tolist())
328 self.__data = A._matrix__data
329 self.__rows = A._matrix__rows
330 self.__cols = A._matrix__cols
331 else:
332 raise TypeError('could not interpret given arguments')
334 def apply(self, f):
335 """
336 Return a copy of self with the function `f` applied elementwise.
337 """
338 new = self.ctx.matrix(self.__rows, self.__cols)
339 for i in xrange(self.__rows):
340 for j in xrange(self.__cols):
341 new[i,j] = f(self[i,j])
342 return new
344 def __nstr__(self, n=None, **kwargs):
345 # Build table of string representations of the elements
346 res = []
347 # Track per-column max lengths for pretty alignment
348 maxlen = [0] * self.cols
349 for i in range(self.rows):
350 res.append([])
351 for j in range(self.cols):
352 if n:
353 string = self.ctx.nstr(self[i,j], n, **kwargs)
354 else:
355 string = str(self[i,j])
356 res[-1].append(string)
357 maxlen[j] = max(len(string), maxlen[j])
358 # Patch strings together
359 for i, row in enumerate(res):
360 for j, elem in enumerate(row):
361 # Pad each element up to maxlen so the columns line up
362 row[j] = elem.rjust(maxlen[j])
363 res[i] = "[" + colsep.join(row) + "]"
364 return rowsep.join(res)
366 def __str__(self):
367 return self.__nstr__()
369 def _toliststr(self, avoid_type=False):
370 """
371 Create a list string from a matrix.
373 If avoid_type: avoid multiple 'mpf's.
374 """
375 # XXX: should be something like self.ctx._types
376 typ = self.ctx.mpf
377 s = '['
378 for i in xrange(self.__rows):
379 s += '['
380 for j in xrange(self.__cols):
381 if not avoid_type or not isinstance(self[i,j], typ):
382 a = repr(self[i,j])
383 else:
384 a = "'" + str(self[i,j]) + "'"
385 s += a + ', '
386 s = s[:-2]
387 s += '],\n '
388 s = s[:-3]
389 s += ']'
390 return s
392 def tolist(self):
393 """
394 Convert the matrix to a nested list.
395 """
396 return [[self[i,j] for j in range(self.__cols)] for i in range(self.__rows)]
398 def __repr__(self):
399 if self.ctx.pretty:
400 return self.__str__()
401 s = 'matrix(\n'
402 s += self._toliststr(avoid_type=True) + ')'
403 return s
405 def __get_element(self, key):
406 '''
407 Fast extraction of the i,j element from the matrix
408 This function is for private use only because is unsafe:
409 1. Does not check on the value of key it expects key to be a integer tuple (i,j)
410 2. Does not check bounds
411 '''
412 if key in self.__data:
413 return self.__data[key]
414 else:
415 return self.ctx.zero
417 def __set_element(self, key, value):
418 '''
419 Fast assignment of the i,j element in the matrix
420 This function is unsafe:
421 1. Does not check on the value of key it expects key to be a integer tuple (i,j)
422 2. Does not check bounds
423 3. Does not check the value type
424 4. Does not reset the LU cache
425 '''
426 if value: # only store non-zeros
427 self.__data[key] = value
428 elif key in self.__data:
429 del self.__data[key]
432 def __getitem__(self, key):
433 '''
434 Getitem function for mp matrix class with slice index enabled
435 it allows the following assingments
436 scalar to a slice of the matrix
437 B = A[:,2:6]
438 '''
439 # Convert vector to matrix indexing
440 if isinstance(key, int) or isinstance(key,slice):
441 # only sufficent for vectors
442 if self.__rows == 1:
443 key = (0, key)
444 elif self.__cols == 1:
445 key = (key, 0)
446 else:
447 raise IndexError('insufficient indices for matrix')
449 if isinstance(key[0],slice) or isinstance(key[1],slice):
451 #Rows
452 if isinstance(key[0],slice):
453 #Check bounds
454 if (key[0].start is None or key[0].start >= 0) and \
455 (key[0].stop is None or key[0].stop <= self.__rows+1):
456 # Generate indices
457 rows = xrange(*key[0].indices(self.__rows))
458 else:
459 raise IndexError('Row index out of bounds')
460 else:
461 # Single row
462 rows = [key[0]]
464 # Columns
465 if isinstance(key[1],slice):
466 # Check bounds
467 if (key[1].start is None or key[1].start >= 0) and \
468 (key[1].stop is None or key[1].stop <= self.__cols+1):
469 # Generate indices
470 columns = xrange(*key[1].indices(self.__cols))
471 else:
472 raise IndexError('Column index out of bounds')
474 else:
475 # Single column
476 columns = [key[1]]
478 # Create matrix slice
479 m = self.ctx.matrix(len(rows),len(columns))
481 # Assign elements to the output matrix
482 for i,x in enumerate(rows):
483 for j,y in enumerate(columns):
484 m.__set_element((i,j),self.__get_element((x,y)))
486 return m
488 else:
489 # single element extraction
490 if key[0] >= self.__rows or key[1] >= self.__cols:
491 raise IndexError('matrix index out of range')
492 if key in self.__data:
493 return self.__data[key]
494 else:
495 return self.ctx.zero
497 def __setitem__(self, key, value):
498 # setitem function for mp matrix class with slice index enabled
499 # it allows the following assingments
500 # scalar to a slice of the matrix
501 # A[:,2:6] = 2.5
502 # submatrix to matrix (the value matrix should be the same size as the slice size)
503 # A[3,:] = B where A is n x m and B is n x 1
504 # Convert vector to matrix indexing
505 if isinstance(key, int) or isinstance(key,slice):
506 # only sufficent for vectors
507 if self.__rows == 1:
508 key = (0, key)
509 elif self.__cols == 1:
510 key = (key, 0)
511 else:
512 raise IndexError('insufficient indices for matrix')
513 # Slice indexing
514 if isinstance(key[0],slice) or isinstance(key[1],slice):
515 # Rows
516 if isinstance(key[0],slice):
517 # Check bounds
518 if (key[0].start is None or key[0].start >= 0) and \
519 (key[0].stop is None or key[0].stop <= self.__rows+1):
520 # generate row indices
521 rows = xrange(*key[0].indices(self.__rows))
522 else:
523 raise IndexError('Row index out of bounds')
524 else:
525 # Single row
526 rows = [key[0]]
527 # Columns
528 if isinstance(key[1],slice):
529 # Check bounds
530 if (key[1].start is None or key[1].start >= 0) and \
531 (key[1].stop is None or key[1].stop <= self.__cols+1):
532 # Generate column indices
533 columns = xrange(*key[1].indices(self.__cols))
534 else:
535 raise IndexError('Column index out of bounds')
536 else:
537 # Single column
538 columns = [key[1]]
539 # Assign slice with a scalar
540 if isinstance(value,self.ctx.matrix):
541 # Assign elements to matrix if input and output dimensions match
542 if len(rows) == value.rows and len(columns) == value.cols:
543 for i,x in enumerate(rows):
544 for j,y in enumerate(columns):
545 self.__set_element((x,y), value.__get_element((i,j)))
546 else:
547 raise ValueError('Dimensions do not match')
548 else:
549 # Assign slice with scalars
550 value = self.ctx.convert(value)
551 for i in rows:
552 for j in columns:
553 self.__set_element((i,j), value)
554 else:
555 # Single element assingment
556 # Check bounds
557 if key[0] >= self.__rows or key[1] >= self.__cols:
558 raise IndexError('matrix index out of range')
559 # Convert and store value
560 value = self.ctx.convert(value)
561 if value: # only store non-zeros
562 self.__data[key] = value
563 elif key in self.__data:
564 del self.__data[key]
566 if self._LU:
567 self._LU = None
568 return
570 def __iter__(self):
571 for i in xrange(self.__rows):
572 for j in xrange(self.__cols):
573 yield self[i,j]
575 def __mul__(self, other):
576 if isinstance(other, self.ctx.matrix):
577 # dot multiplication TODO: use Strassen's method?
578 if self.__cols != other.__rows:
579 raise ValueError('dimensions not compatible for multiplication')
580 new = self.ctx.matrix(self.__rows, other.__cols)
581 for i in xrange(self.__rows):
582 for j in xrange(other.__cols):
583 new[i, j] = self.ctx.fdot((self[i,k], other[k,j])
584 for k in xrange(other.__rows))
585 return new
586 else:
587 # try scalar multiplication
588 new = self.ctx.matrix(self.__rows, self.__cols)
589 for i in xrange(self.__rows):
590 for j in xrange(self.__cols):
591 new[i, j] = other * self[i, j]
592 return new
594 def __matmul__(self, other):
595 return self.__mul__(other)
597 def __rmul__(self, other):
598 # assume other is scalar and thus commutative
599 if isinstance(other, self.ctx.matrix):
600 raise TypeError("other should not be type of ctx.matrix")
601 return self.__mul__(other)
603 def __pow__(self, other):
604 # avoid cyclic import problems
605 #from linalg import inverse
606 if not isinstance(other, int):
607 raise ValueError('only integer exponents are supported')
608 if not self.__rows == self.__cols:
609 raise ValueError('only powers of square matrices are defined')
610 n = other
611 if n == 0:
612 return self.ctx.eye(self.__rows)
613 if n < 0:
614 n = -n
615 neg = True
616 else:
617 neg = False
618 i = n
619 y = 1
620 z = self.copy()
621 while i != 0:
622 if i % 2 == 1:
623 y = y * z
624 z = z*z
625 i = i // 2
626 if neg:
627 y = self.ctx.inverse(y)
628 return y
630 def __div__(self, other):
631 # assume other is scalar and do element-wise divison
632 assert not isinstance(other, self.ctx.matrix)
633 new = self.ctx.matrix(self.__rows, self.__cols)
634 for i in xrange(self.__rows):
635 for j in xrange(self.__cols):
636 new[i,j] = self[i,j] / other
637 return new
639 __truediv__ = __div__
641 def __add__(self, other):
642 if isinstance(other, self.ctx.matrix):
643 if not (self.__rows == other.__rows and self.__cols == other.__cols):
644 raise ValueError('incompatible dimensions for addition')
645 new = self.ctx.matrix(self.__rows, self.__cols)
646 for i in xrange(self.__rows):
647 for j in xrange(self.__cols):
648 new[i,j] = self[i,j] + other[i,j]
649 return new
650 else:
651 # assume other is scalar and add element-wise
652 new = self.ctx.matrix(self.__rows, self.__cols)
653 for i in xrange(self.__rows):
654 for j in xrange(self.__cols):
655 new[i,j] += self[i,j] + other
656 return new
658 def __radd__(self, other):
659 return self.__add__(other)
661 def __sub__(self, other):
662 if isinstance(other, self.ctx.matrix) and not (self.__rows == other.__rows
663 and self.__cols == other.__cols):
664 raise ValueError('incompatible dimensions for subtraction')
665 return self.__add__(other * (-1))
667 def __pos__(self):
668 """
669 +M returns a copy of M, rounded to current working precision.
670 """
671 return (+1) * self
673 def __neg__(self):
674 return (-1) * self
676 def __rsub__(self, other):
677 return -self + other
679 def __eq__(self, other):
680 return self.__rows == other.__rows and self.__cols == other.__cols \
681 and self.__data == other.__data
683 def __len__(self):
684 if self.rows == 1:
685 return self.cols
686 elif self.cols == 1:
687 return self.rows
688 else:
689 return self.rows # do it like numpy
691 def __getrows(self):
692 return self.__rows
694 def __setrows(self, value):
695 for key in self.__data.copy():
696 if key[0] >= value:
697 del self.__data[key]
698 self.__rows = value
700 rows = property(__getrows, __setrows, doc='number of rows')
702 def __getcols(self):
703 return self.__cols
705 def __setcols(self, value):
706 for key in self.__data.copy():
707 if key[1] >= value:
708 del self.__data[key]
709 self.__cols = value
711 cols = property(__getcols, __setcols, doc='number of columns')
713 def transpose(self):
714 new = self.ctx.matrix(self.__cols, self.__rows)
715 for i in xrange(self.__rows):
716 for j in xrange(self.__cols):
717 new[j,i] = self[i,j]
718 return new
720 T = property(transpose)
722 def conjugate(self):
723 return self.apply(self.ctx.conj)
725 def transpose_conj(self):
726 return self.conjugate().transpose()
728 H = property(transpose_conj)
730 def copy(self):
731 new = self.ctx.matrix(self.__rows, self.__cols)
732 new.__data = self.__data.copy()
733 return new
735 __copy__ = copy
737 def column(self, n):
738 m = self.ctx.matrix(self.rows, 1)
739 for i in range(self.rows):
740 m[i] = self[i,n]
741 return m
743class MatrixMethods(object):
745 def __init__(ctx):
746 # XXX: subclass
747 ctx.matrix = type('matrix', (_matrix,), {})
748 ctx.matrix.ctx = ctx
749 ctx.matrix.convert = ctx.convert
751 def eye(ctx, n, **kwargs):
752 """
753 Create square identity matrix n x n.
754 """
755 A = ctx.matrix(n, **kwargs)
756 for i in xrange(n):
757 A[i,i] = 1
758 return A
760 def diag(ctx, diagonal, **kwargs):
761 """
762 Create square diagonal matrix using given list.
764 Example:
765 >>> from mpmath import diag, mp
766 >>> mp.pretty = False
767 >>> diag([1, 2, 3])
768 matrix(
769 [['1.0', '0.0', '0.0'],
770 ['0.0', '2.0', '0.0'],
771 ['0.0', '0.0', '3.0']])
772 """
773 A = ctx.matrix(len(diagonal), **kwargs)
774 for i in xrange(len(diagonal)):
775 A[i,i] = diagonal[i]
776 return A
778 def zeros(ctx, *args, **kwargs):
779 """
780 Create matrix m x n filled with zeros.
781 One given dimension will create square matrix n x n.
783 Example:
784 >>> from mpmath import zeros, mp
785 >>> mp.pretty = False
786 >>> zeros(2)
787 matrix(
788 [['0.0', '0.0'],
789 ['0.0', '0.0']])
790 """
791 if len(args) == 1:
792 m = n = args[0]
793 elif len(args) == 2:
794 m = args[0]
795 n = args[1]
796 else:
797 raise TypeError('zeros expected at most 2 arguments, got %i' % len(args))
798 A = ctx.matrix(m, n, **kwargs)
799 for i in xrange(m):
800 for j in xrange(n):
801 A[i,j] = 0
802 return A
804 def ones(ctx, *args, **kwargs):
805 """
806 Create matrix m x n filled with ones.
807 One given dimension will create square matrix n x n.
809 Example:
810 >>> from mpmath import ones, mp
811 >>> mp.pretty = False
812 >>> ones(2)
813 matrix(
814 [['1.0', '1.0'],
815 ['1.0', '1.0']])
816 """
817 if len(args) == 1:
818 m = n = args[0]
819 elif len(args) == 2:
820 m = args[0]
821 n = args[1]
822 else:
823 raise TypeError('ones expected at most 2 arguments, got %i' % len(args))
824 A = ctx.matrix(m, n, **kwargs)
825 for i in xrange(m):
826 for j in xrange(n):
827 A[i,j] = 1
828 return A
830 def hilbert(ctx, m, n=None):
831 """
832 Create (pseudo) hilbert matrix m x n.
833 One given dimension will create hilbert matrix n x n.
835 The matrix is very ill-conditioned and symmetric, positive definite if
836 square.
837 """
838 if n is None:
839 n = m
840 A = ctx.matrix(m, n)
841 for i in xrange(m):
842 for j in xrange(n):
843 A[i,j] = ctx.one / (i + j + 1)
844 return A
846 def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs):
847 """
848 Create a random m x n matrix.
850 All values are >= min and <max.
851 n defaults to m.
853 Example:
854 >>> from mpmath import randmatrix
855 >>> randmatrix(2) # doctest:+SKIP
856 matrix(
857 [['0.53491598236191806', '0.57195669543302752'],
858 ['0.85589992269513615', '0.82444367501382143']])
859 """
860 if not n:
861 n = m
862 A = ctx.matrix(m, n, **kwargs)
863 for i in xrange(m):
864 for j in xrange(n):
865 A[i,j] = ctx.rand() * (max - min) + min
866 return A
868 def swap_row(ctx, A, i, j):
869 """
870 Swap row i with row j.
871 """
872 if i == j:
873 return
874 if isinstance(A, ctx.matrix):
875 for k in xrange(A.cols):
876 A[i,k], A[j,k] = A[j,k], A[i,k]
877 elif isinstance(A, list):
878 A[i], A[j] = A[j], A[i]
879 else:
880 raise TypeError('could not interpret type')
882 def extend(ctx, A, b):
883 """
884 Extend matrix A with column b and return result.
885 """
886 if not isinstance(A, ctx.matrix):
887 raise TypeError("A should be a type of ctx.matrix")
888 if A.rows != len(b):
889 raise ValueError("Value should be equal to len(b)")
890 A = A.copy()
891 A.cols += 1
892 for i in xrange(A.rows):
893 A[i, A.cols-1] = b[i]
894 return A
896 def norm(ctx, x, p=2):
897 r"""
898 Gives the entrywise `p`-norm of an iterable *x*, i.e. the vector norm
899 `\left(\sum_k |x_k|^p\right)^{1/p}`, for any given `1 \le p \le \infty`.
901 Special cases:
903 If *x* is not iterable, this just returns ``absmax(x)``.
905 ``p=1`` gives the sum of absolute values.
907 ``p=2`` is the standard Euclidean vector norm.
909 ``p=inf`` gives the magnitude of the largest element.
911 For *x* a matrix, ``p=2`` is the Frobenius norm.
912 For operator matrix norms, use :func:`~mpmath.mnorm` instead.
914 You can use the string 'inf' as well as float('inf') or mpf('inf')
915 to specify the infinity norm.
917 **Examples**
919 >>> from mpmath import *
920 >>> mp.dps = 15; mp.pretty = False
921 >>> x = matrix([-10, 2, 100])
922 >>> norm(x, 1)
923 mpf('112.0')
924 >>> norm(x, 2)
925 mpf('100.5186549850325')
926 >>> norm(x, inf)
927 mpf('100.0')
929 """
930 try:
931 iter(x)
932 except TypeError:
933 return ctx.absmax(x)
934 if type(p) is not int:
935 p = ctx.convert(p)
936 if p == ctx.inf:
937 return max(ctx.absmax(i) for i in x)
938 elif p == 1:
939 return ctx.fsum(x, absolute=1)
940 elif p == 2:
941 return ctx.sqrt(ctx.fsum(x, absolute=1, squared=1))
942 elif p > 1:
943 return ctx.nthroot(ctx.fsum(abs(i)**p for i in x), p)
944 else:
945 raise ValueError('p has to be >= 1')
947 def mnorm(ctx, A, p=1):
948 r"""
949 Gives the matrix (operator) `p`-norm of A. Currently ``p=1`` and ``p=inf``
950 are supported:
952 ``p=1`` gives the 1-norm (maximal column sum)
954 ``p=inf`` gives the `\infty`-norm (maximal row sum).
955 You can use the string 'inf' as well as float('inf') or mpf('inf')
957 ``p=2`` (not implemented) for a square matrix is the usual spectral
958 matrix norm, i.e. the largest singular value.
960 ``p='f'`` (or 'F', 'fro', 'Frobenius, 'frobenius') gives the
961 Frobenius norm, which is the elementwise 2-norm. The Frobenius norm is an
962 approximation of the spectral norm and satisfies
964 .. math ::
966 \frac{1}{\sqrt{\mathrm{rank}(A)}} \|A\|_F \le \|A\|_2 \le \|A\|_F
968 The Frobenius norm lacks some mathematical properties that might
969 be expected of a norm.
971 For general elementwise `p`-norms, use :func:`~mpmath.norm` instead.
973 **Examples**
975 >>> from mpmath import *
976 >>> mp.dps = 15; mp.pretty = False
977 >>> A = matrix([[1, -1000], [100, 50]])
978 >>> mnorm(A, 1)
979 mpf('1050.0')
980 >>> mnorm(A, inf)
981 mpf('1001.0')
982 >>> mnorm(A, 'F')
983 mpf('1006.2310867787777')
985 """
986 A = ctx.matrix(A)
987 if type(p) is not int:
988 if type(p) is str and 'frobenius'.startswith(p.lower()):
989 return ctx.norm(A, 2)
990 p = ctx.convert(p)
991 m, n = A.rows, A.cols
992 if p == 1:
993 return max(ctx.fsum((A[i,j] for i in xrange(m)), absolute=1) for j in xrange(n))
994 elif p == ctx.inf:
995 return max(ctx.fsum((A[i,j] for j in xrange(n)), absolute=1) for i in xrange(m))
996 else:
997 raise NotImplementedError("matrix p-norm for arbitrary p")
999if __name__ == '__main__':
1000 import doctest
1001 doctest.testmod()