12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091# 1 "src/owl/linalg/owl_linalg_generic.ml"(*
* OWL - OCaml Scientific and Engineering Computing
* Copyright (c) 2016-2019 Liang Wang <liang.wang@cl.cam.ac.uk>
*)[@@@warning"-6"]openBigarraytype('a,'b)t=('a,'b)Owl_dense_matrix_generic.t(*
We create a local generic matrix module with basic operators. This is only
way to let us use operators to write concise math but avoid circular dependency
at the same time.
*)moduleM=structincludeOwl_dense_matrix_genericincludeOwl_operator.Make_Basic(Owl_dense_matrix_generic)includeOwl_operator.Make_Extend(Owl_dense_matrix_generic)includeOwl_operator.Make_Matrix(Owl_dense_matrix_generic)end(* Helper functions *)letis_squarex=letm,n=M.shapexinm=nletselect_evkeywordev=letk=M.kindevinletm,n=M.shapeevinlets=M.zerosint32mninlet_=matchkeywordwith|`LHP->(let_op=Owl_base_dense_common._re_eltkinM.iteri_2d(funija->if_opa<0.thenM.setsij1l)ev)|`RHP->(let_op=Owl_base_dense_common._re_eltkinM.iteri_2d(funija->if_opa>=0.thenM.setsij1l)ev)|`UDI->(let_op=funa->Owl_base_dense_common.(_abs_eltka|>_re_eltk)inM.iteri_2d(funija->if_opa<1.thenM.setsij1l)ev)|`UDO->(let_op=funa->Owl_base_dense_common.(_abs_eltka|>_re_eltk)inM.iteri_2d(funija->if_opa>=1.thenM.setsij1l)ev)ins(* LU decomposition *)letlux=letx=M.copyxinletm,n=M.shapexinletminmn=Pervasives.minmninleta,ipiv=Owl_lapacke.getrf~a:xinletl=M.trilainletu=M.resize(M.triua)[|n;n|]inlet_a1=Owl_const.one(M.kindx)infori=0tominmn-1doM.setlii_a1done;l,u,ipivletlufactx=leta,ipiv=Owl_lapacke.getrf~a:xina,ipiv(* basic functions *)letinvx=letx=M.copyxinleta,ipiv=Owl_lapacke.getrf~a:xinOwl_lapacke.getri~a~ipivletdetx=letx=M.copyxinletm,n=M.shapexinassert(m=n);leta,ipiv=Owl_lapacke.getrf~a:xinletd=ref(Owl_const.one(M.kindx))inletc=ref0inlet_mul_op=Owl_base_dense_common._mul_elt(M.kindx)infori=0tom-1dod:=_mul_op!d(M.getaii);(* NOTE: +1 to adjust to Fortran index *)if(M.getipiv0i)<>Int32.of_int(i+1)thenbeginc:=!c+1;enddone;matchOwl_maths.is_odd!cwith|true->Owl_base_dense_common._neg_elt(M.kindx)!d|false->!d(* FIXME: need to check ... *)letlogdetx=letx=M.copyxinletm,n=M.shapexinassert(m=n);let_kind=M.kindxinleta,ipiv=Owl_lapacke.getrf~a:xinletd=ref(Owl_const.zero_kind)inletc=ref0inlet_add_op=Owl_base_dense_common._add_elt_kindinlet_log_op=Owl_base_dense_common._log_elt_kindinlet_abs_op=Owl_base_dense_common._abs_elt_kindinfori=0tom-1dolete=M.getaiiind:=_add_op!d(_log_op(_abs_ope));(* NOTE: +1 to adjust to Fortran index *)letp=(M.getipiv0i)<>Int32.of_int(i+1)inletq=e<(Owl_const.zero_kind)in(* implement xor *)if(p&¬q)||(notp&&q)thenc:=!c+1done;matchOwl_maths.is_odd!cwith|true->failwith"logdet: det is negative"|false->!d(* QR decomposition *)let_get_qr_q:typeab.(a,b)kind->(a,b)t->(a,b)t->(a,b)t=funkatau->matchkwith|Float32->Owl_lapacke.orgqratau|Float64->Owl_lapacke.orgqratau|Complex32->Owl_lapacke.ungqratau|Complex64->Owl_lapacke.ungqratau|_->failwith"owl_linalg:_get_qr_q"letqr?(thin=true)?(pivot=false)x=letx=M.copyxinletm,n=M.shapexinletminmn=Pervasives.minmninleta,tau,jpvt=matchpivotwith|true->Owl_lapacke.geqp3x|false->(letjpvt=M.emptyint3200inleta,tau=Owl_lapacke.geqrf~a:xina,tau,jpvt)inletr=matchthinwith|true->M.resize~head:true(M.triua)[|minmn;n|]|false->M.resize~head:true(M.triua)[|m;n|]inleta=matchthinwith|true->a|false->ifm<=nthenaelse(leta'=M.zeros(M.kindx)m(m-n)inM.concat_horizontalaa')inletq=_get_qr_q(M.kindx)atauinq,r,jpvtletqrfact?(pivot=false)x=leta,tau,jpvt=matchpivotwith|true->Owl_lapacke.geqp3x|false->(letjpvt=M.emptyint3200inleta,tau=Owl_lapacke.geqrfxina,tau,jpvt)ina,tau,jpvtlet_get_lq_q:typeab.(a,b)kind->(a,b)t->(a,b)t->(a,b)t=funkatau->matchkwith|Float32->Owl_lapacke.orglqatau|Float64->Owl_lapacke.orglqatau|Complex32->Owl_lapacke.unglqatau|Complex64->Owl_lapacke.unglqatau|_->failwith"owl_linalg:_get_lq_q"letlq?(thin=true)x=letx=M.copyxinletm,n=M.shapexinletminmn=Pervasives.minmninleta,tau=Owl_lapacke.gelqfxinletl=matchthinwith|true->ifm<nthenM.get_slice[[];[0;minmn-1]](M.trila)elseM.trila|false->M.trilainleta=matchthinwith|true->a|false->ifm>=nthenaelseM.resize~head:truea[|n;n|]inletq=_get_lq_q(M.kindx)atauinl,q(* Sigular Value decomposition *)letsvd?(thin=true)x=letx=M.copyxinletjobz=matchthinwith|true->'S'|false->'A'inletu,s,vt=Owl_lapacke.gesdd~jobz~a:xinu,s,vtletsvdvalsx=letx=M.copyxinlet_,s,_=Owl_lapacke.gesdd~jobz:'N'~a:xinsletgsvdxy=letx=M.copyxinlety=M.copyyinletm,_n=M.shapexinletp,_=M.shapeyinletu,v,q,alpha,beta,k,l,r=Owl_lapacke.ggsvd3~jobu:'U'~jobv:'V'~jobq:'Q'~a:x~b:yinletalpha=M.resize~head:truealpha[|1;(k+l)|]inletd1=M.resize~head:true(M.diagmalpha)[|m;k+l|]inletbeta=M.resize~head:truebeta[|1;k+l|]inletbeta=M.resize~head:falsebeta[|1;l|]inletd2=M.resize(M.diagm~kbeta)[|p;k+l|]inu,v,q,d1,d2,rletgsvdvalsxy=letx=M.copyxinlety=M.copyyinlet_,_,_,alpha,beta,k,l,_=Owl_lapacke.ggsvd3~jobu:'N'~jobv:'N'~jobq:'N'~a:x~b:yinletalpha=M.resize~head:truealpha[|1;k+l|]inletbeta=M.resize~head:truebeta[|1;k+l|]inM.(divalphabeta)letrank?tolx=letsv=svdvalsxinletm,n=M.shapexinletmaxmn=Pervasives.maxmnin(* by default using float32 eps *)leteps=Owl_utils.epsFloat32inlettol=matchtolwith|Sometol->tol|None->(float_of_intmaxmn)*.epsinletdtol=tolinletztol=Complex.({re=tol;im=neg_infinity})inlet_count:typeab.(a,b)kind->(a,b)t->int=fun_kindsv->match_kindwith|Float32->M.elt_greater_scalarsvdtol|>M.sum'|>int_of_float|Float64->M.elt_greater_scalarsvdtol|>M.sum'|>int_of_float|Complex32->leta=M.elt_greater_scalarsvztol|>M.sum'inint_of_floatComplex.(a.re)|Complex64->leta=M.elt_greater_scalarsvztol|>M.sum'inint_of_floatComplex.(a.re)|_->failwith"owl_linalg:rank"in_count(M.kindsv)sv(* Cholesky Decomposition *)letchol?(upper=true)x=letx=M.copyxinmatchupperwith|true->Owl_lapacke.potrf'U'x|>M.triu|false->Owl_lapacke.potrf'L'x|>M.tril(* Schur Decomposition *)let_magic_complex:typeabcd.(c,d)kind->(a,b)t->(a,b)t->(c,d)t=funotypreim->letityp=M.kindreinmatchityp,otypwith|Float32,Complex32->M.complexfloat32complex32reim|Float64,Complex64->M.complexfloat64complex64reim|Complex32,Complex32->re|Complex64,Complex64->re|_->failwith"owl_linalg_generic:_magic_complex"letschur:typeabcd.otyp:(c,d)kind->(a,b)t->(a,b)t*(a,b)t*(c,d)t=fun~otypx->assert(is_squarex);letx=M.copyxinlett,z,wr,wi=Owl_lapacke.gees~jobvs:'V'~a:xinletw=_magic_complexotypwrwiint,z,wletschur_tzx=assert(is_squarex);leta=M.copyxinlett,z,_,_=Owl_lapacke.gees~jobvs:'V'~aint,zletordschur:typeabcd.otyp:(c,d)kind->select:(int32,int32_elt)t->(a,b)t->(a,b)t->(a,b)t*(a,b)t*(c,d)t=fun~otyp~selecttq->lett=M.copytinletq=M.copyqinM.iter(funa->assert(a=0l||a=1l))select;letts,zs,wr,wi=Owl_lapacke.trsen~job:'V'~compq:'V'~select~t~qinletws=_magic_complexotypwrwiints,zs,ws(* Generalised Schur Decomposition *)letqz:typeabcd.otyp:(c,d)kind->(a,b)t->(a,b)t->(a,b)t*(a,b)t*(a,b)t*(a,b)t*(c,d)t=fun~otypxy->assert(is_squarex);assert(is_squarey);leta=M.copyxinletb=M.copyyinlets,t,ar,ai,bt,q,z=Owl_lapacke.gges~jobvsl:'V'~jobvsr:'V'~a~binletalpha=_magic_complexotyparaiinletbeta=M.castotypbtinlete=M.(alpha/beta)ins,t,q,z,eletordqz:typeabcd.otyp:(c,d)kind->select:(int32,int32_elt)t->(a,b)t->(a,b)t->(a,b)t->(a,b)t->(a,b)t*(a,b)t*(a,b)t*(a,b)t*(c,d)t=fun~otyp~selectabqz->leta=M.copyainletb=M.copybinletq=M.copyqinletz=M.copyzinleta,b,ar,ai,bt,q,z=Owl_lapacke.tgsen~select~a~b~q~zinletalpha=_magic_complexotyparaiinletbeta=M.castotypbtinlete=M.(alpha/beta)ina,b,q,z,eletqzvals:typeabcd.otyp:(c,d)kind->(a,b)t->(a,b)t->(c,d)t=fun~otypxy->assert(is_squarex);assert(is_squarey);leta=M.copyxinletb=M.copyyinletar,ai,bt,_,_=Owl_lapacke.ggev~jobvl:'N'~jobvr:'N'~a~binletalpha=_magic_complexotyparaiinletbeta=M.castotypbtinM.(alpha/beta)(* TODO: RQ Decomposition *)letrq_x=()[@@warning"-32"](* Eigenvalue problem *)leteig:typeabcd.?permute:bool->?scale:bool->otyp:(a,b)kind->(c,d)t->(a,b)t*(a,b)t=fun?(permute=true)?(scale=true)~otypx->letx=M.copyxinletbalanc=matchpermute,scalewith|true,true->'B'|true,false->'P'|false,true->'S'|false,false->'N'inlet_a,wr,wi,_,vr,_,_,_,_,_,_=Owl_lapacke.geevx~balanc~jobvl:'N'~jobvr:'V'~sense:'N'~a:xin(* TODO: optimise the performance by writing in c *)(* construct eigen vectors from real wr and wi *)let_construct_v:typeab.(float,a)kind->(Complex.t,b)kind->(float,a)t->(float,a)t->(float,a)t->(Complex.t,b)t->unit=funk0k1wrwivrv->let_a0=Owl_const.zero(M.kindwi)inlet_,n=M.shapevinletj=ref0inwhile!j<ndoif(M.getwi0!j)=_a0then(fork=0ton-1doM.setvk!jComplex.({re=M.getvrk!j;im=0.})done)else(fork=0ton-1doM.setvk!jComplex.({re=M.getvrk!j;im=M.getvrk(!j+1)});M.setvk(!j+1)Complex.({re=M.getvrk!j;im=0.-.(M.getvrk(!j+1))});done;j:=!j+1);j:=!j+1done;in(* process eigen vectors *)letm,n=M.shapevrinletv=match(M.kindx)with|Float32->(letv=M.emptycomplex32mnin_construct_vfloat32complex32wrwivrv;Obj.magicv)|Float64->(letv=M.emptycomplex64mnin_construct_vfloat64complex64wrwivrv;Obj.magicv)|Complex32->Obj.magicvr|Complex64->Obj.magicvr|_->failwith"owl_linalg_generic:eig"in(* process eigen values *)letw=match(M.kindx)with|Float32->M.complexfloat32complex32wrwi|>Obj.magic|Float64->M.complexfloat64complex64wrwi|>Obj.magic|Complex32->Obj.magicwr|Complex64->Obj.magicwr|_->failwith"owl_linalg_generic:eigvals"inv,w[@@warning"-27"]leteigvals:typeabcd.?permute:bool->?scale:bool->otyp:(a,b)kind->(c,d)t->(a,b)t=fun?(permute=true)?(scale=true)~otypx->letx=M.copyxinletbalanc=matchpermute,scalewith|true,true->'B'|true,false->'P'|false,true->'S'|false,false->'N'inlet_,wr,wi,_,_,_,_,_,_,_,_=Owl_lapacke.geevx~balanc~jobvl:'N'~jobvr:'N'~sense:'N'~a:xinletw=match(M.kindx)with|Float32->M.complexfloat32complex32wrwi|>Obj.magic|Float64->M.complexfloat64complex64wrwi|>Obj.magic|Complex32->Obj.magicwr|Complex64->Obj.magicwr|_->failwith"owl_linalg_generic:eigvals"inw[@@warning"-27"](* Hessenberg form of matrix *)let_get_hess_q:typeab.(a,b)kind->int->int->(a,b)t->(a,b)t->(a,b)t=funkiloihiatau->matchkwith|Float32->Owl_lapacke.orghriloihiatau|Float64->Owl_lapacke.orghriloihiatau|Complex32->Owl_lapacke.unghriloihiatau|Complex64->Owl_lapacke.unghriloihiatau|_->failwith"owl_linalg:_get_hess_q"lethessx=letx=M.copyxinlet_,n=M.shapexinletilo=1inletihi=ninleta,tau=Owl_lapacke.gehrd~ilo~ihi~a:xinleth=M.triu~k:(-1)ainletq=_get_hess_q(M.kindx)iloihiatauinh,q(* Bunch-Kaufman [Bunch1977] factorization *)letbkfact?(upper=true)?(symmetric=true)?(rook=false)x=letx=M.copyxinletuplo=matchupperwith|true->'U'|false->'L'inleta,ipiv,_ret=matchrookwith|true->(matchsymmetricwith|true->Owl_lapacke.sytrf_rookuplox|false->Owl_lapacke.hetrf_rookuplox)|false->(matchsymmetricwith|true->Owl_lapacke.sytrfuplox|false->Owl_lapacke.hetrfuplox)ina,ipiv(* Check matrix properties *)letis_triux=Owl_matrix._matrix_is_triu(M.kindx)xletis_trilx=Owl_matrix._matrix_is_tril(M.kindx)xletis_symmetricx=Owl_matrix._matrix_is_symmetric(M.kindx)xletis_hermitianx=Owl_matrix._matrix_is_hermitian(M.kindx)xletis_diagx=Owl_matrix._matrix_is_diag(M.kindx)xletis_posdefx=tryignore(cholx);truewith_exn->falselet_minmax_real:typeab.(a,b)kind->(a,b)t->float*float=fun_kv->match(M.kindv)with|Float32->M.minmax'v|Float64->M.minmax'v|Complex32->M.re_c2sv|>M.minmax'|Complex64->M.re_z2dv|>M.minmax'|_->failwith"owl_linalg_generic:_minmax_real"(* local abs function, bear with obj.magic *)let_abs:typeabc.(a,b)kind->(a,b)t->(float,c)t=funkx->matchkwith|Float32->M.absx|>Obj.magic|Float64->M.absx|>Obj.magic|Complex32->M.abs_c2sx|>Obj.magic|Complex64->M.abs_z2dx|>Obj.magic|_->failwith"owl_linalg_generic:_abs"letnorm?(p=2.)x=letk=M.kindxinifp=1.thenx|>_absk|>M.sum_rows|>M.max'elseifp=-1.thenx|>_absk|>M.sum_rows|>M.min'elseifp=2.thenx|>svdvals|>_minmax_realk|>sndelseifp=-2.thenx|>svdvals|>_minmax_realk|>fstelseifp=infinitythenx|>_absk|>M.sum_cols|>M.max'elseifp=neg_infinitythenx|>_absk|>M.sum_cols|>M.min'elsefailwith"owl_linalg_generic:norm:p=±1|±2|±inf"letvecnorm?(p=2.)x=letk=M.kindxinifp=1.thenM.l1norm'x|>Owl_base_dense_common._re_eltkelseifp=2.thenM.l2norm'x|>Owl_base_dense_common._re_eltkelse(letv=M.flattenx|>M.absinifp=infinitythenM.max'v|>Owl_base_dense_common._re_eltkelseifp=neg_infinitythenM.min'v|>Owl_base_dense_common._re_eltkelse(M.pow_scalar_v(Owl_base_dense_common._float_typ_eltkp);leta=M.sum'v|>Owl_base_dense_common._re_eltkina**(1./.p)))letcond?(p=2.)x=ifp=2.then(letv=svdvalsxinletminv,maxv=_minmax_real(M.kindv)vinifmaxv=0.theninfinityelsemaxv/.minv)elseifp=1.||p=infinitythen(assert(M.row_numx=M.col_numx);letx=M.copyxinleta,_ipiv=lufactxinletanorm=norm~pxinlet_norm=ifp=1.then'1'else'I'inletrcond=Owl_lapacke.gecon_normaanormin1./.rcond)elsefailwith"owl_linalg_generic:cond:p=1|2|inf"letrcondx=1./.(cond~p:1.x)(* solve linear system of equations *)letnullx=leteps=Owl_utils.eps(M.kindx)inletm,n=M.shapexinifm=0||n=0thenM.eye(M.kindx)nelse(let_,s,vt=svd~thin:falsexinlets=_abs(M.kinds)sinletmaxsv=M.max'sinletmaxmn=Pervasives.maxmn|>float_of_intinleti=M.elt_greater_scalars(maxmn*.maxsv*.eps)|>M.sum'|>int_of_floatinletvt=M.resize~head:falsevt[|M.row_numvt-i;M.col_numvt|]inM.transposevt)let_get_trans_code:typeab.(a,b)kind->char=function|Float32->'T'|Float64->'T'|Complex32->'C'|Complex64->'C'|_->failwith"owl_linalg_generic:_get_trans_code"(* TODO: add opt parameter to specify the matrix properties so that we can
choose the best solver for better performance.
*)letlinsolve?(trans=false)ab=letma,na=M.shapeainletmb,_nb=M.shapebinassert(ma=mb);leta=M.copyainletb=M.copybinlettrans=matchtranswith|true->_get_trans_code(M.kinda)|false->'N'inmatchma=nawith|true->(leta,ipiv=lufactainletx=Owl_lapacke.getrstransaipivbinx)|false->(let_,x,_=Owl_lapacke.gelstransabinx)letlinregxy=letnx=M.numelxinletny=M.numelyinassert(nx=ny);letx=M.reshapex[|nx;1|]inlety=M.reshapey[|ny;1|]inletk=M.kindxinletp=M.get(M.cov~a:x~b:y)01inletq=M.get(M.var~axis:0x)00inletb=Owl_base_dense_common._div_eltkpqinletc=Owl_base_dense_common._mul_eltkb(M.mean'x)inleta=Owl_base_dense_common._sub_eltk(M.mean'y)cina,bletpinv?tolx=letu,s,vt=svdxin(* by default using float32 eps *)leteps=Owl_utils.epsFloat32inletm,n=M.shapexinleta=float_of_int(Pervasives.maxmn)inletb=_minmax_real(M.kindx)s|>sndinlett=matchtolwith|Sometol->tol|None->eps*.a*.binlettol=Owl_base_dense_common._float_typ_elt(M.kindx)tinlets'=M.(reci_tol~tols|>diagm)inletut=M.ctransposeuinletv=M.ctransposevtinM.(v*@s'*@ut)letsylvesterabc=letra,qa=schur_tzainletrb,qb=schur_tzbinletd=M.((ctransposeqa)*@(c*@qb))inlety,s=Owl_lapacke.trsyl'N''N'1rarbdinletz=M.(qa*@(y*@(ctransposeqb)))inM.mul_scalar_z(Owl_base_dense_common._float_typ_elt(M.kindc)(1./.s));zletlyapunovac=letr,q=schur_tzainletd=M.((ctransposeq)*@(c*@q))inlettb=_get_trans_code(M.kindc)inlety,s=Owl_lapacke.trsyl'N'tb1rrdinletz=M.(q*@(y*@(ctransposeq)))inM.mul_scalar_z(Owl_base_dense_common._float_typ_elt(M.kindc)(1./.s));zlet_discrete_lyapunov_directaq=letn=M.row_numqinletlhs=M.kronaM.(conja)inletlhs=M.((eye(kinda)(row_numlhs))-lhs)inM.reshape(linsolvelhsM.(reshapeq[|-1;1|]))[|n;n|](* bilinear transform reference
* https://old.control.ee.ethz.ch/info/people/mansour/pdf/168--1993-Schur-Cohn,%20Nour%20Eldin-Markov%20Matrices%20and%20the%20Controllability%20Gramians--.pdf *)let_discrete_lyapunov_bilinearaq=letn=M.row_numainletidentity=M.(eye(kinda)n)inletinv_al=invM.(a-identity)inleta'=M.(inv_al*@(a+identity))inletq'=M.(inv_al*@q*@(transposeinv_al))inM.mul_scalar_q'(Owl_base_dense_common._float_typ_elt(M.kinda)2.);lyapunova'M.(negq')letdiscrete_lyapunov?(solver=`default)aq=letsolve=matchsolverwith|`default->ifM.(row_numa)<=10then_discrete_lyapunov_directelse_discrete_lyapunov_bilinear|`bilinear->_discrete_lyapunov_bilinear|`direct->_discrete_lyapunov_directinsolveaqletcareabqr=letg=M.(b*@(invr)*@(transposeb))inletz=M.(concat_vh[|[|a;negg|];[|negq;neg(transposea)|]|])inlett,u,wr,_=Owl_lapacke.gees~jobvs:'V'~a:zinletselect=M.(zerosint32(row_numwr)(col_numwr))inM.iteri_2d(funijre->ifre<0.thenM.setselectij1l)wr;ignore(Owl_lapacke.trsen~job:'V'~compq:'V'~select~t~q:u);letm,n=M.shapeuinletu0=M.get_slice[[0;m/2-1];[0;n/2-1]]uinletu1=M.get_slice[[m/2;m-1];[0;n/2-1]]uinM.(u1*@(invu0))letdareabqr=letg=M.(b*@(invr)*@(transposeb))inletc=M.transpose(inva)inletz=M.(concat_vh[|[|a+g*@c*@q;(negg)*@c|];[|(negc)*@q;c|]|])inlett,u,wr,wi=Owl_lapacke.gees~jobvs:'V'~a:zinletselect=M.(zerosint32(row_numwr)(col_numwr))inM.iter2i_2d(funijreim->ifComplex.(norm{re;im})<=1.thenM.setselectij1l)wrwi;ignore(Owl_lapacke.trsen~job:'V'~compq:'V'~select~t~q:u);letm,n=M.shapeuinletu0=M.get_slice[[0;m/2-1];[0;n/2-1]]uinletu1=M.get_slice[[m/2;m-1];[0;n/2-1]]uinM.(u1*@(invu0))(* helper functions *)letpeakflops?(n=2000)()=letx=M.onesfloat64nn|>M.flatten|>array1_of_genarrayinletz=M.onesfloat64nn|>M.flatten|>array1_of_genarrayinletlayout=Owl_cblas_basic.CblasRowMajorinlettransa=Owl_cblas_basic.CblasNoTransinlettransb=Owl_cblas_basic.CblasNoTransinlett0=Unix.gettimeofday()inOwl_cblas_basic.gemmlayouttransatransbnnn1.0xnxn0.0zn;lett1=Unix.gettimeofday()inletflops=2.*.(float_of_intn)**3./.(t1-.t0)inflops(* Matrix functions *)letmpowxr=letfrac_part,_=Pervasives.modfriniffrac_part<>0.thenfailwith"mpow: fractional powers not implemented";letm,n=M.shapexinassert(m=n);(* integer matrix powers using floats: *)letrec_mpowaccs=ifs=1.thenaccelseifmod_floats2.=0.(* exponent is even? *)theneven_mpowaccselseM.dotx(even_mpowacc(s-.1.))andeven_mpowaccs=letacc2=_mpowacc(s/.2.)inM.dotacc2acc2in(* r is equal to an integer: *)ifr=0.0thenM.(eye(kindx))nelseifr>0.0then_mpowxrelse_mpow(invx)(-.r)(* DEBUG: initial expm implemented with eig, obsoleted *)letexpm_eig:typeabcd.otyp:(c,d)kind->(a,b)t->(c,d)t=fun~otypx->Owl_exception.(check(is_squarex)NOT_SQUARE);letv,w=eig~otypxinletvi=invvinletu=M.(expw|>diagm)inM.(dot(dotvu)vi)[@@warning"-32"]letexpmx=Owl_exception.(check(is_squarex)NOT_SQUARE);(* trivial case *)ifM.shapex=(1,1)thenM.expxelse((* TODO: use gebal to balance to improve accuracy, refer to Julia's impl *)letxe=M.(eye(kindx)(row_numx))inletnorm_x=norm~p:1.xin(* for small norm, use lower order Padé-approximation *)ifnorm_x<=2.097847961257068then(letc=Array.map(Owl_base_dense_common._float_typ_elt(M.kindx))(ifnorm_x>0.9504178996162932then[|17643225600.;8821612800.;2075673600.;302702400.;30270240.;2162160.;110880.;3960.;90.;1.|]elseifnorm_x>0.2539398330063230then[|17297280.;8648640.;1995840.;277200.;25200.;1512.;56.;1.|]elseifnorm_x>0.01495585217958292then[|30240.;15120.;3360.;420.;30.;1.|]else[|120.;60.;12.;1.|])inletx2=M.dotxxinletp=refM.(copyxe)inletu=M.mul_scalar!pc.(1)inletv=M.mul_scalar!pc.(0)infori=1toArray.(lengthc/2-1)doletj=2*iinletk=j+1inp:=M.dot!px2;M.(add_~out:uu(mul_scalar!pc.(k)));M.(add_~out:vv(mul_scalar!pc.(j)));done;letu=M.dotxuinleta=M.subvuinletb=M.addvuinOwl_lapacke.gesvab|>ignore;b)(* for larger norm, Padé-13 approximation *)else(lets=Owl_maths.log2(norm_x/.5.4)inlett=ceilsinletx=ifs>0.thenOwl_base_dense_common._float_typ_elt(M.kindx)(2.**t)|>M.div_scalarxelsexinletc=Array.map(Owl_base_dense_common._float_typ_elt(M.kindx))[|64764752532480000.;32382376266240000.;7771770303897600.;1187353796428800.;129060195264000.;10559470521600.;670442572800.;33522128640.;1323241920.;40840800.;960960.;16380.;182.;1.|]inletx2=M.dotxxinletx4=M.dotx2x2inletx6=M.dotx2x4inletu=M.(x*@(x6*@(x6*$c.(13)+x4*$c.(11)+x2*$c.(9))+x6*$c.(7)+x4*$c.(5)+x2*$c.(3)+xe*$c.(1)))inletv=M.(x6*@(x6*$c.(12)+x4*$c.(10)+x2*$c.(8))+x6*$c.(6)+x4*$c.(4)+x2*$c.(2)+xe*$c.(0))inleta=M.subvuinletb=M.addvuinOwl_lapacke.gesvab|>ignore;letx=refbinifs>0.then(for_i=1toint_of_floattdox:=M.dot!x!xdone;);!x))let_sinm:typeab.(a,b)kind->(a,b)t->(a,b)t=funkx->matchkwith|Float32->(leta=Complex.({re=0.;im=1.})inletx=M.cast_s2cxinM.(expm(a$*x)|>im_c2s))|Float64->(leta=Complex.({re=0.;im=1.})inletx=M.cast_d2zxinM.(expm(a$*x)|>im_z2d))|Complex32->(leta=Complex.({re=0.;im=(-0.5)})inletb=Complex.({re=0.;im=1.})inletc=Complex.({re=0.;im=(-1.)})inM.(a$*(expm(b$*x)-expm(c$*x))))|Complex64->(leta=Complex.({re=0.;im=(-0.5)})inletb=Complex.({re=0.;im=1.})inletc=Complex.({re=0.;im=(-1.)})inM.(a$*(expm(b$*x)-expm(c$*x))))|_->failwith"_sinm: unsupported operation"letsinmx=_sinm(M.kindx)xlet_cosm:typeab.(a,b)kind->(a,b)t->(a,b)t=funkx->matchkwith|Float32->(leta=Complex.({re=0.;im=1.})inletx=M.cast_s2cxinM.(expm(a$*x)|>re_c2s))|Float64->(leta=Complex.({re=0.;im=1.})inletx=M.cast_d2zxinM.(expm(a$*x)|>re_z2d))|Complex32->(leta=Complex.({re=0.5;im=0.})inletb=Complex.({re=0.;im=1.})inletc=Complex.({re=0.;im=(-1.)})inM.(a$*(expm(b$*x)+expm(c$*x))))|Complex64->(leta=Complex.({re=0.5;im=0.})inletb=Complex.({re=0.;im=1.})inletc=Complex.({re=0.;im=(-1.)})inM.(a$*(expm(b$*x)+expm(c$*x))))|_->failwith"_cosm: unsupported operation"letcosmx=_cosm(M.kindx)xlet_sincosm:typeab.(a,b)kind->(a,b)t->(a,b)t*(a,b)t=funkx->matchkwith|Float32->(leta=Complex.({re=0.;im=1.})inletx=M.cast_s2cxinlety=M.(expm(a$*x))inM.(im_c2sy,re_c2sy))|Float64->(leta=Complex.({re=0.;im=1.})inletx=M.cast_d2zxinlety=M.(expm(a$*x))inM.(im_z2dy,re_z2dy))|Complex32->(letb=Complex.({re=0.;im=1.})inletc=Complex.({re=0.;im=(-1.)})inletx=M.(expm(b$*x))inlety=M.(expm(c$*x))inlet_sin=M.(Complex.({re=0.;im=(-0.5)})$*(x-y))inlet_cos=M.(Complex.({re=0.5;im=0.})$*(x+y))in_sin,_cos)|Complex64->(letb=Complex.({re=0.;im=1.})inletc=Complex.({re=0.;im=(-1.)})inletx=M.(expm(b$*x))inlety=M.(expm(c$*x))inlet_sin=M.(Complex.({re=0.;im=(-0.5)})$*(x-y))inlet_cos=M.(Complex.({re=0.5;im=0.})$*(x+y))in_sin,_cos)|_->failwith"_sincosm: unsupported operation"letsincosmx=_sincosm(M.kindx)xlettanmx=lets,c=sincosmxinOwl_lapacke.gesvcs|>ignore;sletsinhmx=leta=Owl_base_dense_common._float_typ_elt(M.kindx)0.5inM.(a$*((expmx)-(expm(negx))))letcoshmx=leta=Owl_base_dense_common._float_typ_elt(M.kindx)0.5inM.(a$*((expmx)+(expm(negx))))letsinhcoshmx=leta=Owl_base_dense_common._float_typ_elt(M.kindx)0.5inletb=expmxinletc=expm(M.negx)inM.(a$*(b-c)),M.(a$*(b+c))lettanhmx=lets,c=sinhcoshmxinOwl_lapacke.gesvcs|>ignore;s(* TODO *)letlogm_x=failwith"logm: not implemented"[@@warning"-32"](* TODO *)letsqrtm_x=failwith"sqrtm: not implemented"[@@warning"-32"](* ends here *)