12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241(* ========================================================================= *)(* - This code originates from John Harrison's HOL LIGHT 2.30 *)(* (see file LICENSE.sos for license, copyright and disclaimer) *)(* - Laurent Théry (thery@sophia.inria.fr) has isolated the HOL *)(* independent bits *)(* - Frédéric Besson (fbesson@irisa.fr) is using it to feed micromega *)(* ========================================================================= *)(* ========================================================================= *)(* Nonlinear universal reals procedure using SOS decomposition. *)(* ========================================================================= *)openNumCompatopenQ.NotationsopenSos_typesopenSos_lib(*
prioritize_real();;
*)letdebugging=reffalseexceptionSanity(* ------------------------------------------------------------------------- *)(* Turn a rational into a decimal string with d sig digits. *)(* ------------------------------------------------------------------------- *)letdecimalize=letrecnormalizey=ifQ.absy</Q.one//Q.tenthennormalize(Q.ten*/y)-1elseifQ.absy>=/Q.onethennormalize(y//Q.ten)+1else0infundx->ifx=/Q.zerothen"0.0"elselety=Q.absxinlete=normalizeyinletz=(Q.pow10(-e)*/y)+/Q.oneinletk=Q.round(Q.pow10d*/z)in(ifx</Q.zerothen"-0."else"0.")^implode(List.tl(explode(Q.to_stringk)))^ife=0then""else"e"^string_of_inte(* ------------------------------------------------------------------------- *)(* Iterations over numbers, and lists indexed by numbers. *)(* ------------------------------------------------------------------------- *)letreciternklfa=matchlwith[]->a|h::t->itern(k+1)tf(fhka)letreciter(m,n)fa=ifn<mthenaelseiter(m+1,n)f(fma)(* ------------------------------------------------------------------------- *)(* The main types. *)(* ------------------------------------------------------------------------- *)typevector=int*(int,Q.t)functypematrix=(int*int)*(int*int,Q.t)functypemonomial=(vname,int)functypepoly=(monomial,Q.t)func(* ------------------------------------------------------------------------- *)(* Assignment avoiding zeros. *)(* ------------------------------------------------------------------------- *)let(|-->)xya=ify=/Q.zerothenaelse(x|->y)a(* ------------------------------------------------------------------------- *)(* This can be generic. *)(* ------------------------------------------------------------------------- *)letelement(d,v)i=tryapplydviQ.zeroletmapaf(d,v)=(d,foldl(funaic->(i|-->fc)a)undefinedv)letis_zero(d,v)=matchvwithEmpty->true|_->false(* ------------------------------------------------------------------------- *)(* Vectors. Conventionally indexed 1..n. *)(* ------------------------------------------------------------------------- *)letvector_0n:vector=(n,undefined)letdim(v:vector)=fstvletvector_constcn=ifc=/Q.zerothenvector_0nelse((n,List.fold_right(funk->k|->c)(1--n)undefined):vector)letvector_cmulc(v:vector)=letn=dimvinifc=/Q.zerothenvector_0nelse(n,mapf(funx->c*/x)(sndv))letvector_of_listl=letn=List.lengthlin((n,List.fold_right2(|->)(1--n)lundefined):vector)(* ------------------------------------------------------------------------- *)(* Matrices; again rows and columns indexed from 1. *)(* ------------------------------------------------------------------------- *)letmatrix_0(m,n):matrix=((m,n),undefined)letdimensions(m:matrix)=fstmletmatrix_cmulc(m:matrix)=leti,j=dimensionsminifc=/Q.zerothenmatrix_0(i,j)else((i,j),mapf(funx->c*/x)(sndm))letmatrix_neg(m:matrix):matrix=(dimensionsm,mapfQ.neg(sndm))letmatrix_add(m1:matrix)(m2:matrix)=letd1=dimensionsm1andd2=dimensionsm2inifd1<>d2thenfailwith"matrix_add: incompatible dimensions"else((d1,combine(+/)(funx->x=/Q.zero)(sndm1)(sndm2)):matrix)letrowk(m:matrix)=leti,j=dimensionsmin((j,foldl(funa(i,j)c->ifi=kthen(j|->c)aelsea)undefined(sndm)):vector)letcolumnk(m:matrix)=leti,j=dimensionsmin((i,foldl(funa(i,j)c->ifj=kthen(i|->c)aelsea)undefined(sndm)):vector)letdiagonal(v:vector)=letn=dimvin(((n,n),foldl(funaic->((i,i)|->c)a)undefined(sndv)):matrix)(* ------------------------------------------------------------------------- *)(* Monomials. *)(* ------------------------------------------------------------------------- *)letmonomial_1=(undefined:monomial)letmonomial_varx:monomial=x|=>1let(monomial_mul:monomial->monomial->monomial)=combine(+)(funx->false)letmonomial_degreex(m:monomial)=tryapplydmx0letmonomial_multidegree(m:monomial)=foldl(funaxk->k+a)0mletmonomial_variablesm=domm(* ------------------------------------------------------------------------- *)(* Polynomials. *)(* ------------------------------------------------------------------------- *)letpoly_0=(undefined:poly)letpoly_isconst(p:poly)=foldl(funamc->m=monomial_1&&a)truepletpoly_varx:poly=monomial_varx|=>Q.oneletpoly_constc=ifc=/Q.zerothenpoly_0elsemonomial_1|=>cletpoly_cmulc(p:poly)=ifc=/Q.zerothenpoly_0elsemapf(funx->c*/x)pletpoly_neg(p:poly):poly=mapfQ.negpletpoly_add(p1:poly)(p2:poly):poly=combine(+/)(funx->x=/Q.zero)p1p2letpoly_subp1p2=poly_addp1(poly_negp2)letpoly_cmmul(c,m)(p:poly)=ifc=/Q.zerothenpoly_0elseifm=monomial_1thenmapf(fund->c*/d)pelsefoldl(funam'd->(monomial_mulmm'|->c*/d)a)poly_0pletpoly_mul(p1:poly)(p2:poly)=foldl(funamc->poly_add(poly_cmmul(c,m)p2)a)poly_0p1letpoly_squarep=poly_mulppletrecpoly_powpk=ifk=0thenpoly_constQ.oneelseifk=1thenpelseletq=poly_square(poly_powp(k/2))inifkmod2=1thenpoly_mulpqelseqletdegreex(p:poly)=foldl(funamc->max(monomial_degreexm)a)0pletmultidegree(p:poly)=foldl(funamc->max(monomial_multidegreem)a)0pletpoly_variables(p:poly)=foldr(funmc->union(monomial_variablesm))p[](* ------------------------------------------------------------------------- *)(* Order monomials for human presentation. *)(* ------------------------------------------------------------------------- *)lethumanorder_varpow(x1,k1)(x2,k2)=x1<x2||(x1=x2&&k1>k2)lethumanorder_monomial=letrecordl1l2=match(l1,l2)with|_,[]->true|[],_->false|h1::t1,h2::t2->humanorder_varpowh1h2||(h1=h2&&ordt1t2)infunm1m2->m1=m2||ord(sorthumanorder_varpow(graphm1))(sorthumanorder_varpow(graphm2))(* ------------------------------------------------------------------------- *)(* Conversions to strings. *)(* ------------------------------------------------------------------------- *)letstring_of_vname(v:vname):string=(v:string)letstring_of_varpowxk=ifk=1thenstring_of_vnamexelsestring_of_vnamex^"^"^string_of_intkletstring_of_monomialm=ifm=monomial_1then"1"elseletvps=List.fold_right(fun(x,k)a->string_of_varpowxk::a)(sorthumanorder_varpow(graphm))[]inString.concat"*"vpsletstring_of_cmonomial(c,m)=ifm=monomial_1thenQ.to_stringcelseifc=/Q.onethenstring_of_monomialmelseQ.to_stringc^"*"^string_of_monomialmletstring_of_poly(p:poly)=ifp=poly_0then"<<0>>"elseletcms=sort(fun(m1,_)(m2,_)->humanorder_monomialm1m2)(graphp)inlets=List.fold_left(funa(m,c)->ifc</Q.zerothena^" - "^string_of_cmonomial(Q.negc,m)elsea^" + "^string_of_cmonomial(c,m))""cmsinlets1=String.subs03ands2=String.subs3(String.lengths-3)in"<<"^(ifs1=" + "thens2else"-"^s2)^">>"(* ------------------------------------------------------------------------- *)(* Printers. *)(* ------------------------------------------------------------------------- *)(*
let print_vector v = Format.print_string(string_of_vector 0 20 v);;
let print_matrix m = Format.print_string(string_of_matrix 20 m);;
let print_monomial m = Format.print_string(string_of_monomial m);;
let print_poly m = Format.print_string(string_of_poly m);;
#install_printer print_vector;;
#install_printer print_matrix;;
#install_printer print_monomial;;
#install_printer print_poly;;
*)(* ------------------------------------------------------------------------- *)(* Conversion from term. *)(* ------------------------------------------------------------------------- *)letrecpoly_of_termt=matchtwith|Zero->poly_0|Constn->poly_constn|Varx->poly_varx|Oppt1->poly_neg(poly_of_termt1)|Add(l,r)->poly_add(poly_of_terml)(poly_of_termr)|Sub(l,r)->poly_sub(poly_of_terml)(poly_of_termr)|Mul(l,r)->poly_mul(poly_of_terml)(poly_of_termr)|Pow(t,n)->poly_pow(poly_of_termt)n(* ------------------------------------------------------------------------- *)(* String of vector (just a list of space-separated numbers). *)(* ------------------------------------------------------------------------- *)letsdpa_of_vector(v:vector)=letn=dimvinletstrs=List.map(o(decimalize20)(elementv))(1--n)inString.concat" "strs^"\n"(* ------------------------------------------------------------------------- *)(* String for a matrix numbered k, in SDPA sparse format. *)(* ------------------------------------------------------------------------- *)letsdpa_of_matrixk(m:matrix)=letpfx=string_of_intk^" 1 "inletms=foldr(fun(i,j)ca->ifi>jthenaelse((i,j),c)::a)(sndm)[]inletmss=sort(increasingfst)msinList.fold_right(fun((i,j),c)a->pfx^string_of_inti^" "^string_of_intj^" "^decimalize20c^"\n"^a)mss""(* ------------------------------------------------------------------------- *)(* String in SDPA sparse format for standard SDP problem: *)(* *)(* X = v_1 * [M_1] + ... + v_m * [M_m] - [M_0] must be PSD *)(* Minimize obj_1 * v_1 + ... obj_m * v_m *)(* ------------------------------------------------------------------------- *)letsdpa_of_problemcommentobjmats=letm=List.lengthmats-1andn,_=dimensions(List.hdmats)in"\""^comment^"\"\n"^string_of_intm^"\n"^"1\n"^string_of_intn^"\n"^sdpa_of_vectorobj^List.fold_right2(funkma->sdpa_of_matrix(k-1)m^a)(1--List.lengthmats)mats""(* ------------------------------------------------------------------------- *)(* More parser basics. *)(* ------------------------------------------------------------------------- *)letwords=end_itlist(funp1p2->p1++p2>>fun(s,t)->s^t)(List.mapa(explodes))lettokens=many(someisspace)++words++many(someisspace)>>fun((_,t),_)->tletdecimal=let(||)=parser_orinletnumeral=someisnuminletdecimalint=atleast1numeral>>oQ.of_stringimplodeinletdecimalfrac=atleast1numeral>>funs->Q.of_string(implodes)//Q.pow10(List.lengths)inletdecimalsig=decimalint++possibly(a"."++decimalfrac>>snd)>>functionh,[x]->h+/x|h,_->hinletsignedprs=a"-"++prs>>oQ.negsnd||a"+"++prs>>snd||prsinletexponent=(a"e"||a"E")++signeddecimalint>>sndinsigneddecimalsig++possiblyexponent>>functionh,[x]->h*/Q.power10x|h,_->hletmkparserps=letx,rst=p(explodes)inifrst=[]thenxelsefailwith"mkparser: unparsed input"(* ------------------------------------------------------------------------- *)(* Parse back a vector. *)(* ------------------------------------------------------------------------- *)let_parse_sdpaoutput,parse_csdpoutput=let(||)=parser_orinletvector=token"{"++listofdecimal(token",")"decimal"++token"}">>fun((_,v),_)->vector_of_listvinletrecskipuptodscrprsinp=(dscr++prs>>snd||some(func->true)++skipuptodscrprs>>snd)inpinletignoreinp=((),[])inletsdpaoutput=skipupto(word"xVec"++token"=")(vector++ignore>>fst)inletcsdpoutput=(decimal++many(a" "++decimal>>snd)>>fun(h,t)->h::t)++(a" "++a"\n"++ignore)>>ovector_of_listfstin(mkparsersdpaoutput,mkparsercsdpoutput)(* ------------------------------------------------------------------------- *)(* The default parameters. Unfortunately this goes to a fixed file. *)(* ------------------------------------------------------------------------- *)let_sdpa_default_parameters="100 unsigned int maxIteration;\n\
1.0E-7 double 0.0 < epsilonStar;\n\
1.0E2 double 0.0 < lambdaStar;\n\
2.0 double 1.0 < omegaStar;\n\
-1.0E5 double lowerBound;\n\
1.0E5 double upperBound;\n\
0.1 double 0.0 <= betaStar < 1.0;\n\
0.2 double 0.0 <= betaBar < 1.0, betaStar <= betaBar;\n\
0.9 double 0.0 < gammaStar < 1.0;\n\
1.0E-7 double 0.0 < epsilonDash;\n"(* ------------------------------------------------------------------------- *)(* These were suggested by Makoto Yamashita for problems where we are *)(* right at the edge of the semidefinite cone, as sometimes happens. *)(* ------------------------------------------------------------------------- *)letsdpa_alt_parameters="1000 unsigned int maxIteration;\n\
1.0E-7 double 0.0 < epsilonStar;\n\
1.0E4 double 0.0 < lambdaStar;\n\
2.0 double 1.0 < omegaStar;\n\
-1.0E5 double lowerBound;\n\
1.0E5 double upperBound;\n\
0.1 double 0.0 <= betaStar < 1.0;\n\
0.2 double 0.0 <= betaBar < 1.0, betaStar <= betaBar;\n\
0.9 double 0.0 < gammaStar < 1.0;\n\
1.0E-7 double 0.0 < epsilonDash;\n"let_sdpa_params=sdpa_alt_parameters(* ------------------------------------------------------------------------- *)(* CSDP parameters; so far I'm sticking with the defaults. *)(* ------------------------------------------------------------------------- *)letcsdp_default_parameters="axtol=1.0e-8\n\
atytol=1.0e-8\n\
objtol=1.0e-8\n\
pinftol=1.0e8\n\
dinftol=1.0e8\n\
maxiter=100\n\
minstepfrac=0.9\n\
maxstepfrac=0.97\n\
minstepp=1.0e-8\n\
minstepd=1.0e-8\n\
usexzgap=1\n\
tweakgap=0\n\
affine=0\n\
printlevel=1\n"letcsdp_params=csdp_default_parameters(* ------------------------------------------------------------------------- *)(* Now call CSDP on a problem and parse back the output. *)(* ------------------------------------------------------------------------- *)letrun_csdpdbgobjmats=letinput_file=Filename.temp_file"sos"".dat-s"inletoutput_file=String.subinput_file0(String.lengthinput_file-6)^".out"andparams_file=Filename.concattemp_path"param.csdp"infile_of_stringinput_file(sdpa_of_problem""objmats);file_of_stringparams_filecsdp_params;letrv=Sys.command("cd "^temp_path^"; csdp "^input_file^" "^output_file^ifdbgthen""else"> /dev/null")inletop=string_of_fileoutput_fileinletres=parse_csdpoutputopinifdbgthen()else(Sys.removeinput_file;Sys.removeoutput_file);(rv,res)(* ------------------------------------------------------------------------- *)(* Try some apparently sensible scaling first. Note that this is purely to *)(* get a cleaner translation to floating-point, and doesn't affect any of *)(* the results, in principle. In practice it seems a lot better when there *)(* are extreme numbers in the original problem. *)(* ------------------------------------------------------------------------- *)letscale_then=letcommon_denominatoramatacc=foldl(funamc->Z.lcm(Q.denc)a)accamatandmaximal_elementamatacc=foldl(funmaxamc->Q.maxmaxa(Q.absc))accamatinfunsolverobjmats->letcd1=Q.of_bigint@@List.fold_rightcommon_denominatormatsZ.oneandcd2=Q.of_bigint@@common_denominator(sndobj)Z.oneinletmats'=List.map(mapf(funx->cd1*/x))matsandobj'=vector_cmulcd2objinletmax1=List.fold_rightmaximal_elementmats'Q.zeroandmax2=maximal_element(sndobj')Q.zeroinletscal1=Q.pow2(20-int_of_float(log(Q.to_floatmax1)/.log2.0))andscal2=Q.pow2(20-int_of_float(log(Q.to_floatmax2)/.log2.0))inletmats''=List.map(mapf(funx->x*/scal1))mats'andobj''=vector_cmulscal2obj'insolverobj''mats''(* ------------------------------------------------------------------------- *)(* Round a vector to "nice" rationals. *)(* ------------------------------------------------------------------------- *)letnice_rationalnx=Q.round(n*/x)//nletnice_vectorn=mapa(nice_rationaln)(* ------------------------------------------------------------------------- *)(* Reduce linear program to SDP (diagonal matrices) and test with CSDP. This *)(* one tests A [-1;x1;..;xn] >= 0 (i.e. left column is negated constants). *)(* ------------------------------------------------------------------------- *)letlinear_program_basica=letm,n=dimensionsainletmats=List.map(funj->diagonal(columnja))(1--n)andobj=vector_constQ.oneminletrv,res=run_csdpfalseobjmatsinifrv=1||rv=2thenfalseelseifrv=0thentrueelsefailwith"linear_program: An error occurred in the SDP solver"(* ------------------------------------------------------------------------- *)(* Test whether a point is in the convex hull of others. Rather than use *)(* computational geometry, express as linear inequalities and call CSDP. *)(* This is a bit lazy of me, but it's easy and not such a bottleneck so far. *)(* ------------------------------------------------------------------------- *)letin_convex_hullptspt=letpts1=(1::pt)::List.map(funx->1::x)ptsinletpts2=List.map(funp->List.map(funx->-x)p@p)pts1inletn=List.lengthpts+1andv=2*(List.lengthpt+1)inletm=v+n-1inletmat=((m,n),itern1pts2(funptsj->itern1pts(funxi->(i,j)|->Q.of_intx))(iter(1,n)(funi->(v+i,i+1)|->Q.one)undefined))inlinear_program_basicmat(* ------------------------------------------------------------------------- *)(* Filter down a set of points to a minimal set with the same convex hull. *)(* ------------------------------------------------------------------------- *)letminimal_convex_hull=letaugment1=function|[]->assertfalse|m::ms->ifin_convex_hullmsmthenmselsems@[m]inletaugmentmms=funpow3augment1(m::ms)infunmons->letmons'=List.fold_rightaugment(List.tlmons)[List.hdmons]infunpow(List.lengthmons')augment1mons'(* ------------------------------------------------------------------------- *)(* Stuff for "equations" (generic A->num functions). *)(* ------------------------------------------------------------------------- *)letequation_cmulceq=ifc=/Q.zerothenEmptyelsemapf(fund->c*/d)eqletequation_addeq1eq2=combine(+/)(funx->x=/Q.zero)eq1eq2letequation_evalassigeq=letvaluev=applyassigvinfoldl(funavc->a+/(valuev*/c))Q.zeroeq(* ------------------------------------------------------------------------- *)(* Eliminate all variables, in an essentially arbitrary order. *)(* ------------------------------------------------------------------------- *)leteliminate_all_equationsone=letchoose_variableeq=letv,_=chooseeqinifv=onethenleteq'=undefineveqinifis_undefinedeq'thenfailwith"choose_variable"elseletw,_=chooseeq'inwelsevinletreceliminateduneqs=matcheqswith|[]->dun|eq::oeqs->ifis_undefinedeqtheneliminatedunoeqselseletv=choose_variableeqinleta=applyeqvinleteq'=equation_cmul(Q.minus_one//a)(undefineveq)inletelime=letb=tryapplydevQ.zeroinifb=/Q.zerotheneelseequation_adde(equation_cmul(Q.negb//a)eq)ineliminate((v|->eq')(mapfelimdun))(List.mapelimoeqs)infuneqs->letassig=eliminateundefinedeqsinletvs=foldl(funaxf->subtract(domf)[one]@a)[]assigin(setifyvs,assig)(* ------------------------------------------------------------------------- *)(* Hence produce the "relevant" monomials: those whose squares lie in the *)(* Newton polytope of the monomials in the input. (This is enough according *)(* to Reznik: "Extremal PSD forms with few terms", Duke Math. Journal, *)(* vol 45, pp. 363--374, 1978. *)(* *)(* These are ordered in sort of decreasing degree. In particular the *)(* constant monomial is last; this gives an order in diagonalization of the *)(* quadratic form that will tend to display constants. *)(* ------------------------------------------------------------------------- *)letnewton_polytopepol=letvars=poly_variablespolinletmons=List.map(funm->List.map(funx->monomial_degreexm)vars)(dompol)andds=List.map(funx->(degreexpol+1)/2)varsinletall=List.fold_right(funn->allpairs(funht->h::t)(0--n))ds[[]]andmons'=minimal_convex_hullmonsinletall'=List.filter(funm->in_convex_hullmons'(List.map(funx->2*x)m))allinList.map(funm->List.fold_right2(funvia->ifi=0thenaelse(v|->i)a)varsmmonomial_1)(List.revall')(* ------------------------------------------------------------------------- *)(* Diagonalize (Cholesky/LDU) the matrix corresponding to a quadratic form. *)(* ------------------------------------------------------------------------- *)letdiagm=letnn=dimensionsminletn=fstnninifsndnn<>nthenfailwith"diagonalize: non-square matrix"elseletrecdiagonalizeim=ifis_zeromthen[]elseleta11=elementm(i,i)inifa11</Q.zerothenfailwith"diagonalize: not PSD"elseifa11=/Q.zerothenifis_zero(rowim)thendiagonalize(i+1)melsefailwith"diagonalize: not PSD"elseletv=rowiminletv'=mapa(funa1k->a1k//a11)vinletm'=((n,n),iter(i+1,n)(funj->iter(i+1,n)(funk->(j,k)|-->elementm(j,k)-/(elementvj*/elementv'k)))undefined)in(a11,v')::diagonalize(i+1)m'indiagonalize1m(* ------------------------------------------------------------------------- *)(* Adjust a diagonalization to collect rationals at the start. *)(* ------------------------------------------------------------------------- *)letderationd=ifd=[]then(Q.zero,d)elseletadj(c,l)=leta=Q.make(foldl(funaic->Z.lcma(Q.denc))Z.one(sndl))(foldl(funaic->Z.gcda(Q.numc))Z.zero(sndl))in(c//(a*/a),mapa(funx->a*/x)l)inletd'=List.mapadjdinleta=Q.make(List.fold_right(oZ.lcm(oQ.denfst))d'Z.one)(List.fold_right(oZ.gcd(oQ.numfst))d'Z.zero)in(Q.one//a,List.map(fun(c,l)->(a*/c,l))d')(* ------------------------------------------------------------------------- *)(* Enumeration of monomials with given multidegree bound. *)(* ------------------------------------------------------------------------- *)letrecenumerate_monomialsdvars=ifd<0then[]elseifd=0then[undefined]elseifvars=[]then[monomial_1]elseletalts=List.map(funk->letoths=enumerate_monomials(d-k)(List.tlvars)inList.map(funks->ifk=0thenkselse(List.hdvars|->k)ks)oths)(0--d)inend_itlist(@)alts(* ------------------------------------------------------------------------- *)(* Enumerate products of distinct input polys with degree <= d. *)(* We ignore any constant input polynomials. *)(* Give the output polynomial and a record of how it was derived. *)(* ------------------------------------------------------------------------- *)letrecenumerate_productsdpols=ifd=0then[(poly_constQ.one,Rational_ltQ.one)]elseifd<0then[]elsematchpolswith|[]->[(poly_constQ.one,Rational_ltQ.one)]|(p,b)::ps->lete=multidegreepinife=0thenenumerate_productsdpselseenumerate_productsdps@List.map(fun(q,c)->(poly_mulpq,Product(b,c)))(enumerate_products(d-e)ps)(* ------------------------------------------------------------------------- *)(* Multiply equation-parametrized poly by regular poly and add accumulator. *)(* ------------------------------------------------------------------------- *)letepoly_pmulpqacc=foldl(funam1c->foldl(funbm2e->letm=monomial_mulm1m2inletes=tryapplydbmundefinedin(m|->equation_add(equation_cmulce)es)b)aq)accp(* ------------------------------------------------------------------------- *)(* Convert regular polynomial. Note that we treat (0,0,0) as -1. *)(* ------------------------------------------------------------------------- *)letepoly_of_polyp=foldl(funamc->(m|->((0,0,0)|=>Q.negc))a)undefinedp(* ------------------------------------------------------------------------- *)(* String for block diagonal matrix numbered k. *)(* ------------------------------------------------------------------------- *)letsdpa_of_blockdiagonalkm=letpfx=string_of_intk^" "inletents=foldl(funa(b,i,j)c->ifi>jthenaelse((b,i,j),c)::a)[]minletentss=sort(increasingfst)entsinList.fold_right(fun((b,i,j),c)a->pfx^string_of_intb^" "^string_of_inti^" "^string_of_intj^" "^decimalize20c^"\n"^a)entss""(* ------------------------------------------------------------------------- *)(* SDPA for problem using block diagonal (i.e. multiple SDPs) *)(* ------------------------------------------------------------------------- *)letsdpa_of_blockproblemcommentnblocksblocksizesobjmats=letm=List.lengthmats-1in"\""^comment^"\"\n"^string_of_intm^"\n"^string_of_intnblocks^"\n"^String.concat" "(List.mapstring_of_intblocksizes)^"\n"^sdpa_of_vectorobj^List.fold_right2(funkma->sdpa_of_blockdiagonal(k-1)m^a)(1--List.lengthmats)mats""(* ------------------------------------------------------------------------- *)(* Hence run CSDP on a problem in block diagonal form. *)(* ------------------------------------------------------------------------- *)letrun_csdpdbgnblocksblocksizesobjmats=letinput_file=Filename.temp_file"sos"".dat-s"inletoutput_file=String.subinput_file0(String.lengthinput_file-6)^".out"andparams_file=Filename.concattemp_path"param.csdp"infile_of_stringinput_file(sdpa_of_blockproblem""nblocksblocksizesobjmats);file_of_stringparams_filecsdp_params;letrv=Sys.command("cd "^temp_path^"; csdp "^input_file^" "^output_file^ifdbgthen""else"> /dev/null")inletop=string_of_fileoutput_fileinletres=parse_csdpoutputopinifdbgthen()else(Sys.removeinput_file;Sys.removeoutput_file);(rv,res)letcsdpnblocksblocksizesobjmats=letrv,res=run_csdp!debuggingnblocksblocksizesobjmatsinifrv=1||rv=2thenfailwith"csdp: Problem is infeasible"elseifrv=3then()(*Format.print_string "csdp warning: Reduced accuracy";
Format.print_newline() *)elseifrv<>0thenfailwith("csdp: error "^string_of_intrv)else();res(* ------------------------------------------------------------------------- *)(* 3D versions of matrix operations to consider blocks separately. *)(* ------------------------------------------------------------------------- *)letbmatrix_add=combine(+/)(funx->x=/Q.zero)letbmatrix_cmulcbm=ifc=/Q.zerothenundefinedelsemapf(funx->c*/x)bmletbmatrix_neg=bmatrix_cmulQ.minus_one(* ------------------------------------------------------------------------- *)(* Smash a block matrix into components. *)(* ------------------------------------------------------------------------- *)letblocksblocksizesbm=List.map(fun(bs,b0)->letm=foldl(funa(b,i,j)c->ifb=b0then((i,j)|->c)aelsea)undefinedbmin(((bs,bs),m):matrix))(List.combineblocksizes(1--List.lengthblocksizes))(* ------------------------------------------------------------------------- *)(* Positiv- and Nullstellensatz. Flag "linf" forces a linear representation. *)(* ------------------------------------------------------------------------- *)letreal_positivnullstellensatz_generallinfdeqsleqspol=letvars=List.fold_right(ounionpoly_variables)((pol::eqs)@List.mapfstleqs)[]inletmonoid=iflinfthen(poly_constQ.one,Rational_ltQ.one)::List.filter(fun(p,c)->multidegreep<=d)leqselseenumerate_productsdleqsinletnblocks=List.lengthmonoidinletmk_idmultiplierkp=lete=d-multidegreepinletmons=enumerate_monomialsevarsinletnons=List.combinemons(1--List.lengthmons)in(mons,List.fold_right(fun(m,n)->m|->((-k,-n,n)|=>Q.one))nonsundefined)inletmk_sqmultiplierk(p,c)=lete=(d-multidegreep)/2inletmons=enumerate_monomialsevarsinletnons=List.combinemons(1--List.lengthmons)in(mons,List.fold_right(fun(m1,n1)->List.fold_right(fun(m2,n2)a->letm=monomial_mulm1m2inifn1>n2thenaelseletc=ifn1=n2thenQ.oneelseQ.twoinlete=tryapplydamundefinedin(m|->equation_add((k,n1,n2)|=>c)e)a)nons)nonsundefined)inletsqmonlist,sqs=List.split(List.map2mk_sqmultiplier(1--List.lengthmonoid)monoid)andidmonlist,ids=List.split(List.map2mk_idmultiplier(1--List.lengtheqs)eqs)inletblocksizes=List.mapList.lengthsqmonlistinletbigsum=List.fold_right2(funpqa->epoly_pmulpqa)eqsids(List.fold_right2(fun(p,c)sa->epoly_pmulpsa)monoidsqs(epoly_of_poly(poly_negpol)))inleteqns=foldl(funame->e::a)[]bigsuminletpvs,assig=eliminate_all_equations(0,0,0)eqnsinletqvars=(0,0,0)::pvsinletallassig=List.fold_right(funv->v|->(v|=>Q.one))pvsassiginletmk_matrixv=foldl(funm(b,i,j)ass->ifb<0thenmelseletc=tryapplydassvQ.zeroinifc=/Q.zerothenmelse((b,j,i)|->c)(((b,i,j)|->c)m))undefinedallassiginletdiagents=foldl(funa(b,i,j)e->ifb>0&&i=jthenequation_addeaelsea)undefinedallassiginletmats=List.mapmk_matrixqvarsandobj=(List.lengthpvs,itern1pvs(funvi->i|-->tryapplyddiagentsvQ.zero)undefined)inletraw_vec=ifpvs=[]thenvector_00elsescale_then(csdpnblocksblocksizes)objmatsinletfind_roundingd=if!debuggingthen(Format.print_string("Trying rounding with limit "^Q.to_stringd);Format.print_newline())else();letvec=nice_vectordraw_vecinletblockmat=iter(1,dimvec)(funia->bmatrix_add(bmatrix_cmul(elementveci)(List.nthmatsi))a)(bmatrix_neg(List.nthmats0))inletallmats=blocksblocksizesblockmatin(vec,List.mapdiagallmats)inletvec,ratdias=ifpvs=[]thenfind_roundingQ.oneelsetryfindfind_rounding(List.mapQ.of_int(1--31)@List.mapQ.pow2(5--66))inletnewassigs=List.fold_right(funk->List.nthpvs(k-1)|->elementveck)(1--dimvec)((0,0,0)|=>Q.minus_one)inletfinalassigs=foldl(funave->(v|->equation_evalnewassigse)a)newassigsallassiginletpoly_of_epolyp=foldl(funave->(v|-->equation_evalfinalassigse)a)undefinedpinletmk_sosmons=letmk_sq(c,m)=(c,List.fold_right(funka->(List.nthmons(k-1)|-->elementmk)a)(1--List.lengthmons)undefined)inList.mapmk_sqinletsqs=List.map2mk_sossqmonlistratdiasandcfs=List.mappoly_of_epolyidsinletmsq=List.filter(fun(a,b)->b<>[])(List.map2(funab->(a,b))monoidsqs)inleteval_sqsqs=List.fold_right(fun(c,q)->poly_add(poly_cmulc(poly_mulqq)))sqspoly_0inletsanity=List.fold_right(fun((p,c),s)->poly_add(poly_mulp(eval_sqs)))msq(List.fold_right2(funpq->poly_add(poly_mulpq))cfseqs(poly_negpol))inifnot(is_undefinedsanity)thenraiseSanityelse(cfs,List.map(fun(a,b)->(snda,b))msq)(* ------------------------------------------------------------------------- *)(* The ordering so we can create canonical HOL polynomials. *)(* ------------------------------------------------------------------------- *)letdest_monomialmon=sort(increasingfst)(graphmon)letmonomial_order=letreclexorderl1l2=match(l1,l2)with|[],[]->true|vps,[]->false|[],vps->true|(x1,n1)::vs1,(x2,n2)::vs2->ifx1<x2thentrueelseifx2<x1thenfalseelseifn1<n2thenfalseelseifn2<n1thentrueelselexordervs1vs2infunm1m2->ifm2=monomial_1thentrueelseifm1=monomial_1thenfalseelseletmon1=dest_monomialm1andmon2=dest_monomialm2inletdeg1=List.fold_right(o(+)snd)mon10anddeg2=List.fold_right(o(+)snd)mon20inifdeg1<deg2thenfalseelseifdeg1>deg2thentrueelselexordermon1mon2(* ------------------------------------------------------------------------- *)(* Map back polynomials and their composites to HOL. *)(* ------------------------------------------------------------------------- *)letterm_of_varpowxk=ifk=1thenVarxelsePow(Varx,k)letterm_of_monomialm=ifm=monomial_1thenConstQ.oneelseletm'=dest_monomialminletvps=List.fold_right(fun(x,k)a->term_of_varpowxk::a)m'[]inend_itlist(funst->Mul(s,t))vpsletterm_of_cmonomial(m,c)=ifm=monomial_1thenConstcelseifc=/Q.onethenterm_of_monomialmelseMul(Constc,term_of_monomialm)letterm_of_polyp=ifp=poly_0thenZeroelseletcms=List.mapterm_of_cmonomial(sort(fun(m1,_)(m2,_)->monomial_orderm1m2)(graphp))inend_itlist(funt1t2->Add(t1,t2))cmsletterm_of_sqterm(c,p)=Product(Rational_ltc,Square(term_of_polyp))letterm_of_sos(pr,sqs)=ifsqs=[]thenprelseProduct(pr,end_itlist(funab->Sum(a,b))(List.mapterm_of_sqtermsqs))(* ------------------------------------------------------------------------- *)(* Some combinatorial helper functions. *)(* ------------------------------------------------------------------------- *)letrecallpermutationsl=ifl=[]then[[]]elseList.fold_right(funhacc->List.map(funt->h::t)(allpermutations(subtractl[h]))@acc)l[]letchangevariables_monomialzoln(m:monomial)=foldl(funaxk->(List.assocxzoln|->k)a)monomial_1mletchangevariableszolnpol=foldl(funamc->(changevariables_monomialzolnm|->c)a)poly_0pol(* ------------------------------------------------------------------------- *)(* Return to original non-block matrices. *)(* ------------------------------------------------------------------------- *)letsdpa_of_vector(v:vector)=letn=dimvinletstrs=List.map(o(decimalize20)(elementv))(1--n)inString.concat" "strs^"\n"letsdpa_of_matrixk(m:matrix)=letpfx=string_of_intk^" 1 "inletms=foldr(fun(i,j)ca->ifi>jthenaelse((i,j),c)::a)(sndm)[]inletmss=sort(increasingfst)msinList.fold_right(fun((i,j),c)a->pfx^string_of_inti^" "^string_of_intj^" "^decimalize20c^"\n"^a)mss""letsdpa_of_problemcommentobjmats=letm=List.lengthmats-1andn,_=dimensions(List.hdmats)in"\""^comment^"\"\n"^string_of_intm^"\n"^"1\n"^string_of_intn^"\n"^sdpa_of_vectorobj^List.fold_right2(funkma->sdpa_of_matrix(k-1)m^a)(1--List.lengthmats)mats""letrun_csdpdbgobjmats=letinput_file=Filename.temp_file"sos"".dat-s"inletoutput_file=String.subinput_file0(String.lengthinput_file-6)^".out"andparams_file=Filename.concattemp_path"param.csdp"infile_of_stringinput_file(sdpa_of_problem""objmats);file_of_stringparams_filecsdp_params;letrv=Sys.command("cd "^temp_path^"; csdp "^input_file^" "^output_file^ifdbgthen""else"> /dev/null")inletop=string_of_fileoutput_fileinletres=parse_csdpoutputopinifdbgthen()else(Sys.removeinput_file;Sys.removeoutput_file);(rv,res)letcsdpobjmats=letrv,res=run_csdp!debuggingobjmatsinifrv=1||rv=2thenfailwith"csdp: Problem is infeasible"elseifrv=3then()(* (Format.print_string "csdp warning: Reduced accuracy";
Format.print_newline()) *)elseifrv<>0thenfailwith("csdp: error "^string_of_intrv)else();res(* ------------------------------------------------------------------------- *)(* Sum-of-squares function with some lowbrow symmetry reductions. *)(* ------------------------------------------------------------------------- *)letsumofsquares_general_symmetrytoolpol=letvars=poly_variablespolandlpps=newton_polytopepolinletn=List.lengthlppsinletsym_eqs=letinvariants=List.filter(funvars'->is_undefined(poly_subpol(changevariables(List.combinevarsvars')pol)))(allpermutationsvars)inletlpns=List.combinelpps(1--List.lengthlpps)inletlppcs=List.filter(fun(m,(n1,n2))->n1<=n2)(allpairs(fun(m1,n1)(m2,n2)->((m1,m2),(n1,n2)))lpnslpns)inletclppcs=end_itlist(@)(List.map(fun((m1,m2),(n1,n2))->List.map(funvars'->((changevariables_monomial(List.combinevarsvars')m1,changevariables_monomial(List.combinevarsvars')m2),(n1,n2)))invariants)lppcs)inletclppcs_dom=setify(List.mapfstclppcs)inletclppcs_cls=List.map(fund->List.filter(fun(e,_)->e=d)clppcs)clppcs_dominleteqvcls=List.map(osetify(List.mapsnd))clppcs_clsinletmk_eqclsacc=matchclswith|[]->raiseSanity|[h]->acc|h::t->List.map(funk->(k|->Q.minus_one)(h|=>Q.one))t@accinList.fold_rightmk_eqeqvcls[]inleteqs=foldl(funaxy->y::a)[](itern1lpps(funm1n1->itern1lpps(funm2n2f->letm=monomial_mulm1m2inifn1>n2thenfelseletc=ifn1=n2thenQ.oneelseQ.twoin(m|->((n1,n2)|->c)(tryapplydfmundefined))f))(foldl(funamc->(m|->((0,0)|=>c))a)undefinedpol))@sym_eqsinletpvs,assig=eliminate_all_equations(0,0)eqsinletallassig=List.fold_right(funv->v|->(v|=>Q.one))pvsassiginletqvars=(0,0)::pvsinletdiagents=end_itlistequation_add(List.map(funi->applyallassig(i,i))(1--n))inletmk_matrixv:matrix=((n,n),foldl(funm(i,j)ass->letc=tryapplydassvQ.zeroinifc=/Q.zerothenmelse((j,i)|->c)(((i,j)|->c)m))undefinedallassig)inletmats=List.mapmk_matrixqvarsandobj=(List.lengthpvs,itern1pvs(funvi->i|-->tryapplyddiagentsvQ.zero)undefined)inletraw_vec=ifpvs=[]thenvector_00elsetoolobjmatsinletfind_roundingd=if!debuggingthen(Format.print_string("Trying rounding with limit "^Q.to_stringd);Format.print_newline())else();letvec=nice_vectordraw_vecinletmat=iter(1,dimvec)(funia->matrix_add(matrix_cmul(elementveci)(List.nthmatsi))a)(matrix_neg(List.nthmats0))inderation(diagmat)inletrat,dia=ifpvs=[]thenletmat=matrix_neg(List.nthmats0)inderation(diagmat)elsetryfindfind_rounding(List.mapQ.of_int(1--31)@List.mapQ.pow2(5--66))inletpoly_of_lin(d,v)=(d,foldl(funaic->(List.nthlpps(i-1)|->c)a)undefined(sndv))inletlins=List.mappoly_of_lindiainletsqs=List.map(fun(d,l)->poly_mul(poly_constd)(poly_powl2))linsinletsos=poly_cmulrat(end_itlistpoly_addsqs)inifis_undefined(poly_subsospol)then(rat,lins)elseraiseSanityletsumofsquares=sumofsquares_general_symmetrycsdp