| 1 | ||
|
Editor: 127.0.0.1
Time: 2007/11/11 11:57:29 GMT-8 |
||
| Note: transferred from axiom-developer | ||
changed: - Row echelon form and other basic operations for sparse matrices. LU decomposition for ordinary matrices. (C) 1995 Werner M. Seiler (axiom-xl version) \begin{aldor} #include "axiom.as" #pile macro Sy == Symbol macro L == List macro V == Vector macro M == Matrix macro F == Fraction macro I == Integer macro NNI == NonNegativeInteger macro B == Boolean macro OUT == OutputForm macro ROWREC == Record(Indices:L C, Entries:L D) macro iter == "iterated"::Sy macro rand == "random"::Sy -- ------------------------- -- -- SparseEchelonMatrix (SEM) -- -- ------------------------- -- +++ Description: +++ \axiom{SparseEchelonMatrix(C,D)} implements sparse matrices whose columns +++ are enumerated by the \axiomType{OrderedSet} \axiom{C} and whose entries +++ belong to the \axiomType{GcdDomain} \axiom{D}. The basic operation of +++ this domain is the computation of an row echelon form. The used algorithm +++ tries to maintain the sparsity and is especially adapted to matrices who +++ are already close to a row echelon form. SparseEchelonMatrix(C:OrderedSet,D:Ring):Cat == Def where Cat ==> CoercibleTo OUT with shallowlyMutable ++ Matrices may be altered destructively. finiteAggregate ++ Matrices are finite. coerce : % -> M D ++ \axiom{coerce(A)} yields the matrix \axiom{A} in the usual matrix type. copy : % -> % ++ \axiom{copy(A)} returns a copy of the matrix \axiom{A}. ncols : % -> NNI ++ \axiom{ncols(A)} returns the number of columns of the matrix \axiom{A}. nrows : % -> NNI ++ \axiom{nrows(A)} returns the number of rows of the matrix \axiom{A}. allIndices : % -> L C ++ \axiom{allIndices(A)} returns all indices used for enumerating the ++ columns of the matrix \axiom{A}. elimZeroCols_! : % -> () ++ \axiom{elimZeroCols!(A)} removes columns which contain only zeros. ++ This effects basically only the value of \axiom{allIndices(A)}. purge_! : (%,C->B) -> () ++ \axiom{purge!(A,crit)} eliminates all columns belonging to an index ++ \axiom{c} such that \axiom{crit(c)} yields \axiom{true}. sortedPurge_! : (%,C->B) -> () ++ \axiom{sortedPurge!(A,crit)} is like \axiom{purge}, however, with the ++ additional assumption that \axiom{crit} respects the ordering of the ++ indices. new : (L C, I) -> % ++ \axiom{new(inds,nrows)} generates a new matrix with \axiom{nrows} ++ rows and columns enumerated by the indices \axiom{inds}. The matrix ++ is empty, i.e. the zero matrix. elt : (%,I,C) -> D ++ \axiom{elt(A,i,c)} returns the entry of the matrix \axiom{A} in row ++ \axiom{i} and in the column with index \axiom{c}. setelt_! : (%,I,C,D) -> () ++ \axiom{setelt!(A,i,c,d)} sets the entry of the matrix \axiom{A} in ++ row \axiom{i} and in the column with index \axiom{c} to the value ++ \axiom{d}. row : (%,I) -> ROWREC ++ \axiom{row(A,i)} returns the \axiom{i}-th row of the matrix \axiom{A}. setRow_! : (%,I,ROWREC) -> () ++ \axiom{setRow!(A,i,ind,ent)} sets the \axiom{i}-th row of the matrix ++ \axiom{A} to the value \axiom{r}. setRow_! : (%,I,L C,L D) -> () ++ \axiom{setRow!(A,i,ind,ent)} sets the \axiom{i}-th row of the matrix ++ \axiom{A}. Its indices are \axiom{ind}; the entries \axiom{ent}. deleteRow_! : (%,I) -> () ++ \axiom{deleteRow(A,i)} deletes the \axiom{i}-th row of the matrix ++ \axiom{A}. consRow_! : (%,ROWREC) -> () ++ \axiom{consRow!(A,r)} inserts the row \axiom{r} at the top of the ++ matrix \axiom{A}. appendRow_! : (%,ROWREC) -> () ++ \axiom{appendRow!(A,r)} appends the row \axiom{r} at the end of the ++ matrix \axiom{A}. extract : (%,I,I) -> % ++ \axiom{extract(A,i1,i2)} extracts the rows \axiom{i1} to \axiom{i2} ++ and returns them as a new matrix. rowEchelon : % -> Record(Ech:%, Lt:M D, Pivots:L D, Rank:NNI) ++ \axiom{primitiveRowEchelon(A)} computes a row echelon form for the ++ matrix \axiom{A}. The algorithm used is fraction-free elimination. ++ It is especially adapted to matrices already close to row echelon ++ form. The transformation matrix, the used pivots and the rank of the ++ matrix are also returned. pivot : (%,I) -> Record(Index:C, Entry:D) ++ \axiom{pivot(A,i)} returns the leading entry of the \axiom{i}-th row ++ of the matrix \axiom{A} together with its index. pivots : % -> ROWREC ++ \axiom{pivots(A)} returns all leading entries of the matrix \axiom{A} ++ together with their indices. * : (M D,%) -> % ++ \axiom{L*A} implements left multiplication with a usual matrix. join : (%,%) -> % ++ \axiom{join(A,B)} vertically concats the matrices \axiom{A} and ++ \axiom{B}. horizJoin : (%,%) -> % ++ \axiom{horizJoin(A,B)} horizontally concats the matrices \axiom{A} and ++ \axiom{B}. It is assumed that all indices of \axiom{B} are smaller than ++ those of \axiom{A}. horizSplit : (%,C) -> Record(Left:%, Right:%) ++ \axiom{horizSplit(A,c)} splits the matrix \axiom{A} into two at the ++ column given by \axiom{c}. The first column of the right matrix is ++ enumerated by the first index less or equal to \axiom{c}. --if D has GcdDomain then -- setGcdMode : Sy -> Sy -- ++ \axiom{setGcdMode(s)} sets a new value for the flag deciding on -- ++ the method used to compute gcd's for lists. Possible values for -- ++ \axiom{s} are \axiom{iterated} and \axiom{random}. -- * : (M F D,%) -> % -- ++ \axiom{L*A} implements left multiplication with a usual matrix over -- ++ the quotient field of \axiom{D}. -- primitiveRowEchelon : % -> Record(Ech:%, Lt:M F D, Pivots:L D, Rank:NNI) -- ++ \axiom{primitiveRowEchelon(A)} computes a row echelon form for the -- ++ matrix \axiom{A}. The algorithm used is fraction-free elimination. -- ++ Every row is made primitive by division by the gcd. The algorithm -- ++ is especially adapted to matrices already close to row echelon -- ++ form. The transformation matrix, the used pivots and the rank of the -- ++ matrix are also returned. Def ==> add import from C, D, M D import from L ROWREC, V ROWREC, L NNI local minInd:I := minIndex([i for i:NNI in 1..1]) offset:I := minInd-1 emptyRec:ROWREC := [empty()$(L C), empty()$(L D)] noChecks?:B := false --D has lazyRep -- flag for lazy representation seed:I := 113 -- seed for random number generation GCDmode:Sy := iter -- flag for gcd algorithm greater(c1:C,c2:C):B == c2<c1 greater(r1:ROWREC,r2:ROWREC):B == empty? r1.Indices => false empty? r2.Indices => true first(r2.Indices) < first(r1.Indices) -- -------------- -- -- Representation -- -- -------------- -- -- For efficency reasons most checks for correct index ranges are omitted. macro Rep == Record(NCols:NNI, NRows:NNI, AllInds:L C, Rows:V ROWREC) import from Rep ncols(A:%):NNI == rep(A).NCols nrows(A:%):NNI == rep(A).NRows allIndices(A:%):L C == copy rep(A).AllInds row(A:%,i:I):ROWREC == -- i<0 or i>rep(A).NRows => error "index out of range" qelt(rep(A).Rows,i) setRow_!(A:%,i:I,r:ROWREC):() == -- i<0 or i>rep(A).NRows => error "index out of range" qsetelt!(rep(A).Rows,i,r) setRow_!(A:%,i:I,inds:L C,ents:L D):() == -- i<0 or i>rep(A).NRows => error "index out of range" -- #inds ~= #ents => error "improper row" qsetelt!(rep(A).Rows, i, [inds,ents]) new(inds:L C,n:I):% == free emptyRec per [#inds, n::NNI, inds, [record explode emptyRec for i in 1..n]] elt(A:%,i:I,c:C):D == r := row(A,i) pos := position(c,r.Indices) pos<minInd => 0$D qelt(r.Entries,pos) setelt_!(A:%,i:I,c:C,d:D):() == r := row(A,i) pos := position(c,r.Indices) if pos>=minInd then qsetelt!(r.Entries,pos,d) else j := minInd for ind in r.Indices while c<ind repeat j := j+1 r.Indices := insert!(c,r.Indices,j) r.Entries := insert!(d,r.Entries,j) qsetelt!(rep(A).Rows,i,r) coerce(A:%):M D == (nc,nr,ai,ro) := explode rep A zero? nc => error "cannot coerce matrix with zero columns" AA:M D := new(nr,nc,0$D) local inds:L C local ents:L D for r:ROWREC in entries(ro) for i in minRowIndex(AA).. repeat inds := r.Indices ents := r.Entries for ind in ai for j in minColIndex(AA).. _ while not empty? inds repeat if ind=first inds then qsetelt!(AA, i, j, first ents) inds := rest inds ents := rest ents AA coerce(A:%):OUT == zero? rep(A).NCols => 0$D ::OUT A::(M D)::OUT copy(A:%):% == (Ancol,Anrow,Ainds,Arows) := explode rep A resRows:V ROWREC := new(Anrow,emptyRec) for l in 1..Anrow repeat r:ROWREC := qelt(Arows,l) qsetelt!(resRows, l, [copy r.Indices, copy r.Entries]) per [Ancol, Anrow, copy Ainds, resRows] -- ----------------------- -- -- Basic Matrix Operations -- -- ----------------------- -- elimZeroCols_!(A:%):() == newInds:L C := empty() for r in entries(rep(A).Rows) repeat newInds := removeDuplicates! merge!(greater, newInds, r.Indices) rep(A).AllInds := newInds purge_!(A:%,crit:C->B):() == rA := rep A newInds:L C := empty() for c in rA.AllInds repeat if not crit c then newInds := cons(c,newInds) newInds := reverse! newInds local r:ROWREC if #newInds ~= #rA.AllInds then rA.AllInds := newInds for l in 1..rA.NRows repeat r := qelt(rA.Rows,l) newInds:L C := empty() newEnts:L D := empty() for c:C in r.Indices for e:D in r.Entries repeat if not crit c then newInds := cons(c,newInds) newEnts := cons(e,newEnts) qsetelt!(rA.Rows, l, [reverse! newInds, reverse! newEnts]) sortedPurge_!(A:%,crit:C->B):() == rA := rep A local r:ROWREC if crit first rA.AllInds then while not(empty? rA.AllInds) and crit first rA.AllInds repeat rA.AllInds := rest rA.AllInds for l in 1..rA.NRows repeat r := qelt(rA.Rows,l) while not(empty? r.Indices) and crit first r.Indices repeat r.Indices := rest r.Indices r.Entries := rest r.Entries qsetelt!(rA.Rows,l,r) deleteRow_!(A:%,i:I):() == rA := rep A ((rA.NRows)::I<i) => A nr := ((rA.NRows)::I-1)::NNI resRows:V ROWREC := new(nr,emptyRec) for l in 1..(i-1) repeat qsetelt!(resRows,l,qelt(rA.Rows,l)) for l in (i+1)::NNI..rA.NRows repeat qsetelt!(resRows,l-1,qelt(rA.Rows,l)) rA.NRows := nr rA.Rows := resRows consRow_!(A:%,r:ROWREC):() == rA := rep A rA.NRows := rA.NRows + 1 newRows:L ROWREC := cons(r,entries rA.Rows) rA.Rows := construct newRows newInds := setDifference(r.Indices,rA.AllInds) if not empty? newInds then rA.AllInds := merge(greater,rA.AllInds,sort!(greater,newInds)) appendRow_!(A:%,r:ROWREC):() == rA := rep A rA.NRows := rA.NRows + 1 newRows:L ROWREC := concat(entries rA.Rows, r) rA.Rows := construct newRows newInds := setDifference(r.Indices,rA.AllInds) if not empty? newInds then rA.AllInds := merge(greater,rA.AllInds,sort!(greater,newInds)) extract(A:%,i1:I,i2:I):% == nr := (i2-i1+1)::NNI resRows:V ROWREC := new(nr,emptyRec) newInds:L C := empty() for i in i1..i2 repeat qsetelt!(resRows,i-i1+1,row(A,i)) newInds := removeDuplicates! merge(greater,newInds,row(A,i).Indices) per [rep(A).NCols, nr, newInds, resRows] join(A1:%,A2:%):% == rA1 := rep A1 rA2 := rep A2 newInds := removeDuplicates! merge(greater,rA1.AllInds,rA2.AllInds) newNRows := rA1.NRows + rA2.NRows newRows:V ROWREC := new(newNRows,emptyRec) for l in 1..rA1.NRows repeat qsetelt!(newRows, l, qelt(rA1.Rows,l)) for l in 1..rA2.NRows repeat qsetelt!(newRows, rA1.NRows+l, qelt(rA2.Rows,l)) per [#newInds, newNRows, newInds, newRows] horizJoin(A1:%,A2:%):% == rA1 := rep A1 rA2 := rep A2 rA1.NRows~=rA2.NRows => error "incompatible dimensions in horizJoin" newInds := append(rA1.AllInds,rA2.AllInds) res:% := new(newInds,rA1.NRows) for i in 1..rA1.NRows repeat r1 := row(A1,i) r2 := row(A2,i) setRow!(res, i, append(r1.Indices,r2.Indices), _ append(r1.Entries,r2.Entries)) res horizSplit(A:%,c:C):Record(Left:%,Right:%) == rA := rep A rinds:L C := allIndices A linds:L C := empty() while not(empty? rinds) and (first(rinds)>c) repeat linds := cons(first(rinds),linds) rinds := rest rinds empty? linds => [new(linds,rA.NRows), A] linds := reverse! linds empty? rinds => [A, new(rinds,rA.NRows)] LA:% := new(linds,rA.NRows) RA:% := new(rinds,rA.NRows) for i in 1..rA.NRows repeat r := row(A,i) ri:L C := r.Indices re:L D := r.Entries li:L C := empty() le:L D := empty() while not(empty? ri) and (first(ri)>c) repeat li := cons(first(ri),li) le := cons(first re, le) ri := rest ri re := rest re if not empty? li then li := reverse! li le := reverse! le setRow!(LA,i,li,le) if not empty? ri then setRow!(RA,i,ri,re) [LA,RA] -- ----------- -- -- Row Echelon -- -- ----------- -- addRows(d1:D,r1:ROWREC,d2:D,r2:ROWREC):ROWREC == -- Computes linear combination of two rows. -- Local function. empty? r1.Indices => one? d2 => r2 [r2.Indices, [d2*e2 for e2 in r2.Entries]] empty? r2.Indices => one? d1 => r1 [r1.Indices, [d1*e1 for e1 in r1.Entries]] resI:L C := empty() resE:L D := empty() local lent1:L D local lent2:L D if not(noChecks?) and one? d1 then lent1 := r1.Entries else lent1 := [d1*e1 for e1 in r1.Entries] if not(noChecks?) and one? d2 then lent2 := copy r2.Entries else lent2 := [d2*e2 for e2 in r2.Entries] lind2 := copy r2.Indices for c1 in r1.Indices for e1 in lent1 repeat while not(empty? lind2) and c1<first(lind2) repeat resI := cons(first lind2, resI) resE := cons(first(lent2), resE) lind2 := rest lind2 lent2 := rest lent2 if not(empty? lind2) and first(lind2)=c1 then sum := e1+first(lent2) if noChecks? or not zero? sum then resI := cons(c1,resI) resE := cons(sum,resE) lind2 := rest lind2 lent2 := rest lent2 else resI := cons(c1,resI) resE := cons(e1,resE) resI := concat!(reverse! resI, lind2) resE := concat!(reverse! resE, lent2) while not(empty? resE) and zero? first resE repeat resI := rest resI resE := rest resE [resI,resE] pivot(A:%,i:I):Record(Index:C,Entry:D) == r := row(A,i) empty? r.Indices => error "empty row" [first r.Indices, first r.Entries] pivots(A:%):ROWREC == resI:L C := empty() resE:L D := empty() for r in entries rep(A).Rows | not empty? r.Indices repeat resI := cons(first r.Indices, resI) resE := cons(first r.Entries, resE) [reverse! resI, reverse! resE] rowEchelon(AA:%):Record(Ech:%, Lt:M D, Pivots:L D, Rank:NNI) == A := rep copy AA LTr:M D := diagonalMatrix [1$D for i in 1..A.NRows] Pivs:L D := empty() -- check pivots for i in 1..A.NRows repeat r := qelt(A.Rows,i) changed?:B := false while not(empty? r.Entries) and zero? first r.Entries repeat r.Entries := rest r.Entries r.Indices := rest r.Indices changed? := true if changed? then qsetelt!(A.Rows,i,r) -- sort rows by pivots (bubble sort) notSorted?:B := true while notSorted? repeat notSorted? := false oldr:ROWREC := qelt(A.Rows,1) for i in 2..A.NRows repeat newr:ROWREC := qelt(A.Rows,i) if greater(newr,oldr) then qsetelt!(A.Rows,i,oldr) qsetelt!(A.Rows,i-1,newr) swapRows!(LTr,i-1,i) notSorted? := true else oldr := newr -- fraction-free elimination early?:B := false local pivlen,pivrow,rk:NNI for i in 1..A.NRows repeat r:ROWREC := qelt(A.Rows,i) if empty? r.Indices then rk:NNI := (i-1)::NNI early? := true break -- search good pivot pivind := first r.Indices pivlen := #r.Indices pivrow := i k:I := 0 for j in (i+1)..A.NRows _ while not(empty? qelt(A.Rows,j).Indices) and _ pivind=first(qelt(A.Rows,j).Indices) repeat len:NNI := #qelt(A.Rows,j).Indices k := k+1 if len<pivlen then pivlen := len pivrow := j piv:D := first qelt(A.Rows,pivrow).Entries Pivs := cons(piv,Pivs) -- elimination necessary? if k>0 then if pivrow~=i then pr := qelt(A.Rows,pivrow) qsetelt!(A.Rows,pivrow,qelt(A.Rows,i)) qsetelt!(A.Rows,i,pr) swapRows!(LTr,i,pivrow) -- elimination (and resorting of rows) pr := copy qelt(A.Rows,i) pr.Indices := rest pr.Indices pr.Entries := rest pr.Entries for j in (i+1)..(i+k) repeat r := copy qelt(A.Rows,i+1) c := first r.Entries r.Indices := rest r.Indices r.Entries := rest r.Entries r := addRows(piv,r,-c,pr) for l in 1..A.NRows repeat f := piv*qelt(LTr,i+1,l) - c*qelt(LTr,i,l) qsetelt!(LTr,i+1,l,f) for l in (i+2)..(2*i+k+1-j) repeat qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) swapRows!(LTr,l-1,l) for l in (2*i+k+2-j)..A.NRows _ while greater(qelt(A.Rows,l),r) repeat qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) swapRows!(LTr,l-1,l) qsetelt!(A.Rows,l-1,r) if not early? then rk:NNI := A.NRows [per(A), LTr, Pivs, rk] -- -------------- -- -- Multiplication -- -- -------------- -- (LL:M D) * (AA:%) : % == ncols(LL)~=AA.NRows => error "improper matrix dimensions" A := rep copy AA rlen := nrows LL res:% := new(A.AllInds,rlen) for c in A.AllInds repeat tmp:V D := new(rlen,0$D) for i in 1..A.NRows repeat r:ROWREC := qelt(A.Rows,i) inds:L C := r.Indices if not(empty? inds) and first(inds)=c then for k in 1..rlen | not zero? qelt(LL,k,i) repeat qsetelt!(tmp, k, qelt(tmp,k) + qelt(LL,k,i)*first(r.Entries)) r.Entries := rest r.Entries r.Indices := rest inds qsetelt!(A.Rows,i,r) for k in 1..rlen | not zero? qelt(tmp,k) repeat r := qelt(res.Rows,k) r.Indices := cons(c,r.Indices) r.Entries := cons(qelt(tmp,k),r.Entries) qsetelt!(res.Rows,k,r) for k in 1..rlen repeat r := qelt(res.Rows,k) r.Indices := reverse! r.Indices r.Entries := reverse! r.Entries qsetelt!(res.Rows,k,r) per res -- ---------------------------- -- -- primitive form in gcd domain -- -- ---------------------------- -- --if D has Join(GcdDomain,IntegralDomain) then -- import from Fraction D -- setGcdMode(s:Sy):Sy == -- free GCDmode -- tmp := GCDmode -- (s=iter) or (s=rand) => -- GCDmode := s -- tmp -- error "unknown gcd mode" -- randomGCD(le:L D):D == -- -- Probabilistic technique. -- free seed -- #le=2 => gcd(first le, second le) -- f := first le -- g := second le -- l := rest rest le -- while not empty? l repeat -- one? first l => return 1$D -- f := f + (1+random(seed)$I)*first(l) -- l := rest l -- if not empty? l then -- one? first l => return 1$D -- g := g + (1+random(seed)$I)*first(l) -- l := rest l -- h := gcd(f,g) -- l := [h] -- for e in le repeat -- tmp := exquo(e,h) -- if tmp case "failed" then -- l := cons(e,l) -- one?(#l) => h -- randomGCD l -- iteratedGCD(le:L D):D == -- -- Computes gcd iteratively -- res := gcd(first le, second le) -- l := rest rest le -- while not(empty?(l) or one?(res)) repeat -- res := gcd(res, first l) -- l := rest l -- res -- makePrimitive(r:ROWREC):Record(GCD:D, Row:ROWREC) == -- -- remove common gcd of row -- free GCDmode -- le := r.Entries -- one?(#le) => [first le, [r.Indices,[1$D]]] -- local g:D -- if GCDmode=iterated then -- g := iteratedGCD le -- else -- g := randomGCD le -- one? g => [1,r] -- le := [exquo(e,g)::D for e in le] -- [g, [r.Indices,le]] -- primitiveRowEchelon(AA:%) : _ -- Record(Ech:%, Lt:M F D, Pivots:L D, Rank:NNI) == -- A := rep copy AA -- LTr:M F D := diagonalMatrix [1$(F D) for i in 1..A.NRows] -- Pivs:L D := empty() -- -- check pivots -- for i in 1..A.NRows repeat -- r := qelt(A.Rows,i) -- changed?:B := false -- while not(empty? r.Entries) and zero? first r.Entries repeat -- r.Entries := rest r.Entries -- r.Indices := rest r.Indices -- changed? := true -- if changed? then -- qsetelt!(A.Rows,i,r) -- -- sort rows by pivots (bubble sort) -- notSorted?:B := true -- while notSorted? repeat -- notSorted? := false -- oldr := qelt(A.Rows,1) -- for i in 2..A.NRows repeat -- newr := qelt(A.Rows,i) -- if greater(newr,oldr) then -- qsetelt!(A.Rows,i,oldr) -- qsetelt!(A.Rows,i-1,newr) -- swapRows!(LTr,i-1,i) -- notSorted? := true -- else -- oldr := newr -- -- primitive fraction-free elimination -- early?:B := false -- local pivlen,pivrow,rk:NNI -- for i in 1..A.NRows until finished? repeat -- r := qelt(A.Rows,i) -- if empty? r.Indices then -- rk:NNI := (i-1)::NNI -- early? := true -- break -- -- search good pivot -- pivind := first r.Indices -- pivlen := #r.Indices -- pivrow := i -- k:I := 0 -- for j in (i+1)..A.NRows _ -- while not(empty? qelt(A.Rows,j).Indices) and _ -- pivind=first(qelt(A.Rows,j).Indices) repeat -- len := #qelt(A.Rows,j).Indices -- k := k+1 -- if len<pivlen then -- pivlen := len -- pivrow := j -- -- make row primitive -- tmp := makePrimitive qelt(A.Rows,pivrow) -- if not one? tmp.GCD then -- qsetelt!(A.Rows,pivrow,tmp.Row) -- q:FD := 1/tmp.GCD -- for l in 1..A.NRows | not zero? qelt(LTr,pivrow,l) repeat -- qsetelt!(LTr,pivrow,l,q*qelt(LTr,pivrow,l)) -- piv:D := first qelt(A.Rows,pivrow).Entries -- Pivs := cons(piv,Pivs) -- -- elimination necessary? -- if k>0 then -- if pivrow~=i then -- pr := qelt(A.Rows,pivrow) -- qsetelt!(A.Rows,pivrow,qelt(A.Rows,i)) -- qsetelt!(A.Rows,i,pr) -- swapRows!(LTr,i,pivrow) -- -- elimination (and resorting of rows) -- pr := copy tmp.Row -- pr.Indices := rest pr.Indices -- pr.Entries := rest pr.Entries -- for j in (i+1)..(i+k) repeat -- r := copy qelt(A.Rows,i+1) -- c := first r.Entries -- r.Indices := rest r.Indices -- r.Entries := rest r.Entries -- r := addRows(piv,r,-c,pr) -- for l in 1..A.NRows repeat -- fd:FD := piv *$FD qelt(LTr,i+1,l) - (c*qelt(LTr,i,l))::FD -- qsetelt!(LTr,i+1,l,fd) -- for l in (i+2)..(2*i+k+1-j) repeat -- qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) -- swapRows!(LTr,l-1,l) -- for l in (2*i+k+2-j)..A.NRows _ -- while greater(qelt(A.Rows,l),r) repeat -- qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) -- swapRows!(LTr,l-1,l) -- qsetelt!(A.Rows,l-1,r) -- if not early? then -- rk:NNI := A.NRows -- [per(A), LTr, Pivs, rk] -- mult(f:F D,d:D):D == -- res := numer(f)*d -- tmp := exquo(res,denom(f)) -- tmp case "failed" => error "cannot divide in mult" -- tmp::D -- (LL:M F D) * (AA:%) : % == -- ncols(LL)~=AA.NRows => error "improper matrix dimensions" -- A := rep copy AA -- rlen := nrows LL -- res:% := new(A.AllInds,rlen) -- for c in A.AllInds repeat -- tmp:V F D := new(rlen,0$FD) -- for i in 1..A.NRows repeat -- r := qelt(A.Rows,i) -- inds := r.Indices -- if not(empty? inds) and first(inds)=c then -- for k in 1..rlen | not zero? qelt(LL,k,i) repeat -- qsetelt!(tmp, k, qelt(tmp,k) + qelt(LL,k,i)*first(r.Entries)) -- r.Entries := rest r.Entries -- r.Indices := rest inds -- qsetelt!(A.Rows,i,r) -- for k in 1..rlen | not zero? qelt(tmp,k) repeat -- d:Union(value:D,failed:'failed') := retractIfCan qelt(tmp,k) -- d case failed => error "cannot divide in *" -- r := qelt(res.Rows,k) -- r.Indices := cons(c,r.Indices) -- r.Entries := cons(d::D,r.Entries) -- qsetelt!(res.Rows,k,r) -- for k in 1..rlen repeat -- r := qelt(res.Rows,k) -- r.Indices := reverse! r.Indices -- r.Entries := reverse! r.Entries -- qsetelt!(res.Rows,k,r) -- per res -- --------------------- -- -- LUDecomposition (LUD) -- -- --------------------- -- +++ Description: +++ \axiomType{LUDecomposition} contains procedures to solve linear systems of +++ equations or to compute inverses using a LU decomposition. LUDecomposition(D:Field) : Cat == Def where Cat ==> with LUDecomp : M D -> Record(LU:M D, Perm:V I, Pivots:L D) ++ \axiom{LUDecomp(A)} computes a LU decomposition of \axiom{A} ++ using the algorithm of Crout. \axiom{LU} contains both triangular ++ matrices; \axiom{Perm} is the permutation used for partial ++ pivoting and \axiom{Pivots} yields the used pivots. LUSolve : (M D, V I, V D) -> V D ++ \axiom{LUSolve(LU,Perm,B)} uses a previously computed LU ++ decomposition to solve a linear system with right hand side ++ \axiom{B}. \axiom{LU} and \axiom{Perm} are as given by ++ \axiom{LUDecomp}. LUInverse : M D -> Record(Inv:M D, Pivots:L D) ++ \axiom{LUInverse(A)} computes the inverse of \axiom{A} using a LU ++ decomposition. Def ==> add import from D LUDecomp(AA:M D):Record(LU:M D, Perm:V I, Pivots:L D) == -- LU decomposition using Crout's algorithm with partial pivoting. A := copy AA minR := minRowIndex A; maxR := maxRowIndex A minC := minColIndex A; maxC := maxColIndex A maxR~=maxC => error "LU decomposition only of square matrices" PermV:V I := new((maxR-minR+1)::NNI,0) Pivs:L D := empty() for j in minC..maxC repeat for i in minR..(j-1) repeat s := qelt(A,i,j) for k in minR..(i-1) repeat s := s - qelt(A,i,k)*qelt(A,k,j) qsetelt!(A,i,j,s) i0:I := -1 for i in j..maxR repeat s := qelt(A,i,j) for k in minC..(j-1) repeat s := s - qelt(A,i,k)*qelt(A,k,j) qsetelt!(A,i,j,s) if not(zero? s) and i0<0 then i0 := i -- first non-zero pivot i0<0 => error "singular matrix in LUDecomp" if j~=i0 then swapRows!(A,j,i0) qsetelt!(PermV,j,i0) Pivs := cons(qelt(A,j,j),Pivs) if j~=maxC then d := 1/qelt(A,j,j) for k in (j+1)..maxR repeat qsetelt!(A,k,j,d*qelt(A,k,j)) [A,PermV,Pivs] LUSolve(LU:M D,Perm:V I,XX:V D):V D == -- Solves LU decomposed linear system for right hand side XX X := copy XX minR := minRowIndex LU; maxR := maxRowIndex LU maxIndex(X)~=maxR => error "Wrong dimensions in LUSolve" ii:I := -1 for i in minR..maxR repeat -- forward substitution ip := qelt(Perm,i) s := qelt(X,ip) qsetelt!(X,ip,qelt(X,i)) if ii>=0 then for j in ii..(i-1) repeat s := s - qelt(LU,i,j)*qelt(X,j) else if not zero? s then ii := i qsetelt!(X,i,s) for i in maxR..minR by -1 repeat -- back substitution s := qelt(X,i) for j in (i+1)..maxR repeat s := s - qelt(LU,i,j)*qelt(X,j) qsetelt!(X,i,s/qelt(LU,i,i)) X LUInverse(A:M D):Record(Inv:M D, Pivots:L D) == -- Inversion via LU decomposition Alu := LUDecomp A n := ncols A res:M D := new(n,n,0) for i in minRowIndex(A)..maxRowIndex(A) repeat v:V D := new(n,0) qsetelt!(v,i,1) res := setColumn!(res,i,LUSolve(Alu.LU,Alu.Perm,v)) [res,Alu.Pivots] \end{aldor}
Row echelon form and other basic operations for sparse matrices.
LU decomposition for ordinary matrices.
(C) 1995 Werner M. Seiler (axiom-xl version)
#include "axiom.as" #pile
macro Sy == Symbol macro L == List macro V == Vector macro M == Matrix macro F == Fraction macro I == Integer macro NNI == NonNegativeInteger macro B == Boolean macro OUT == OutputForm macro ROWREC == Record(Indices:L C, Entries:L D) macro iter == "iterated"::Sy macro rand == "random"::Sy
-- ------------------------- -- -- SparseEchelonMatrix (SEM) -- -- ------------------------- --
+++ Description: +++ \axiom{SparseEchelonMatrix(C,D)} implements sparse matrices whose columns +++ are enumerated by the \axiomType{OrderedSet} \axiom{C} and whose entries +++ belong to the \axiomType{GcdDomain} \axiom{D}. The basic operation of +++ this domain is the computation of an row echelon form. The used algorithm +++ tries to maintain the sparsity and is especially adapted to matrices who +++ are already close to a row echelon form.
SparseEchelonMatrix(C:OrderedSet,D:Ring):Cat == Def where
Cat ==> CoercibleTo OUT with
shallowlyMutable ++ Matrices may be altered destructively.
finiteAggregate ++ Matrices are finite.
coerce : % -> M D ++ \axiom{coerce(A)} yields the matrix \axiom{A} in the usual matrix type.
copy : % -> % ++ \axiom{copy(A)} returns a copy of the matrix \axiom{A}.
ncols : % -> NNI ++ \axiom{ncols(A)} returns the number of columns of the matrix \axiom{A}.
nrows : % -> NNI ++ \axiom{nrows(A)} returns the number of rows of the matrix \axiom{A}.
allIndices : % -> L C ++ \axiom{allIndices(A)} returns all indices used for enumerating the ++ columns of the matrix \axiom{A}.
elimZeroCols_! : % -> () ++ \axiom{elimZeroCols!(A)} removes columns which contain only zeros. ++ This effects basically only the value of \axiom{allIndices(A)}.
purge_! : (%,C->B) -> () ++ \axiom{purge!(A,crit)} eliminates all columns belonging to an index ++ \axiom{c} such that \axiom{crit(c)} yields \axiom{true}.
sortedPurge_! : (%,C->B) -> () ++ \axiom{sortedPurge!(A,crit)} is like \axiom{purge}, however, with the ++ additional assumption that \axiom{crit} respects the ordering of the ++ indices.
new : (L C, I) -> % ++ \axiom{new(inds,nrows)} generates a new matrix with \axiom{nrows} ++ rows and columns enumerated by the indices \axiom{inds}. The matrix ++ is empty, i.e. the zero matrix.
elt : (%,I,C) -> D ++ \axiom{elt(A,i,c)} returns the entry of the matrix \axiom{A} in row ++ \axiom{i} and in the column with index \axiom{c}.
setelt_! : (%,I,C,D) -> () ++ \axiom{setelt!(A,i,c,d)} sets the entry of the matrix \axiom{A} in ++ row \axiom{i} and in the column with index \axiom{c} to the value ++ \axiom{d}.
row : (%,I) -> ROWREC ++ \axiom{row(A,i)} returns the \axiom{i}-th row of the matrix \axiom{A}.
setRow_! : (%,I,ROWREC) -> () ++ \axiom{setRow!(A,i,ind,ent)} sets the \axiom{i}-th row of the matrix ++ \axiom{A} to the value \axiom{r}.
setRow_! : (%,I,L C,L D) -> () ++ \axiom{setRow!(A,i,ind,ent)} sets the \axiom{i}-th row of the matrix ++ \axiom{A}. Its indices are \axiom{ind}; the entries \axiom{ent}.
deleteRow_! : (%,I) -> () ++ \axiom{deleteRow(A,i)} deletes the \axiom{i}-th row of the matrix ++ \axiom{A}.
consRow_! : (%,ROWREC) -> () ++ \axiom{consRow!(A,r)} inserts the row \axiom{r} at the top of the ++ matrix \axiom{A}.
appendRow_! : (%,ROWREC) -> () ++ \axiom{appendRow!(A,r)} appends the row \axiom{r} at the end of the ++ matrix \axiom{A}.
extract : (%,I,I) -> % ++ \axiom{extract(A,i1,i2)} extracts the rows \axiom{i1} to \axiom{i2} ++ and returns them as a new matrix.
rowEchelon : % -> Record(Ech:%, Lt:M D, Pivots:L D, Rank:NNI) ++ \axiom{primitiveRowEchelon(A)} computes a row echelon form for the ++ matrix \axiom{A}. The algorithm used is fraction-free elimination. ++ It is especially adapted to matrices already close to row echelon ++ form. The transformation matrix, the used pivots and the rank of the ++ matrix are also returned.
pivot : (%,I) -> Record(Index:C, Entry:D) ++ \axiom{pivot(A,i)} returns the leading entry of the \axiom{i}-th row ++ of the matrix \axiom{A} together with its index.
pivots : % -> ROWREC ++ \axiom{pivots(A)} returns all leading entries of the matrix \axiom{A} ++ together with their indices.
* : (M D,%) -> % ++ \axiom{L*A} implements left multiplication with a usual matrix.
join : (%,%) -> % ++ \axiom{join(A,B)} vertically concats the matrices \axiom{A} and ++ \axiom{B}.
horizJoin : (%,%) -> % ++ \axiom{horizJoin(A,B)} horizontally concats the matrices \axiom{A} and ++ \axiom{B}. It is assumed that all indices of \axiom{B} are smaller than ++ those of \axiom{A}.
horizSplit : (%,C) -> Record(Left:%, Right:%) ++ \axiom{horizSplit(A,c)} splits the matrix \axiom{A} into two at the ++ column given by \axiom{c}. The first column of the right matrix is ++ enumerated by the first index less or equal to \axiom{c}.
--if D has GcdDomain then
-- setGcdMode : Sy -> Sy -- ++ \axiom{setGcdMode(s)} sets a new value for the flag deciding on -- ++ the method used to compute gcd's for lists. Possible values for -- ++ \axiom{s} are \axiom{iterated} and \axiom{random}.
-- * : (M F D,%) -> % -- ++ \axiom{L*A} implements left multiplication with a usual matrix over -- ++ the quotient field of \axiom{D}.
-- primitiveRowEchelon : % -> Record(Ech:%, Lt:M F D, Pivots:L D, Rank:NNI) -- ++ \axiom{primitiveRowEchelon(A)} computes a row echelon form for the -- ++ matrix \axiom{A}. The algorithm used is fraction-free elimination. -- ++ Every row is made primitive by division by the gcd. The algorithm -- ++ is especially adapted to matrices already close to row echelon -- ++ form. The transformation matrix, the used pivots and the rank of the -- ++ matrix are also returned.
Def ==> add
import from C, D, M D import from L ROWREC, V ROWREC, L NNI
local minInd:I := minIndex([i for i:NNI in 1..1]) offset:I := minInd-1 emptyRec:ROWREC := [empty()$(L C), empty()$(L D)] noChecks?:B := false --D has lazyRep -- flag for lazy representation seed:I := 113 -- seed for random number generation GCDmode:Sy := iter -- flag for gcd algorithm
greater(c1:C,c2:C):B == c2<c1
greater(r1:ROWREC,r2:ROWREC):B == empty? r1.Indices => false empty? r2.Indices => true first(r2.Indices) < first(r1.Indices)
-- -------------- -- -- Representation -- -- -------------- --
-- For efficency reasons most checks for correct index ranges are omitted.
macro Rep == Record(NCols:NNI, NRows:NNI, AllInds:L C, Rows:V ROWREC) import from Rep
ncols(A:%):NNI == rep(A).NCols
nrows(A:%):NNI == rep(A).NRows
allIndices(A:%):L C == copy rep(A).AllInds
row(A:%,i:I):ROWREC == -- i<0 or i>rep(A).NRows => error "index out of range" qelt(rep(A).Rows,i)
setRow_!(A:%,i:I,r:ROWREC):() == -- i<0 or i>rep(A).NRows => error "index out of range" qsetelt!(rep(A).Rows,i,r)
setRow_!(A:%,i:I,inds:L C,ents:L D):() == -- i<0 or i>rep(A).NRows => error "index out of range" -- #inds ~= #ents => error "improper row" qsetelt!(rep(A).Rows, i, [inds,ents])
new(inds:L C,n:I):% == free emptyRec per [#inds, n::NNI, inds, [record explode emptyRec for i in 1..n]]
elt(A:%,i:I,c:C):D == r := row(A,i) pos := position(c,r.Indices) pos<minInd => 0$D qelt(r.Entries,pos)
setelt_!(A:%,i:I,c:C,d:D):() == r := row(A,i) pos := position(c,r.Indices) if pos>=minInd then qsetelt!(r.Entries,pos,d) else j := minInd for ind in r.Indices while c<ind repeat j := j+1 r.Indices := insert!(c,r.Indices,j) r.Entries := insert!(d,r.Entries,j) qsetelt!(rep(A).Rows,i,r)
coerce(A:%):M D == (nc,nr,ai,ro) := explode rep A zero? nc => error "cannot coerce matrix with zero columns" AA:M D := new(nr,nc,0$D) local inds:L C local ents:L D for r:ROWREC in entries(ro) for i in minRowIndex(AA).. repeat inds := r.Indices ents := r.Entries for ind in ai for j in minColIndex(AA).. _ while not empty? inds repeat if ind=first inds then qsetelt!(AA, i, j, first ents) inds := rest inds ents := rest ents AA
coerce(A:%):OUT == zero? rep(A).NCols => 0$D ::OUT A::(M D)::OUT
copy(A:%):% == (Ancol,Anrow,Ainds,Arows) := explode rep A resRows:V ROWREC := new(Anrow,emptyRec) for l in 1..Anrow repeat r:ROWREC := qelt(Arows,l) qsetelt!(resRows, l, [copy r.Indices, copy r.Entries]) per [Ancol, Anrow, copy Ainds, resRows]
-- ----------------------- -- -- Basic Matrix Operations -- -- ----------------------- --
elimZeroCols_!(A:%):() == newInds:L C := empty() for r in entries(rep(A).Rows) repeat newInds := removeDuplicates! merge!(greater, newInds, r.Indices) rep(A).AllInds := newInds
purge_!(A:%,crit:C->B):() == rA := rep A newInds:L C := empty() for c in rA.AllInds repeat if not crit c then newInds := cons(c,newInds) newInds := reverse! newInds local r:ROWREC if #newInds ~= #rA.AllInds then rA.AllInds := newInds for l in 1..rA.NRows repeat r := qelt(rA.Rows,l) newInds:L C := empty() newEnts:L D := empty() for c:C in r.Indices for e:D in r.Entries repeat if not crit c then newInds := cons(c,newInds) newEnts := cons(e,newEnts) qsetelt!(rA.Rows, l, [reverse! newInds, reverse! newEnts])
sortedPurge_!(A:%,crit:C->B):() == rA := rep A local r:ROWREC if crit first rA.AllInds then while not(empty? rA.AllInds) and crit first rA.AllInds repeat rA.AllInds := rest rA.AllInds for l in 1..rA.NRows repeat r := qelt(rA.Rows,l) while not(empty? r.Indices) and crit first r.Indices repeat r.Indices := rest r.Indices r.Entries := rest r.Entries qsetelt!(rA.Rows,l,r)
deleteRow_!(A:%,i:I):() == rA := rep A ((rA.NRows)::I<i) => A nr := ((rA.NRows)::I-1)::NNI resRows:V ROWREC := new(nr,emptyRec) for l in 1..(i-1) repeat qsetelt!(resRows,l,qelt(rA.Rows,l)) for l in (i+1)::NNI..rA.NRows repeat qsetelt!(resRows,l-1,qelt(rA.Rows,l)) rA.NRows := nr rA.Rows := resRows
consRow_!(A:%,r:ROWREC):() == rA := rep A rA.NRows := rA.NRows + 1 newRows:L ROWREC := cons(r,entries rA.Rows) rA.Rows := construct newRows newInds := setDifference(r.Indices,rA.AllInds) if not empty? newInds then rA.AllInds := merge(greater,rA.AllInds,sort!(greater,newInds))
appendRow_!(A:%,r:ROWREC):() == rA := rep A rA.NRows := rA.NRows + 1 newRows:L ROWREC := concat(entries rA.Rows, r) rA.Rows := construct newRows newInds := setDifference(r.Indices,rA.AllInds) if not empty? newInds then rA.AllInds := merge(greater,rA.AllInds,sort!(greater,newInds))
extract(A:%,i1:I,i2:I):% == nr := (i2-i1+1)::NNI resRows:V ROWREC := new(nr,emptyRec) newInds:L C := empty() for i in i1..i2 repeat qsetelt!(resRows,i-i1+1,row(A,i)) newInds := removeDuplicates! merge(greater,newInds,row(A,i).Indices) per [rep(A).NCols, nr, newInds, resRows]
join(A1:%,A2:%):% == rA1 := rep A1 rA2 := rep A2 newInds := removeDuplicates! merge(greater,rA1.AllInds,rA2.AllInds) newNRows := rA1.NRows + rA2.NRows newRows:V ROWREC := new(newNRows,emptyRec) for l in 1..rA1.NRows repeat qsetelt!(newRows, l, qelt(rA1.Rows,l)) for l in 1..rA2.NRows repeat qsetelt!(newRows, rA1.NRows+l, qelt(rA2.Rows,l)) per [#newInds, newNRows, newInds, newRows]
horizJoin(A1:%,A2:%):% == rA1 := rep A1 rA2 := rep A2 rA1.NRows~=rA2.NRows => error "incompatible dimensions in horizJoin" newInds := append(rA1.AllInds,rA2.AllInds) res:% := new(newInds,rA1.NRows) for i in 1..rA1.NRows repeat r1 := row(A1,i) r2 := row(A2,i) setRow!(res, i, append(r1.Indices,r2.Indices), _ append(r1.Entries,r2.Entries)) res
horizSplit(A:%,c:C):Record(Left:%,Right:%) == rA := rep A rinds:L C := allIndices A linds:L C := empty() while not(empty? rinds) and (first(rinds)>c) repeat linds := cons(first(rinds),linds) rinds := rest rinds empty? linds => [new(linds,rA.NRows), A] linds := reverse! linds empty? rinds => [A, new(rinds,rA.NRows)] LA:% := new(linds,rA.NRows) RA:% := new(rinds,rA.NRows) for i in 1..rA.NRows repeat r := row(A,i) ri:L C := r.Indices re:L D := r.Entries li:L C := empty() le:L D := empty() while not(empty? ri) and (first(ri)>c) repeat li := cons(first(ri),li) le := cons(first re, le) ri := rest ri re := rest re if not empty? li then li := reverse! li le := reverse! le setRow!(LA,i,li,le) if not empty? ri then setRow!(RA,i,ri,re) [LA,RA]
-- ----------- -- -- Row Echelon -- -- ----------- --
addRows(d1:D,r1:ROWREC,d2:D,r2:ROWREC):ROWREC == -- Computes linear combination of two rows. -- Local function. empty? r1.Indices => one? d2 => r2 [r2.Indices, [d2*e2 for e2 in r2.Entries]] empty? r2.Indices => one? d1 => r1 [r1.Indices, [d1*e1 for e1 in r1.Entries]] resI:L C := empty() resE:L D := empty() local lent1:L D local lent2:L D if not(noChecks?) and one? d1 then lent1 := r1.Entries else lent1 := [d1*e1 for e1 in r1.Entries] if not(noChecks?) and one? d2 then lent2 := copy r2.Entries else lent2 := [d2*e2 for e2 in r2.Entries] lind2 := copy r2.Indices
for c1 in r1.Indices for e1 in lent1 repeat while not(empty? lind2) and c1<first(lind2) repeat resI := cons(first lind2, resI) resE := cons(first(lent2), resE) lind2 := rest lind2 lent2 := rest lent2 if not(empty? lind2) and first(lind2)=c1 then sum := e1+first(lent2) if noChecks? or not zero? sum then resI := cons(c1,resI) resE := cons(sum,resE) lind2 := rest lind2 lent2 := rest lent2 else resI := cons(c1,resI) resE := cons(e1,resE)
resI := concat!(reverse! resI, lind2) resE := concat!(reverse! resE, lent2) while not(empty? resE) and zero? first resE repeat resI := rest resI resE := rest resE [resI,resE]
pivot(A:%,i:I):Record(Index:C,Entry:D) == r := row(A,i) empty? r.Indices => error "empty row" [first r.Indices, first r.Entries]
pivots(A:%):ROWREC == resI:L C := empty() resE:L D := empty() for r in entries rep(A).Rows | not empty? r.Indices repeat resI := cons(first r.Indices, resI) resE := cons(first r.Entries, resE) [reverse! resI, reverse! resE]
rowEchelon(AA:%):Record(Ech:%, Lt:M D, Pivots:L D, Rank:NNI) == A := rep copy AA LTr:M D := diagonalMatrix [1$D for i in 1..A.NRows] Pivs:L D := empty()
-- check pivots for i in 1..A.NRows repeat r := qelt(A.Rows,i) changed?:B := false while not(empty? r.Entries) and zero? first r.Entries repeat r.Entries := rest r.Entries r.Indices := rest r.Indices changed? := true if changed? then qsetelt!(A.Rows,i,r)
-- sort rows by pivots (bubble sort) notSorted?:B := true while notSorted? repeat notSorted? := false oldr:ROWREC := qelt(A.Rows,1) for i in 2..A.NRows repeat newr:ROWREC := qelt(A.Rows,i) if greater(newr,oldr) then qsetelt!(A.Rows,i,oldr) qsetelt!(A.Rows,i-1,newr) swapRows!(LTr,i-1,i) notSorted? := true else oldr := newr
-- fraction-free elimination early?:B := false local pivlen,pivrow,rk:NNI for i in 1..A.NRows repeat r:ROWREC := qelt(A.Rows,i) if empty? r.Indices then rk:NNI := (i-1)::NNI early? := true break -- search good pivot pivind := first r.Indices pivlen := #r.Indices pivrow := i k:I := 0 for j in (i+1)..A.NRows _ while not(empty? qelt(A.Rows,j).Indices) and _ pivind=first(qelt(A.Rows,j).Indices) repeat len:NNI := #qelt(A.Rows,j).Indices k := k+1 if len<pivlen then pivlen := len pivrow := j piv:D := first qelt(A.Rows,pivrow).Entries Pivs := cons(piv,Pivs)
-- elimination necessary? if k>0 then if pivrow~=i then pr := qelt(A.Rows,pivrow) qsetelt!(A.Rows,pivrow,qelt(A.Rows,i)) qsetelt!(A.Rows,i,pr) swapRows!(LTr,i,pivrow)
-- elimination (and resorting of rows) pr := copy qelt(A.Rows,i) pr.Indices := rest pr.Indices pr.Entries := rest pr.Entries for j in (i+1)..(i+k) repeat r := copy qelt(A.Rows,i+1) c := first r.Entries r.Indices := rest r.Indices r.Entries := rest r.Entries r := addRows(piv,r,-c,pr) for l in 1..A.NRows repeat f := piv*qelt(LTr,i+1,l) - c*qelt(LTr,i,l) qsetelt!(LTr,i+1,l,f) for l in (i+2)..(2*i+k+1-j) repeat qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) swapRows!(LTr,l-1,l) for l in (2*i+k+2-j)..A.NRows _ while greater(qelt(A.Rows,l),r) repeat qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) swapRows!(LTr,l-1,l) qsetelt!(A.Rows,l-1,r)
if not early? then rk:NNI := A.NRows [per(A), LTr, Pivs, rk]
-- -------------- -- -- Multiplication -- -- -------------- --
(LL:M D) * (AA:%) : % == ncols(LL)~=AA.NRows => error "improper matrix dimensions" A := rep copy AA rlen := nrows LL res:% := new(A.AllInds,rlen)
for c in A.AllInds repeat tmp:V D := new(rlen,0$D) for i in 1..A.NRows repeat r:ROWREC := qelt(A.Rows,i) inds:L C := r.Indices if not(empty? inds) and first(inds)=c then for k in 1..rlen | not zero? qelt(LL,k,i) repeat qsetelt!(tmp, k, qelt(tmp,k) + qelt(LL,k,i)*first(r.Entries)) r.Entries := rest r.Entries r.Indices := rest inds qsetelt!(A.Rows,i,r) for k in 1..rlen | not zero? qelt(tmp,k) repeat r := qelt(res.Rows,k) r.Indices := cons(c,r.Indices) r.Entries := cons(qelt(tmp,k),r.Entries) qsetelt!(res.Rows,k,r)
for k in 1..rlen repeat r := qelt(res.Rows,k) r.Indices := reverse! r.Indices r.Entries := reverse! r.Entries qsetelt!(res.Rows,k,r) per res
-- ---------------------------- -- -- primitive form in gcd domain -- -- ---------------------------- --
--if D has Join(GcdDomain,IntegralDomain) then
-- import from Fraction D
-- setGcdMode(s:Sy):Sy == -- free GCDmode -- tmp := GCDmode -- (s=iter) or (s=rand) => -- GCDmode := s -- tmp -- error "unknown gcd mode"
-- randomGCD(le:L D):D == -- -- Probabilistic technique. -- free seed -- #le=2 => gcd(first le, second le) -- f := first le -- g := second le -- l := rest rest le -- while not empty? l repeat -- one? first l => return 1$D -- f := f + (1+random(seed)$I)*first(l) -- l := rest l -- if not empty? l then -- one? first l => return 1$D -- g := g + (1+random(seed)$I)*first(l) -- l := rest l -- h := gcd(f,g) -- l := [h] -- for e in le repeat -- tmp := exquo(e,h) -- if tmp case "failed" then -- l := cons(e,l) -- one?(#l) => h -- randomGCD l
-- iteratedGCD(le:L D):D == -- -- Computes gcd iteratively -- res := gcd(first le, second le) -- l := rest rest le -- while not(empty?(l) or one?(res)) repeat -- res := gcd(res, first l) -- l := rest l -- res
-- makePrimitive(r:ROWREC):Record(GCD:D, Row:ROWREC) == -- -- remove common gcd of row -- free GCDmode -- le := r.Entries -- one?(#le) => [first le, [r.Indices,[1$D]]] -- local g:D -- if GCDmode=iterated then -- g := iteratedGCD le -- else -- g := randomGCD le -- one? g => [1,r] -- le := [exquo(e,g)::D for e in le] -- [g, [r.Indices,le]]
-- primitiveRowEchelon(AA:%) : _ -- Record(Ech:%, Lt:M F D, Pivots:L D, Rank:NNI) == -- A := rep copy AA -- LTr:M F D := diagonalMatrix [1$(F D) for i in 1..A.NRows] -- Pivs:L D := empty()
-- -- check pivots -- for i in 1..A.NRows repeat -- r := qelt(A.Rows,i) -- changed?:B := false -- while not(empty? r.Entries) and zero? first r.Entries repeat -- r.Entries := rest r.Entries -- r.Indices := rest r.Indices -- changed? := true -- if changed? then -- qsetelt!(A.Rows,i,r)
-- -- sort rows by pivots (bubble sort) -- notSorted?:B := true -- while notSorted? repeat -- notSorted? := false -- oldr := qelt(A.Rows,1) -- for i in 2..A.NRows repeat -- newr := qelt(A.Rows,i) -- if greater(newr,oldr) then -- qsetelt!(A.Rows,i,oldr) -- qsetelt!(A.Rows,i-1,newr) -- swapRows!(LTr,i-1,i) -- notSorted? := true -- else -- oldr := newr
-- -- primitive fraction-free elimination -- early?:B := false -- local pivlen,pivrow,rk:NNI -- for i in 1..A.NRows until finished? repeat -- r := qelt(A.Rows,i) -- if empty? r.Indices then -- rk:NNI := (i-1)::NNI -- early? := true -- break -- -- search good pivot -- pivind := first r.Indices -- pivlen := #r.Indices -- pivrow := i -- k:I := 0 -- for j in (i+1)..A.NRows _ -- while not(empty? qelt(A.Rows,j).Indices) and _ -- pivind=first(qelt(A.Rows,j).Indices) repeat -- len := #qelt(A.Rows,j).Indices -- k := k+1 -- if len<pivlen then -- pivlen := len -- pivrow := j
-- -- make row primitive -- tmp := makePrimitive qelt(A.Rows,pivrow) -- if not one? tmp.GCD then -- qsetelt!(A.Rows,pivrow,tmp.Row) -- q:FD := 1/tmp.GCD -- for l in 1..A.NRows | not zero? qelt(LTr,pivrow,l) repeat -- qsetelt!(LTr,pivrow,l,q*qelt(LTr,pivrow,l)) -- piv:D := first qelt(A.Rows,pivrow).Entries -- Pivs := cons(piv,Pivs)
-- -- elimination necessary? -- if k>0 then -- if pivrow~=i then -- pr := qelt(A.Rows,pivrow) -- qsetelt!(A.Rows,pivrow,qelt(A.Rows,i)) -- qsetelt!(A.Rows,i,pr) -- swapRows!(LTr,i,pivrow)
-- -- elimination (and resorting of rows) -- pr := copy tmp.Row -- pr.Indices := rest pr.Indices -- pr.Entries := rest pr.Entries -- for j in (i+1)..(i+k) repeat -- r := copy qelt(A.Rows,i+1) -- c := first r.Entries -- r.Indices := rest r.Indices -- r.Entries := rest r.Entries -- r := addRows(piv,r,-c,pr) -- for l in 1..A.NRows repeat -- fd:FD := piv *$FD qelt(LTr,i+1,l) - (c*qelt(LTr,i,l))::FD -- qsetelt!(LTr,i+1,l,fd) -- for l in (i+2)..(2*i+k+1-j) repeat -- qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) -- swapRows!(LTr,l-1,l) -- for l in (2*i+k+2-j)..A.NRows _ -- while greater(qelt(A.Rows,l),r) repeat -- qsetelt!(A.Rows,l-1,qelt(A.Rows,l)) -- swapRows!(LTr,l-1,l) -- qsetelt!(A.Rows,l-1,r)
-- if not early? then -- rk:NNI := A.NRows -- [per(A), LTr, Pivs, rk]
-- mult(f:F D,d:D):D == -- res := numer(f)*d -- tmp := exquo(res,denom(f)) -- tmp case "failed" => error "cannot divide in mult" -- tmp::D
-- (LL:M F D) * (AA:%) : % == -- ncols(LL)~=AA.NRows => error "improper matrix dimensions" -- A := rep copy AA -- rlen := nrows LL -- res:% := new(A.AllInds,rlen)
-- for c in A.AllInds repeat -- tmp:V F D := new(rlen,0$FD) -- for i in 1..A.NRows repeat -- r := qelt(A.Rows,i) -- inds := r.Indices -- if not(empty? inds) and first(inds)=c then -- for k in 1..rlen | not zero? qelt(LL,k,i) repeat -- qsetelt!(tmp, k, qelt(tmp,k) + qelt(LL,k,i)*first(r.Entries)) -- r.Entries := rest r.Entries -- r.Indices := rest inds -- qsetelt!(A.Rows,i,r) -- for k in 1..rlen | not zero? qelt(tmp,k) repeat -- d:Union(value:D,failed:'failed') := retractIfCan qelt(tmp,k) -- d case failed => error "cannot divide in *" -- r := qelt(res.Rows,k) -- r.Indices := cons(c,r.Indices) -- r.Entries := cons(d::D,r.Entries) -- qsetelt!(res.Rows,k,r)
-- for k in 1..rlen repeat -- r := qelt(res.Rows,k) -- r.Indices := reverse! r.Indices -- r.Entries := reverse! r.Entries -- qsetelt!(res.Rows,k,r) -- per res
-- --------------------- -- -- LUDecomposition (LUD) -- -- --------------------- --
+++ Description: +++ \axiomType{LUDecomposition} contains procedures to solve linear systems of +++ equations or to compute inverses using a LU decomposition.
LUDecomposition(D:Field) : Cat == Def where
Cat ==> with
LUDecomp : M D -> Record(LU:M D, Perm:V I, Pivots:L D) ++ \axiom{LUDecomp(A)} computes a LU decomposition of \axiom{A} ++ using the algorithm of Crout. \axiom{LU} contains both triangular ++ matrices; \axiom{Perm} is the permutation used for partial ++ pivoting and \axiom{Pivots} yields the used pivots.
LUSolve : (M D, V I, V D) -> V D ++ \axiom{LUSolve(LU,Perm,B)} uses a previously computed LU ++ decomposition to solve a linear system with right hand side ++ \axiom{B}. \axiom{LU} and \axiom{Perm} are as given by ++ \axiom{LUDecomp}.
LUInverse : M D -> Record(Inv:M D, Pivots:L D) ++ \axiom{LUInverse(A)} computes the inverse of \axiom{A} using a LU ++ decomposition.
Def ==> add
import from D
LUDecomp(AA:M D):Record(LU:M D, Perm:V I, Pivots:L D) == -- LU decomposition using Crout's algorithm with partial pivoting. A := copy AA minR := minRowIndex A; maxR := maxRowIndex A minC := minColIndex A; maxC := maxColIndex A maxR~=maxC => error "LU decomposition only of square matrices" PermV:V I := new((maxR-minR+1)::NNI,0) Pivs:L D := empty()
for j in minC..maxC repeat for i in minR..(j-1) repeat s := qelt(A,i,j) for k in minR..(i-1) repeat s := s - qelt(A,i,k)*qelt(A,k,j) qsetelt!(A,i,j,s)
i0:I := -1 for i in j..maxR repeat s := qelt(A,i,j) for k in minC..(j-1) repeat s := s - qelt(A,i,k)*qelt(A,k,j) qsetelt!(A,i,j,s) if not(zero? s) and i0<0 then i0 := i -- first non-zero pivot
i0<0 => error "singular matrix in LUDecomp" if j~=i0 then swapRows!(A,j,i0) qsetelt!(PermV,j,i0) Pivs := cons(qelt(A,j,j),Pivs)
if j~=maxC then d := 1/qelt(A,j,j) for k in (j+1)..maxR repeat qsetelt!(A,k,j,d*qelt(A,k,j))
[A,PermV,Pivs]
LUSolve(LU:M D,Perm:V I,XX:V D):V D == -- Solves LU decomposed linear system for right hand side XX X := copy XX minR := minRowIndex LU; maxR := maxRowIndex LU maxIndex(X)~=maxR => error "Wrong dimensions in LUSolve" ii:I := -1
for i in minR..maxR repeat -- forward substitution ip := qelt(Perm,i) s := qelt(X,ip) qsetelt!(X,ip,qelt(X,i)) if ii>=0 then for j in ii..(i-1) repeat s := s - qelt(LU,i,j)*qelt(X,j) else if not zero? s then ii := i qsetelt!(X,i,s)
for i in maxR..minR by -1 repeat -- back substitution s := qelt(X,i) for j in (i+1)..maxR repeat s := s - qelt(LU,i,j)*qelt(X,j) qsetelt!(X,i,s/qelt(LU,i,i))
X
LUInverse(A:M D):Record(Inv:M D, Pivots:L D) == -- Inversion via LU decomposition Alu := LUDecomp A n := ncols A res:M D := new(n,n,0)
for i in minRowIndex(A)..maxRowIndex(A) repeat v:V D := new(n,0) qsetelt!(v,i,1) res := setColumn!(res,i,LUSolve(Alu.LU,Alu.Perm,v))
[res,Alu.Pivots]
Compiling FriCAS source code from file
/var/zope2/var/LatexWiki/2184467348127326179-25px001.as using
AXIOM-XL compiler and options
-O -Fasy -Fao -Flsp -laxiom -Mno-AXL_W_WillObsolete -DAxiom -Y $AXIOM/algebra
Use the system command )set compiler args to change these
options.
#1 (Warning) Deprecated message prefix: use `ALDOR_' instead of `_AXL'
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 57:
elimZeroCols_! : % -> ()
................^
[L57 C17] #2 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 61:
purge_! : (%,C->B) -> ()
.........^
[L61 C10] #3 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 65:
sortedPurge_! : (%,C->B) -> ()
...............^
[L65 C16] #4 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 79:
setelt_! : (%,I,C,D) -> ()
..........^
[L79 C11] #5 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 87:
setRow_! : (%,I,ROWREC) -> ()
..........^
[L87 C11] #6 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 91:
setRow_! : (%,I,L C,L D) -> ()
..........^
[L91 C11] #7 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 95:
deleteRow_! : (%,I) -> ()
.............^
[L95 C14] #8 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 99:
consRow_! : (%,ROWREC) -> ()
...........^
[L99 C12] #9 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 103:
appendRow_! : (%,ROWREC) -> ()
.............^
[L103 C14] #10 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 210:
setRow_!(A:%,i:I,r:ROWREC):() ==
..........^
[L210 C11] #11 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 215:
setRow_!(A:%,i:I,inds:L C,ents:L D):() ==
..........^
[L215 C11] #12 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 233:
setelt_!(A:%,i:I,c:C,d:D):() ==
..........^
[L233 C11] #13 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 274:
r:ROWREC := qelt(Arows,l)
.........................^
[L274 C26] #20 (Error) (After Macro Expansion) Argument 1 of `qelt' did not match any possible parameter type.
The rejected type is Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: Arows
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 275:
qsetelt!(resRows, l, [copy r.Indices, copy r.Entries])
.................^
[L275 C18] #21 (Error) (After Macro Expansion) Argument 1 of `qsetelt!' did not match any possible parameter type.
The rejected type is Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: resRows
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 283:
elimZeroCols_!(A:%):() ==
................^
[L283 C17] #14 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 290:
purge_!(A:%,crit:C->B):() ==
.........^
[L290 C10] #15 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 301:
r := qelt(rA.Rows,l)
......................^
[L301 C23] #22 (Error) (After Macro Expansion) Argument 1 of `qelt' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 308:
qsetelt!(rA.Rows, l, [reverse! newInds, reverse! newEnts])
.....................^
[L308 C22] #23 (Error) (After Macro Expansion) Argument 1 of `qsetelt!' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 311:
sortedPurge_!(A:%,crit:C->B):() ==
...............^
[L311 C16] #16 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 318:
r := qelt(rA.Rows,l)
......................^
[L318 C23] #24 (Error) (After Macro Expansion) Argument 1 of `qelt' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 322:
qsetelt!(rA.Rows,l,r)
.....................^
[L322 C22] #25 (Error) (After Macro Expansion) Argument 1 of `qsetelt!' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 325:
deleteRow_!(A:%,i:I):() ==
.............^
[L325 C14] #17 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 333:
qsetelt!(resRows,l-1,qelt(rA.Rows,l))
....................................^
[L333 C37] #26 (Error) (After Macro Expansion) Argument 1 of `qelt' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 338:
consRow_!(A:%,r:ROWREC):() ==
...........^
[L338 C12] #18 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 348:
appendRow_!(A:%,r:ROWREC):() ==
.............^
[L348 C14] #19 (Warning) Escape character ignored. Do you mean '__'?
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 375:
qsetelt!(newRows, l, qelt(rA1.Rows,l))
.....................................^
[L375 C38] #27 (Error) (After Macro Expansion) Argument 1 of `qelt' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA1(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 377:
qsetelt!(newRows, rA1.NRows+l, qelt(rA2.Rows,l))
...............................................^
[L377 C48] #28 (Error) (After Macro Expansion) Argument 1 of `qelt' did not match any possible parameter type.
The rejected type is Rows: Vector(Record(Indices: List(C), Entries: List(D))).
Expected one of:
-- List(C)
-- List(D)
-- List(Record(Indices: List(C), Entries: List(D)))
-- List(NonNegativeInteger)
Expanded expression was: rA2(Rows)
"/var/zope2/var/LatexWiki/2184467348127326179-25px001.as", line 386:
res:% := new(newInds,rA1.NRows)
...................^
[L386 C20] #29 (Error) (After Macro Expansion) Argument 1 of `new' did not match any possible parameter type.
The rejected type is List(C).
Expected one of:
-- SingleInteger
-- NonNegativeInteger
Expanded expression was: newInds
[L386 C20] #30 (Fatal Error) (After Macro Expansion) Too many errors (use `-M emax=n' or `-M no-emax' to change the limit).
Expanded expression was: ()
The )library system command was not called after compilation.