123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203(*****************************************************************************)(* *)(* MIT License *)(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)moduletypeRing_sig=sigtypetvaladd:t->t->tvalmul:t->t->tvalnegate:t->tvalzero:tvalone:tvaleq:t->t->boolendmoduletypeField_sig=sigincludeRing_sigvalinverse_exn:t->tend(** This refers to the mathematical generalization of vector space called
"module", where the field of scalars is replaced by a ring *)moduletypeModule_sig=sigtypettypematrix=tarrayarray(** [zeros r c] is a matrix with [r] rows and [c] columns filled with zeros *)valzeros:int->int->matrix(** [identity n] is the identity matrix of dimension [n] *)validentity:int->matrix(** matrix equality *)valequal:matrix->matrix->bool(** matrix addition *)valadd:matrix->matrix->matrix(** matrix multiplication *)valmul:matrix->matrix->matrix(** matrix transposition *)valtranspose:matrix->matrix(** [row_add ~coeff i j m] adds to the i-th row, the j-th row times coeff in m *)valrow_add:?coeff:t->int->int->matrix->unit(** [row_swap i j m] swaps the i-th and j-th rows of m *)valrow_swap:int->int->matrix->unit(** [row_mul coeff i m] multiplies the i-th row by coeff in m *)valrow_mul:t->int->matrix->unit(** [filter_cols f m] removes the columns of [m] whose index does not satisfy [f] *)valfilter_cols:(int->bool)->matrix->matrix(** splits matrix [m] into the first n columns and the rest, producing two matrices *)valsplit_n:int->matrix->matrix*matrixendmoduletypeVectorSpace_sig=sigincludeModule_sig(** reduced row Echelon form of m *)valreduced_row_echelon_form:matrix->matrix(** [inverse m] is the inverse matrix of m
@raise [Invalid_argument] if [m] is not invertible *)valinverse:matrix->matrixendmoduleMake_Module(Ring:Ring_sig):Module_sigwithtypet=Ring.t=structtypet=Ring.ttypematrix=tarrayarrayletzerosrc=Array.make_matrixrcRing.zeroletidentityn=Array.(initn(funi->initnRing.(funj->ifi=jthenoneelsezero)))letequal=Array.(for_all2(for_all2Ring.eq))letadd=Array.(map2(map2Ring.add))letmulm1m2=letnb_rows=Array.lengthm1inletnb_cols=Array.lengthm2.(0)inletn=Array.lengthm1.(0)inassert(Array.lengthm2=n);letp=zerosnb_rowsnb_colsinfori=0tonb_rows-1doforj=0tonb_cols-1dofork=0ton-1dop.(i).(j)<-Ring.(addp.(i).(j)@@mulm1.(i).(k)m2.(k).(j))donedonedone;plettransposem=letnb_rows=Array.lengthminletnb_cols=Array.lengthm.(0)inArray.(initnb_cols(funi->initnb_rows(funj->m.(j).(i))))letrow_add?(coeff=Ring.one)ijm=m.(i)<-Array.map2Ring.(funab->adda(mulcoeffb))m.(i)m.(j)letrow_swapijm=letaux=m.(i)inm.(i)<-m.(j);m.(j)<-auxletrow_mulcoeffim=m.(i)<-Array.map(Ring.mulcoeff)m.(i)letfilter_colsf=Array.map(funrow->List.filteri(funi_->fi)(Array.to_listrow)|>Array.of_list)letsplit_nnm=(filter_cols(funi->i<n)m,filter_cols(funi->i>=n)m)endmoduleMake_VectorSpace(Field:Field_sig):VectorSpace_sigwithtypet=Field.t=structincludeMake_Module(Field)letreduced_row_echelon_formm=letn=Array.lengthmin(* returns the first non-zero index in the row *)letfind_pivotrow=letrecauxcnt=function|[]->None|x::xs->ifField.(eqzerox)thenaux(cnt+1)xselseSomecntinaux0(Array.to_listrow)inletmove_zeros_to_bottomm=letis_non_zero_row=Array.exists(funa->notField.(eqzeroa))inletrecauxnonzeroszeros=function|[]->Array.of_list(List.revnonzeros@zeros)|r::rs->ifis_non_zero_rowrthenaux(r::nonzeros)zerosrselseauxnonzeros(r::zeros)rsinaux[][](Array.to_listm)inletrecauxk=ifk>=Array.lengthmthenmelsematchfind_pivotm.(k)with|Somejwhenj<n->row_mul(Field.inverse_exnm.(k).(j))km;Array.iteri(funi_->ifi<>kthenrow_add~coeff:Field.(negate@@m.(i).(j))ikm)m;row_swapkjm;aux(k+1)|_->aux(k+1)inaux0|>move_zeros_to_bottomletinversem=letn=Array.lengthminletid_n=identityninletaugmented=Array.(map2appendmid_n)inletreduced=reduced_row_echelon_formaugmentedinletresidue,inv=split_nnreducedinletis_zero_row=Array.for_allField.(eqzero)inifArray.existsis_zero_rowresiduethenraise@@Invalid_argument"matrix [m] is not invertible"elseinvend