One Hat Cyber Team
Your IP :
216.73.216.115
Server IP :
194.44.31.54
Server :
Linux zen.imath.kiev.ua 4.18.0-553.77.1.el8_10.x86_64 #1 SMP Fri Oct 3 14:30:23 UTC 2025 x86_64
Server Software :
Apache/2.4.37 (Rocky Linux) OpenSSL/1.1.1k
PHP Version :
5.6.40
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
usr
/
share
/
Macaulay2
/
View File Name :
Chordal.m2
newPackage( "Chordal", Version => "0.2", Date => "May 15, 2021", Authors => { {Name => "Diego Cifuentes", Email => "diegcif@mit.edu", HomePage => "http://www.mit.edu/~diegcif"}, {Name => "Pablo A. Parrilo", Email => "parrilo@mit.edu", HomePage => "http://www.mit.edu/~parrilo/"} }, Headline => "routines that exploit chordal structure", Keywords => {"Commutative Algebra", "Graph Theory"}, AuxiliaryFiles => true, PackageImports => {"PrimaryDecomposition"}, PackageExports => {"Graphs","TriangularSets"} ) -- Copyright 2017 Diego Cifuentes, Pablo Parrilo. -- You may redistribute this file under the terms of the GNU General -- Public License as published by the Free Software Foundation, -- either version 2 of the License, or any later version. export { --Types "ChordalNetNode", "ChordalNetRank", "ChordalGraph", "ElimTree", "ChordalNet", "ChordalNetChain", --Methods/Functions "chordalGraph", "treewidth", "elimTree", "constraintGraph", "suggestVariableOrder", "chordalNet", "displayNet", "chordalElim", "chordalTria", "codimCount", "label", "rootCount", "nextChain", "nextOrderedPartition", "reduceNet", "reduceDimension", "adjacentMinorsIdeal", "chromaticIdeal", "subsetsProductsIdeal", "toLex", "nodes", "setDefaultConfiguration", "isTriangular", "structure", "cliques", --Method options "GetTable" } protect NoRoot, protect counter --##########################################################################-- -- GLOBAL VARIABLES --##########################################################################-- BIGPRIME = 10007; --##########################################################################-- -- DEBUGGING METHODS --##########################################################################-- debugPrint = s -> if debugLevel>0 then print s -- print dynamic programming state printState = (N,States) -> ( for j in N.elimTree.nodes do( print ("Rank: " | toString j); print for Nj in nodes(N,j) list States#Nj; ); print ("Rank: null"); print {States#null}; ) --##########################################################################-- -- SHORTCUTS --##########################################################################-- uniqueFast = x -> keys set x; toLex = I -> ( R := ring I; (S,phi) := changeRing(R,,Lex); return phi(I); ) setDefaultConfiguration = method() setDefaultConfiguration(Package,String,String) := (packge,key,val) -> setDefaultConfiguration(toString packge,key,val) setDefaultConfiguration(String,String,String) := (packge,key,val) -> ( initFile := (packge) -> ( folder := applicationDirectory(); file := folder | "init-" | packge | ".m2"; if not fileExists file then error "init file not found"; return file; ); addQuotes := s -> if s#0=="\"" then s else "\""|s|"\""; key = addQuotes key; val = addQuotes val; file := initFile(packge); s := replaceKeyValue (key, val, get file); if s===null then error "key not found"; file << s << close; print "Configuration changed. Please restart or reload package." ); replaceKeyValue = (key,val,text) -> ( r := regex(key,text); if r===null then return null; i := plus first r; r = regex("\"",i,text); if r===null then return null; j := first first r; r = regex("\"",j+1,text); if r===null then return null; k := first first r; s := substring((0,j), text) | val | substring (k+1,text); return s; ); --################################### -- Type definitions --################################### -- type representing chordal graphs ChordalGraph = new Type of Digraph -- type representing the elimination tree of a chordal graph ElimTree = new Type of HashTable -- type representing chordal networks ChordalNet = new Type of MutableHashTable -- type representing a rank of a chordal network ChordalNetRank = new Type of MutableHashTable -- TODO: this should be immutable -- type representing a node of a chordal network ChordalNetNode = new Type of MutableHashTable -- type representing a chain of a chordal network ChordalNetChain = new Type of HashTable --################################### -- Chordal graphs --################################### -- ChordalGraph constructor -- finds a chordal completion of a graph chordalGraph = method() chordalGraph (Digraph) := ChordalGraph => DG -> ( (cG,tw) := chordalClosure(DG); new ChordalGraph from cG ) -- completes the graph using a given ordering chordalGraph (Graph,List) := ChordalGraph => (G,ordering) -> ( if ordering!={} then G = reorderGraph(G,ordering); DG := directEdges(G); return chordalGraph(DG); ) -- completes the graph, searching for a good ordering chordalGraph (Graph) := ChordalGraph => G -> ( (o,tw) := minDegreeOrder(G); return chordalGraph(G,o); ) -- chordal graph given by an elimination tree chordalGraph (ElimTree) := ChordalGraph => tree -> ( DG:= digraph applyPairs(tree.cliques, (i,C) -> (i,delete(i,C))); DG = reorderGraph(DG, tree.nodes); return chordalGraph DG; ) -- completes a digraph using the ordering from G.vertexSet chordalClosure = DG -> ( A := mutableMatrix DG.adjacencyMatrix; tw := 0; for i to numRows(A)-1 do ( degi := fillMat(A,i,false); tw = max(tw,degi); ); Gc := graph(DG.vertexSet, matrix A); return (Gc,tw); ) fillMat = (A,i,sym) -> ( I := neighborsAdjMat(A,i); for e in subsets(I,2) do ( A_(e_0,e_1) = 1; if sym then A_(e_1,e_0) = 1; ); return #I ) neighborsAdjMat = (A,i) -> ( Ai := first entries A^{i}; I := positions (Ai, j-> j!=0); return I; ) -- reorders the vertices of a graph reorderGraph = (G,ordering) -> graph(ordering, edges G) -- directs the edges of a graph according to some given order directEdges = method() directEdges(Graph,List) := (G,o) -> ( V := G.vertexSet; A := G.adjacencyMatrix; I := toList(0..<numRows(A)); U := matrix table(I,I, (i,j)-> if i<=j then A_(o_i,o_j) else 0); return digraph(V_o,U); ) directEdges(Graph) := G -> ( return directEdges(G,toList(0..<#G.vertexSet)); ) -- minimum degree ordering minDegreeOrder = (G) -> ( A := mutableMatrix G.adjacencyMatrix; V := G.vertexSet; S := 0..<numRows(A); tw := 0; ordering := while #S>0 list ( i := minDegreeVertex(A); Vi := V_(S_i); degi:= fillMat(A,i,true); tw = max(tw,degi); J := {0..<i}|{i+1..<#S}; A = submatrix (A,J,J); S = S_J; Vi ); return (ordering,tw); ) minDegreeVertex = (A) -> ( mdeg := infinity; vert := 0; for i to numRows(A)-1 do ( I := neighborsAdjMat(A,i); degi := #I; simplicial := sum(flatten entries A^I_I)==#I*(#I-1); if simplicial then return i; if degi < mdeg then ( mdeg = degi; vert = i; ); ); return vert; ) --################################### -- Custom methods for chordal graphs --################################### -- TODO: independenceNumber isChordal(ChordalGraph):= G -> true isPerfect(ChordalGraph):= G -> true treewidth = method() treewidth(ChordalGraph):= G -> max(for v in G.vertexSet list #children(G,v)) treewidth(ElimTree):= tree -> max(for v in tree.nodes list #tree.cliques#v)-1 cliqueNumber(ChordalGraph) := G -> treewidth(G)+1 chromaticNumber(ChordalGraph):= G -> cliqueNumber G inducedSubgraph(ChordalGraph):= G -> chordalGraph inducedSubgraph G --################################### -- Elimination tree --################################### -- ElimTree constructor elimTree = method() elimTree (List,HashTable,HashTable) := ElimTree => (sortednodes,parnt,clique) -> ( roots := {}; child := new MutableHashTable from sortednodes / (i->(i,{})); for i in sortednodes do ( p := parnt#i; if p===null then roots = append(roots,i) else child#p = append(child#p,i); ); child#null = roots; child = new HashTable from child; new ElimTree from { symbol nodes => sortednodes, symbol parents => parnt, symbol cliques => clique, symbol children => child } ) elimTree (ChordalGraph) := ElimTree => G -> ( X := G.vertexSet; I := toList (0..<#X); posit := hashTable( I / (i-> (X#i,i)) ); smallest := L -> ( idx := min( L / (j->posit#j) ); if idx != infinity then X#idx ); parnt := hashTable( for i in X list (i,smallest toList children(G,i)) ); sortverts := V -> for i in X list ( if member(i,V) then i else continue); cliques := hashTable( for i in X list (i,prepend(i,sortverts children(G,i))) ); return elimTree(X,parnt,cliques); ) elimTree (ChordalNet) := ElimTree => N -> N.elimTree -- is clique Xj contained in a unique Xc? cliqueNotContained = (j,tree) -> ( C := tree.children#j; K := tree.cliques; if #C!=1 then return true; c := first C; contained := isSubset(K#j, K#c); return not contained; ) -- enumerate the pairs (j,p) where p is parent of j nodePairs = method(Options => {NoRoot=>false, Reverse=>false}) nodePairs (ElimTree) := opts -> tree -> ( X := tree.nodes; if opts.Reverse then X = reverse(X); npairs := for j in X list ( p := tree.parents#j; if opts.NoRoot and p===null then continue; (j,p) ); return npairs; ) leaves(ElimTree) := tree -> select(tree.nodes, i->tree.children#i=={}) --################################### -- Constraint graph of an ideal --################################### -- constructs the constraint graph of a polynomial set constraintGraph = method() constraintGraph (Ring,List) := Graph => (R,F) -> ( E := set(); for f in F do ( Vf := support f; Ef := set subsets(Vf, 2); E = E + Ef; ); return graph(gens R, toList E); ) constraintGraph (Ideal) := Graph => I -> constraintGraph(ring I, I_*); -- suggests a good variable ordering suggestVariableOrder = method() suggestVariableOrder (Ideal):= I -> ( g := constraintGraph(I); (X,tw) := minDegreeOrder(g); return X; ) changeRing = (R,X,mOrder) -> ( kk := coefficientRing R; if X===null then X = gens R; if mOrder===null then mOrder = Lex; S := kk(monoid[X,MonomialOrder=>mOrder]); phi := map(S,R); return (S,phi) ) -- verifies if the ordering is lexicographic hasLexOrder = method() hasLexOrder(Ring) := R -> ( opts := (options R).MonomialOrder; haslex := false; badkeys := (Weights,GRevLex,RevLex,GroupLex,GroupRevLex); for o in opts do ( if member(o#0,badkeys) then return false; if o#0==Lex then haslex = true; ); return haslex; ) hasLexOrder(Ideal) := I -> hasLexOrder ring I --################################### -- Chordal networks --################################### -- ChordalNetRank constructor chordalNetRank = method() chordalNetRank (ZZ,Thing,VisibleList) := ChordalNetRank => (k,i,nods) -> ( new ChordalNetRank from { symbol nodes => nods, symbol counter => k, symbol rank => i} ) chordalNetRank (Thing,VisibleList) := ChordalNetRank => (i,nods) -> ( return chordalNetRank(0,i,nods); ) chordalNetRank (Thing) := ChordalNetRank => i -> ( return chordalNetRank(i,{}); ) -- ChordalNetNode constructor chordalNetNode = method() chordalNetNode(String,Thing,VisibleList,VisibleList) := ChordalNetNode => (nname,i,F,H) -> ( --TODO preprocess generators (reduction) H = select(H, h -> not isConstant h); new ChordalNetNode from { symbol label => nname, symbol gens => F, symbol ineqs => rsort H, symbol rank => i, symbol parents => {}, symbol children => {}} ) chordalNetNode(ChordalNetRank,Thing,VisibleList,VisibleList) :=ChordalNetNode => (NRi,i,F,H) -> ( nname := getNodeLabel(NRi); return chordalNetNode(nname,i,F,H); ) getNodeLabel = NRi -> ( k := NRi.counter; NRi.counter = k + 1; label := toString(NRi.rank) | "_" | toString(k); return label; ) -- ChordalNet constructor chordalNet = method() chordalNet(Ideal) := ChordalNet => (I) -> ( g := constraintGraph(I); gc := chordalGraph(g,{}); tree := elimTree(gc); if not hasLexOrder I then error("Monomial order must be Lex"); F := I_*; struct:= getStruct(F); N := new MutableHashTable from for i in tree.nodes list i => chordalNetRank(i); initializeNet(N,tree,F); CN := new ChordalNet from { symbol elimTree => tree, symbol ring => ring I, symbol structure => struct, symbol isTriangular => false, symbol net => N}; return CN; ) chordalNet(Ideal,List) := ChordalNet => (I,X) -> ( R := ring I; (S,phi) := changeRing(R,X,Lex); return chordalNet(phi I); ) chordalNet(Ideal,String) := ChordalNet => (I,str) -> ( if str!="SuggestOrder" then error("string not recognized"); X := suggestVariableOrder(I); return chordalNet(I,X); ) initializeNet = (Nnet,tree,F) -> ( FF := distributePolys(tree,F); for i in tree.nodes do Nnet#i.nodes = { chordalNetNode(Nnet#i,i,FF#i,{}) }; for jp in nodePairs(tree,NoRoot=>true) do ( (j,p):= jp; Nj:= Nnet#j.nodes#0; Np:= Nnet#p.nodes#0; Nj.parents = {Np}; addChildren(Np, Nj); ); ); -- distribute polynomials on the tree distributePolys = (tree,F) -> ( cliques := applyPairs(tree.cliques ,(i,C)->(i,set C)); placePoly := f -> ( Xf := support f; queue := {mvar f}; minranks := {}; while #queue>0 do( i := first queue; queue = drop(queue,1); C := tree.children#i; R := for c in C list ( if isSubset(Xf,cliques#c) then c else continue); queue = queue | R; if #R==0 then minranks = append(minranks,i); ); return minranks; ); FF := new MutableHashTable from for i in tree.nodes list i => {}; for f in F do( ranks := placePoly f; for i in ranks do FF#i = append(FF#i, f); ); return FF; ) getStruct = (F) -> ( nF := max(F / (f-> #terms f)); if nF==1 then return "Monomial" else if nF==2 then return "Binomial" else return "None"; ) -- properties of a chordal network structure = N -> N.structure isTriangular = method() isTriangular(ChordalNet) := N -> N.isTriangular --################################### -- Access chordal network nodes/arcs --################################### -- properties of a node gens (ChordalNetNode) := Ni -> Ni.gens ineqs (ChordalNetNode) := Ni -> Ni.ineqs rank (ChordalNetNode) := Ni -> Ni.rank label = method() label (ChordalNetNode) := Ni -> Ni.label parents (ChordalNetNode) := Ni -> Ni.parents children (ChordalNetNode) := Ni -> Ni.children -- enumerate pairs (i,Ni) where Ni is a rank i node nodes = method(Options => {Reverse=>false}) nodes (ChordalNet,RingElement) := opts -> (N,i) -> (N.net#i).nodes nodes (ChordalNet) := opts -> N -> ( ranks := N.elimTree.nodes; if opts.Reverse then ranks = reverse(ranks); return flatten for i in ranks list nodes(N,i); ) -- enumerate arcs of the network netArcs = N -> ( tree := N.elimTree; E := for ip in nodePairs(tree,NoRoot=>true) list ( (i,p):= ip; for Ni in nodes(N,i) list for Np in Ni.parents list (Ni,Np) ); return flatten flatten E; ) -- children of a node nodeChildren = (rk, Ni) -> select( Ni.children, Nc->Nc.rank==rk ) --################################### -- Representation and Visualization --################################### -- printing custom types net(ChordalGraph) := Net => G -> ( V := vertexSet G; A := adjacencyMatrix G; H := hashTable apply(#V, i -> V_i => V_(positions(first entries A^{i}, j -> j != 0))); P := for v in V list (v,H#v); horizontalJoin flatten ( net class G, "{", stack (horizontalJoin \ apply(P, (k,v) -> (net k, " => ", net v))), " }" ) ) net(ElimTree) := Net => tree -> ( x := tree.parents; horizontalJoin flatten ( net class tree, "{", stack (horizontalJoin \ sort apply(pairs x,(k,v) -> (net k, " => ", net v))), "}" ) ) net(ChordalNetNode) := Net => Ni -> ( F := Ni.gens; netgens := if #F==0 then " " else if #F==1 then net first F else net F; H := Ni.ineqs; netineqs := if #H==0 then "" else if #H==1 then " / " | net first H else " / " | net H; return netgens | netineqs; ) net(ChordalNetRank) := Net => Nrk -> net Nrk.nodes net(ChordalNet) := Net => N -> ( P := for i in N.elimTree.nodes list (i,N.net#i); horizontalJoin flatten ( net class N, "{ ", stack (horizontalJoin \ apply(P,(k,v) -> (net k, " => ", net v))), " }" ) ) net(TriaSystem) := Net => T -> ( F := T.gens; netgens := if #F==0 then " " else if #F==1 then net first F else net F; H := T.ineqs; netineqs := if #H==0 then "" else if #H==1 then " / " | net first H else " / " | net H; return netgens | netineqs; ) size(ChordalNet) := N -> for i in N.elimTree.nodes list #nodes(N,i) -- display an elimination tree displayGraph (String, String, ElimTree) := (dotfilename, jpgfilename, tree) -> ( writeDotFile(dotfilename, tree); displayDotFile(dotfilename, jpgfilename); ) displayGraph (ElimTree) := (tree) -> ( dotfilename:= temporaryFileName()|".dot"; jpgfilename:= temporaryFileName()|".jpg"; displayGraph(dotfilename, jpgfilename, tree); ) writeDotFile (String, ElimTree) := (filename, tree) -> ( fil := openOut filename; fil << "digraph G {" << endl; for i in tree.nodes do fil << "\t" | toString i | " ;" << endl; for ip in nodePairs(tree,NoRoot=>true) do( (i,p):= ip; Ci := tree.cliques#i; Ci = delete(p,delete(i,Ci)); fil << "\t" | toString i | " -> " | toString p | ";" << endl; attr := "[dir=none,style=dotted]"; fil << "\t" | toString i | " -> " | toString Ci | attr | ";" << endl; ); fil << "}" << endl << close; ) -- display a chordal network displayNet = method() displayNet (String,String,Function,ChordalNet) := (dotfilename, jpgfilename, fun, N) -> ( writeDotFile(dotfilename, fun, N); displayDotFile(dotfilename, jpgfilename); ) displayNet (Function,ChordalNet) := (fun,N) -> ( dotfilename:= temporaryFileName()|".dot"; jpgfilename:= temporaryFileName()|".jpg"; displayNet(dotfilename, jpgfilename, fun, N); ) displayNet (ChordalNet) := (N) -> displayNet(net, N); writeDotFile (String,Function,ChordalNet) := (filename, fun, N) -> ( fil := openOut filename; fil << "digraph G {" << endl; for iNR in pairs(N.net) do( (i,NRi):= iNR; fil << "\tsubgraph cluster_" | toString i | " {" << endl; for Ni in NRi.nodes do fil << "\t\t" | toString Ni.label | " [label=\""|toString fun(Ni)|"\"];" << endl; fil << "\t}" << endl; ); E := netArcs N; for e in E do( (Ni,Np):= e; fil << "\t" | toString Ni.label | " -> " | toString Np.label | ";" << endl; ); fil << "}" << endl << close; ) displayDotFile = (dotfilename,jpgfilename) -> ( dotBinary := ((options Graphs).Configuration)#"DotBinary"; runcmd(dotBinary | " -Tjpg " | dotfilename | " -o " | jpgfilename); show URL("file://" | toAbsolutePath jpgfilename); ) runcmd = cmd -> ( stderr << "-- running: " << cmd << endl; r := run cmd; if r != 0 then error("-- command failed, error return code ", r); ) --################################### -- Digraph and Ring maps --################################### -- digraph associated to a chordal network digraph(ChordalNet) := Digraph => opts -> N -> ( tree := N.elimTree; nods := {}; edges := {}; alias := Ni -> Ni.label; for ip in nodePairs(tree) do( (i,p):= ip; NRi := nodes(N,i); nods = nods | (NRi/alias); if p===null then continue; Er := for Ni in NRi list( {alias(Ni),(Ni.parents)/alias} ); edges = edges | Er; ); G := digraph (nods,edges); return G; ) -- construct chordal network from a digraph chordalNet(HashTable,HashTable,ElimTree,Digraph) := ChordalNet => (eqs,ranks,tree,DG) -> ( V := vertices DG; R := ring ranks#(V_0); node:= hashTable for v in V list( if not instance(v,String) then error "labels must be strings"; i := ranks#v; (F,H) := eqs#v; v => chordalNetNode(v,i,F,H) ); N := new MutableHashTable from for i in tree.nodes list( Vi:= select(V, v -> ranks#v==i); NRi:= for v in Vi list node#v; i => chordalNetRank(i,NRi) ); for v in V do( Nv := node#v; P := for p in toList children(DG,v) list node#p; C := for c in toList parents(DG,v) list node#c; addParents(Nv,P); addChildren(Nv,C); ); CN := new ChordalNet from { symbol elimTree => tree, symbol ring => R, symbol structure => "None", symbol isTriangular => false, symbol net => N}; return CN; ) RingMap ElimTree := ElimTree => (phi,tree) -> ( X:= phi \ tree.nodes; parnt:= applyPairs( tree.parents, (i,p) -> if p=!=null then (phi i, phi p) else (phi i, null) ); clique:= applyPairs( tree.cliques, (i,Xi) -> (phi i, phi \ Xi) ); return elimTree(X,parnt,clique); ) RingMap ChordalNet := ChordalNet => (phi,N) -> ( if source phi=!=ring N then error("Rings don't match"); if not hasLexOrder target phi then error("Monomial order must be Lex"); tree := elimTree N; nods := nodes(N); Phi := hashTable for Ni in nods list Ni => chordalNetNode( Ni.label, phi Ni.rank, phi\Ni.gens, phi\Ni.ineqs); N' := new MutableHashTable from for i in tree.nodes list( i' := phi i; NRi' := for Ni in nodes(N,i) list Phi#Ni; i' => chordalNetRank(i', NRi') ); for Ni in nods do( Ni' := Phi#Ni; P' := for Np in Ni.parents list Phi#Np; C' := for Nc in Ni.children list Phi#Nc; addParents(Ni',P'); addChildren(Ni',C'); ); CN := new ChordalNet from { symbol elimTree => phi tree, symbol ring => target phi, symbol structure => N.structure, symbol isTriangular => N.isTriangular, symbol net => N'}; return CN; ) --################################### -- Modifying chordal networks --################################### -- modify parents/children of a node addChildren = method() addChildren(ChordalNetNode,ChordalNetNode) := ( Ni, Nc ) -> ( Ni.children = uniqueFast append(Ni.children, Nc); ) addChildren(ChordalNetNode,VisibleList) := ( Ni, childn ) -> ( Ni.children = uniqueFast join(Ni.children, childn); ) addParents = method() addParents(ChordalNetNode,ChordalNetNode) := ( Ni, Np ) -> ( Ni.parents = uniqueFast append(Ni.parents, Np); ) addParents(ChordalNetNode,VisibleList) := ( Ni, parnts ) -> ( Ni.parents = uniqueFast join(Ni.parents, parnts); ) delChildren = method() delChildren(ChordalNetNode,ChordalNetNode) := ( Ni, Nc ) -> ( Ni.children = delete(Nc,Ni.children); ) delChildren(ChordalNetNode) := Ni -> ( Ni.children = {}; ) delParents = method() delParents(ChordalNetNode,ChordalNetNode) := ( Ni, Np ) -> ( Ni.parents = delete(Np,Ni.parents); ) delParents(ChordalNetNode) := Ni -> ( Ni.parents = {}; ) -- delete node deleteNode = (NRj,Nj) -> ( disconectNode(Nj); NRj.nodes = delete(Nj, NRj.nodes); ); -- create/remove arcs between nodes createArc = ( Ni, Np ) -> ( addParents(Ni, Np); addChildren(Np, Ni); ) removeArc = ( Ni, Np ) -> ( delChildren(Np, Ni); delParents(Ni, Np); ) disconectNode = ( Ni ) -> ( for Np in Ni.parents do delChildren(Np, Ni); for Nc in Ni.children do delParents(Nc, Ni); delParents(Ni); delChildren(Ni); ) copyAllArcs = ( Ni, Ni2 ) -> ( copyOutArcs(Ni,Ni2); copyInArcs(Ni,Ni2,); ) copyOutArcs = ( Ni, Ni2 ) -> ( NP:= Ni.parents; for Np in NP do addChildren(Np, Ni2); addParents(Ni2, copy(NP)); ) copyInArcs = ( Ni, Ni2, filter ) -> ( NC:= Ni.children; if filter=!=null then NC=select(NC,filter); for Nc in NC do addParents(Nc, Ni2); addChildren(Ni2, copy(NC)); ) -- copy node Ni, ignoring the arcs from rank rk copyNodeExcept = (NRi,Ni,rk) -> ( Ni2 := chordalNetNode(NRi,Ni.rank,{},{}); copyOutArcs(Ni,Ni2); copyInArcs(Ni,Ni2, Nc -> Nc.rank=!=rk ); return Ni2; ) --################################### -- Chordal elimination --################################### -- Grobner operations netNodeGb = method(); netNodeGb(ChordalNetNode,OptionTable) := (Ni,opts) -> ( I := ideal Ni.gens; Ni.gens = first entries gens gb(I,opts); ) -- Elimination operations -- TODO: merge after elimination can be done more efficiently -- if two nodes Ni, Ni2 agree on the last elements, then it suffices to eliminate only one of them. netNodeElim = method(); netNodeElim(Thing,ChordalNetRank,ChordalNetNode) := (p,NRp,Ni) -> ( i := Ni.rank; NP := Ni.parents; if #NP==0 then error("node has no parent"); hasi := f -> (x:= mvar f; x=!=null and x>p); F := partition(f -> hasi(f), Ni.gens, {true,false}); H := partition(f -> hasi(f), Ni.ineqs, {true,false}); Ni.gens = F#true; Ni.ineqs = H#true; NP2 := for Np in NP list ( Np2 := copyNodeExcept(NRp,Np,i); Np2.gens = join(Np.gens, F#false); Np2.ineqs = join(Np.ineqs, H#false); createArc(Ni,Np2); removeArc(Ni,Np); if not any(Np.children, Nj->Nj.rank==i) then disconectNode(Np); Np2 ); return NP2; ) netRankElim = method(); netRankElim(Thing,ElimTree,ChordalNetRank,ChordalNetRank) := (p,tree,NRp, NRi) -> ( elim := Ni -> netNodeElim(p,NRp,Ni); NRp.nodes = mergeOut(NRp.rank,tree,NRi.nodes,elim); NRi.nodes = mergeIn(NRi.nodes,Ni->{Ni}); ) -- Chordal elimination chordalElim = method(Options=> {Algorithm=>Inhomogeneous,DegreeLimit=>{}}); chordalElim(ChordalNet) := opts -> N -> ( if N.isTriangular then error("input is triangularized"); goodInitial := (i,Ni) -> ( J := ideal for f in Ni.gens list initial(i,f); return J==1; ); tree := N.elimTree; guaranteed := true; debugPrint("\n%%%%%%%% Chordal Elim %%%%%%%%"); for ip in nodePairs(tree) do ( (i,p):= ip; debugPrint("\nNODE: " | toString (i,p) ); if cliqueNotContained(i,tree) then for Ni in nodes(N,i) do( if guaranteed then guaranteed = goodInitial(i,Ni); netNodeGb(Ni,opts); ); debugPrint "\nGroebner Step"; debugPrint N; if p===null then continue; debugPrint "Elimination Step"; debugPrint N; netRankElim(p,tree,N.net#p,N.net#i); ); return guaranteed; ) --################################### -- Chordal triangularization --################################### -- Triangulation operations netNodeTria = method(); netNodeTria (List,ChordalNetRank,ChordalNetNode) := (TT,NRi,Ni) -> ( i := Ni.rank; TN := for Tj in TT list ( Fj := Tj.gens; Hj := Tj.ineqs; Nj := chordalNetNode(NRi,i,Fj,Hj); copyAllArcs( Ni, Nj ); Nj ); disconectNode( Ni ); return TN; ) netNodeTria (Ring,String,ChordalNetRank,ChordalNetNode) := (R,alg,NRi,Ni) -> ( F := Ni.gens; H := Ni.ineqs; TT := triangularize(R,F,H,TriangularDecompAlgorithm=>alg); return netNodeTria(TT,NRi,Ni); ) netRankTria = method(); netRankTria (Ring,String,ElimTree,ChordalNetRank) := (R,alg,tree,NRi) -> ( tria := Ni -> netNodeTria(R,alg,NRi,Ni); if alg=="Maple" or alg=="Epsilon" then( F := hashTable for Ni in NRi.nodes list Ni => Ni.gens; H := hashTable for Ni in NRi.nodes list Ni => Ni.ineqs; TT := triangularizeBatch(alg,R,F,H); tria = Ni -> netNodeTria(TT#Ni,NRi,Ni); ); NRi.nodes = mergeOut(NRi.rank,tree,NRi.nodes,tria); ) netRankDelIneqs = NRi -> for Ni in NRi.nodes do Ni.ineqs = {}; -- Merge operations mergeOut = (i,tree,nods,lists) -> ( Ci := tree.children#i; cmpChild := (Ni,Nj) -> ( badC := 0; for c in Ci do if set(nodeChildren(c,Ni))=!=set(nodeChildren(c,Nj)) then( badC = badC+1; if badC==2 then return false; ); true ); cmp := (Ni,Nj) -> if #Ci<=1 then true else cmpChild(Ni,Nj); key := Ni -> (Ni.gens,Ni.ineqs,set Ni.parents); collide := (Nj,Ncopy) -> (copyInArcs(Ncopy,Nj,); disconectNode(Ncopy);); return toList uniqueObjects(nods, lists, key, cmp, collide); ) mergeIn = (nods,lists) -> ( key := Ni -> (Ni.gens,set Ni.children); collide := (Nj,Ncopy) -> (copyOutArcs(Ncopy,Nj); disconectNode(Ncopy);); return toList uniqueObjects(nods, lists, key, , collide); ) -- Looks for non equivalent objects in some lists -- Two objects l,j are equivalent if: key(l)=key(j) and cmp(l,k) uniqueObjects = (domain, lists, key, cmp, collide) -> ( searchj := (L,j,seenkj) -> for i in seenkj do if cmp===null or cmp(L#i,j) then return L#i; L := new MutableList; seen := new MutableHashTable; cnt := 0; for i in domain do( J := lists(i); for j in J do( kj := key(j); if seen#?kj then ( l := searchj(L,j,seen#kj); if l=!=null then ( if collide=!=null then collide(l,j); continue; ); seen#kj = append(seen#kj,cnt); ) else seen#kj = {cnt}; L#cnt = j; cnt = cnt+1; ); ); return L; ) -- Chordal Triangularization -- TODO: simplify network by prunning unneeded arcs -- TODO: allow inputs with size > 1 chordalTria = method(Options=> {TriangularDecompAlgorithm=>null}); chordalTria(ChordalNet) := opts -> N -> ( if N.isTriangular then return; if max size N > 1 then error("network size > 1"); tree := N.elimTree; badC := not testContainedCliques N; R := N.ring; alg := selectTriaAlg(opts.TriangularDecompAlgorithm, N.structure,R); ranks := tree.nodes; debugPrint("\n%%%%%%%% Chordal Tria %%%%%%%%"); for ip in nodePairs(tree) do ( (i,p):= ip; debugPrint("\nNODE: " | toString (i,p) ); if badC or cliqueNotContained(i,tree) then netRankTria(N.ring,alg,tree,N.net#i); debugPrint "\nTriangulation Step"; debugPrint N; if p===null then( netRankDelIneqs(N.net#i); continue; ); netRankElim(p,tree,N.net#p,N.net#i); netRankDelIneqs(N.net#i); debugPrint "Elimination Step"; debugPrint N; ); checkConsistency N; relabelNet N; N.isTriangular = true; ) testContainedCliques = N -> ( tree := N.elimTree; for i in tree.nodes do if not cliqueNotContained(i,tree) then( Ni:= first nodes(N,i); if #Ni.gens>0 then return false; ); return true; ) selectTriaAlg = (alg,struct,R) -> ( if alg=!=null then return alg; if struct=!=null and struct=!="None" then return struct; if not checkInterface("Maple") then error "MapleInterface failed. Maple is needed unless ideal is binomial."; if char R==0 and checkInterface("Epsilon") then return "Epsilon"; return "Maple"; ); relabelNet = N -> ( for iNR in pairs(N.net) do( (i,NRi):= iNR; NRi.counter = 0; for Ni in NRi.nodes do Ni.label = getNodeLabel(NRi); ); ) --################################### -- Polynomial reductions --################################### -- simple polynomial reduction reduceEqs = F -> ( (M,C) := coefficients matrix {F}; C = lift(C, coefficientRing ring C); C = transpose rowReduce transpose C; return first entries (M*C); ) rowReduce = A -> ( (P,L,U) := LUdecomposition A; rows:= for Ui in entries U list( j := position(Ui, e -> e!=0); if j===null then continue; p := Ui#j; for e in Ui list e//p ); return matrix rows; ) --################################### -- Triangular systems --################################### triaSystem(ChordalNet,ChordalNetChain) := TriaSystem => (N,C) -> ( F := flatten for Ni in values C list Ni.gens; F = reduceEqs F; H := initial F; return triaSystem(N.ring,F,H); ) --################################### -- ChordalNet Reduction --################################### reduceNet = method() reduceNet(ChordalNet) := N -> ( reduceDiamonds N; ) -- remove disconnected nodes checkConsistency = N -> ( tree:= N.elimTree; for Nj in nodes(N,Reverse=>true) do ( j:= Nj.rank; if tree.parents#j=!=null and #Nj.parents==0 then deleteNode(N.net#j,Nj); ); ) -- reduces diamonds of the following form -- Ni -- Np Np' with Np contained in Np' -- Nk reduceDiamonds = N -> ( keepMaximal := (nods,cmp,del) -> ( P:= partition(Ni->Ni.gens, nods); C0:= if P#?{} then P#{} else {}; for fC in pairs(P) do( (f,C):= fC; C1:= if f==={} then C else C|C0; if #C1<=1 then continue; maxl := maximalObjects(C1,cmp); badN:= select(C, Np->not maxl#Np); for Np in badN do del(Np); ); ); tree:= N.elimTree; cmp0:= (S,T) -> if S>=T then 1 else if S<=T then -1 else 0; for jp in nodePairs(tree,NoRoot=>true) do ( (j,p):= jp; child:= Ni -> select(Ni.children, Nc -> Nc.rank=!=j ); cmp1:= (Ni,Nk) -> cmp0( set(Ni.parents|child(Ni)), set(Nk.parents|child(Nk)) ); for Nj in nodes(N,j) do keepMaximal(Nj.parents,cmp1,Np->removeArc(Nj,Np)); ); cmp2:= (Ni,Nk) -> cmp0( set(Ni.children), set(Nk.children) ); for jp in nodePairs(tree,Reverse=>true,NoRoot=>true) do ( (j,p):= jp; --if #tree.children#p!=1 then continue; --seems unnecessary for Np in nodes(N,p) do keepMaximal(nodeChildren(j,Np),cmp2,Nj->removeArc(Nj,Np)); ); ) -- maximal elements of a partial order -- cmp(a,b) = 1 if a>=b -- cmp(a,b) = -1 if a<b -- cmp(a,b) = 0 if incomparable maximalObjects = (Os,cmp) -> ( cmp2 := (a,b) -> cmp(b,a); return minimalObjects(Os,cmp2); ) --################################### -- Chains of a chordal network --################################### chordalNetChain = method() chordalNetChain(HashTable) := ChordalNetChain => chain -> ( if mutable chain then chain = new HashTable from chain; new ChordalNetChain from chain ) codim(ChordalNetChain) := optsC -> ( -- ignore first argument (options) chain:= optsC_1; cdim:= sum for Nj in values chain list #Nj.gens; return cdim; ) dim(ChordalNetChain) := C -> #(keys C) - codim C -- iterates over the chains of a chordal network nextChain = method(); nextChain(ChordalNet) := ChordalNetChain => (N) -> ( tree := N.elimTree; ranks := tree.nodes; C := new MutableHashTable from for j in ranks list (j,null); rRanks := reverse(ranks); success:= fillChain(N,C,rRanks,0,); if not success then return null; return chordalNetChain(C); ) nextChain(ChordalNetChain,ChordalNet) := ChordalNetChain => (chain,N) -> ( tree:= N.elimTree; ranks := tree.nodes; rRanks := reverse ranks; C:= new MutableHashTable from chain; succ:= false; for i to #ranks-1 when not succ do ( j := ranks_i; ir := #ranks-i-1; succ= fillChain(N,C,rRanks,ir,C#j); ); if not succ then return null; return chordalNetChain(C); ) fillChain = (N,C,ranks,i,Nj0) -> ( tree := N.elimTree; j:= ranks#i; succj := nextNj(N,C,tree,j,Nj0,); if not succj then return false else if i==#ranks-1 then return true; succ:= fillChain(N,C,ranks,i+1,); return succ; ) nextNj = (N,C,tree,j,Nj0,filter) -> ( p:= tree.parents#j; NRj:= if p===null then nodes(N,j) else nodeChildren(j,C#p); if filter=!=null then NRj = select(NRj, Nj -> filter(Nj) ); k:= if Nj0===null then 0 else 1+position(NRj, Nj->Nj===C#j); if k==#NRj then return false; C#j = NRj_k; return true; ) -- iterates over the chains of a given codimension -- TODO: use codimension counts to make it more efficient nextChain(ZZ,ChordalNet) := ChordalNetChain => (cdim,N) -> ( cdimTab := getCdimTable(N); if not member(cdim,cdimTab#0#null) then return (null,cdimTab); tree := N.elimTree; ranks := tree.nodes; rRanks := reverse(ranks); C := new MutableHashTable from for j in ranks list (j,null); Cdim := new MutableHashTable from C; roots := tree.children#null; succ := fillChainRoots(N,C,rRanks,cdimTab,0,Cdim,roots,cdim,); if not succ then return null; return (chordalNetChain(C),cdimTab); ) nextChain(ChordalNetChain,Sequence,ZZ,ChordalNet) := ChordalNetChain => (chain,cdimTab,cdim,N) -> ( if codim chain =!= cdim then error "bad codimension of initial chain"; tree:= N.elimTree; ranks := tree.nodes; C:= new MutableHashTable from chain; Cdim:= new MutableHashTable from for j in ranks list (j,null); succ:= false; ranks = append(ranks,null); rRanks:= reverse ranks; for i to #ranks-1 when not succ do ( j := ranks_i; child:= tree.children#j; P:= for c in child list Cdim#c; cdimj:= sum P; if j=!=null then Cdim#j = cdimj + #(C#j.gens); ir := #ranks-i-1; if #child>0 then succ= fillChainRoots(N,C,rRanks,cdimTab,ir+1,Cdim,child,cdimj,P); if not succ and j=!=null then succ= fillChainI(N,C,rRanks,cdimTab,ir,Cdim,C#j); ); if not succ then return null; return chordalNetChain(C); ) getCdimTable = N -> ( tree := N.elimTree; nodeTab := codimCount(N, GetTable=>true); nodeTab = applyPairs(nodeTab, (Nj,f)-> (Nj,flatten exponents f)); rankTab := hashTable for j in tree.nodes list( dimsj:= for Nj in nodes(N,j) list nodeTab#Nj; (j,unique flatten dimsj) ); return (nodeTab,rankTab); ) fillChainRoots = (N,C,ranks,cdimTab,i,Cdim,roots,cdim,P0) -> ( L:= for c in roots list cdimTab#1#c; P:= if P0===null then nextOrderedPartition(cdim,L) else nextOrderedPartition(P0,cdim,L); while P=!=null do( for i to #roots-1 do Cdim#(roots_i) = P_i; success:= fillChainI(N,C,ranks,cdimTab,i,Cdim,); if success then return true; P = nextOrderedPartition(P,cdim,L); ); return false; ) fillChainI = (N,C,ranks,cdimTab,i,Cdim,Nj0) -> ( tree := N.elimTree; j:= ranks#i; filter := Nj -> member(Cdim#j,cdimTab#0#Nj); succj := nextNj(N,C,tree,j,Nj0,filter); if not succj then return false else if i==#ranks-1 then return true; child := tree.children#j; cdim := Cdim#j - #(C#j.gens); succ:= if #child>0 then fillChainRoots(N,C,ranks,cdimTab,i+1,Cdim,child,cdim,) else fillChainI(N,C,ranks,cdimTab,i+1,Cdim,); return succ; ) -- given lists L1..Lk of distinct nonnegative integers, -- iterate over all tuples (l1,..,lk) with sum equal to S nextOrderedPartition = method() nextOrderedPartition(ZZ,List) := (S,Lists) -> ( k:=#Lists; if k==1 then return if member(S,Lists_0) then {S} else null; P:= new MutableList from (0..<#Lists); Lmin := Lists / min; success:= fillPartitionI(Lists,Lmin,P,0,S); if success then return toList P; return null; ) nextOrderedPartition(List,ZZ,List) := (partn,S,Lists) -> ( k:=#Lists; if k==1 then return null; L:= new MutableList from Lists; Lmin := Lists / min; P:= new MutableList from partn; for i in reverse(0..<k) do( Li:= select(Lists_i, l -> l<=S-sum(drop(Lmin,i+1))); j:= position(Li,l-> l==P#i); L#i = drop(Li,j+1); Si:= S - sum drop(partn,{i,k-1}); success:= fillPartitionI(L,Lmin,P,i,Si); if success then return toList P; L#i = Li; ); return null; ) fillPartitionI = (L,Lmin,P,i,S) -> ( if i==#P-1 then( if not member(S,L#i) then return false; P#i = S; return true; ); Li:= select(L#i, l -> l<=S-sum(drop(Lmin,i+1))); for l in Li do( P#i = l; succ:= fillPartitionI(L,Lmin,P,i+1,S-l); if succ then return true; ); return false; ) --################################### -- Methods for chordal networks --################################### ring(ChordalNet) := N -> N.ring checkTriangular = N -> if not N.isTriangular then error "Network must be triangular"; codim(ChordalNet) := optsN -> ( -- ignore first argument (options) N := optsN_1; checkTriangular N; initState := Nj -> if Nj=!=null then #Nj.gens else 0; initMess := Np -> infinity; message := (cdimNj,messg) -> min(cdimNj,messg); update := (cdimNp,messg) -> cdimNp+messg; Cdims := messagePassing(N,initState,initMess,message,update); return Cdims#null; ) dim(ChordalNet) := N -> numgens(N.ring) - codim N codimCount = method(Options => {GetTable=>false}) codimCount(ChordalNet) := opts -> N -> ( checkTriangular N; tree := N.elimTree; S := ZZ(monoid[getSymbol "t"]); t := first gens S; initState := Nj -> if Nj=!=null then t^(#Nj.gens) else 1_S; initMess := Np -> 0_S; message := (cdimNj,messg) -> cdimNj+messg; update := (cdimNp,messg) -> cdimNp*messg; Cdims := messagePassing(N,initState,initMess,message,update); return if opts.GetTable then Cdims else Cdims#null; ) rootCount = method() rootCount(ChordalNet) := N -> ( checkTriangular N; initState := Nj -> if Nj===null then 1 else if #Nj.gens==0 then 0 else (f:=first Nj.gens; degree(Nj.rank,f)); initMess := Np -> 0; message := (rootsNj,messg) -> rootsNj+messg; update := (rootsNp,messg) -> rootsNp*messg; Nroots := messagePassing(N,initState,initMess,message,update); return Nroots#null; ) -- message passing algorithm on the nodes of a chordal network -- top-down pass over the tree messagePassing = (N,initState,initMess,message,update) -> ( tree := N.elimTree; nods := append(nodes(N), null); States := new MutableHashTable from for Nj in nods list Nj=>initState(Nj); for jp in nodePairs(tree) do ( (j,p):= jp; nodesj := nodes(N,j); nodesp := if p===null then {null} else nodes(N,p); messg := new MutableHashTable from for Np in nodesp list Np=>initMess(Np); for Np in nodesp do ( NJ:= if p=!=null then nodeChildren(j,Np) else nodesj; for Nj in NJ do messg#Np = message(States#Nj,messg#Np); ); for Np in nodesp do States#Np = update(States#Np,messg#Np); ); if debugLevel>0 then printState(N,States); return new HashTable from States; ) -- radical membership -- top-down pass over the tree pseudoRemainder(RingElement,ChordalNet) := (f,N) -> ( initializeTop := (nods,R,f) -> ( x0 := mvar f; for Nj in nods list( j:= if Nj=!=null then Nj.rank else null; if j===x0 then Nj=>f else Nj=>0_R ) ); initializeSplit := (nods,R,f) -> ( P := partition(m -> mvar m, terms f, (gens R)|{null}); P = applyPairs ( P , (j,Fj) -> (j, sum Fj) ); assert( sum values P == f ); for Nj in nods list( j:= if Nj=!=null then Nj.rank else null; Nj => P#j ) ); premEval := (Dict, dict, f, Np) -> ( if Np===null or f==0 then return f; x := Np.rank; Fp := Np.gens; if #Fp>0 then f = pseudoRemainder(x, f, Fp#0); f = dict#x(f); return f; ); affineCoeffs := (kk,S) -> ( r := 0_kk; hashTable for i to #S-1 list( ri := random kk; if i<#S-1 then r = r + ri else ri = 1_kk - r; S#i=>ri ) ); checkTriangular N; if ring N =!= ring f then error("Rings don't match"); R := N.ring; if char R == 0 then( Q := ZZ/BIGPRIME[gens R, MonomialOrder=>Lex]; phi := map(Q,R); return pseudoRemainder(phi f, phi N); ); kk := coefficientRing R; dict := hashTable for x in gens R list (x, map(R,R,{x=>random kk})); Dict := product values dict; tree := N.elimTree; nods := append(nodes N, null); F := new MutableHashTable from if hasInitials N then initializeTop(nods,R,f) else initializeSplit(nods,R,f); for jp in nodePairs(tree) do ( (j,p):= jp; nodesj := nodes(N,j); nodesp := if p===null then {null} else nodes(N,p); for Nj in nodesj do F#Nj = premEval(Dict,dict,F#Nj,Nj); for Np in nodesp do ( NJ:= if p=!=null then nodeChildren(j,Np) else nodesj; C:= affineCoeffs(kk,NJ); for Nj in NJ do F#Np = C#Nj * F#Nj + F#Np; ); ); if debugLevel>0 then printState(N,F); return F#null; ) RingElement % ChordalNet := (f,N) -> pseudoRemainder(f,N) hasInitials = N -> ( for Nj in nodes N list ( Fj:= Nj.gens; if #Fj>0 then if #(indices leadTerm Fj#0) > 1 then return true; ); return false; ); -- simple test to check if all chains are prime isPrimeSimple(ChordalNet) := N -> ( (tailAny,nonTailAll) := tailNodes N; for Nj in tailAny do if not isPrime Nj.gens#0 then return false; for Nj in nonTailAll do if degree(Nj.rank,Nj.gens#0)>1 then return false; return true; ) -- tailAny: nodes that are the tail of some chain -- tailAll: nodes that are the tail of all chains -- nonTailAll: complement of tailAll -- bottom-up pass over the tree tailNodes = N -> ( isZero := Nj -> #Nj.gens==0; checkTriangular N; tree := N.elimTree; roots := tree.children#null; r := min roots; nods := nodes(N); nonzero := select(nods, Nj -> not isZero Nj); tailAny := nonzero; nonTailAll := select(nonzero, Nj -> Nj.rank=!=r); if #leaves(tree)>1 then -- TODO: handle more leaves return (tailAny,nonTailAll); -- any path consisting of zeros anyZeroPath := new MutableHashTable from for Nj in nods list Nj=>(Nj.rank==r); -- all paths consist of zeros allZeroPath := copy anyZeroPath; anyP := Np -> isZero Np and anyZeroPath#Np; allP := Np -> isZero Np and allZeroPath#Np; for jp in nodePairs(tree,Reverse=>true,NoRoot=>true) do ( (j,p):= jp; nodesj := nodes(N,j); for Nj in nodesj do( NP:= Nj.parents; anyZeroPath#Nj = any(NP,anyP); allZeroPath#Nj = all(NP,allP); ); ); tailAny = select(nonzero, Nj -> anyZeroPath#Nj); nonTailAll = select(nonzero, Nj -> not allZeroPath#Nj); return (tailAny,nonTailAll); ) -- isolates the top dimensional components of a chordal network -- bottom-up pass over the tree topComponents(ChordalNet) := N -> ( tree:= N.elimTree; (nodeTab,rankTab) := getCdimTable(N); nodeTab = applyPairs(nodeTab, (Nj,cdims)->(Nj,min(cdims))); rankTab = applyPairs(rankTab, (j,cdims)->(j,min(cdims))); for Nj in nodes(N,Reverse=>true) do ( j:= Nj.rank; cdimNj:= nodeTab#Nj; if tree.parents#j=!=null then ( for Np in Nj.parents do( cdimNp:= min(nodeChildren(j,Np) / (Nk->nodeTab#Nk) ); if cdimNp<cdimNj then removeArc(Nj,Np); ); if #Nj.parents==0 then deleteNode(N.net#j,Nj); )else if rankTab#j<cdimNj then deleteNode(N.net#j,Nj); ); ) -- eliminates arcs/nodes absent in the top k dimensions -- bottom-up pass over the tree reduceDimension = (N,k) -> ( tree:= N.elimTree; (nodeTab,rankTab) := getCdimTable(N); nodeTab = applyPairs(nodeTab, (Nj,cdims)->(Nj,min(cdims))); nods := append(nodes(N), null); regret := new MutableHashTable from for Nj in nods list if Nj===null then Nj=>0 else Nj=>infinity; for jp in nodePairs(tree,Reverse=>true) do ( (j,p):= jp; nodesj := nodes(N,j); nodesp := if p===null then {null} else nodes(N,p); for Np in nodesp do ( NJ:= if p=!=null then nodeChildren(j,Np) else nodesj; cdim := min(NJ / (Nj->nodeTab#Nj)); for Nj in NJ do( regretjp := regret#Np + (nodeTab#Nj - cdim); if regretjp >= k then( if Np=!=null then removeArc(Nj,Np); if #Nj.parents==0 then deleteNode(N.net#j,Nj); ); regret#Nj = min(regretjp,regret#Nj); ); ); ); checkConsistency N; ) -- Components of the network (assuming they are prime) -- brute force enumeration components(ChordalNet,ZZ) := (N,topk) -> ( R := N.ring; (Rd,phi) := changeRing(R,,GRevLex); psi := map(R,Rd); isMinimalPrem := (T,S0) -> ( return not any(S0, I0 -> isContainedIn(I0,T)); ); componentsCdim := (S,cdim) -> ( L := new MutableList; (C,data) := nextChain(cdim,N); S0 := flatten values S; while C=!=null do( T := triaSystem(N,C); if isMinimalPrem(T,S0) then L#(#L) = psi saturate phi T; C = nextChain(C,data,cdim,N); ); S#cdim = unique toList L; ); Cdims := sort flatten exponents codimCount N; if topk>0 then Cdims = take(Cdims,{0,topk-1}); Q := coefficientRing R; S := new MutableHashTable; for cdim in Cdims do( componentsCdim(S,cdim) ); return new HashTable from S; ) components(ChordalNet) := (N) -> components(N,0) isContainedIn = method() -- is I contained in sat(T)? isContainedIn(Ideal,TriaSystem) := (I,T) -> all(I_*, f -> pseudoRemainder(f,T)==0) -- is V(I) contained in Z(T)? -- this implies that sat(T) \subset I isContainedIn(TriaSystem,Ideal) := (T,I) -> ( F:= T.gens; H:= T.ineqs; return all(F, f -> f%I==0) and (I+ideal H)==1; ) --################################### -- Examples --################################### -- ring with n variables nVarsRing = (kk,n) -> ( x := getSymbol "x"; X := if n<=26 then vars(0..<n) else (x_0..x_(n-1)); return kk(monoid[X,MonomialOrder=>Lex]); ) -- ideal of adjacent minors of a matrix adjacentMinorsIdeal = (kk,m,n) -> ( if m>n then (m,n)=(n,m); R := nVarsRing(kk,m*n); M := genericMatrix(R,m,n); F := for i to n-m list ( L := toList(i..i+m-1); determinant(M_L) ); return ideal F; ) -- ideal of q-colorings chromaticIdeal = (kk,G,q) -> ( V := vertices G; n := #vertices G; if V!=toList(0..(n-1)) then error "vertex set must be 0,1,..,n-1"; R := nVarsRing(kk,n); F := for E in edges G list( (i,j):= toSequence elements E; sum for d to q-1 list R_i^d*R_j^(q-1-d) ); F = F | for i to n-1 list (R_i)^q-1; return ideal F; ) subsetsProductsIdeal = (kk,n,k) -> ( R:= nVarsRing(kk,n); return ideal (subsets(gens R, k) / (S -> product(S))); ) --##########################################################################-- -- Documentation and Tests --##########################################################################-- beginDocumentation() load "./Chordal/ChordalDoc.m2"; TEST /// load (Chordal#"source directory" | "./Chordal/test_chordal.m2") ///