12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227 |
- """
- Basic methods common to all matrices to be used
- when creating more advanced matrices (e.g., matrices over rings,
- etc.).
- """
- from collections import defaultdict
- from collections.abc import Iterable
- from inspect import isfunction
- from functools import reduce
- from sympy.assumptions.refine import refine
- from sympy.core import SympifyError, Add
- from sympy.core.basic import Atom
- from sympy.core.decorators import call_highest_priority
- from sympy.core.kind import Kind, NumberKind
- from sympy.core.logic import fuzzy_and, FuzzyBool
- from sympy.core.mod import Mod
- from sympy.core.singleton import S
- from sympy.core.symbol import Symbol
- from sympy.core.sympify import sympify
- from sympy.functions.elementary.complexes import Abs, re, im
- from .utilities import _dotprodsimp, _simplify
- from sympy.polys.polytools import Poly
- from sympy.utilities.iterables import flatten, is_sequence
- from sympy.utilities.misc import as_int, filldedent
- from sympy.tensor.array import NDimArray
- from .utilities import _get_intermediate_simp_bool
- class MatrixError(Exception):
- pass
- class ShapeError(ValueError, MatrixError):
- """Wrong matrix shape"""
- pass
- class NonSquareMatrixError(ShapeError):
- pass
- class NonInvertibleMatrixError(ValueError, MatrixError):
- """The matrix in not invertible (division by multidimensional zero error)."""
- pass
- class NonPositiveDefiniteMatrixError(ValueError, MatrixError):
- """The matrix is not a positive-definite matrix."""
- pass
- class MatrixRequired:
- """All subclasses of matrix objects must implement the
- required matrix properties listed here."""
- rows = None # type: int
- cols = None # type: int
- _simplify = None
- @classmethod
- def _new(cls, *args, **kwargs):
- """`_new` must, at minimum, be callable as
- `_new(rows, cols, mat) where mat is a flat list of the
- elements of the matrix."""
- raise NotImplementedError("Subclasses must implement this.")
- def __eq__(self, other):
- raise NotImplementedError("Subclasses must implement this.")
- def __getitem__(self, key):
- """Implementations of __getitem__ should accept ints, in which
- case the matrix is indexed as a flat list, tuples (i,j) in which
- case the (i,j) entry is returned, slices, or mixed tuples (a,b)
- where a and b are any combination of slices and integers."""
- raise NotImplementedError("Subclasses must implement this.")
- def __len__(self):
- """The total number of entries in the matrix."""
- raise NotImplementedError("Subclasses must implement this.")
- @property
- def shape(self):
- raise NotImplementedError("Subclasses must implement this.")
- class MatrixShaping(MatrixRequired):
- """Provides basic matrix shaping and extracting of submatrices"""
- def _eval_col_del(self, col):
- def entry(i, j):
- return self[i, j] if j < col else self[i, j + 1]
- return self._new(self.rows, self.cols - 1, entry)
- def _eval_col_insert(self, pos, other):
- def entry(i, j):
- if j < pos:
- return self[i, j]
- elif pos <= j < pos + other.cols:
- return other[i, j - pos]
- return self[i, j - other.cols]
- return self._new(self.rows, self.cols + other.cols, entry)
- def _eval_col_join(self, other):
- rows = self.rows
- def entry(i, j):
- if i < rows:
- return self[i, j]
- return other[i - rows, j]
- return classof(self, other)._new(self.rows + other.rows, self.cols,
- entry)
- def _eval_extract(self, rowsList, colsList):
- mat = list(self)
- cols = self.cols
- indices = (i * cols + j for i in rowsList for j in colsList)
- return self._new(len(rowsList), len(colsList),
- [mat[i] for i in indices])
- def _eval_get_diag_blocks(self):
- sub_blocks = []
- def recurse_sub_blocks(M):
- i = 1
- while i <= M.shape[0]:
- if i == 1:
- to_the_right = M[0, i:]
- to_the_bottom = M[i:, 0]
- else:
- to_the_right = M[:i, i:]
- to_the_bottom = M[i:, :i]
- if any(to_the_right) or any(to_the_bottom):
- i += 1
- continue
- else:
- sub_blocks.append(M[:i, :i])
- if M.shape == M[:i, :i].shape:
- return
- else:
- recurse_sub_blocks(M[i:, i:])
- return
- recurse_sub_blocks(self)
- return sub_blocks
- def _eval_row_del(self, row):
- def entry(i, j):
- return self[i, j] if i < row else self[i + 1, j]
- return self._new(self.rows - 1, self.cols, entry)
- def _eval_row_insert(self, pos, other):
- entries = list(self)
- insert_pos = pos * self.cols
- entries[insert_pos:insert_pos] = list(other)
- return self._new(self.rows + other.rows, self.cols, entries)
- def _eval_row_join(self, other):
- cols = self.cols
- def entry(i, j):
- if j < cols:
- return self[i, j]
- return other[i, j - cols]
- return classof(self, other)._new(self.rows, self.cols + other.cols,
- entry)
- def _eval_tolist(self):
- return [list(self[i,:]) for i in range(self.rows)]
- def _eval_todok(self):
- dok = {}
- rows, cols = self.shape
- for i in range(rows):
- for j in range(cols):
- val = self[i, j]
- if val != self.zero:
- dok[i, j] = val
- return dok
- def _eval_vec(self):
- rows = self.rows
- def entry(n, _):
- # we want to read off the columns first
- j = n // rows
- i = n - j * rows
- return self[i, j]
- return self._new(len(self), 1, entry)
- def _eval_vech(self, diagonal):
- c = self.cols
- v = []
- if diagonal:
- for j in range(c):
- for i in range(j, c):
- v.append(self[i, j])
- else:
- for j in range(c):
- for i in range(j + 1, c):
- v.append(self[i, j])
- return self._new(len(v), 1, v)
- def col_del(self, col):
- """Delete the specified column."""
- if col < 0:
- col += self.cols
- if not 0 <= col < self.cols:
- raise IndexError("Column {} is out of range.".format(col))
- return self._eval_col_del(col)
- def col_insert(self, pos, other):
- """Insert one or more columns at the given column position.
- Examples
- ========
- >>> from sympy import zeros, ones
- >>> M = zeros(3)
- >>> V = ones(3, 1)
- >>> M.col_insert(1, V)
- Matrix([
- [0, 1, 0, 0],
- [0, 1, 0, 0],
- [0, 1, 0, 0]])
- See Also
- ========
- col
- row_insert
- """
- # Allows you to build a matrix even if it is null matrix
- if not self:
- return type(self)(other)
- pos = as_int(pos)
- if pos < 0:
- pos = self.cols + pos
- if pos < 0:
- pos = 0
- elif pos > self.cols:
- pos = self.cols
- if self.rows != other.rows:
- raise ShapeError(
- "The matrices have incompatible number of rows ({} and {})"
- .format(self.rows, other.rows))
- return self._eval_col_insert(pos, other)
- def col_join(self, other):
- """Concatenates two matrices along self's last and other's first row.
- Examples
- ========
- >>> from sympy import zeros, ones
- >>> M = zeros(3)
- >>> V = ones(1, 3)
- >>> M.col_join(V)
- Matrix([
- [0, 0, 0],
- [0, 0, 0],
- [0, 0, 0],
- [1, 1, 1]])
- See Also
- ========
- col
- row_join
- """
- # A null matrix can always be stacked (see #10770)
- if self.rows == 0 and self.cols != other.cols:
- return self._new(0, other.cols, []).col_join(other)
- if self.cols != other.cols:
- raise ShapeError(
- "The matrices have incompatible number of columns ({} and {})"
- .format(self.cols, other.cols))
- return self._eval_col_join(other)
- def col(self, j):
- """Elementary column selector.
- Examples
- ========
- >>> from sympy import eye
- >>> eye(2).col(0)
- Matrix([
- [1],
- [0]])
- See Also
- ========
- row
- col_del
- col_join
- col_insert
- """
- return self[:, j]
- def extract(self, rowsList, colsList):
- r"""Return a submatrix by specifying a list of rows and columns.
- Negative indices can be given. All indices must be in the range
- $-n \le i < n$ where $n$ is the number of rows or columns.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(4, 3, range(12))
- >>> m
- Matrix([
- [0, 1, 2],
- [3, 4, 5],
- [6, 7, 8],
- [9, 10, 11]])
- >>> m.extract([0, 1, 3], [0, 1])
- Matrix([
- [0, 1],
- [3, 4],
- [9, 10]])
- Rows or columns can be repeated:
- >>> m.extract([0, 0, 1], [-1])
- Matrix([
- [2],
- [2],
- [5]])
- Every other row can be taken by using range to provide the indices:
- >>> m.extract(range(0, m.rows, 2), [-1])
- Matrix([
- [2],
- [8]])
- RowsList or colsList can also be a list of booleans, in which case
- the rows or columns corresponding to the True values will be selected:
- >>> m.extract([0, 1, 2, 3], [True, False, True])
- Matrix([
- [0, 2],
- [3, 5],
- [6, 8],
- [9, 11]])
- """
- if not is_sequence(rowsList) or not is_sequence(colsList):
- raise TypeError("rowsList and colsList must be iterable")
- # ensure rowsList and colsList are lists of integers
- if rowsList and all(isinstance(i, bool) for i in rowsList):
- rowsList = [index for index, item in enumerate(rowsList) if item]
- if colsList and all(isinstance(i, bool) for i in colsList):
- colsList = [index for index, item in enumerate(colsList) if item]
- # ensure everything is in range
- rowsList = [a2idx(k, self.rows) for k in rowsList]
- colsList = [a2idx(k, self.cols) for k in colsList]
- return self._eval_extract(rowsList, colsList)
- def get_diag_blocks(self):
- """Obtains the square sub-matrices on the main diagonal of a square matrix.
- Useful for inverting symbolic matrices or solving systems of
- linear equations which may be decoupled by having a block diagonal
- structure.
- Examples
- ========
- >>> from sympy import Matrix
- >>> from sympy.abc import x, y, z
- >>> A = Matrix([[1, 3, 0, 0], [y, z*z, 0, 0], [0, 0, x, 0], [0, 0, 0, 0]])
- >>> a1, a2, a3 = A.get_diag_blocks()
- >>> a1
- Matrix([
- [1, 3],
- [y, z**2]])
- >>> a2
- Matrix([[x]])
- >>> a3
- Matrix([[0]])
- """
- return self._eval_get_diag_blocks()
- @classmethod
- def hstack(cls, *args):
- """Return a matrix formed by joining args horizontally (i.e.
- by repeated application of row_join).
- Examples
- ========
- >>> from sympy import Matrix, eye
- >>> Matrix.hstack(eye(2), 2*eye(2))
- Matrix([
- [1, 0, 2, 0],
- [0, 1, 0, 2]])
- """
- if len(args) == 0:
- return cls._new()
- kls = type(args[0])
- return reduce(kls.row_join, args)
- def reshape(self, rows, cols):
- """Reshape the matrix. Total number of elements must remain the same.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(2, 3, lambda i, j: 1)
- >>> m
- Matrix([
- [1, 1, 1],
- [1, 1, 1]])
- >>> m.reshape(1, 6)
- Matrix([[1, 1, 1, 1, 1, 1]])
- >>> m.reshape(3, 2)
- Matrix([
- [1, 1],
- [1, 1],
- [1, 1]])
- """
- if self.rows * self.cols != rows * cols:
- raise ValueError("Invalid reshape parameters %d %d" % (rows, cols))
- return self._new(rows, cols, lambda i, j: self[i * cols + j])
- def row_del(self, row):
- """Delete the specified row."""
- if row < 0:
- row += self.rows
- if not 0 <= row < self.rows:
- raise IndexError("Row {} is out of range.".format(row))
- return self._eval_row_del(row)
- def row_insert(self, pos, other):
- """Insert one or more rows at the given row position.
- Examples
- ========
- >>> from sympy import zeros, ones
- >>> M = zeros(3)
- >>> V = ones(1, 3)
- >>> M.row_insert(1, V)
- Matrix([
- [0, 0, 0],
- [1, 1, 1],
- [0, 0, 0],
- [0, 0, 0]])
- See Also
- ========
- row
- col_insert
- """
- # Allows you to build a matrix even if it is null matrix
- if not self:
- return self._new(other)
- pos = as_int(pos)
- if pos < 0:
- pos = self.rows + pos
- if pos < 0:
- pos = 0
- elif pos > self.rows:
- pos = self.rows
- if self.cols != other.cols:
- raise ShapeError(
- "The matrices have incompatible number of columns ({} and {})"
- .format(self.cols, other.cols))
- return self._eval_row_insert(pos, other)
- def row_join(self, other):
- """Concatenates two matrices along self's last and rhs's first column
- Examples
- ========
- >>> from sympy import zeros, ones
- >>> M = zeros(3)
- >>> V = ones(3, 1)
- >>> M.row_join(V)
- Matrix([
- [0, 0, 0, 1],
- [0, 0, 0, 1],
- [0, 0, 0, 1]])
- See Also
- ========
- row
- col_join
- """
- # A null matrix can always be stacked (see #10770)
- if self.cols == 0 and self.rows != other.rows:
- return self._new(other.rows, 0, []).row_join(other)
- if self.rows != other.rows:
- raise ShapeError(
- "The matrices have incompatible number of rows ({} and {})"
- .format(self.rows, other.rows))
- return self._eval_row_join(other)
- def diagonal(self, k=0):
- """Returns the kth diagonal of self. The main diagonal
- corresponds to `k=0`; diagonals above and below correspond to
- `k > 0` and `k < 0`, respectively. The values of `self[i, j]`
- for which `j - i = k`, are returned in order of increasing
- `i + j`, starting with `i + j = |k|`.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(3, 3, lambda i, j: j - i); m
- Matrix([
- [ 0, 1, 2],
- [-1, 0, 1],
- [-2, -1, 0]])
- >>> _.diagonal()
- Matrix([[0, 0, 0]])
- >>> m.diagonal(1)
- Matrix([[1, 1]])
- >>> m.diagonal(-2)
- Matrix([[-2]])
- Even though the diagonal is returned as a Matrix, the element
- retrieval can be done with a single index:
- >>> Matrix.diag(1, 2, 3).diagonal()[1] # instead of [0, 1]
- 2
- See Also
- ========
- diag
- """
- rv = []
- k = as_int(k)
- r = 0 if k > 0 else -k
- c = 0 if r else k
- while True:
- if r == self.rows or c == self.cols:
- break
- rv.append(self[r, c])
- r += 1
- c += 1
- if not rv:
- raise ValueError(filldedent('''
- The %s diagonal is out of range [%s, %s]''' % (
- k, 1 - self.rows, self.cols - 1)))
- return self._new(1, len(rv), rv)
- def row(self, i):
- """Elementary row selector.
- Examples
- ========
- >>> from sympy import eye
- >>> eye(2).row(0)
- Matrix([[1, 0]])
- See Also
- ========
- col
- row_del
- row_join
- row_insert
- """
- return self[i, :]
- @property
- def shape(self):
- """The shape (dimensions) of the matrix as the 2-tuple (rows, cols).
- Examples
- ========
- >>> from sympy import zeros
- >>> M = zeros(2, 3)
- >>> M.shape
- (2, 3)
- >>> M.rows
- 2
- >>> M.cols
- 3
- """
- return (self.rows, self.cols)
- def todok(self):
- """Return the matrix as dictionary of keys.
- Examples
- ========
- >>> from sympy import Matrix
- >>> M = Matrix.eye(3)
- >>> M.todok()
- {(0, 0): 1, (1, 1): 1, (2, 2): 1}
- """
- return self._eval_todok()
- def tolist(self):
- """Return the Matrix as a nested Python list.
- Examples
- ========
- >>> from sympy import Matrix, ones
- >>> m = Matrix(3, 3, range(9))
- >>> m
- Matrix([
- [0, 1, 2],
- [3, 4, 5],
- [6, 7, 8]])
- >>> m.tolist()
- [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
- >>> ones(3, 0).tolist()
- [[], [], []]
- When there are no rows then it will not be possible to tell how
- many columns were in the original matrix:
- >>> ones(0, 3).tolist()
- []
- """
- if not self.rows:
- return []
- if not self.cols:
- return [[] for i in range(self.rows)]
- return self._eval_tolist()
- def todod(M):
- """Returns matrix as dict of dicts containing non-zero elements of the Matrix
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix([[0, 1],[0, 3]])
- >>> A
- Matrix([
- [0, 1],
- [0, 3]])
- >>> A.todod()
- {0: {1: 1}, 1: {1: 3}}
- """
- rowsdict = {}
- Mlol = M.tolist()
- for i, Mi in enumerate(Mlol):
- row = {j: Mij for j, Mij in enumerate(Mi) if Mij}
- if row:
- rowsdict[i] = row
- return rowsdict
- def vec(self):
- """Return the Matrix converted into a one column matrix by stacking columns
- Examples
- ========
- >>> from sympy import Matrix
- >>> m=Matrix([[1, 3], [2, 4]])
- >>> m
- Matrix([
- [1, 3],
- [2, 4]])
- >>> m.vec()
- Matrix([
- [1],
- [2],
- [3],
- [4]])
- See Also
- ========
- vech
- """
- return self._eval_vec()
- def vech(self, diagonal=True, check_symmetry=True):
- """Reshapes the matrix into a column vector by stacking the
- elements in the lower triangle.
- Parameters
- ==========
- diagonal : bool, optional
- If ``True``, it includes the diagonal elements.
- check_symmetry : bool, optional
- If ``True``, it checks whether the matrix is symmetric.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m=Matrix([[1, 2], [2, 3]])
- >>> m
- Matrix([
- [1, 2],
- [2, 3]])
- >>> m.vech()
- Matrix([
- [1],
- [2],
- [3]])
- >>> m.vech(diagonal=False)
- Matrix([[2]])
- Notes
- =====
- This should work for symmetric matrices and ``vech`` can
- represent symmetric matrices in vector form with less size than
- ``vec``.
- See Also
- ========
- vec
- """
- if not self.is_square:
- raise NonSquareMatrixError
- if check_symmetry and not self.is_symmetric():
- raise ValueError("The matrix is not symmetric.")
- return self._eval_vech(diagonal)
- @classmethod
- def vstack(cls, *args):
- """Return a matrix formed by joining args vertically (i.e.
- by repeated application of col_join).
- Examples
- ========
- >>> from sympy import Matrix, eye
- >>> Matrix.vstack(eye(2), 2*eye(2))
- Matrix([
- [1, 0],
- [0, 1],
- [2, 0],
- [0, 2]])
- """
- if len(args) == 0:
- return cls._new()
- kls = type(args[0])
- return reduce(kls.col_join, args)
- class MatrixSpecial(MatrixRequired):
- """Construction of special matrices"""
- @classmethod
- def _eval_diag(cls, rows, cols, diag_dict):
- """diag_dict is a defaultdict containing
- all the entries of the diagonal matrix."""
- def entry(i, j):
- return diag_dict[(i, j)]
- return cls._new(rows, cols, entry)
- @classmethod
- def _eval_eye(cls, rows, cols):
- vals = [cls.zero]*(rows*cols)
- vals[::cols+1] = [cls.one]*min(rows, cols)
- return cls._new(rows, cols, vals, copy=False)
- @classmethod
- def _eval_jordan_block(cls, size: int, eigenvalue, band='upper'):
- if band == 'lower':
- def entry(i, j):
- if i == j:
- return eigenvalue
- elif j + 1 == i:
- return cls.one
- return cls.zero
- else:
- def entry(i, j):
- if i == j:
- return eigenvalue
- elif i + 1 == j:
- return cls.one
- return cls.zero
- return cls._new(size, size, entry)
- @classmethod
- def _eval_ones(cls, rows, cols):
- def entry(i, j):
- return cls.one
- return cls._new(rows, cols, entry)
- @classmethod
- def _eval_zeros(cls, rows, cols):
- return cls._new(rows, cols, [cls.zero]*(rows*cols), copy=False)
- @classmethod
- def _eval_wilkinson(cls, n):
- def entry(i, j):
- return cls.one if i + 1 == j else cls.zero
- D = cls._new(2*n + 1, 2*n + 1, entry)
- wminus = cls.diag(list(range(-n, n + 1)), unpack=True) + D + D.T
- wplus = abs(cls.diag(list(range(-n, n + 1)), unpack=True)) + D + D.T
- return wminus, wplus
- @classmethod
- def diag(kls, *args, strict=False, unpack=True, rows=None, cols=None, **kwargs):
- """Returns a matrix with the specified diagonal.
- If matrices are passed, a block-diagonal matrix
- is created (i.e. the "direct sum" of the matrices).
- kwargs
- ======
- rows : rows of the resulting matrix; computed if
- not given.
- cols : columns of the resulting matrix; computed if
- not given.
- cls : class for the resulting matrix
- unpack : bool which, when True (default), unpacks a single
- sequence rather than interpreting it as a Matrix.
- strict : bool which, when False (default), allows Matrices to
- have variable-length rows.
- Examples
- ========
- >>> from sympy import Matrix
- >>> Matrix.diag(1, 2, 3)
- Matrix([
- [1, 0, 0],
- [0, 2, 0],
- [0, 0, 3]])
- The current default is to unpack a single sequence. If this is
- not desired, set `unpack=False` and it will be interpreted as
- a matrix.
- >>> Matrix.diag([1, 2, 3]) == Matrix.diag(1, 2, 3)
- True
- When more than one element is passed, each is interpreted as
- something to put on the diagonal. Lists are converted to
- matrices. Filling of the diagonal always continues from
- the bottom right hand corner of the previous item: this
- will create a block-diagonal matrix whether the matrices
- are square or not.
- >>> col = [1, 2, 3]
- >>> row = [[4, 5]]
- >>> Matrix.diag(col, row)
- Matrix([
- [1, 0, 0],
- [2, 0, 0],
- [3, 0, 0],
- [0, 4, 5]])
- When `unpack` is False, elements within a list need not all be
- of the same length. Setting `strict` to True would raise a
- ValueError for the following:
- >>> Matrix.diag([[1, 2, 3], [4, 5], [6]], unpack=False)
- Matrix([
- [1, 2, 3],
- [4, 5, 0],
- [6, 0, 0]])
- The type of the returned matrix can be set with the ``cls``
- keyword.
- >>> from sympy import ImmutableMatrix
- >>> from sympy.utilities.misc import func_name
- >>> func_name(Matrix.diag(1, cls=ImmutableMatrix))
- 'ImmutableDenseMatrix'
- A zero dimension matrix can be used to position the start of
- the filling at the start of an arbitrary row or column:
- >>> from sympy import ones
- >>> r2 = ones(0, 2)
- >>> Matrix.diag(r2, 1, 2)
- Matrix([
- [0, 0, 1, 0],
- [0, 0, 0, 2]])
- See Also
- ========
- eye
- diagonal
- .dense.diag
- .expressions.blockmatrix.BlockMatrix
- .sparsetools.banded
- """
- from sympy.matrices.matrices import MatrixBase
- from sympy.matrices.dense import Matrix
- from sympy.matrices import SparseMatrix
- klass = kwargs.get('cls', kls)
- if unpack and len(args) == 1 and is_sequence(args[0]) and \
- not isinstance(args[0], MatrixBase):
- args = args[0]
- # fill a default dict with the diagonal entries
- diag_entries = defaultdict(int)
- rmax = cmax = 0 # keep track of the biggest index seen
- for m in args:
- if isinstance(m, list):
- if strict:
- # if malformed, Matrix will raise an error
- _ = Matrix(m)
- r, c = _.shape
- m = _.tolist()
- else:
- r, c, smat = SparseMatrix._handle_creation_inputs(m)
- for (i, j), _ in smat.items():
- diag_entries[(i + rmax, j + cmax)] = _
- m = [] # to skip process below
- elif hasattr(m, 'shape'): # a Matrix
- # convert to list of lists
- r, c = m.shape
- m = m.tolist()
- else: # in this case, we're a single value
- diag_entries[(rmax, cmax)] = m
- rmax += 1
- cmax += 1
- continue
- # process list of lists
- for i, mi in enumerate(m):
- for j, _ in enumerate(mi):
- diag_entries[(i + rmax, j + cmax)] = _
- rmax += r
- cmax += c
- if rows is None:
- rows, cols = cols, rows
- if rows is None:
- rows, cols = rmax, cmax
- else:
- cols = rows if cols is None else cols
- if rows < rmax or cols < cmax:
- raise ValueError(filldedent('''
- The constructed matrix is {} x {} but a size of {} x {}
- was specified.'''.format(rmax, cmax, rows, cols)))
- return klass._eval_diag(rows, cols, diag_entries)
- @classmethod
- def eye(kls, rows, cols=None, **kwargs):
- """Returns an identity matrix.
- Parameters
- ==========
- rows : rows of the matrix
- cols : cols of the matrix (if None, cols=rows)
- kwargs
- ======
- cls : class of the returned matrix
- """
- if cols is None:
- cols = rows
- if rows < 0 or cols < 0:
- raise ValueError("Cannot create a {} x {} matrix. "
- "Both dimensions must be positive".format(rows, cols))
- klass = kwargs.get('cls', kls)
- rows, cols = as_int(rows), as_int(cols)
- return klass._eval_eye(rows, cols)
- @classmethod
- def jordan_block(kls, size=None, eigenvalue=None, *, band='upper', **kwargs):
- """Returns a Jordan block
- Parameters
- ==========
- size : Integer, optional
- Specifies the shape of the Jordan block matrix.
- eigenvalue : Number or Symbol
- Specifies the value for the main diagonal of the matrix.
- .. note::
- The keyword ``eigenval`` is also specified as an alias
- of this keyword, but it is not recommended to use.
- We may deprecate the alias in later release.
- band : 'upper' or 'lower', optional
- Specifies the position of the off-diagonal to put `1` s on.
- cls : Matrix, optional
- Specifies the matrix class of the output form.
- If it is not specified, the class type where the method is
- being executed on will be returned.
- Returns
- =======
- Matrix
- A Jordan block matrix.
- Raises
- ======
- ValueError
- If insufficient arguments are given for matrix size
- specification, or no eigenvalue is given.
- Examples
- ========
- Creating a default Jordan block:
- >>> from sympy import Matrix
- >>> from sympy.abc import x
- >>> Matrix.jordan_block(4, x)
- Matrix([
- [x, 1, 0, 0],
- [0, x, 1, 0],
- [0, 0, x, 1],
- [0, 0, 0, x]])
- Creating an alternative Jordan block matrix where `1` is on
- lower off-diagonal:
- >>> Matrix.jordan_block(4, x, band='lower')
- Matrix([
- [x, 0, 0, 0],
- [1, x, 0, 0],
- [0, 1, x, 0],
- [0, 0, 1, x]])
- Creating a Jordan block with keyword arguments
- >>> Matrix.jordan_block(size=4, eigenvalue=x)
- Matrix([
- [x, 1, 0, 0],
- [0, x, 1, 0],
- [0, 0, x, 1],
- [0, 0, 0, x]])
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Jordan_matrix
- """
- klass = kwargs.pop('cls', kls)
- eigenval = kwargs.get('eigenval', None)
- if eigenvalue is None and eigenval is None:
- raise ValueError("Must supply an eigenvalue")
- elif eigenvalue != eigenval and None not in (eigenval, eigenvalue):
- raise ValueError(
- "Inconsistent values are given: 'eigenval'={}, "
- "'eigenvalue'={}".format(eigenval, eigenvalue))
- else:
- if eigenval is not None:
- eigenvalue = eigenval
- if size is None:
- raise ValueError("Must supply a matrix size")
- size = as_int(size)
- return klass._eval_jordan_block(size, eigenvalue, band)
- @classmethod
- def ones(kls, rows, cols=None, **kwargs):
- """Returns a matrix of ones.
- Parameters
- ==========
- rows : rows of the matrix
- cols : cols of the matrix (if None, cols=rows)
- kwargs
- ======
- cls : class of the returned matrix
- """
- if cols is None:
- cols = rows
- klass = kwargs.get('cls', kls)
- rows, cols = as_int(rows), as_int(cols)
- return klass._eval_ones(rows, cols)
- @classmethod
- def zeros(kls, rows, cols=None, **kwargs):
- """Returns a matrix of zeros.
- Parameters
- ==========
- rows : rows of the matrix
- cols : cols of the matrix (if None, cols=rows)
- kwargs
- ======
- cls : class of the returned matrix
- """
- if cols is None:
- cols = rows
- if rows < 0 or cols < 0:
- raise ValueError("Cannot create a {} x {} matrix. "
- "Both dimensions must be positive".format(rows, cols))
- klass = kwargs.get('cls', kls)
- rows, cols = as_int(rows), as_int(cols)
- return klass._eval_zeros(rows, cols)
- @classmethod
- def companion(kls, poly):
- """Returns a companion matrix of a polynomial.
- Examples
- ========
- >>> from sympy import Matrix, Poly, Symbol, symbols
- >>> x = Symbol('x')
- >>> c0, c1, c2, c3, c4 = symbols('c0:5')
- >>> p = Poly(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + x**5, x)
- >>> Matrix.companion(p)
- Matrix([
- [0, 0, 0, 0, -c0],
- [1, 0, 0, 0, -c1],
- [0, 1, 0, 0, -c2],
- [0, 0, 1, 0, -c3],
- [0, 0, 0, 1, -c4]])
- """
- poly = kls._sympify(poly)
- if not isinstance(poly, Poly):
- raise ValueError("{} must be a Poly instance.".format(poly))
- if not poly.is_monic:
- raise ValueError("{} must be a monic polynomial.".format(poly))
- if not poly.is_univariate:
- raise ValueError(
- "{} must be a univariate polynomial.".format(poly))
- size = poly.degree()
- if not size >= 1:
- raise ValueError(
- "{} must have degree not less than 1.".format(poly))
- coeffs = poly.all_coeffs()
- def entry(i, j):
- if j == size - 1:
- return -coeffs[-1 - i]
- elif i == j + 1:
- return kls.one
- return kls.zero
- return kls._new(size, size, entry)
- @classmethod
- def wilkinson(kls, n, **kwargs):
- """Returns two square Wilkinson Matrix of size 2*n + 1
- $W_{2n + 1}^-, W_{2n + 1}^+ =$ Wilkinson(n)
- Examples
- ========
- >>> from sympy import Matrix
- >>> wminus, wplus = Matrix.wilkinson(3)
- >>> wminus
- Matrix([
- [-3, 1, 0, 0, 0, 0, 0],
- [ 1, -2, 1, 0, 0, 0, 0],
- [ 0, 1, -1, 1, 0, 0, 0],
- [ 0, 0, 1, 0, 1, 0, 0],
- [ 0, 0, 0, 1, 1, 1, 0],
- [ 0, 0, 0, 0, 1, 2, 1],
- [ 0, 0, 0, 0, 0, 1, 3]])
- >>> wplus
- Matrix([
- [3, 1, 0, 0, 0, 0, 0],
- [1, 2, 1, 0, 0, 0, 0],
- [0, 1, 1, 1, 0, 0, 0],
- [0, 0, 1, 0, 1, 0, 0],
- [0, 0, 0, 1, 1, 1, 0],
- [0, 0, 0, 0, 1, 2, 1],
- [0, 0, 0, 0, 0, 1, 3]])
- References
- ==========
- .. [1] https://blogs.mathworks.com/cleve/2013/04/15/wilkinsons-matrices-2/
- .. [2] J. H. Wilkinson, The Algebraic Eigenvalue Problem, Claredon Press, Oxford, 1965, 662 pp.
- """
- klass = kwargs.get('cls', kls)
- n = as_int(n)
- return klass._eval_wilkinson(n)
- class MatrixProperties(MatrixRequired):
- """Provides basic properties of a matrix."""
- def _eval_atoms(self, *types):
- result = set()
- for i in self:
- result.update(i.atoms(*types))
- return result
- def _eval_free_symbols(self):
- return set().union(*(i.free_symbols for i in self if i))
- def _eval_has(self, *patterns):
- return any(a.has(*patterns) for a in self)
- def _eval_is_anti_symmetric(self, simpfunc):
- if not all(simpfunc(self[i, j] + self[j, i]).is_zero for i in range(self.rows) for j in range(self.cols)):
- return False
- return True
- def _eval_is_diagonal(self):
- for i in range(self.rows):
- for j in range(self.cols):
- if i != j and self[i, j]:
- return False
- return True
- # _eval_is_hermitian is called by some general SymPy
- # routines and has a different *args signature. Make
- # sure the names don't clash by adding `_matrix_` in name.
- def _eval_is_matrix_hermitian(self, simpfunc):
- mat = self._new(self.rows, self.cols, lambda i, j: simpfunc(self[i, j] - self[j, i].conjugate()))
- return mat.is_zero_matrix
- def _eval_is_Identity(self) -> FuzzyBool:
- def dirac(i, j):
- if i == j:
- return 1
- return 0
- return all(self[i, j] == dirac(i, j)
- for i in range(self.rows)
- for j in range(self.cols))
- def _eval_is_lower_hessenberg(self):
- return all(self[i, j].is_zero
- for i in range(self.rows)
- for j in range(i + 2, self.cols))
- def _eval_is_lower(self):
- return all(self[i, j].is_zero
- for i in range(self.rows)
- for j in range(i + 1, self.cols))
- def _eval_is_symbolic(self):
- return self.has(Symbol)
- def _eval_is_symmetric(self, simpfunc):
- mat = self._new(self.rows, self.cols, lambda i, j: simpfunc(self[i, j] - self[j, i]))
- return mat.is_zero_matrix
- def _eval_is_zero_matrix(self):
- if any(i.is_zero == False for i in self):
- return False
- if any(i.is_zero is None for i in self):
- return None
- return True
- def _eval_is_upper_hessenberg(self):
- return all(self[i, j].is_zero
- for i in range(2, self.rows)
- for j in range(min(self.cols, (i - 1))))
- def _eval_values(self):
- return [i for i in self if not i.is_zero]
- def _has_positive_diagonals(self):
- diagonal_entries = (self[i, i] for i in range(self.rows))
- return fuzzy_and(x.is_positive for x in diagonal_entries)
- def _has_nonnegative_diagonals(self):
- diagonal_entries = (self[i, i] for i in range(self.rows))
- return fuzzy_and(x.is_nonnegative for x in diagonal_entries)
- def atoms(self, *types):
- """Returns the atoms that form the current object.
- Examples
- ========
- >>> from sympy.abc import x, y
- >>> from sympy import Matrix
- >>> Matrix([[x]])
- Matrix([[x]])
- >>> _.atoms()
- {x}
- >>> Matrix([[x, y], [y, x]])
- Matrix([
- [x, y],
- [y, x]])
- >>> _.atoms()
- {x, y}
- """
- types = tuple(t if isinstance(t, type) else type(t) for t in types)
- if not types:
- types = (Atom,)
- return self._eval_atoms(*types)
- @property
- def free_symbols(self):
- """Returns the free symbols within the matrix.
- Examples
- ========
- >>> from sympy.abc import x
- >>> from sympy import Matrix
- >>> Matrix([[x], [1]]).free_symbols
- {x}
- """
- return self._eval_free_symbols()
- def has(self, *patterns):
- """Test whether any subexpression matches any of the patterns.
- Examples
- ========
- >>> from sympy import Matrix, SparseMatrix, Float
- >>> from sympy.abc import x, y
- >>> A = Matrix(((1, x), (0.2, 3)))
- >>> B = SparseMatrix(((1, x), (0.2, 3)))
- >>> A.has(x)
- True
- >>> A.has(y)
- False
- >>> A.has(Float)
- True
- >>> B.has(x)
- True
- >>> B.has(y)
- False
- >>> B.has(Float)
- True
- """
- return self._eval_has(*patterns)
- def is_anti_symmetric(self, simplify=True):
- """Check if matrix M is an antisymmetric matrix,
- that is, M is a square matrix with all M[i, j] == -M[j, i].
- When ``simplify=True`` (default), the sum M[i, j] + M[j, i] is
- simplified before testing to see if it is zero. By default,
- the SymPy simplify function is used. To use a custom function
- set simplify to a function that accepts a single argument which
- returns a simplified expression. To skip simplification, set
- simplify to False but note that although this will be faster,
- it may induce false negatives.
- Examples
- ========
- >>> from sympy import Matrix, symbols
- >>> m = Matrix(2, 2, [0, 1, -1, 0])
- >>> m
- Matrix([
- [ 0, 1],
- [-1, 0]])
- >>> m.is_anti_symmetric()
- True
- >>> x, y = symbols('x y')
- >>> m = Matrix(2, 3, [0, 0, x, -y, 0, 0])
- >>> m
- Matrix([
- [ 0, 0, x],
- [-y, 0, 0]])
- >>> m.is_anti_symmetric()
- False
- >>> from sympy.abc import x, y
- >>> m = Matrix(3, 3, [0, x**2 + 2*x + 1, y,
- ... -(x + 1)**2, 0, x*y,
- ... -y, -x*y, 0])
- Simplification of matrix elements is done by default so even
- though two elements which should be equal and opposite would not
- pass an equality test, the matrix is still reported as
- anti-symmetric:
- >>> m[0, 1] == -m[1, 0]
- False
- >>> m.is_anti_symmetric()
- True
- If ``simplify=False`` is used for the case when a Matrix is already
- simplified, this will speed things up. Here, we see that without
- simplification the matrix does not appear anti-symmetric:
- >>> m.is_anti_symmetric(simplify=False)
- False
- But if the matrix were already expanded, then it would appear
- anti-symmetric and simplification in the is_anti_symmetric routine
- is not needed:
- >>> m = m.expand()
- >>> m.is_anti_symmetric(simplify=False)
- True
- """
- # accept custom simplification
- simpfunc = simplify
- if not isfunction(simplify):
- simpfunc = _simplify if simplify else lambda x: x
- if not self.is_square:
- return False
- return self._eval_is_anti_symmetric(simpfunc)
- def is_diagonal(self):
- """Check if matrix is diagonal,
- that is matrix in which the entries outside the main diagonal are all zero.
- Examples
- ========
- >>> from sympy import Matrix, diag
- >>> m = Matrix(2, 2, [1, 0, 0, 2])
- >>> m
- Matrix([
- [1, 0],
- [0, 2]])
- >>> m.is_diagonal()
- True
- >>> m = Matrix(2, 2, [1, 1, 0, 2])
- >>> m
- Matrix([
- [1, 1],
- [0, 2]])
- >>> m.is_diagonal()
- False
- >>> m = diag(1, 2, 3)
- >>> m
- Matrix([
- [1, 0, 0],
- [0, 2, 0],
- [0, 0, 3]])
- >>> m.is_diagonal()
- True
- See Also
- ========
- is_lower
- is_upper
- sympy.matrices.matrices.MatrixEigen.is_diagonalizable
- diagonalize
- """
- return self._eval_is_diagonal()
- @property
- def is_weakly_diagonally_dominant(self):
- r"""Tests if the matrix is row weakly diagonally dominant.
- Explanation
- ===========
- A $n, n$ matrix $A$ is row weakly diagonally dominant if
- .. math::
- \left|A_{i, i}\right| \ge \sum_{j = 0, j \neq i}^{n-1}
- \left|A_{i, j}\right| \quad {\text{for all }}
- i \in \{ 0, ..., n-1 \}
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix([[3, -2, 1], [1, -3, 2], [-1, 2, 4]])
- >>> A.is_weakly_diagonally_dominant
- True
- >>> A = Matrix([[-2, 2, 1], [1, 3, 2], [1, -2, 0]])
- >>> A.is_weakly_diagonally_dominant
- False
- >>> A = Matrix([[-4, 2, 1], [1, 6, 2], [1, -2, 5]])
- >>> A.is_weakly_diagonally_dominant
- True
- Notes
- =====
- If you want to test whether a matrix is column diagonally
- dominant, you can apply the test after transposing the matrix.
- """
- if not self.is_square:
- return False
- rows, cols = self.shape
- def test_row(i):
- summation = self.zero
- for j in range(cols):
- if i != j:
- summation += Abs(self[i, j])
- return (Abs(self[i, i]) - summation).is_nonnegative
- return fuzzy_and(test_row(i) for i in range(rows))
- @property
- def is_strongly_diagonally_dominant(self):
- r"""Tests if the matrix is row strongly diagonally dominant.
- Explanation
- ===========
- A $n, n$ matrix $A$ is row strongly diagonally dominant if
- .. math::
- \left|A_{i, i}\right| > \sum_{j = 0, j \neq i}^{n-1}
- \left|A_{i, j}\right| \quad {\text{for all }}
- i \in \{ 0, ..., n-1 \}
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix([[3, -2, 1], [1, -3, 2], [-1, 2, 4]])
- >>> A.is_strongly_diagonally_dominant
- False
- >>> A = Matrix([[-2, 2, 1], [1, 3, 2], [1, -2, 0]])
- >>> A.is_strongly_diagonally_dominant
- False
- >>> A = Matrix([[-4, 2, 1], [1, 6, 2], [1, -2, 5]])
- >>> A.is_strongly_diagonally_dominant
- True
- Notes
- =====
- If you want to test whether a matrix is column diagonally
- dominant, you can apply the test after transposing the matrix.
- """
- if not self.is_square:
- return False
- rows, cols = self.shape
- def test_row(i):
- summation = self.zero
- for j in range(cols):
- if i != j:
- summation += Abs(self[i, j])
- return (Abs(self[i, i]) - summation).is_positive
- return fuzzy_and(test_row(i) for i in range(rows))
- @property
- def is_hermitian(self):
- """Checks if the matrix is Hermitian.
- In a Hermitian matrix element i,j is the complex conjugate of
- element j,i.
- Examples
- ========
- >>> from sympy import Matrix
- >>> from sympy import I
- >>> from sympy.abc import x
- >>> a = Matrix([[1, I], [-I, 1]])
- >>> a
- Matrix([
- [ 1, I],
- [-I, 1]])
- >>> a.is_hermitian
- True
- >>> a[0, 0] = 2*I
- >>> a.is_hermitian
- False
- >>> a[0, 0] = x
- >>> a.is_hermitian
- >>> a[0, 1] = a[1, 0]*I
- >>> a.is_hermitian
- False
- """
- if not self.is_square:
- return False
- return self._eval_is_matrix_hermitian(_simplify)
- @property
- def is_Identity(self) -> FuzzyBool:
- if not self.is_square:
- return False
- return self._eval_is_Identity()
- @property
- def is_lower_hessenberg(self):
- r"""Checks if the matrix is in the lower-Hessenberg form.
- The lower hessenberg matrix has zero entries
- above the first superdiagonal.
- Examples
- ========
- >>> from sympy import Matrix
- >>> a = Matrix([[1, 2, 0, 0], [5, 2, 3, 0], [3, 4, 3, 7], [5, 6, 1, 1]])
- >>> a
- Matrix([
- [1, 2, 0, 0],
- [5, 2, 3, 0],
- [3, 4, 3, 7],
- [5, 6, 1, 1]])
- >>> a.is_lower_hessenberg
- True
- See Also
- ========
- is_upper_hessenberg
- is_lower
- """
- return self._eval_is_lower_hessenberg()
- @property
- def is_lower(self):
- """Check if matrix is a lower triangular matrix. True can be returned
- even if the matrix is not square.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(2, 2, [1, 0, 0, 1])
- >>> m
- Matrix([
- [1, 0],
- [0, 1]])
- >>> m.is_lower
- True
- >>> m = Matrix(4, 3, [0, 0, 0, 2, 0, 0, 1, 4, 0, 6, 6, 5])
- >>> m
- Matrix([
- [0, 0, 0],
- [2, 0, 0],
- [1, 4, 0],
- [6, 6, 5]])
- >>> m.is_lower
- True
- >>> from sympy.abc import x, y
- >>> m = Matrix(2, 2, [x**2 + y, y**2 + x, 0, x + y])
- >>> m
- Matrix([
- [x**2 + y, x + y**2],
- [ 0, x + y]])
- >>> m.is_lower
- False
- See Also
- ========
- is_upper
- is_diagonal
- is_lower_hessenberg
- """
- return self._eval_is_lower()
- @property
- def is_square(self):
- """Checks if a matrix is square.
- A matrix is square if the number of rows equals the number of columns.
- The empty matrix is square by definition, since the number of rows and
- the number of columns are both zero.
- Examples
- ========
- >>> from sympy import Matrix
- >>> a = Matrix([[1, 2, 3], [4, 5, 6]])
- >>> b = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
- >>> c = Matrix([])
- >>> a.is_square
- False
- >>> b.is_square
- True
- >>> c.is_square
- True
- """
- return self.rows == self.cols
- def is_symbolic(self):
- """Checks if any elements contain Symbols.
- Examples
- ========
- >>> from sympy import Matrix
- >>> from sympy.abc import x, y
- >>> M = Matrix([[x, y], [1, 0]])
- >>> M.is_symbolic()
- True
- """
- return self._eval_is_symbolic()
- def is_symmetric(self, simplify=True):
- """Check if matrix is symmetric matrix,
- that is square matrix and is equal to its transpose.
- By default, simplifications occur before testing symmetry.
- They can be skipped using 'simplify=False'; while speeding things a bit,
- this may however induce false negatives.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(2, 2, [0, 1, 1, 2])
- >>> m
- Matrix([
- [0, 1],
- [1, 2]])
- >>> m.is_symmetric()
- True
- >>> m = Matrix(2, 2, [0, 1, 2, 0])
- >>> m
- Matrix([
- [0, 1],
- [2, 0]])
- >>> m.is_symmetric()
- False
- >>> m = Matrix(2, 3, [0, 0, 0, 0, 0, 0])
- >>> m
- Matrix([
- [0, 0, 0],
- [0, 0, 0]])
- >>> m.is_symmetric()
- False
- >>> from sympy.abc import x, y
- >>> m = Matrix(3, 3, [1, x**2 + 2*x + 1, y, (x + 1)**2, 2, 0, y, 0, 3])
- >>> m
- Matrix([
- [ 1, x**2 + 2*x + 1, y],
- [(x + 1)**2, 2, 0],
- [ y, 0, 3]])
- >>> m.is_symmetric()
- True
- If the matrix is already simplified, you may speed-up is_symmetric()
- test by using 'simplify=False'.
- >>> bool(m.is_symmetric(simplify=False))
- False
- >>> m1 = m.expand()
- >>> m1.is_symmetric(simplify=False)
- True
- """
- simpfunc = simplify
- if not isfunction(simplify):
- simpfunc = _simplify if simplify else lambda x: x
- if not self.is_square:
- return False
- return self._eval_is_symmetric(simpfunc)
- @property
- def is_upper_hessenberg(self):
- """Checks if the matrix is the upper-Hessenberg form.
- The upper hessenberg matrix has zero entries
- below the first subdiagonal.
- Examples
- ========
- >>> from sympy import Matrix
- >>> a = Matrix([[1, 4, 2, 3], [3, 4, 1, 7], [0, 2, 3, 4], [0, 0, 1, 3]])
- >>> a
- Matrix([
- [1, 4, 2, 3],
- [3, 4, 1, 7],
- [0, 2, 3, 4],
- [0, 0, 1, 3]])
- >>> a.is_upper_hessenberg
- True
- See Also
- ========
- is_lower_hessenberg
- is_upper
- """
- return self._eval_is_upper_hessenberg()
- @property
- def is_upper(self):
- """Check if matrix is an upper triangular matrix. True can be returned
- even if the matrix is not square.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(2, 2, [1, 0, 0, 1])
- >>> m
- Matrix([
- [1, 0],
- [0, 1]])
- >>> m.is_upper
- True
- >>> m = Matrix(4, 3, [5, 1, 9, 0, 4, 6, 0, 0, 5, 0, 0, 0])
- >>> m
- Matrix([
- [5, 1, 9],
- [0, 4, 6],
- [0, 0, 5],
- [0, 0, 0]])
- >>> m.is_upper
- True
- >>> m = Matrix(2, 3, [4, 2, 5, 6, 1, 1])
- >>> m
- Matrix([
- [4, 2, 5],
- [6, 1, 1]])
- >>> m.is_upper
- False
- See Also
- ========
- is_lower
- is_diagonal
- is_upper_hessenberg
- """
- return all(self[i, j].is_zero
- for i in range(1, self.rows)
- for j in range(min(i, self.cols)))
- @property
- def is_zero_matrix(self):
- """Checks if a matrix is a zero matrix.
- A matrix is zero if every element is zero. A matrix need not be square
- to be considered zero. The empty matrix is zero by the principle of
- vacuous truth. For a matrix that may or may not be zero (e.g.
- contains a symbol), this will be None
- Examples
- ========
- >>> from sympy import Matrix, zeros
- >>> from sympy.abc import x
- >>> a = Matrix([[0, 0], [0, 0]])
- >>> b = zeros(3, 4)
- >>> c = Matrix([[0, 1], [0, 0]])
- >>> d = Matrix([])
- >>> e = Matrix([[x, 0], [0, 0]])
- >>> a.is_zero_matrix
- True
- >>> b.is_zero_matrix
- True
- >>> c.is_zero_matrix
- False
- >>> d.is_zero_matrix
- True
- >>> e.is_zero_matrix
- """
- return self._eval_is_zero_matrix()
- def values(self):
- """Return non-zero values of self."""
- return self._eval_values()
- class MatrixOperations(MatrixRequired):
- """Provides basic matrix shape and elementwise
- operations. Should not be instantiated directly."""
- def _eval_adjoint(self):
- return self.transpose().conjugate()
- def _eval_applyfunc(self, f):
- out = self._new(self.rows, self.cols, [f(x) for x in self])
- return out
- def _eval_as_real_imag(self): # type: ignore
- return (self.applyfunc(re), self.applyfunc(im))
- def _eval_conjugate(self):
- return self.applyfunc(lambda x: x.conjugate())
- def _eval_permute_cols(self, perm):
- # apply the permutation to a list
- mapping = list(perm)
- def entry(i, j):
- return self[i, mapping[j]]
- return self._new(self.rows, self.cols, entry)
- def _eval_permute_rows(self, perm):
- # apply the permutation to a list
- mapping = list(perm)
- def entry(i, j):
- return self[mapping[i], j]
- return self._new(self.rows, self.cols, entry)
- def _eval_trace(self):
- return sum(self[i, i] for i in range(self.rows))
- def _eval_transpose(self):
- return self._new(self.cols, self.rows, lambda i, j: self[j, i])
- def adjoint(self):
- """Conjugate transpose or Hermitian conjugation."""
- return self._eval_adjoint()
- def applyfunc(self, f):
- """Apply a function to each element of the matrix.
- Examples
- ========
- >>> from sympy import Matrix
- >>> m = Matrix(2, 2, lambda i, j: i*2+j)
- >>> m
- Matrix([
- [0, 1],
- [2, 3]])
- >>> m.applyfunc(lambda i: 2*i)
- Matrix([
- [0, 2],
- [4, 6]])
- """
- if not callable(f):
- raise TypeError("`f` must be callable.")
- return self._eval_applyfunc(f)
- def as_real_imag(self, deep=True, **hints):
- """Returns a tuple containing the (real, imaginary) part of matrix."""
- # XXX: Ignoring deep and hints...
- return self._eval_as_real_imag()
- def conjugate(self):
- """Return the by-element conjugation.
- Examples
- ========
- >>> from sympy import SparseMatrix, I
- >>> a = SparseMatrix(((1, 2 + I), (3, 4), (I, -I)))
- >>> a
- Matrix([
- [1, 2 + I],
- [3, 4],
- [I, -I]])
- >>> a.C
- Matrix([
- [ 1, 2 - I],
- [ 3, 4],
- [-I, I]])
- See Also
- ========
- transpose: Matrix transposition
- H: Hermite conjugation
- sympy.matrices.matrices.MatrixBase.D: Dirac conjugation
- """
- return self._eval_conjugate()
- def doit(self, **hints):
- return self.applyfunc(lambda x: x.doit(**hints))
- def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False):
- """Apply evalf() to each element of self."""
- options = {'subs':subs, 'maxn':maxn, 'chop':chop, 'strict':strict,
- 'quad':quad, 'verbose':verbose}
- return self.applyfunc(lambda i: i.evalf(n, **options))
- def expand(self, deep=True, modulus=None, power_base=True, power_exp=True,
- mul=True, log=True, multinomial=True, basic=True, **hints):
- """Apply core.function.expand to each entry of the matrix.
- Examples
- ========
- >>> from sympy.abc import x
- >>> from sympy import Matrix
- >>> Matrix(1, 1, [x*(x+1)])
- Matrix([[x*(x + 1)]])
- >>> _.expand()
- Matrix([[x**2 + x]])
- """
- return self.applyfunc(lambda x: x.expand(
- deep, modulus, power_base, power_exp, mul, log, multinomial, basic,
- **hints))
- @property
- def H(self):
- """Return Hermite conjugate.
- Examples
- ========
- >>> from sympy import Matrix, I
- >>> m = Matrix((0, 1 + I, 2, 3))
- >>> m
- Matrix([
- [ 0],
- [1 + I],
- [ 2],
- [ 3]])
- >>> m.H
- Matrix([[0, 1 - I, 2, 3]])
- See Also
- ========
- conjugate: By-element conjugation
- sympy.matrices.matrices.MatrixBase.D: Dirac conjugation
- """
- return self.T.C
- def permute(self, perm, orientation='rows', direction='forward'):
- r"""Permute the rows or columns of a matrix by the given list of
- swaps.
- Parameters
- ==========
- perm : Permutation, list, or list of lists
- A representation for the permutation.
- If it is ``Permutation``, it is used directly with some
- resizing with respect to the matrix size.
- If it is specified as list of lists,
- (e.g., ``[[0, 1], [0, 2]]``), then the permutation is formed
- from applying the product of cycles. The direction how the
- cyclic product is applied is described in below.
- If it is specified as a list, the list should represent
- an array form of a permutation. (e.g., ``[1, 2, 0]``) which
- would would form the swapping function
- `0 \mapsto 1, 1 \mapsto 2, 2\mapsto 0`.
- orientation : 'rows', 'cols'
- A flag to control whether to permute the rows or the columns
- direction : 'forward', 'backward'
- A flag to control whether to apply the permutations from
- the start of the list first, or from the back of the list
- first.
- For example, if the permutation specification is
- ``[[0, 1], [0, 2]]``,
- If the flag is set to ``'forward'``, the cycle would be
- formed as `0 \mapsto 2, 2 \mapsto 1, 1 \mapsto 0`.
- If the flag is set to ``'backward'``, the cycle would be
- formed as `0 \mapsto 1, 1 \mapsto 2, 2 \mapsto 0`.
- If the argument ``perm`` is not in a form of list of lists,
- this flag takes no effect.
- Examples
- ========
- >>> from sympy import eye
- >>> M = eye(3)
- >>> M.permute([[0, 1], [0, 2]], orientation='rows', direction='forward')
- Matrix([
- [0, 0, 1],
- [1, 0, 0],
- [0, 1, 0]])
- >>> from sympy import eye
- >>> M = eye(3)
- >>> M.permute([[0, 1], [0, 2]], orientation='rows', direction='backward')
- Matrix([
- [0, 1, 0],
- [0, 0, 1],
- [1, 0, 0]])
- Notes
- =====
- If a bijective function
- `\sigma : \mathbb{N}_0 \rightarrow \mathbb{N}_0` denotes the
- permutation.
- If the matrix `A` is the matrix to permute, represented as
- a horizontal or a vertical stack of vectors:
- .. math::
- A =
- \begin{bmatrix}
- a_0 \\ a_1 \\ \vdots \\ a_{n-1}
- \end{bmatrix} =
- \begin{bmatrix}
- \alpha_0 & \alpha_1 & \cdots & \alpha_{n-1}
- \end{bmatrix}
- If the matrix `B` is the result, the permutation of matrix rows
- is defined as:
- .. math::
- B := \begin{bmatrix}
- a_{\sigma(0)} \\ a_{\sigma(1)} \\ \vdots \\ a_{\sigma(n-1)}
- \end{bmatrix}
- And the permutation of matrix columns is defined as:
- .. math::
- B := \begin{bmatrix}
- \alpha_{\sigma(0)} & \alpha_{\sigma(1)} &
- \cdots & \alpha_{\sigma(n-1)}
- \end{bmatrix}
- """
- from sympy.combinatorics import Permutation
- # allow british variants and `columns`
- if direction == 'forwards':
- direction = 'forward'
- if direction == 'backwards':
- direction = 'backward'
- if orientation == 'columns':
- orientation = 'cols'
- if direction not in ('forward', 'backward'):
- raise TypeError("direction='{}' is an invalid kwarg. "
- "Try 'forward' or 'backward'".format(direction))
- if orientation not in ('rows', 'cols'):
- raise TypeError("orientation='{}' is an invalid kwarg. "
- "Try 'rows' or 'cols'".format(orientation))
- if not isinstance(perm, (Permutation, Iterable)):
- raise ValueError(
- "{} must be a list, a list of lists, "
- "or a SymPy permutation object.".format(perm))
- # ensure all swaps are in range
- max_index = self.rows if orientation == 'rows' else self.cols
- if not all(0 <= t <= max_index for t in flatten(list(perm))):
- raise IndexError("`swap` indices out of range.")
- if perm and not isinstance(perm, Permutation) and \
- isinstance(perm[0], Iterable):
- if direction == 'forward':
- perm = list(reversed(perm))
- perm = Permutation(perm, size=max_index+1)
- else:
- perm = Permutation(perm, size=max_index+1)
- if orientation == 'rows':
- return self._eval_permute_rows(perm)
- if orientation == 'cols':
- return self._eval_permute_cols(perm)
- def permute_cols(self, swaps, direction='forward'):
- """Alias for
- ``self.permute(swaps, orientation='cols', direction=direction)``
- See Also
- ========
- permute
- """
- return self.permute(swaps, orientation='cols', direction=direction)
- def permute_rows(self, swaps, direction='forward'):
- """Alias for
- ``self.permute(swaps, orientation='rows', direction=direction)``
- See Also
- ========
- permute
- """
- return self.permute(swaps, orientation='rows', direction=direction)
- def refine(self, assumptions=True):
- """Apply refine to each element of the matrix.
- Examples
- ========
- >>> from sympy import Symbol, Matrix, Abs, sqrt, Q
- >>> x = Symbol('x')
- >>> Matrix([[Abs(x)**2, sqrt(x**2)],[sqrt(x**2), Abs(x)**2]])
- Matrix([
- [ Abs(x)**2, sqrt(x**2)],
- [sqrt(x**2), Abs(x)**2]])
- >>> _.refine(Q.real(x))
- Matrix([
- [ x**2, Abs(x)],
- [Abs(x), x**2]])
- """
- return self.applyfunc(lambda x: refine(x, assumptions))
- def replace(self, F, G, map=False, simultaneous=True, exact=None):
- """Replaces Function F in Matrix entries with Function G.
- Examples
- ========
- >>> from sympy import symbols, Function, Matrix
- >>> F, G = symbols('F, G', cls=Function)
- >>> M = Matrix(2, 2, lambda i, j: F(i+j)) ; M
- Matrix([
- [F(0), F(1)],
- [F(1), F(2)]])
- >>> N = M.replace(F,G)
- >>> N
- Matrix([
- [G(0), G(1)],
- [G(1), G(2)]])
- """
- return self.applyfunc(
- lambda x: x.replace(F, G, map=map, simultaneous=simultaneous, exact=exact))
- def rot90(self, k=1):
- """Rotates Matrix by 90 degrees
- Parameters
- ==========
- k : int
- Specifies how many times the matrix is rotated by 90 degrees
- (clockwise when positive, counter-clockwise when negative).
- Examples
- ========
- >>> from sympy import Matrix, symbols
- >>> A = Matrix(2, 2, symbols('a:d'))
- >>> A
- Matrix([
- [a, b],
- [c, d]])
- Rotating the matrix clockwise one time:
- >>> A.rot90(1)
- Matrix([
- [c, a],
- [d, b]])
- Rotating the matrix anticlockwise two times:
- >>> A.rot90(-2)
- Matrix([
- [d, c],
- [b, a]])
- """
- mod = k%4
- if mod == 0:
- return self
- if mod == 1:
- return self[::-1, ::].T
- if mod == 2:
- return self[::-1, ::-1]
- if mod == 3:
- return self[::, ::-1].T
- def simplify(self, **kwargs):
- """Apply simplify to each element of the matrix.
- Examples
- ========
- >>> from sympy.abc import x, y
- >>> from sympy import SparseMatrix, sin, cos
- >>> SparseMatrix(1, 1, [x*sin(y)**2 + x*cos(y)**2])
- Matrix([[x*sin(y)**2 + x*cos(y)**2]])
- >>> _.simplify()
- Matrix([[x]])
- """
- return self.applyfunc(lambda x: x.simplify(**kwargs))
- def subs(self, *args, **kwargs): # should mirror core.basic.subs
- """Return a new matrix with subs applied to each entry.
- Examples
- ========
- >>> from sympy.abc import x, y
- >>> from sympy import SparseMatrix, Matrix
- >>> SparseMatrix(1, 1, [x])
- Matrix([[x]])
- >>> _.subs(x, y)
- Matrix([[y]])
- >>> Matrix(_).subs(y, x)
- Matrix([[x]])
- """
- if len(args) == 1 and not isinstance(args[0], (dict, set)) and iter(args[0]) and not is_sequence(args[0]):
- args = (list(args[0]),)
- return self.applyfunc(lambda x: x.subs(*args, **kwargs))
- def trace(self):
- """
- Returns the trace of a square matrix i.e. the sum of the
- diagonal elements.
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix(2, 2, [1, 2, 3, 4])
- >>> A.trace()
- 5
- """
- if self.rows != self.cols:
- raise NonSquareMatrixError()
- return self._eval_trace()
- def transpose(self):
- """
- Returns the transpose of the matrix.
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix(2, 2, [1, 2, 3, 4])
- >>> A.transpose()
- Matrix([
- [1, 3],
- [2, 4]])
- >>> from sympy import Matrix, I
- >>> m=Matrix(((1, 2+I), (3, 4)))
- >>> m
- Matrix([
- [1, 2 + I],
- [3, 4]])
- >>> m.transpose()
- Matrix([
- [ 1, 3],
- [2 + I, 4]])
- >>> m.T == m.transpose()
- True
- See Also
- ========
- conjugate: By-element conjugation
- """
- return self._eval_transpose()
- @property
- def T(self):
- '''Matrix transposition'''
- return self.transpose()
- @property
- def C(self):
- '''By-element conjugation'''
- return self.conjugate()
- def n(self, *args, **kwargs):
- """Apply evalf() to each element of self."""
- return self.evalf(*args, **kwargs)
- def xreplace(self, rule): # should mirror core.basic.xreplace
- """Return a new matrix with xreplace applied to each entry.
- Examples
- ========
- >>> from sympy.abc import x, y
- >>> from sympy import SparseMatrix, Matrix
- >>> SparseMatrix(1, 1, [x])
- Matrix([[x]])
- >>> _.xreplace({x: y})
- Matrix([[y]])
- >>> Matrix(_).xreplace({y: x})
- Matrix([[x]])
- """
- return self.applyfunc(lambda x: x.xreplace(rule))
- def _eval_simplify(self, **kwargs):
- # XXX: We can't use self.simplify here as mutable subclasses will
- # override simplify and have it return None
- return MatrixOperations.simplify(self, **kwargs)
- def _eval_trigsimp(self, **opts):
- from sympy.simplify.trigsimp import trigsimp
- return self.applyfunc(lambda x: trigsimp(x, **opts))
- def upper_triangular(self, k=0):
- """Return the elements on and above the kth diagonal of a matrix.
- If k is not specified then simply returns upper-triangular portion
- of a matrix
- Examples
- ========
- >>> from sympy import ones
- >>> A = ones(4)
- >>> A.upper_triangular()
- Matrix([
- [1, 1, 1, 1],
- [0, 1, 1, 1],
- [0, 0, 1, 1],
- [0, 0, 0, 1]])
- >>> A.upper_triangular(2)
- Matrix([
- [0, 0, 1, 1],
- [0, 0, 0, 1],
- [0, 0, 0, 0],
- [0, 0, 0, 0]])
- >>> A.upper_triangular(-1)
- Matrix([
- [1, 1, 1, 1],
- [1, 1, 1, 1],
- [0, 1, 1, 1],
- [0, 0, 1, 1]])
- """
- def entry(i, j):
- return self[i, j] if i + k <= j else self.zero
- return self._new(self.rows, self.cols, entry)
- def lower_triangular(self, k=0):
- """Return the elements on and below the kth diagonal of a matrix.
- If k is not specified then simply returns lower-triangular portion
- of a matrix
- Examples
- ========
- >>> from sympy import ones
- >>> A = ones(4)
- >>> A.lower_triangular()
- Matrix([
- [1, 0, 0, 0],
- [1, 1, 0, 0],
- [1, 1, 1, 0],
- [1, 1, 1, 1]])
- >>> A.lower_triangular(-2)
- Matrix([
- [0, 0, 0, 0],
- [0, 0, 0, 0],
- [1, 0, 0, 0],
- [1, 1, 0, 0]])
- >>> A.lower_triangular(1)
- Matrix([
- [1, 1, 0, 0],
- [1, 1, 1, 0],
- [1, 1, 1, 1],
- [1, 1, 1, 1]])
- """
- def entry(i, j):
- return self[i, j] if i + k >= j else self.zero
- return self._new(self.rows, self.cols, entry)
- class MatrixArithmetic(MatrixRequired):
- """Provides basic matrix arithmetic operations.
- Should not be instantiated directly."""
- _op_priority = 10.01
- def _eval_Abs(self):
- return self._new(self.rows, self.cols, lambda i, j: Abs(self[i, j]))
- def _eval_add(self, other):
- return self._new(self.rows, self.cols,
- lambda i, j: self[i, j] + other[i, j])
- def _eval_matrix_mul(self, other):
- def entry(i, j):
- vec = [self[i,k]*other[k,j] for k in range(self.cols)]
- try:
- return Add(*vec)
- except (TypeError, SympifyError):
- # Some matrices don't work with `sum` or `Add`
- # They don't work with `sum` because `sum` tries to add `0`
- # Fall back to a safe way to multiply if the `Add` fails.
- return reduce(lambda a, b: a + b, vec)
- return self._new(self.rows, other.cols, entry)
- def _eval_matrix_mul_elementwise(self, other):
- return self._new(self.rows, self.cols, lambda i, j: self[i,j]*other[i,j])
- def _eval_matrix_rmul(self, other):
- def entry(i, j):
- return sum(other[i,k]*self[k,j] for k in range(other.cols))
- return self._new(other.rows, self.cols, entry)
- def _eval_pow_by_recursion(self, num):
- if num == 1:
- return self
- if num % 2 == 1:
- a, b = self, self._eval_pow_by_recursion(num - 1)
- else:
- a = b = self._eval_pow_by_recursion(num // 2)
- return a.multiply(b)
- def _eval_pow_by_cayley(self, exp):
- from sympy.discrete.recurrences import linrec_coeffs
- row = self.shape[0]
- p = self.charpoly()
- coeffs = (-p).all_coeffs()[1:]
- coeffs = linrec_coeffs(coeffs, exp)
- new_mat = self.eye(row)
- ans = self.zeros(row)
- for i in range(row):
- ans += coeffs[i]*new_mat
- new_mat *= self
- return ans
- def _eval_pow_by_recursion_dotprodsimp(self, num, prevsimp=None):
- if prevsimp is None:
- prevsimp = [True]*len(self)
- if num == 1:
- return self
- if num % 2 == 1:
- a, b = self, self._eval_pow_by_recursion_dotprodsimp(num - 1,
- prevsimp=prevsimp)
- else:
- a = b = self._eval_pow_by_recursion_dotprodsimp(num // 2,
- prevsimp=prevsimp)
- m = a.multiply(b, dotprodsimp=False)
- lenm = len(m)
- elems = [None]*lenm
- for i in range(lenm):
- if prevsimp[i]:
- elems[i], prevsimp[i] = _dotprodsimp(m[i], withsimp=True)
- else:
- elems[i] = m[i]
- return m._new(m.rows, m.cols, elems)
- def _eval_scalar_mul(self, other):
- return self._new(self.rows, self.cols, lambda i, j: self[i,j]*other)
- def _eval_scalar_rmul(self, other):
- return self._new(self.rows, self.cols, lambda i, j: other*self[i,j])
- def _eval_Mod(self, other):
- return self._new(self.rows, self.cols, lambda i, j: Mod(self[i, j], other))
- # Python arithmetic functions
- def __abs__(self):
- """Returns a new matrix with entry-wise absolute values."""
- return self._eval_Abs()
- @call_highest_priority('__radd__')
- def __add__(self, other):
- """Return self + other, raising ShapeError if shapes do not match."""
- if isinstance(other, NDimArray): # Matrix and array addition is currently not implemented
- return NotImplemented
- other = _matrixify(other)
- # matrix-like objects can have shapes. This is
- # our first sanity check.
- if hasattr(other, 'shape'):
- if self.shape != other.shape:
- raise ShapeError("Matrix size mismatch: %s + %s" % (
- self.shape, other.shape))
- # honest SymPy matrices defer to their class's routine
- if getattr(other, 'is_Matrix', False):
- # call the highest-priority class's _eval_add
- a, b = self, other
- if a.__class__ != classof(a, b):
- b, a = a, b
- return a._eval_add(b)
- # Matrix-like objects can be passed to CommonMatrix routines directly.
- if getattr(other, 'is_MatrixLike', False):
- return MatrixArithmetic._eval_add(self, other)
- raise TypeError('cannot add %s and %s' % (type(self), type(other)))
- @call_highest_priority('__rtruediv__')
- def __truediv__(self, other):
- return self * (self.one / other)
- @call_highest_priority('__rmatmul__')
- def __matmul__(self, other):
- other = _matrixify(other)
- if not getattr(other, 'is_Matrix', False) and not getattr(other, 'is_MatrixLike', False):
- return NotImplemented
- return self.__mul__(other)
- def __mod__(self, other):
- return self.applyfunc(lambda x: x % other)
- @call_highest_priority('__rmul__')
- def __mul__(self, other):
- """Return self*other where other is either a scalar or a matrix
- of compatible dimensions.
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix([[1, 2, 3], [4, 5, 6]])
- >>> 2*A == A*2 == Matrix([[2, 4, 6], [8, 10, 12]])
- True
- >>> B = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
- >>> A*B
- Matrix([
- [30, 36, 42],
- [66, 81, 96]])
- >>> B*A
- Traceback (most recent call last):
- ...
- ShapeError: Matrices size mismatch.
- >>>
- See Also
- ========
- matrix_multiply_elementwise
- """
- return self.multiply(other)
- def multiply(self, other, dotprodsimp=None):
- """Same as __mul__() but with optional simplification.
- Parameters
- ==========
- dotprodsimp : bool, optional
- Specifies whether intermediate term algebraic simplification is used
- during matrix multiplications to control expression blowup and thus
- speed up calculation. Default is off.
- """
- isimpbool = _get_intermediate_simp_bool(False, dotprodsimp)
- other = _matrixify(other)
- # matrix-like objects can have shapes. This is
- # our first sanity check. Double check other is not explicitly not a Matrix.
- if (hasattr(other, 'shape') and len(other.shape) == 2 and
- (getattr(other, 'is_Matrix', True) or
- getattr(other, 'is_MatrixLike', True))):
- if self.shape[1] != other.shape[0]:
- raise ShapeError("Matrix size mismatch: %s * %s." % (
- self.shape, other.shape))
- # honest SymPy matrices defer to their class's routine
- if getattr(other, 'is_Matrix', False):
- m = self._eval_matrix_mul(other)
- if isimpbool:
- return m._new(m.rows, m.cols, [_dotprodsimp(e) for e in m])
- return m
- # Matrix-like objects can be passed to CommonMatrix routines directly.
- if getattr(other, 'is_MatrixLike', False):
- return MatrixArithmetic._eval_matrix_mul(self, other)
- # if 'other' is not iterable then scalar multiplication.
- if not isinstance(other, Iterable):
- try:
- return self._eval_scalar_mul(other)
- except TypeError:
- pass
- return NotImplemented
- def multiply_elementwise(self, other):
- """Return the Hadamard product (elementwise product) of A and B
- Examples
- ========
- >>> from sympy import Matrix
- >>> A = Matrix([[0, 1, 2], [3, 4, 5]])
- >>> B = Matrix([[1, 10, 100], [100, 10, 1]])
- >>> A.multiply_elementwise(B)
- Matrix([
- [ 0, 10, 200],
- [300, 40, 5]])
- See Also
- ========
- sympy.matrices.matrices.MatrixBase.cross
- sympy.matrices.matrices.MatrixBase.dot
- multiply
- """
- if self.shape != other.shape:
- raise ShapeError("Matrix shapes must agree {} != {}".format(self.shape, other.shape))
- return self._eval_matrix_mul_elementwise(other)
- def __neg__(self):
- return self._eval_scalar_mul(-1)
- @call_highest_priority('__rpow__')
- def __pow__(self, exp):
- """Return self**exp a scalar or symbol."""
- return self.pow(exp)
- def pow(self, exp, method=None):
- r"""Return self**exp a scalar or symbol.
- Parameters
- ==========
- method : multiply, mulsimp, jordan, cayley
- If multiply then it returns exponentiation using recursion.
- If jordan then Jordan form exponentiation will be used.
- If cayley then the exponentiation is done using Cayley-Hamilton
- theorem.
- If mulsimp then the exponentiation is done using recursion
- with dotprodsimp. This specifies whether intermediate term
- algebraic simplification is used during naive matrix power to
- control expression blowup and thus speed up calculation.
- If None, then it heuristically decides which method to use.
- """
- if method is not None and method not in ['multiply', 'mulsimp', 'jordan', 'cayley']:
- raise TypeError('No such method')
- if self.rows != self.cols:
- raise NonSquareMatrixError()
- a = self
- jordan_pow = getattr(a, '_matrix_pow_by_jordan_blocks', None)
- exp = sympify(exp)
- if exp.is_zero:
- return a._new(a.rows, a.cols, lambda i, j: int(i == j))
- if exp == 1:
- return a
- diagonal = getattr(a, 'is_diagonal', None)
- if diagonal is not None and diagonal():
- return a._new(a.rows, a.cols, lambda i, j: a[i,j]**exp if i == j else 0)
- if exp.is_Number and exp % 1 == 0:
- if a.rows == 1:
- return a._new([[a[0]**exp]])
- if exp < 0:
- exp = -exp
- a = a.inv()
- # When certain conditions are met,
- # Jordan block algorithm is faster than
- # computation by recursion.
- if method == 'jordan':
- try:
- return jordan_pow(exp)
- except MatrixError:
- if method == 'jordan':
- raise
- elif method == 'cayley':
- if not exp.is_Number or exp % 1 != 0:
- raise ValueError("cayley method is only valid for integer powers")
- return a._eval_pow_by_cayley(exp)
- elif method == "mulsimp":
- if not exp.is_Number or exp % 1 != 0:
- raise ValueError("mulsimp method is only valid for integer powers")
- return a._eval_pow_by_recursion_dotprodsimp(exp)
- elif method == "multiply":
- if not exp.is_Number or exp % 1 != 0:
- raise ValueError("multiply method is only valid for integer powers")
- return a._eval_pow_by_recursion(exp)
- elif method is None and exp.is_Number and exp % 1 == 0:
- # Decide heuristically which method to apply
- if a.rows == 2 and exp > 100000:
- return jordan_pow(exp)
- elif _get_intermediate_simp_bool(True, None):
- return a._eval_pow_by_recursion_dotprodsimp(exp)
- elif exp > 10000:
- return a._eval_pow_by_cayley(exp)
- else:
- return a._eval_pow_by_recursion(exp)
- if jordan_pow:
- try:
- return jordan_pow(exp)
- except NonInvertibleMatrixError:
- # Raised by jordan_pow on zero determinant matrix unless exp is
- # definitely known to be a non-negative integer.
- # Here we raise if n is definitely not a non-negative integer
- # but otherwise we can leave this as an unevaluated MatPow.
- if exp.is_integer is False or exp.is_nonnegative is False:
- raise
- from sympy.matrices.expressions import MatPow
- return MatPow(a, exp)
- @call_highest_priority('__add__')
- def __radd__(self, other):
- return self + other
- @call_highest_priority('__matmul__')
- def __rmatmul__(self, other):
- other = _matrixify(other)
- if not getattr(other, 'is_Matrix', False) and not getattr(other, 'is_MatrixLike', False):
- return NotImplemented
- return self.__rmul__(other)
- @call_highest_priority('__mul__')
- def __rmul__(self, other):
- return self.rmultiply(other)
- def rmultiply(self, other, dotprodsimp=None):
- """Same as __rmul__() but with optional simplification.
- Parameters
- ==========
- dotprodsimp : bool, optional
- Specifies whether intermediate term algebraic simplification is used
- during matrix multiplications to control expression blowup and thus
- speed up calculation. Default is off.
- """
- isimpbool = _get_intermediate_simp_bool(False, dotprodsimp)
- other = _matrixify(other)
- # matrix-like objects can have shapes. This is
- # our first sanity check. Double check other is not explicitly not a Matrix.
- if (hasattr(other, 'shape') and len(other.shape) == 2 and
- (getattr(other, 'is_Matrix', True) or
- getattr(other, 'is_MatrixLike', True))):
- if self.shape[0] != other.shape[1]:
- raise ShapeError("Matrix size mismatch.")
- # honest SymPy matrices defer to their class's routine
- if getattr(other, 'is_Matrix', False):
- m = self._eval_matrix_rmul(other)
- if isimpbool:
- return m._new(m.rows, m.cols, [_dotprodsimp(e) for e in m])
- return m
- # Matrix-like objects can be passed to CommonMatrix routines directly.
- if getattr(other, 'is_MatrixLike', False):
- return MatrixArithmetic._eval_matrix_rmul(self, other)
- # if 'other' is not iterable then scalar multiplication.
- if not isinstance(other, Iterable):
- try:
- return self._eval_scalar_rmul(other)
- except TypeError:
- pass
- return NotImplemented
- @call_highest_priority('__sub__')
- def __rsub__(self, a):
- return (-self) + a
- @call_highest_priority('__rsub__')
- def __sub__(self, a):
- return self + (-a)
- class MatrixCommon(MatrixArithmetic, MatrixOperations, MatrixProperties,
- MatrixSpecial, MatrixShaping):
- """All common matrix operations including basic arithmetic, shaping,
- and special matrices like `zeros`, and `eye`."""
- _diff_wrt = True # type: bool
- class _MinimalMatrix:
- """Class providing the minimum functionality
- for a matrix-like object and implementing every method
- required for a `MatrixRequired`. This class does not have everything
- needed to become a full-fledged SymPy object, but it will satisfy the
- requirements of anything inheriting from `MatrixRequired`. If you wish
- to make a specialized matrix type, make sure to implement these
- methods and properties with the exception of `__init__` and `__repr__`
- which are included for convenience."""
- is_MatrixLike = True
- _sympify = staticmethod(sympify)
- _class_priority = 3
- zero = S.Zero
- one = S.One
- is_Matrix = True
- is_MatrixExpr = False
- @classmethod
- def _new(cls, *args, **kwargs):
- return cls(*args, **kwargs)
- def __init__(self, rows, cols=None, mat=None, copy=False):
- if isfunction(mat):
- # if we passed in a function, use that to populate the indices
- mat = [mat(i, j) for i in range(rows) for j in range(cols)]
- if cols is None and mat is None:
- mat = rows
- rows, cols = getattr(mat, 'shape', (rows, cols))
- try:
- # if we passed in a list of lists, flatten it and set the size
- if cols is None and mat is None:
- mat = rows
- cols = len(mat[0])
- rows = len(mat)
- mat = [x for l in mat for x in l]
- except (IndexError, TypeError):
- pass
- self.mat = tuple(self._sympify(x) for x in mat)
- self.rows, self.cols = rows, cols
- if self.rows is None or self.cols is None:
- raise NotImplementedError("Cannot initialize matrix with given parameters")
- def __getitem__(self, key):
- def _normalize_slices(row_slice, col_slice):
- """Ensure that row_slice and col_slice do not have
- `None` in their arguments. Any integers are converted
- to slices of length 1"""
- if not isinstance(row_slice, slice):
- row_slice = slice(row_slice, row_slice + 1, None)
- row_slice = slice(*row_slice.indices(self.rows))
- if not isinstance(col_slice, slice):
- col_slice = slice(col_slice, col_slice + 1, None)
- col_slice = slice(*col_slice.indices(self.cols))
- return (row_slice, col_slice)
- def _coord_to_index(i, j):
- """Return the index in _mat corresponding
- to the (i,j) position in the matrix. """
- return i * self.cols + j
- if isinstance(key, tuple):
- i, j = key
- if isinstance(i, slice) or isinstance(j, slice):
- # if the coordinates are not slices, make them so
- # and expand the slices so they don't contain `None`
- i, j = _normalize_slices(i, j)
- rowsList, colsList = list(range(self.rows))[i], \
- list(range(self.cols))[j]
- indices = (i * self.cols + j for i in rowsList for j in
- colsList)
- return self._new(len(rowsList), len(colsList),
- [self.mat[i] for i in indices])
- # if the key is a tuple of ints, change
- # it to an array index
- key = _coord_to_index(i, j)
- return self.mat[key]
- def __eq__(self, other):
- try:
- classof(self, other)
- except TypeError:
- return False
- return (
- self.shape == other.shape and list(self) == list(other))
- def __len__(self):
- return self.rows*self.cols
- def __repr__(self):
- return "_MinimalMatrix({}, {}, {})".format(self.rows, self.cols,
- self.mat)
- @property
- def shape(self):
- return (self.rows, self.cols)
- class _CastableMatrix: # this is needed here ONLY FOR TESTS.
- def as_mutable(self):
- return self
- def as_immutable(self):
- return self
- class _MatrixWrapper:
- """Wrapper class providing the minimum functionality for a matrix-like
- object: .rows, .cols, .shape, indexability, and iterability. CommonMatrix
- math operations should work on matrix-like objects. This one is intended for
- matrix-like objects which use the same indexing format as SymPy with respect
- to returning matrix elements instead of rows for non-tuple indexes.
- """
- is_Matrix = False # needs to be here because of __getattr__
- is_MatrixLike = True
- def __init__(self, mat, shape):
- self.mat = mat
- self.shape = shape
- self.rows, self.cols = shape
- def __getitem__(self, key):
- if isinstance(key, tuple):
- return sympify(self.mat.__getitem__(key))
- return sympify(self.mat.__getitem__((key // self.rows, key % self.cols)))
- def __iter__(self): # supports numpy.matrix and numpy.array
- mat = self.mat
- cols = self.cols
- return iter(sympify(mat[r, c]) for r in range(self.rows) for c in range(cols))
- class MatrixKind(Kind):
- """
- Kind for all matrices in SymPy.
- Basic class for this kind is ``MatrixBase`` and ``MatrixExpr``,
- but any expression representing the matrix can have this.
- Parameters
- ==========
- element_kind : Kind
- Kind of the element. Default is
- :class:`sympy.core.kind.NumberKind`,
- which means that the matrix contains only numbers.
- Examples
- ========
- Any instance of matrix class has ``MatrixKind``:
- >>> from sympy import MatrixSymbol
- >>> A = MatrixSymbol('A', 2,2)
- >>> A.kind
- MatrixKind(NumberKind)
- Although expression representing a matrix may be not instance of
- matrix class, it will have ``MatrixKind`` as well:
- >>> from sympy import MatrixExpr, Integral
- >>> from sympy.abc import x
- >>> intM = Integral(A, x)
- >>> isinstance(intM, MatrixExpr)
- False
- >>> intM.kind
- MatrixKind(NumberKind)
- Use ``isinstance()`` to check for ``MatrixKind`` without specifying
- the element kind. Use ``is`` with specifying the element kind:
- >>> from sympy import Matrix
- >>> from sympy.core import NumberKind
- >>> from sympy.matrices import MatrixKind
- >>> M = Matrix([1, 2])
- >>> isinstance(M.kind, MatrixKind)
- True
- >>> M.kind is MatrixKind(NumberKind)
- True
- See Also
- ========
- sympy.core.kind.NumberKind
- sympy.core.kind.UndefinedKind
- sympy.core.containers.TupleKind
- sympy.sets.sets.SetKind
- """
- def __new__(cls, element_kind=NumberKind):
- obj = super().__new__(cls, element_kind)
- obj.element_kind = element_kind
- return obj
- def __repr__(self):
- return "MatrixKind(%s)" % self.element_kind
- def _matrixify(mat):
- """If `mat` is a Matrix or is matrix-like,
- return a Matrix or MatrixWrapper object. Otherwise
- `mat` is passed through without modification."""
- if getattr(mat, 'is_Matrix', False) or getattr(mat, 'is_MatrixLike', False):
- return mat
- if not(getattr(mat, 'is_Matrix', True) or getattr(mat, 'is_MatrixLike', True)):
- return mat
- shape = None
- if hasattr(mat, 'shape'): # numpy, scipy.sparse
- if len(mat.shape) == 2:
- shape = mat.shape
- elif hasattr(mat, 'rows') and hasattr(mat, 'cols'): # mpmath
- shape = (mat.rows, mat.cols)
- if shape:
- return _MatrixWrapper(mat, shape)
- return mat
- def a2idx(j, n=None):
- """Return integer after making positive and validating against n."""
- if not isinstance(j, int):
- jindex = getattr(j, '__index__', None)
- if jindex is not None:
- j = jindex()
- else:
- raise IndexError("Invalid index a[%r]" % (j,))
- if n is not None:
- if j < 0:
- j += n
- if not (j >= 0 and j < n):
- raise IndexError("Index out of range: a[%s]" % (j,))
- return int(j)
- def classof(A, B):
- """
- Get the type of the result when combining matrices of different types.
- Currently the strategy is that immutability is contagious.
- Examples
- ========
- >>> from sympy import Matrix, ImmutableMatrix
- >>> from sympy.matrices.common import classof
- >>> M = Matrix([[1, 2], [3, 4]]) # a Mutable Matrix
- >>> IM = ImmutableMatrix([[1, 2], [3, 4]])
- >>> classof(M, IM)
- <class 'sympy.matrices.immutable.ImmutableDenseMatrix'>
- """
- priority_A = getattr(A, '_class_priority', None)
- priority_B = getattr(B, '_class_priority', None)
- if None not in (priority_A, priority_B):
- if A._class_priority > B._class_priority:
- return A.__class__
- else:
- return B.__class__
- try:
- import numpy
- except ImportError:
- pass
- else:
- if isinstance(A, numpy.ndarray):
- return B.__class__
- if isinstance(B, numpy.ndarray):
- return A.__class__
- raise TypeError("Incompatible classes %s, %s" % (A.__class__, B.__class__))
|