From a60c87734d5c3b379c007add686d10cc31cc8375 Mon Sep 17 00:00:00 2001 From: Jay Yang Date: Sun, 14 May 2023 23:50:58 -0500 Subject: [PATCH] Add a CellularResolutions package. This is joint work with Aleksandra Sobieska. --- M2/Macaulay2/packages/=distributed-packages | 1 + M2/Macaulay2/packages/CellularResolutions.m2 | 759 +++++++++ .../packages/CellularResolutions/doc.m2 | 1505 +++++++++++++++++ .../packages/CellularResolutions/tests.m2 | 521 ++++++ 4 files changed, 2786 insertions(+) create mode 100644 M2/Macaulay2/packages/CellularResolutions.m2 create mode 100644 M2/Macaulay2/packages/CellularResolutions/doc.m2 create mode 100644 M2/Macaulay2/packages/CellularResolutions/tests.m2 diff --git a/M2/Macaulay2/packages/=distributed-packages b/M2/Macaulay2/packages/=distributed-packages index 0f2e7c66ad6..fb33cfe5b73 100644 --- a/M2/Macaulay2/packages/=distributed-packages +++ b/M2/Macaulay2/packages/=distributed-packages @@ -252,3 +252,4 @@ GeometricDecomposability PseudomonomialPrimaryDecomposition PolyominoIdeals MatchingFields +CellularResolutions diff --git a/M2/Macaulay2/packages/CellularResolutions.m2 b/M2/Macaulay2/packages/CellularResolutions.m2 new file mode 100644 index 00000000000..9d32c503452 --- /dev/null +++ b/M2/Macaulay2/packages/CellularResolutions.m2 @@ -0,0 +1,759 @@ +-- -*- coding: utf-8 -*- +-- This file is in the public domain +newPackage( + "CellularResolutions", + Version => "1.0", + Date => "May 14, 2023", + Authors => { + {Name => "Jay Yang", Email => "jayy@wustl.edu"}, + {Name => "Aleksandra Sobieska", Email => "asobieska@math.wisc.edu"} + }, + Headline => "A package for cellular resolutions of monomial ideals", + AuxiliaryFiles => true, -- set to true if package comes with auxiliary files + PackageExports => {"Polyhedra", "SimplicialComplexes", "Posets"} + ) + +export {--types + "CellComplex", + "Cell", + --methods + "boundary", + "boundaryCells", + "cells", + "cellComplex", + "cellComplexSphere", + "cellComplexRPn", + "cellComplexTorus", + "cellLabel", + "hullComplex", + "isCycle", + "isFree", + "isMinimal", + "isSimplex", + "newCell", + "newSimplexCell", + "maxCells", + "relabelCellComplex", + "scarfComplex", + "subcomplex", + "taylorComplex", + --symbols (for optional parameters) + "CellDimension", + "InferLabels", + "LabelRing", + "Reduced", + "Prune" + } +protect labelRing +protect label +protect cellDimension +protect CellDimension +protect InferLabels +protect LabelRing +protect Reduced +protect Prune + +hasAttribute = value Core#"private dictionary"#"hasAttribute"; +getAttribute = value Core#"private dictionary"#"getAttribute"; +ReverseDictionary = value Core#"private dictionary"#"ReverseDictionary"; + +CellComplex = new Type of HashTable +CellComplex.synonym = "cell complex" +CellComplex.GlobalAssignHook = globalAssignFunction +CellComplex.GlobalReleaseHook = globalReleaseFunction +--Note, the mutable hash table means that equality works "Correctly" +Cell = new Type of MutableHashTable +Cell.synonym = "cell" + +maxAndAllCells := (lst) -> ( + if #lst == 0 then return (new HashTable,new HashTable); + bdfn := c -> set boundaryCells c; + maxcells := set lst; + bdcells := sum (maxcells/bdfn); + allcells := maxcells + bdcells; + maxcells = maxcells - bdcells; + while #bdcells != 0 do ( + bdcells = sum (bdcells/bdfn); + allcells = allcells + bdcells; + maxcells = maxcells - bdcells; + ); + (partition(dim,toList maxcells), partition(dim,toList allcells)) + ) + +--returns a hashtable of lists of cells indexed by dimension +cellsFromMaxCells := lst -> ( + pendingCells := set lst; + finishedCells := {}; + while #pendingCells !=0 do ( + c := (elements pendingCells)#0; + pendingCells = pendingCells + ((set ((boundary c)/first)) - finishedCells) - {c}; + finishedCells = append(finishedCells,c); + ); + partition(dim,finishedCells) + ) + +--Private constructor, creates the cache +mkCellComplex := (labelRingVal, cellsVal, maxCellsVal) -> ( + new CellComplex from { + symbol labelRing => labelRingVal, + symbol cells => cellsVal, + cache => new CacheTable from ( + if maxCellsVal === null + then {} + else {symbol maxCells => maxCellsVal}) + } + ) + +cellComplex = method(Options=>true, TypicalValue=>CellComplex) +cellComplex(Ring,List) := {} >> o -> (R,maxCells) -> ( + (realMaxCells,allCells) := maxAndAllCells maxCells; + mkCellComplex(R, allCells, realMaxCells) + ) + +cellComplex(Ring,SimplicialComplex) := {Labels=>null} >> o -> (S,C) -> ( + R := ring C; + Cfaces := new HashTable from faces C; + --cells indexes Cells by monomials corresponding to faces of the simplicial complex + cells := new MutableHashTable from {}; + for i from 0 to dim C do ( + for simplex in Cfaces#i do ( + label := + (if class(o.Labels) === HashTable + then (if o.Labels#?simplex + then o.Labels#simplex + else null + ) + else null); + bd := if i==0 + then {} + else + for x in gens R list ( + if simplex%x==0 then cells#(simplex//x) + else continue); + cells#simplex = if label === null then newCell bd else newCell(bd,label) + ); + ); + cellComplex(S,values cells) + ) + +maxCells = method(TypicalValue=>HashTable) +maxCells(CellComplex) := (cacheValue (symbol maxCells)) (cellComplex -> + ( + lst := flatten values cells cellComplex; + if #lst == 0 then return new HashTable; + bdfn := c -> set boundaryCells c; + maxcells := set lst; + bdcells := sum (maxcells/bdfn); + maxcells = maxcells - bdcells; + while #bdcells != 0 do ( + bdcells = sum (bdcells/bdfn); + maxcells = maxcells - bdcells; + ); + partition(dim,toList maxcells) + )) + +--Define dimension for cell +dim(Cell) := (cell) -> cell.cellDimension + +--Define dimension for cell complex +dim(CellComplex) := (cellComplex) -> max keys cellComplex.cells + +--Define ring for cell complex +ring(CellComplex) := (cellComplex) -> cellComplex.labelRing + + +cellLabel = method() +cellLabel(Cell) := (cell) -> cell.label + +--Make a cell, internal function +makeCell := (lst, l, d) -> ( + bdim := -1; + for cell in lst do ( + if bdim < 0 + then bdim = dim cell#0 + else assert(bdim == dim cell#0) + ); + n := max(bdim + 1,d); + new Cell from { + symbol cellDimension => n, + symbol boundary => lst, -- could verify that it's a list + symbol label => l + } + ); + +chainToVirtualTally := (lst) -> ( + if lst == {} + then new VirtualTally from {} + else sum(lst, (cell,deg) -> new VirtualTally from {cell => deg}) + ) + +boundary = method() +boundary(Cell) := List => (cell) -> cell.boundary +boundaryCells = method(TypicalValue=>List) +boundaryCells(Cell) := (cell) -> apply(boundary(cell), c -> first c) +--Boundary function, returns the boundary as a VirtualTally +boundaryTally := (cell) -> chainToVirtualTally cell.boundary + + +internalCycleCheck := (lst) -> ((sum(lst,l -> ( + c := l#0; + deg := l#1; + if deg>0 + then sum(deg,i -> boundaryTally c) + else - sum( - deg,i -> boundaryTally c)))) ? 0) == symbol == + +--Check if a chain, represented by a list is a boundary +isCycle = method(TypicalValue=>Boolean) +isCycle(List) := {Reduced=>true} >> o -> (lst) -> + (if o.Reduced + then ( + p := partition(x -> dim (x#0) == 0, lst); + zeroDimCells := if p#?true then p#true else {}; + otherCells := if p#?false then p#false else {}; + internalCycleCheck otherCells and sum(zeroDimCells,last) == 0 + ) + else internalCycleCheck lst) + + +--Figure out an orientation automatically +inferOrientation := (lst) -> ( + if #lst == 2 and (dim first lst) == 0 then ( + ret := {(lst#0,1),(lst#1,-1)}; + if not isCycle ret then error "The given list of cells do not form a cycle"; + return ret + ); + boundaryChain := new VirtualTally from {}; + remainingCells := lst; + --the "?" based comparison is a workaround for "==" not working correctly for VirtualTally==ZZ + while (boundaryChain ? 0) != symbol == or #remainingCells!=0 list ( + if (boundaryChain ? 0) == symbol == + then ( + if remainingCells =!= lst then error "The orientation on the cycle is non-unique"; + nextCell := last remainingCells; + remainingCells = drop(remainingCells,-1); + boundaryChain = boundaryTally nextCell; + (nextCell,1) + ) + else ( + c := (keys boundaryChain)#0; + nextElems := select(remainingCells,c2 -> (boundaryTally c2)#?c); + if #nextElems==0 then error "The given list of cells do not form a cycle"; + newBoundaryComponent := boundaryTally (nextElems#0); + remainingCells = delete(nextElems#0,remainingCells);--Inefficient + --check sign equality + if (boundaryChain#c)*(newBoundaryComponent#c)<0 + then ( + boundaryChain = boundaryChain + boundaryTally (nextElems#0); + (nextElems#0,1) + ) + else ( + boundaryChain = boundaryChain - boundaryTally (nextElems#0); + (nextElems#0,-1) + ) + ) + ) + ) + +--Convert it to a submodule of R^1 if possible +toModule := (R,x) -> ( + if instance(x,Module) then return x; + if instance(x,Ideal) then return module x; + if instance(x,RingElement) then return image matrix {{x}}; + if instance(x,Number) then return image matrix {{x_R}}; + error "Expected a Module, Ideal, RingElement, or Number" + ) + +inferLabel := boundary -> ( + if boundary == {} then return 1; + if instance(boundary#0,Sequence) then return boundary/first//inferLabel; + if all(boundary/cellLabel,b -> instance(b,RingElement) or instance(b,Number)) + then boundary/cellLabel//lcm + else ( + rings := select(boundary/cellLabel, b -> not (instance(b,RingElement) or instance(b,Number)))/ring; + nonNumberRings := select(rings,r -> ancestor(Number,r)); + R := if nonNumberRings==={} then rings#0 else nonNumberRings#0; + boundary/cellLabel/(x -> toModule(R,x))//intersect + ) + ) + +--Attach a cell +newCell = method(Options => {CellDimension=>null}, TypicalValue=>Cell) +newCell(List,Number) := +newCell(List,RingElement) := +newCell(List,Module) := +newCell(List,Ideal) := opt -> (boundary,label) -> ( + if #boundary!=0 and instance(boundary#0,Cell) + then return newCell(inferOrientation boundary,label,CellDimension=>opt.CellDimension); + if not isCycle boundary then error "Expected the boundary to be a cycle"; + cd := if opt.CellDimension=!=null then opt.CellDimension else 0; + c := makeCell(boundary,label,cd); + if opt.CellDimension=!=null and dim c > cd then error "Incorrect CellDimesion optional parameter"; + c + ) +newCell(List) := opt -> cells -> newCell(cells,inferLabel cells,CellDimension=>opt.CellDimension); + + + +isSimplexBoundary := (lst) -> ( + if #lst==0 then return true; + bdim := dim first lst#0; + all(lst,isSimplex @@ first) and + all(lst,i -> dim first i == bdim) and + (#lst == bdim+2) and + (length lst == length unique (lst/first)) and + (isCycle lst) + ) + +isSimplex = method(TypicalValue=>Boolean); +isSimplex(Cell) := cell -> + isSimplexBoundary boundary cell + +newSimplexCell = method(TypicalValue=>Cell); +newSimplexCell(List) := (boundary) -> ( + if #boundary!=0 and instance(boundary#0,Cell) + then return newSimplexCell inferOrientation boundary; + if not isSimplexBoundary boundary then error "The given boundary is not a valid boundary for a simplex"; + newCell boundary + ) +newSimplexCell(List,Number) := +newSimplexCell(List,RingElement) := +newSimplexCell(List,Module) := +newSimplexCell(List,Ideal) := (boundary,label) -> ( + if #boundary!=0 and instance(boundary#0,Cell) + then return newSimplexCell(inferOrientation boundary,label); + if not isSimplexBoundary boundary then error "The given boundary is not a valid boundary for a simplex"; + newCell(boundary,label) + ) + +--Relabel function +relabelCellComplex = method(Options=>{InferLabels=>true},TypicalValue=>CellComplex); +relabelCellComplex(CellComplex,HashTable) := o -> (C,T) -> ( + dimC := dim C; + R := ring C; + tablecellsbydim := for i to dimC list select(keys T, c -> dim c == i); + relabeledcells := new MutableHashTable; + for c in cells(0,C) do relabeledcells#c = ( + if any(tablecellsbydim#0, cell -> cell === c) then newCell({},T#c) + else newCell({},cellLabel c) + ); + for i from 1 to dimC do ( + for c in cells(i,C) do ( + newbd := for b in boundaryCells c list relabeledcells#b; + newlabel := if any(tablecellsbydim#i, cell -> cell === c) then T#c + else if not o.InferLabels then cellLabel(c) + else inferLabel(newbd); + relabeledcells#c = newCell(newbd, newlabel); + ); + ); + cellComplex(R, flatten values relabeledcells) + ) + +RingMap ** CellComplex := (f,c) -> ( + if source f =!= ring c then error "source ring should match label ring"; + R := source f; + S := target f; + allCells := flatten values cells(c); + -- ht := hashTable apply(allCells, c -> (c,f ** toModule(R,cellLabel c))); + ht := hashTable apply(allCells, c -> (c,f(cellLabel c))); + tempCellComplex := relabelCellComplex(c,ht); + cellComplex(S,flatten values cells tempCellComplex) + ) + +--Get list of cells +cells = method(); +cells(CellComplex) := HashTable => (cellComplex) -> cellComplex.cells +cells(ZZ,CellComplex) := List => (r,cellComplex) -> ( + if cellComplex.cells#?r + then cellComplex.cells#r + else {} + ) + +skeleton(ZZ,CellComplex) := CellComplex => (n,cellComplex) -> ( + c := new HashTable from select(pairs cellComplex.cells, (k,v) -> k<=n); + mkCellComplex(cellComplex.labelRing,c,null) + ) + +--take a hash table of RingElements/Matrices, and make a matrix, or 0 +sparseBlockMap := (codomain,domain,ht) -> ( + ks := keys ht; + if ks == {} then return map(codomain,domain,0); + rows := max (ks/first) + 1; + columns := max (ks/(p->p#1)) + 1; + maybeHt := p -> ( + if ht#?p then ht#p else 0 + ); + assert(rows <= #components codomain); + assert(columns <= #components domain); + map(codomain,domain,matrix apply(#components codomain,i -> apply(#components domain, j -> maybeHt(i,j))))) + +--Create one boundary map in the chain complex +boundaryMap(ZZ,CellComplex) := opts -> (r,cellComplex) -> ( + R := cellComplex.labelRing; + t := r-1; + rCells := cells(r,cellComplex); + tCells := cells(t,cellComplex); + --We define these tables in two steps so that the ordering of the modules in domain and codomain + --is consistent, especially between calls to boundary with different values for "r". + domainModules := apply(toList rCells, c-> (c,toModule(R,cellLabel c))); + codomainModules := apply(toList tCells, c -> (c,toModule(R,cellLabel c))); + domainModulesTable := + new HashTable from domainModules; + codomainModulesTable := + new HashTable from codomainModules; + domain := if domainModules == {} then R^0 else directSum(apply(domainModules,last)); + codomain := if t==-1 then R^1 else if codomainModules == {} then R^0 else directSum(apply(codomainModules,last)); + tCellsIndexed := new HashTable from toList apply(pairs(tCells),reverse); + i := 0; + L := flatten for F in rCells list ( + l := if t==-1 + then (0,i) => inducedMap(codomain,domainModulesTable#F) + else for p in pairs boundaryTally F list( + (cell,deg) := p; + if dim cell < dim F - 1 then continue; + (tCellsIndexed#cell,i) => deg_R*inducedMap(codomainModulesTable#cell,domainModulesTable#F)); + i = i+1; + l + ); + sparseBlockMap(codomain,domain,new HashTable from L) + ); + +chainComplex(CellComplex) := {Reduced=>true, Prune=>true} >> o -> (cellComplex) -> ( + if not cellComplex.cache.?chainComplex then ( + cellComplex.cache.chainComplex = + (chainComplex apply(max((dim cellComplex) + 1,1), r -> boundaryMap(r,cellComplex)))[1] + ); + ret := if not o.Reduced then ( + Ccopy := chainComplex apply(max cellComplex.cache.chainComplex, + i -> cellComplex.cache.chainComplex.dd_(i+1)); + Ccopy + ) + else cellComplex.cache.chainComplex; + if o.Prune then ( + prune ret --how expensive is prune? should it be cached? + ) + else ret + ); + +--Get homology directly from cell complex +homology(ZZ,CellComplex) := opts -> (i,cellComplex) -> ( + homology_i chainComplex(cellComplex) + ); + +homology(CellComplex) := opts -> (cellComplex) -> ( + homology chainComplex(cellComplex) + ); + +--Get cohomology directly from cell complex +cohomology(ZZ,CellComplex) := opts -> (i,cellComplex) -> ( + cohomology_i Hom(chainComplex(cellComplex),cellComplex.labelRing^1) + ); + +---------- +---Here there be polyhedra +---------- +cellComplex(Ring,Polyhedron) := {Labels => null} >> o -> (R,P) -> ( + if not isCompact P then error "The given polyhedron is not compact."; + Pdim := dim P; + Pfaces := applyPairs(faces P, (i,lst) -> (Pdim-i,apply(lst,first))); + verts := vertices P; + vertexCells := apply(numColumns verts, + if o.Labels =!= null + then (n -> newCell({},o.Labels#(verts_n))) + else (n -> newCell({}))); + cells := new MutableHashTable; + for i from 0 to Pdim do ( + for face in Pfaces#i do ( + bd := if i!=0 + then for f in Pfaces#(i-1) list (if isSubset(f,face) then cells#f else continue) + else {}; + cells#face = + if i==0 + then vertexCells#(face#0) + else newCell bd; + ); + ); + cellComplex(R,flatten values cells) + ); + +cellComplex(Ring,PolyhedralComplex) := {Labels => null} >> o -> (R,P) -> ( + Pdim := dim P; + Pfaces := applyPairs(faces P, (i,lst) -> (Pdim-i-1,apply(lst,first))); + verts := vertices P; + vertexCells := apply(numColumns verts, + if o.Labels =!= null + then (n -> newCell({},o.Labels#(verts_n))) + else (n -> newCell({}))); + cells := new MutableHashTable; + for i from 0 to Pdim do ( + for face in Pfaces#i do ( + bd := if i!=0 + then for f in Pfaces#(i-1) list (if isSubset(f,face) then cells#f else continue) + else {}; + cells#face = + if i==0 + then vertexCells#(face#0) + else newCell bd; + ); + ); + cellComplex(R,flatten values cells) + ); + +------------- +-- Posets +------------- + +facePoset(CellComplex) := (cellComplex) -> ( + G := flatten values cells cellComplex; + contain := (a,b) -> member(a,boundaryCells b) or a === b;-- a contained or equal b + P := poset(G,contain); + rel := allRelations P; + M := transitiveClosure(G,rel); + poset(G,rel,M) + ) + +------------- +-- Minimality +------------- + +isFree = method(TypicalValue => Boolean); +--check if all the labels are free modules +isFree(CellComplex) := (cellComplex) -> ( + R := cellComplex.labelRing; + all(flatten values cells cellComplex,c -> isFreeModule prune toModule(R,cellLabel c)) + ) + +isCellMinimal := (R,cell) -> ( + label := toModule(R,cellLabel cell); + all(boundary cell, c -> toModule(R,cellLabel first c) != label) + ) + +isMinimal = method(TypicalValue => Boolean) +--Check if a labeled cell complex is minimal, Note: we assume the cell complex is free (see isFree) +isMinimal(CellComplex) := (cellComplex) -> ( + R := cellComplex.labelRing; + all(flatten values cells cellComplex,c -> isCellMinimal(R,c)) + ) + +subcomplex = method(TypicalValue => CellComplex, Options=>{LabelRing=>null}) +subcomplex(CellComplex,RingElement) := o -> (C,m) -> ( + allCells := flatten values cells C; + R := ring C; + S := if o.LabelRing =!= null then o.LabelRing else coefficientRing R; + withNewLabels := apply(allCells, c -> (c,if (m % ideal toModule(R,cellLabel c))==0 then 1_S else 0)); + nontrivialCells := select(withNewLabels, p -> (p#1) != 0); + finalCells := new MutableHashTable; + --it is important here that the cells in allCells are sorted by dimension at this point + for p in nontrivialCells do ( + c := p#0; + l := p#1; + newBoundary := apply(boundary c, p -> (finalCells#(p#0),p#1)); + finalCells#c = newCell(newBoundary,l); + ); + cellComplex(S,values finalCells) + ) + +subcomplex(CellComplex,List) := o -> (C,d) -> ( + allCells := flatten values cells C; + R := ring C; + S := if o.LabelRing =!= null then o.LabelRing else coefficientRing R; + withNewLabels := apply(allCells, c -> (c,source basis(d,toModule(R,cellLabel c),SourceRing=>S))); + nontrivialCells := select(withNewLabels, p -> (p#1) != 0); + finalCells := new MutableHashTable; + --it is important here that the cells in allCells are sorted by dimension at this point + for p in nontrivialCells do ( + c := p#0; + l := p#1; + newBoundary := apply(boundary c, p -> (finalCells#(p#0),p#1)); + finalCells#c = newCell(newBoundary,l); + ); + cellComplex(R,values finalCells) + ) + +subcomplex(CellComplex,ZZ) := o -> (C,d) -> subcomplex(C,{d},LabelRing=>o.LabelRing); + +CellComplex _ RingElement := (C,m) -> (subcomplex(C,m)) +CellComplex _ List := (C,d) -> (subcomplex(C,d)) +CellComplex _ ZZ := (C,d) -> (subcomplex(C,{d})) + +--------- +-- Output +--------- + +net(Cell) := (cell) -> ( + "Cell of dimension " | (dim cell) | " with label " | (net cellLabel cell) + ) + +net(CellComplex) := (cellComplex) -> ( + if hasAttribute (cellComplex, ReverseDictionary) then return getAttribute (cellComplex, ReverseDictionary); + d := dim cellComplex; + nTotalCells := #(flatten values cells cellComplex); + if nTotalCells == 0 + then "empty CellComplex" + else ( + ("CellComplex over " | (net cellComplex.labelRing) | " of dimension " | d | " with " | nTotalCells | " total cells") || + stack(apply(d+1,i -> net cells_i cellComplex))) + ); + + +------------------------ +-- Common cell complexes +------------------------ + +cellComplexSphere = method(TypicalValue=>CellComplex); +cellComplexSphere(Ring,ZZ) := (R,n) -> ( + if n<0 then error "cellComplexSphere expects a non-negative integer"; + v := newSimplexCell {}; + if n==0 then ( + w := newSimplexCell {}; + cellComplex(R,{v,w}) + ) + else( + c := newCell({(v,0)},CellDimension=>n); + cellComplex (R,{c}) + ) + ) + +cellComplexRPn = method(TypicalValue=>CellComplex); +cellComplexRPn(Ring,ZZ) := (R,n) -> ( + if n<0 then error "cellComplexRPn expects a non-negative integer"; + t := newSimplexCell {}; + if n==0 then return cellComplex(R,{t}); + for i from 1 to n do( + attachingDegree := if even i then 2 else 0; + t = newCell {(t,attachingDegree)}; + ); + cellComplex(R,{t}) + ) + +cellComplexTorus = method(TypicalValue=>CellComplex); +cellComplexTorus(Ring,ZZ) := (R,n) -> ( + if n<0 then error "cellComplexTorus expects a non-negative integer"; + v := newSimplexCell {}; + if n==0 then return cellComplex(R,{v}); + cells := new MutableHashTable; + for s in subsets(n) do ( + k := #s; + cells#s = newCell apply(subsets(s,k-1), s' -> (cells#s',0)) + ); + cellComplex(R,{cells#(toList (0..(n-1)))}) + ) + + +---------------------------- +-- Specific chain complexes +---------------------------- + +taylorComplex = method(TypicalValue=>CellComplex); +taylorComplex(MonomialIdeal) := (I) -> ( + gensI := I_*; + r := #gensI; + if r == 0 then error "taylorComplex expects a non-zero monomialIdeal"; + cells := new MutableHashTable; + for i to r-1 do cells#{i} = newSimplexCell({},gensI#i); + for k from 2 to r do ( + for s in subsets(r,k) do ( + bd := for t in subsets(s,k-1) list cells#t; + cells#s = newSimplexCell(bd, lcm(gensI_s)); + ); + ); + cellComplex(ring I, {cells#(toList (0..(r-1)))}) + ) + +scarfComplex = method(TypicalValue=>CellComplex) +scarfComplex(MonomialIdeal) := (I) -> ( + gensI := I_*; + r := #gensI; + if r == 0 then error "scarftComplex expects a non-zero monomialIdeal"; + cells := new MutableHashTable; + dupLabels := new MutableHashTable; + for i to r-1 do cells#(gensI#i) = newSimplexCell({},gensI#i); + for k from 2 to r do ( + hasCells := false; + for s in subsets(r,k) do ( + --boundary cell via the labels + bdLabels := for t in subsets(s,k-1) list lcm(gensI_t); + incompleteBoundary := false; + bd := for label in bdLabels list ( + if cells#?label then cells#label else (incompleteBoundary = true; break;)); + if incompleteBoundary then continue; + --compute the label of the cell + m := lcm(gensI_s); + --figure out if we have a duplicate label, if we do remove any cells with + --the same label, otherwise add the cell + if not dupLabels#?m + then if cells#?m + then ( + dupLabels#m = true; + remove(cells,m)) + else ( + hasCells = true; + cells#m = newSimplexCell(bd, m)); + ); + --if there are no cells at dimension k, there won't be any of higher dimension + if not hasCells then break; + ); + cellComplex(ring I, values cells) + ) + +hullComplex = method(TypicalValue=>CellComplex); +hullComplex(MonomialIdeal) := (I) -> ( + n := numgens ring I; + hullComplex((n+1)!+1,I) + ) +hullComplex(ZZ,MonomialIdeal) := (t,I) -> hullComplex(t/1,I) +hullComplex(QQ,MonomialIdeal) := (t,I) -> ( + gensI := I_*; + R := ring I; + n := numgens R; + expvecs := flatten (gensI/exponents); + verts := for a in expvecs list for i from 0 to (n-1) list t^(a#i); + P := convexHull(transpose matrix verts, id_(ZZ^n)); + Pdim := dim P; + Pfaces := new HashTable from select(pairs faces P, (k,v) -> (k <= Pdim and k > 0)); --weird inequalities bc codim + Pfaces = applyValues(Pfaces, v -> select(v,p -> p#1 == {})); -- selecting compact faces + Pfaces = applyValues(Pfaces, v -> apply(v,p -> p#0)); --get the vertices for the faces + Pfaces = applyKeys(Pfaces, d -> Pdim-d); --flipping from codim to dim + cells := new MutableHashTable; + for v in Pfaces#0 do cells#v = newCell({},gensI#(v#0)); + for i from 1 to Pdim-1 do ( + for face in Pfaces#i do ( + bd := for f in Pfaces#(i-1) list (if isSubset(f,face) then cells#f else continue); + cells#face = newCell bd + ); + ); + cellComplex(R,flatten values cells) + ) + +isWellDefined(Cell) := (C) -> ( + R := ring C.label; + M := toModule(R,C.label); + if not isSubmodule M then return false; + --check that the boundary labels are compatible + --boundary is a list of pairs where the first element is the cell + if not all(C.boundary, x -> isSubset(M,toModule(R,(x#0).label))) then return false; + --check that the boundary is a cycle in homology + isCycle(C.boundary) + ) + +isWellDefined(CellComplex) := (C) -> ( + allCells := flatten values C.cells; + containingModule := null; + for cell in allCells do ( + if ring cell.label =!= C.labelRing then return false; + if not isWellDefined cell then return false; + M := toModule(C.labelRing,cell.label); + if containingModule === null then containingModule = ambient M; + if containingModule != ambient M then return false; + ); + true + ) + +---------------------------- + +---------------------------- + + +load "./CellularResolutions/doc.m2" +load "./CellularResolutions/tests.m2" + +end diff --git a/M2/Macaulay2/packages/CellularResolutions/doc.m2 b/M2/Macaulay2/packages/CellularResolutions/doc.m2 new file mode 100644 index 00000000000..e6175f77c4d --- /dev/null +++ b/M2/Macaulay2/packages/CellularResolutions/doc.m2 @@ -0,0 +1,1505 @@ +-- This file is in the public domain + +beginDocumentation() + +doc /// + Key + CellularResolutions + Headline + A package for cellular resolutions of monomial ideals + Description + Text + This package aims to make working with cellular resolutions of + monomial ideals possible. Although the focus is on + those constructs needed to work with cellular resolutions, + the package additionally provides basic + functions to work with cell complexes. For + some direct ways to construct common cellular resolutions for + monomial ideals, see @TO taylorComplex@ and @TO hullComplex@. + Text + More generally, cell complexes can be constructed by creating cells + using @TO newCell@ or @TO newSimplexCell@, and then the maximal + cells can be provided to @TO cellComplex@ to construct a cell complex +/// + +doc /// + Key + CellComplex + Headline + the class of all cell complexes + Description + Text + A cell complex in this context is the combinatorial data of a + CW-complex, i.e. a collection of cells in various dimensions along + with their boundary expressed as a sequence of cells along with an + orientation such that the boundary is a cycle. + Caveat + Not every object represented by a @CODE "CellComplex"@ object + corresponds to a topological cell complex. In general there is no + way to check that such a topological realization exists. + SeeAlso + Cell +/// + +doc /// + Key + Cell + Headline + the class of all cells in cell complexes + Description + Text + This class represents a single cell in a cell complex. A cell has + a boundary, a dimension, and a label. In most cases, the label should + be a monomial. But the cells in this package may be anything. + However for functions such as @TO chainComplex@ to work, the labels + should be either monomials, ideals, or modules. + In the monomial case, we can view the label as a module by taking + the submodule generated by the monomial in the ring. There should be + canonical inclusions maps from the label on any cell to the labels + of the cells in its boundary. + SeeAlso + CellComplex +/// + +doc /// + Key + cellComplex + (cellComplex,Ring,List) + Headline + create a cell complex + Usage + cellComplex(R,maxCells) + Inputs + R : Ring + that specifies the base ring for interpreting the labels + maxCells : List + that specifies the maximal cells for the complex + Outputs + : CellComplex + Description + Text + The method cellComplex takes a ring R and a list of maximal cells and constructs the + corresponding cell complex. While the intent is for the supplied cells to be maximal, + including non-maximal cells is not forbidden and will generate valid cell complexes. + Text + The ring R is the ring over which the labels are interpreted. This function does not + directly check that the labels are in the correct ring. + Example + c = newCell {} + C = cellComplex(QQ,{c}); + R = QQ[x,y]; + C = cellComplex(R,{c}); + SeeAlso + newCell + newSimplexCell + isWellDefined +/// + +doc /// + Key + newCell + (newCell,List,Number) + (newCell,List,RingElement) + (newCell,List,Module) + (newCell,List,Ideal) + (newCell,List) + CellDimension + [newCell,CellDimension] + Headline + creates a new cell + Usage + newCell(boundary,label) + newCell(boundary) + Inputs + boundary : List + that gives the boundary of the new cell either as a list of pairs + of cells and their orientation, or a list of cells. + label : {Number, RingElement, Module, Ideal} + that gives a label to associate to the cell, otherwise attempt to + infer it based on the labels on the boundary + CellDimension => ZZ + that gives an explicit dimension to the cell + Outputs + : Cell + that was created + Description + Text + This function creates a new cell, to be added to a cell complex, + if given a list of cells without any orientation information, it + attempts to infer the orientation. + Text + If given an empty list for the boundary, the function creates a + 0-cell (a vertex). + Text + If not given a label, and the labels on the boundary are monomials + or monomial ideals from the same ring, then the label is the lcm + of the labels of the boundary. + Text + If the dimension cannot be directly inferred from the boundary, + CellDimension can be used to specify the true dimension + Example + R = QQ[x,y] + a = newCell({},x); + b = newCell({},y); + c1 = newCell {(a,1),(a,-1)}; + c2 = newCell {a,a}; + c3 = newCell {a,b}; + C = cellComplex(R,{c1,c2,c3}); + Caveat + This function does not check that there is a valid map from the boundary + of an n-cell to the given boundary. It only checks that the boundary + forms a cycle in homology. + SeeAlso + cellComplex + newSimplexCell +/// + +doc /// + Key + newSimplexCell + (newSimplexCell,List,Number) + (newSimplexCell,List,RingElement) + (newSimplexCell,List,Module) + (newSimplexCell,List,Ideal) + (newSimplexCell,List) + Headline + create a new cell + Usage + newSimplexCell(boundary,label) + newSimplexCell(boundary) + Inputs + boundary : List + that gives the boundary of the new cell either as a list of pairs + of cells and their orientation, or a list of cells. + label : {Number,RingElement, Module, Ideal} + that gives a label to associate to the cell, otherwise attempt to + infer it based on the labels on the boundary + Outputs + : Cell + that was created + Description + Text + This function will only create simplices, and it will verify that + the new cell is a simplex, as such does not have the caveat of + @TO newCell@. Otherwise it has the same behavior. This is + particularly useful in constructing $\Delta$-complexes. + Example + v1 = newSimplexCell {}; + v2 = newSimplexCell {}; + e1 = newSimplexCell {v1,v2}; + e2 = newSimplexCell {(v1,1),(v2,-1)}; + C = cellComplex(ZZ, {e1,e2}); + Text + One cannot use this command to create cells that are not simplices. + SeeAlso + cellComplex + newCell + isSimplex +/// + +doc /// + Key + (cellComplex,Ring,SimplicialComplex) + [(cellComplex,Ring,SimplicialComplex),Labels] + Headline + Creates a cell complex from a given simplicial complex + Usage + cellComplex(S,D) + Inputs + S : Ring + D : SimplicialComplex + from which the cell complex is created + Labels => HashTable + that maps simplices represented by monomials to labels + Outputs + : CellComplex + that matches those of the given simplicial complex + Description + Text + This returns a cellular complex whose faces are those of the given simplicial complex. + Example + R = QQ[a..f]; + I = monomialIdeal(a*f, b*d, c*e); + Delta = simplicialComplex I; + C = cellComplex(QQ,Delta) + Text + The optional parameter Label can be used to provide labels to the resulting cell complex. + By default all cells get a label of 1. + Example + S = QQ[x,y]; + H = hashTable {a => x^5, b => y*x^4, c=>y^2*x^3, d => y^3*x^2, e => y^4*x, f => x^5}; + C = cellComplex(S,Delta,Labels=>H) + applyValues(cells C, l -> apply(l,cellLabel)) + SeeAlso + cellComplex +/// + +doc /// + Key + (ring,CellComplex) + Headline + return the base ring of a cell complex + Usage + ring C + Inputs + C : CellComplex + Outputs + : Ring + the base ring of the cell complex C + Description + Text + This returns the base ring associated to a cell complex C, which is used to interpret labels. + Example + R = QQ[x,y]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y^2); + e = newSimplexCell {vx,vy}; + C = cellComplex(R,{e}); + ring(C) + SeeAlso + cellComplex +/// + +doc /// + Key + cellLabel + (cellLabel,Cell) + Headline + return the label of a cell + Usage + cellLabel C + Inputs + C : Cell + Outputs + : Thing + the label of the cell + Description + Text + The label is provided at the creation of the cell and cannot be modified, neither + this function nor any of the cell creation functions attempt to interpret the label + and while the labels generally should be monomials, this package makes no attempt + to enforce such a requirement. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x^2); + v2 = newSimplexCell({},x*y^2); + v3 = newSimplexCell {}; + e = newSimplexCell {v1,v2}; + C = cellComplex(R,{e,v3}); + cellLabel v1 + cellLabel e + cellLabel v3 + SeeAlso + Cell + newSimplexCell + newCell +/// + +doc /// + Key + (dim,CellComplex) + Headline + compute the dimension of a cell complex + Usage + dim C + Inputs + C : CellComplex + the complex to compute the dimension of + Outputs + : ZZ + the dimension of the complex + Description + Text + Given a cell complex C, dim returns its dimension. + The dimension is equal to the largest dimension of a cell in C. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + C = cellComplex(R,{exy,vz}); + dim C + SeeAlso + (dim,Cell) +/// + +doc /// + Key + (dim,Cell) + Headline + compute the dimension of a cell + Usage + dim C + Inputs + C : Cell + the cell to compute the dimension of + Outputs + : ZZ + the dimension of the cell + Description + Text + Given a cell C, dim returns its dimension. + In general the dimension of the cell is inferred from the + dimension of its boundary. However, this can be overridden + if the cell is created using @TO newCell@ with the @TO CellDimension@ + option. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + C = cellComplex(R,{exy,vz}); + dim(vz) + dim(exy) + SeeAlso + (dim,CellComplex) +/// + +doc /// + Key + cells + (cells,CellComplex) + Headline + return the cells of a cell complex as a hashtable whose keys are cell dimensions + Usage + cells(C) + Inputs + C : CellComplex + the CellComplex whose cells are to be returned + Outputs + : HashTable + the cells of C + Description + Text + Given a cell complex, this function will return all the cells in that cell complex, + index by dimension. The order of the cells in each dimension is arbitrary. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + C = cellComplex(R,{exy,vz}); + cells(C) + Example + R = QQ; + P = convexHull matrix {{1,1,-1,-1},{1,-1,1,-1}}; + C = cellComplex(R,P); + cells C + SeeAlso + (cells,ZZ,CellComplex) + +/// + +doc /// + Key + (cells,ZZ,CellComplex) + Headline + return the cells of a cell complex + Usage + cells(ZZ,C) + Inputs + r : ZZ + the dimension of the cells to be returned + C : CellComplex + whose r-cells are to be returned + Outputs + : List + the r-cells of C + Description + Text + Given a dimension r and a cell complex C, cells returns a list of all the cells + of dimension r in C. The order of the returned cells is arbitrary. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + C = cellComplex(R,{exy,vz}); + cells(0,C) + cells(1,C) + cells(2,C) + SeeAlso + (cells,CellComplex) +/// + +doc /// + Key + boundary + (boundary,Cell) + Headline + returns the boundary cells along with relative orientations + Usage + boundary(C) + Inputs + C : Cell + Outputs + : List + of two-element sequences where the first element is the boundary cell + and the second element is an integer representing the orientation of + the boundary cell relative to C + Description + Text + Given a cell C, this command returns a list whose elements are two-element sequences. + The first element of each tuple is a boundary cell of C and the second element + is the attaching degree of that boundary cell relative to C. + This differs from @TO boundaryCells@ in that it returns the boundary cells and + their corresponding attaching degree, whereas boundaryCells returns only the boundary cells. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + C = cellComplex(R,{exy,vz}); + boundary(exy) + boundary(vz) + Example + R = QQ; + P = convexHull matrix {{1,1,-1,-1},{1,-1,1,-1}}; + C = cellComplex(R,P); + f = (cells(2,C))#0; + boundary(f) + SeeAlso + (boundaryCells,Cell) +/// + +doc /// + Key + boundaryCells + (boundaryCells,Cell) + Headline + returns the boundary cells of the given cell + Usage + boundaryCells(C) + Inputs + C : Cell + whose boundary is to be returned + Outputs + : List + of the cells in the boundary of C + Description + Text + Given a cell C, this command returns a list whose elements are the boundary cells of C. + This differs from @TO boundary@ in that it returns only the boundary cells of C, + whereas boundaryCells returns a list of two-element sequences of the boundary cells and + their corresponding orientation. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + C = cellComplex(R,{exy,vz}); + boundaryCells(exy) + boundaryCells(vz) + Example + R = QQ; + P = convexHull matrix {{1,1,-1,-1},{1,-1,1,-1}}; + C = cellComplex(R,P); + f = (cells(2,C))#0; + boundaryCells(f) + SeeAlso + (boundaryMap,ZZ,CellComplex) +/// + +doc /// + Key + (boundaryMap,ZZ,CellComplex) + Headline + compute the boundary map of a cell complex from r-faces to (r-1)-faces + Usage + boundaryMap(ZZ,CellComplex) + Inputs + r : ZZ + the source cell-dimension + C : CellComplex + the CellComplex + Outputs + : Matrix + the boundary map from r-faces to (r-1)-faces of C + Description + Text + This function returns the map in the chain complex from the r-th homological degree + to the (r-1)-th homological degree. + Text + For example, below we construct the Taylor complex for the monomial ideal $\langle x,y,z\rangle$ + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell {vx,vy}; + exz = newSimplexCell {vx,vz}; + eyz = newSimplexCell {vy,vz}; + f = newSimplexCell {exy,exz,eyz}; + C = cellComplex(R,{f}); + d1 = boundaryMap_1 C + d2 = boundaryMap_2 C + assert(d1*d2==0) + SeeAlso + (chainComplex,CellComplex) + (chainComplex,SimplicialComplex) +/// + +doc /// + Key + (chainComplex,CellComplex) + Reduced + [(chainComplex,CellComplex),Reduced] + Prune + [(chainComplex,CellComplex),Prune] + Headline + compute the cellular chain complex for a cell complex + Usage + chainComplex C + Inputs + C : CellComplex + the cell complex for which to compute the chain complex + Reduced => Boolean + that controls whether the augmented chain complexes is used + Prune => Boolean + that controls whether the modules in the chain complex are pruned + Outputs + : ChainComplex + the dimension of the complex + Description + Text + This constructs the cellular chain complex for a cell complex, + taking into account the labels on the cells. The resulting + cell complex will be a complex of modules over the ring + associated to the cell complex. + By default, the option "Reduced" is set to true, so + the resulting ChainComplex has a rank 1 free module in homological degree -1. + Example + R = QQ[x] + a = newSimplexCell({},x); + b1 = newCell {a,a}; + b2 = newCell {a,a}; + C = cellComplex(R,{b1,b2}); + chainComplex C + chainComplex(C,Reduced=>false) + Text + For details see Combinatorial Commutative Algebra Section 4.1. + If we restrict to the case of monomial labels, then, subject to + some acyclicity conditions, the resulting complex will be + (up to a shift by 1 in homological degree) a resolution of $S/I$ where $I$ is + the ideal generated by the labels of the vertices. + With the "Reduced" option set to false, the resulting complex will + be a resolution of the ideal $I$ as a module. + Example + R = QQ[x,y,z]; + I = ideal(x,y,z); + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + exy = newSimplexCell({vx,vy}); + exz = newSimplexCell({vx,vz}); + eyz = newSimplexCell({vy,vz}); + f = newSimplexCell({exy,exz,eyz}); + C = cellComplex(R,{f}); + betti chainComplex(C)[-1] + assert(betti chainComplex(C)[-1] == betti res I); + Text + The option "Prune," also defaulted to true, controls whether the modules in the complex + are pruned before being returned. With the "Prune" option set to the default of true, + the resulting complex is visually nicer. However, unless the labeling ring is + fine graded, some of the structure will be lost. + Example + R = QQ[x,y] + a = newSimplexCell({},x); + b = newSimplexCell({},y); + e = newCell {a,b}; + C = cellComplex(R,{e}); + chainComplex C + chainComplex(C,Prune=>false) + SeeAlso + (boundaryMap,ZZ,CellComplex) + (chainComplex,SimplicialComplex) + (homology,CellComplex) + (homology,ZZ,CellComplex) + (cohomology,ZZ,CellComplex) +/// + +doc /// + Key + (homology,CellComplex) + Headline + compute the homology modules of a cell complex + Usage + homology(C) + Inputs + C : CellComplex + with labels in ring(C) + Outputs + : GradedModule + the graded module, the homology of C with coefficients in ring(C) + Description + Text + This computes the reduced homology of the cellular complex arising from the labeled cell complex C. + Text + If the labels are all 1, then this will be the standard homology of the cell complex over + the label ring, as in the following example. + Example + R = QQ[x] + a = newSimplexCell({},1); + b1 = newCell {a,a}; + b2 = newCell {a,a}; + C = cellComplex(R,{b1,b2}); + HH C + prune oo + Text + However if the cells instead labeled with monomials (or monomial ideals) from the ring + the homology of the corresponding complex of R modules is given. + Example + R = QQ[x] + a = newSimplexCell({},x); + b1 = newCell {a,a}; + b2 = newCell {a,a}; + C = cellComplex(R,{b1,b2}); + HH C + prune oo + SeeAlso + (homology,ZZ,CellComplex) + (chainComplex,CellComplex) +/// + +doc /// + Key + (homology,ZZ,CellComplex) + Headline + compute the homology modules of a cell complex + Usage + homology(r,C) + Inputs + r : ZZ + an integer + C : CellComplex + with labels in ring(C) + Outputs + : Module + the r-th homology module of C with coefficients in ring(C) + Description + Text + This computes the reduced homology in degree r of the cellular complex arising from the cell complex C. + For more details on the labels, see @TO (homology,CellComplex)@. As an example, we can + compute the 0-th and 1st homology of a wedge of two circles. + Example + R = QQ + a = newSimplexCell({},1); + b1 = newCell {a,a}; + b2 = newCell {a,a}; + C = cellComplex(R,{b1,b2}); + homology(0,C) + homology(1,C) + prune oo + Text + We can make this example slightly more interesting by changing the label ring and + adding a non-unit label. Note in particular that this has a non-zero 0-th homology. + Example + R = QQ[x] + a = newSimplexCell({},x); + b1 = newCell {a,a}; + b2 = newCell {a,a}; + C = cellComplex(R,{b1,b2}); + homology(0,C) + prune oo + homology(1,C) + prune oo + SeeAlso + (homology,CellComplex) + (cohomology,ZZ,CellComplex) + (chainComplex,CellComplex) +/// + +doc /// + Key + (cohomology,ZZ,CellComplex) + Headline + cohomology of a cell complex + Usage + cohomology(r,C) + Inputs + r : ZZ + a non-negative integer + C : CellComplex + Outputs + : Module + the r-th cohomology module of C + Description + Text + This computes the reduced cohomology in degree r of the labeled cell complex. + In particular, it constructs the co-chain complex by dualizing by + the label ring, and takes the homology of that chain complex. As an example + we can compute the cohomology of the wedge of two circles. + Example + R = QQ[x] + a = newSimplexCell({},x); + b1 = newCell {a,a}; + b2 = newCell {a,a}; + C = cellComplex(R,{b1,b2}); + cohomology(-1,C) + cohomology(0,C) + cohomology(1,C) + Text + Or in a more interesting case, we have the cohomology over the integers of + $\mathbb{RP}^3$. + Example + C = cellComplexRPn(ZZ,3); + cohomology(0,C) + cohomology(1,C) + cohomology(2,C) + cohomology(3,C) + SeeAlso + (homology,CellComplex) + (homology,ZZ,CellComplex) + (chainComplex,CellComplex) +/// + +doc /// + Key + isMinimal + (isMinimal,CellComplex) + Headline + check if a labeled cell complex supports a minimal resolution + Usage + isMinimal C + Inputs + C : CellComplex + Outputs + : Boolean + true if the cellular resolution supported on C is minimal + false otherwise + Description + Text + This determines whether the cell complex C supports a minimal free resolution of the monomialIdeal I generated by the labels of the vertices of C. + Note: we assume the cell complex is free. + Example + R = QQ[x,y,z]; + v1 = newCell({},x^2*y); + v2 = newCell({},y*z); + v3 = newCell({},z^3); + e12 = newCell({v1,v2}); + e13 = newCell({v1,v3}); + e23 = newCell({v2,v3}); + f123 = newCell({e12,e13,e23}); + C = cellComplex(R,{e12,e23}); + isMinimal C + D = cellComplex(R,{f123}); + isMinimal D + Caveat + This function makes no attempt to check that the cell complex gives a resolution or + that the complex is a complex of free modules. + SeeAlso + (isFree,CellComplex) +/// + +doc /// + Key + isSimplex + (isSimplex,Cell) + Headline + check if a cell is a simplex + Usage + isSimplex C + Inputs + C : Cell + a cell + Outputs + : Boolean + true if the cell C is a simplex + false otherwise + Description + Text + This determines whether a cell C is a simplex. + A simplex in this context is a cell of dimension $d$ with a boundary + containing exactly $d+1$ dimension $d-1$ cells + Example + v1 = newCell {}; + v2 = newCell {}; + e1 = newCell {v1,v2}; + isSimplex e1 + e2 = newCell {v1,v1}; + isSimplex e2 +/// + +doc /// + Key + isCycle + (isCycle,List) + Headline + checks if a list of cells with orientation make a cycle + Usage + isCycle L + Inputs + L : List + whose entries are pairs, whose first entry is a cell and whose second entry is a degree + Outputs + : Boolean + true if the given cell-degree pairs form a cycle + false otherwise + Description + Text + This determines whether the given pairs, whose first entry is a cell and + whose second entry is its multiplicity, form a cycle in the homological sense. + Example + R = QQ[x,y,z]; + vx = newSimplexCell({},x); + vy = newSimplexCell({},y); + vz = newSimplexCell({},z); + lxy = newSimplexCell({vx,vy}); + lyz = newSimplexCell({vy,vz}); + lxz = newSimplexCell({vx,vz}); + assert(isCycle {(lxy,1)} == false); + assert(isCycle {{lxy,1},{lyz,1},{lxz,-1}} == true); + assert(isCycle {{lxy,1},{lyz,1},{lxz,1}} == false); +/// + +doc /// + Key + (facePoset,CellComplex) + Headline + generates the face poset of a cell complex + Usage + facePoset C + Inputs + C : CellComplex + Outputs + : Poset + the face poset of C + Description + Text + The face poset of a cell complex is the poset of cells with partial ordering given by inclusion. + The example below constructs the square as a cellular complex and returns the face poset. + Example + R = QQ; + v1 = newCell {}; + v2 = newCell {}; + v3 = newCell {}; + v4 = newCell {}; + e12 = newCell({v1,v2}); + e23 = newCell({v2,v3}); + e34 = newCell({v3,v4}); + e41 = newCell({v4,v1}); + f = newCell({e12,e23,e34,e41}); + C = cellComplex(R,{f}); + facePoset C +/// + +doc /// + Key + (skeleton,ZZ,CellComplex) + Headline + computes the $r$-skeleton of a cell complex + Usage + skeleton(ZZ,CellComplex) + Inputs + r : ZZ + the dimension of the skeleton + C : CellComplex + which to take the skeleton of + Outputs + : CellComplex + the $r$-skeleton of the cell complex $C$ + Description + Text + The $r$-skeleton of a cell complex is the union of its cells whose dimension is at most $r$. + Example + R = QQ[x]; + P = hypercube 3; + C = cellComplex(R,P); + dim C + cells C + S = skeleton(2,C); + dim S + cells S +/// + +doc /// + Key + isFree + (isFree,CellComplex) + Headline + checks if the labels of a cell complex are free modules + Usage + isFree(CellComplex) + Inputs + C : CellComplex + Outputs + : Boolean + true if all modules associated to the cell labels are free + Description + Text + This command checks if the modules associated to the labels in the cell complex are all free, + which is necessary for the complex to give a free resolution. + Text + A monomial label always gives a free module, since the module for a monomial label + is the free module generated by that monomial. + Text + Additionally, this function does not simply apply @TO (isFreeModule,Module)@ to every label, + as in general, the labels are given as submodules, and so @TO (isFreeModule,Module)@ would + return false in nearly all cases. Instead the labels are first pruned prior to calling + @TO (isFreeModule,Module)@. + Example + R = QQ[x,y,z]; + v1 = newCell({},ideal(x,y)); + C1 = cellComplex(R,{v1}); + isFree C1 + v2 = newCell({},x*y); + C2 = cellComplex(R,{v2}); + isFree C2 +/// + +doc /// + Key + (cellComplex,Ring,Polyhedron) + [(cellComplex,Ring,Polyhedron),Labels] + Headline + creates cell complex from given polyhedron + Usage + cellComplex(Ring,Polyhedron) + Inputs + R : Ring + that specifies the base ring + P : Polyhedron + Labels => HashTable + that maps vertices in the polyhedron to labels + Outputs + : CellComplex + whose cells are the faces of the given polyhedron P + Description + Text + Given a polyhedron, this command returns the cell complex whose + cells correspond to the faces of the polyhedron. The faces have + the default label 1. + Example + R = QQ; + P = convexHull matrix {{1,1,-1,-1},{1,-1,1,-1}}; + faces P + C = cellComplex(R,P); + cells C + Text + The labels on the vertices can be controlled via the optional parameter Labels + This parameter expects a hash table whose keys are vectors corresponding to the vertices of the polyhedron with desired labels as corresponding values. + Example + S = QQ[x,y,z,w]; + v = vertices P; + H = hashTable {v_0 => x*y, v_1 => y*z, v_2 => x*w, v_3 => y*z}; + labeledC = cellComplex(S, P, Labels => H); + for i to dim labeledC list cells(i,labeledC)/cellLabel + SeeAlso + (cellComplex,Ring,PolyhedralComplex) +/// + +doc /// + Key + (cellComplex,Ring,PolyhedralComplex) + [(cellComplex,Ring,PolyhedralComplex),Labels] + Headline + creates cell complex from given polyhedral complex + Usage + cellComplex(Ring,PolyhedralComplex) + Inputs + R : Ring + that specifies the base ring + P : PolyhedralComplex + Labels => HashTable + that maps vertices in the polyhedron to labels + Outputs + : CellComplex + whose cells are the faces of the given polyhedral complex + Description + Text + Given a polyhedral complex, this command returns the + cell complex whose cells correspond to the faces of the polyhedral + complex. The faces have the default label 1. + Example + R = QQ[x]; + P1 = convexHull matrix {{2,2,0},{1,-1,0}}; + P2 = convexHull matrix {{2,-2,0},{1,1,0}}; + P3 = convexHull matrix {{-2,-2,0},{1,-1,0}}; + P4 = convexHull matrix {{-2,2,0},{-1,-1,0}}; + F = polyhedralComplex {P1,P2,P3,P4}; + C = cellComplex(R,F); + facePoset C + Text + The labels on the vertices can be controlled via the optional parameter Labels + This parameter expects a hash table whose keys are vectors corresponding to the vertices of the polyhedron with desired labels as corresponding values. + SeeAlso + (cellComplex,Ring,Polyhedron) +/// + +doc /// + Key + relabelCellComplex + (relabelCellComplex,CellComplex,HashTable) + InferLabels + [relabelCellComplex,InferLabels] + Headline + relabels a cell complex + Usage + relabelCellComplex(C,H) + Inputs + C : CellComplex + H : HashTable + whose keys are cells of C and whose corresponding value is the cell's new label + InferLabels => Boolean + Outputs + : CellComplex + whose cells are relabeled by the values in the hashtable H + Description + Text + Given a cell complex C and a hashtable, whose key-value pairs + are a cell from C and a new label for that cell, this command + relabels C accordingly. Labels for cells not provided in the + hashtable are inferred to be the least common multiple of the labels + of their boundary cells, unless the option InferLabels is set to false. + Example + R = QQ[a,b,c]; + P1 = convexHull matrix {{0,1,0},{0,0,1}}; + P2 = convexHull matrix {{1,0,1},{0,1,1}}; + P = polyhedralComplex {P1,P2}; + C = cellComplex(R,P); + verts = cells(0,C); + v0 = verts#0; + v1 = verts#1; + v2 = verts#2; + v3 = verts#3; + T = new HashTable from {v0 => a^2*b, v1 => b*c^2, v2 => b^2, v3 => a*c}; + relabeledC = relabelCellComplex(C,T); + for c in cells(0,relabeledC) list cellLabel(c) + for c in cells(1,relabeledC) list cellLabel(c) + SeeAlso + cellLabel + (symbol **,RingMap,CellComplex) +/// + +doc /// + Key + (symbol **,RingMap,CellComplex) + Headline + tensors labels via a ring map + Usage + f ** C + Inputs + f : RingMap + C : CellComplex + Outputs + : CellComplex + whose cells labels are given by taking the tensor product by the ring map f + Description + Text + Given a ring map f and a cell complex C, then for each label, viewed as a module M, + this function constructs a cell complex whose new labels are f ** M + Example + S = QQ[x,y,z]; + R = QQ[a,b,c]; + f = map(R,S,matrix{{a,b,c^2}}); + v1 = newCell({},x); + v2 = newCell({},y); + v3 = newCell({},z); + e12 = newCell({v1,v2}); + e23 = newCell({v2,v3}); + C = cellComplex(S,{e12,e23}); + cells(1,C)/cellLabel + D = f ** C; + cells(1,D)/cellLabel + SeeAlso + relabelCellComplex +/// + +doc /// + Key + cellComplexSphere + (cellComplexSphere,Ring,ZZ) + Headline + gives a sphere as a cell complex + Usage + cellComplexSphere(R,n) + Inputs + R : Ring + that specifies the base ring + n : ZZ + that specifies the dimension of the sphere + Outputs + : CellComplex + an n-dimensional sphere + Description + Text + This function constructs an n-dimensional sphere in the typical way for a CW-complex: + a single n-dimensional cell attached to a single 0-dimensional cell. + Example + S = cellComplexSphere(QQ,3) + cells(S) + chainComplex S + prune homology S + SeeAlso + cellComplexRPn + cellComplexTorus +/// + +doc /// + Key + cellComplexRPn + (cellComplexRPn,Ring,ZZ) + Headline + gives a $RP^n$ as a cell complex + Usage + cellComplexRPn(R,n) + Inputs + R : Ring + that specifies the base ring + n : ZZ + that specifies the dimension of the projective space + Outputs + : CellComplex + representing n-dimensional projective space + Description + Text + This function constructs n-dimensional projective space as a cell complex with + the typical CW-structure: a single cell of each dimension, where each r-cell + is attached as a 2-sheeted covering to the (r-1)-cell. + Example + QP5 = cellComplexRPn(QQ,5) + prune homology QP5 + ZP6 = cellComplexRPn(ZZ,6) + prune homology ZP6 + SeeAlso + cellComplexSphere + cellComplexTorus +/// + +doc /// + Key + cellComplexTorus + (cellComplexTorus,Ring,ZZ) + Headline + gives a torus as a cell complex + Usage + cellComplexTorus(R,n) + Inputs + R : Ring + that specifies the base ring + n : ZZ + that specifies the dimension of the torus + Outputs + : CellComplex + the n-dimensional torus + Description + Text + This function returns the n-dimensional torus as a cell complex in the usual way: + the product of n copies of $S^1$. + Example + T3 = cellComplexTorus(QQ,3) + cells(T3) + prune homology T3 + SeeAlso + cellComplexSphere + cellComplexRPn +/// + + +doc /// + Key + hullComplex + (hullComplex,QQ,MonomialIdeal) + (hullComplex,ZZ,MonomialIdeal) + (hullComplex,MonomialIdeal) + Headline + gives the hull complex of a monomial ideal + Usage + hullComplex I + hullComplex(t,I) + Inputs + t : {QQ,ZZ} + I : MonomialIdeal + Outputs + : CellComplex + the hull complex of $I$ as described in Bayer-Sturmfels' ``Cellular Resolutions of Monomial Modules'' + Description + Text + Given a monomial ideal $I$, this function returns the hull complex of that ideal. + If an rational number $t$ is provided, this will set the base used in + the exponents used to construct the polytope as described in + ``Combinatorial Commutative Algebra.'' The resulting complex is only a + resolution for $t\gg 0$. In particular $t > (n+1)!$ is sufficient where + $n$ is the number of variables in the ring. If t is not provided, + $(n+1)!+1$ will be used. + Text + The example given below can be found as Example 4.23 in Miller-Sturmfels' + ``Combinatorial Commutative Algebra.'' + In this example, the resolution supported on the hull complex is minimal, + but this is not always the case. We also see that for $t=3/2$ the resulting + complex is no longer a resolution. + Example + S = QQ[x,y,z]; + I = monomialIdeal (x^2*z, x*y*z, y^2*z, x^3*y^5, x^4*y^4, x^5*y^3); + H = hullComplex I + chainComplex H + cells(1,H)/cellLabel + cells(2,H)/cellLabel + isMinimal H + H2 = hullComplex (3/2,I) + prune HH chainComplex H2 + SeeAlso + (taylorComplex,MonomialIdeal) +/// + + +doc /// + Key + taylorComplex + (taylorComplex,MonomialIdeal) + Headline + gives the Taylor complex of a monomial ideal + Usage + taylorComplex I + Inputs + I : MonomialIdeal + Outputs + : CellComplex + the Taylor complex of $I$ + Description + Text + Given a monomial ideal I, this function returns the Taylor complex of that ideal. + Recall that the Taylor complex is a simplex with vertices labeled by the generators + of the ideal. + Example + S = QQ[x,y,z]; + I = monomialIdeal (x^2, y^2, z^2); + T = taylorComplex I + C = chainComplex T + C.dd + SeeAlso + (hullComplex,MonomialIdeal) +/// + +doc /// + Key + scarfComplex + (scarfComplex,MonomialIdeal) + Headline + gives the hull complex of a monomial ideal + Usage + scarfComplex I + Inputs + I : MonomialIdeal + Outputs + : CellComplex + the scarf complex of $I$ + Description + Text + Given a monomial ideal $I$, this function returns the Scarf complex of that ideal. + This complex, which is a subcomplex of the Taylor complex, + if it is a resolution is always minimal, but it need not be a resolution + in general. + Example + S = QQ[x,y,z]; + I = monomialIdeal (x^2*z, x*y*z, y^2*z, x^3*y^5, x^4*y^4, x^5*y^3); + C = scarfComplex I + chainComplex C + cells(1,C)/cellLabel + cells(2,C)/cellLabel + isMinimal C + SeeAlso + (taylorComplex,MonomialIdeal) + (hullComplex,MonomialIdeal) +/// + + +doc /// + Key + maxCells + (maxCells,CellComplex) + Headline + gives the maximal cells of a cell complex + Usage + maxCells C + Inputs + C : CellComplex + Outputs + : HashTable + whose keys are the dimensions of the maximal cells of C and whose + respective values are a list of the maximal cells of that dimension + Description + Text + Given a cell complex C, this function returns a hash table containing the maximal cells + with respect to containment, sorted by dimension + Example + S = QQ[x,y,z]; + v1 = newCell({},x); + v2 = newCell({},y); + v3 = newCell({},z); + e = newCell({v1,v2}); + C = cellComplex(S,{e,v3}); + maxCells C + SeeAlso + (cells,CellComplex) +/// + +doc /// + Key + (isWellDefined,Cell) + Headline + checks if a cell is well defined + Usage + isWellDefined C + Inputs + C : Cell + Outputs + : Boolean + Description + Text + This function checks if a cell is well defined in the following sense. + First, the boundary must form a cycle in homology. Second, the labels + on the boundary cells when interpreted as modules must be submodules + of the label of the input cell, again interpreted as a module. Finally + the label itself must be a submodule (not necessarily proper). + Text + Ring element labels are interpreted as the module of the principal ideal + generated by the element in the ring. Importantly, if all labels are + ring elements then the condition is simply that the labels on the boundary + should divide the labels on the current cell. + Text + In the following example, the cell is not well defined because + the labels of the cells in the boundary of e do not all divide the label + on e. Each of the vertices v1 and v2 are well defined. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},y); + e = newSimplexCell({v1,v2},x); + isWellDefined v1 + isWellDefined v2 + isWellDefined e + Text + Note however that extending this example, this function does not check if the cells + in the boundary are themselves well defined. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},y); + e1 = newSimplexCell({v1,v2},x); + e2 = newSimplexCell({v1,v2},y); + f = newCell({(e1,1),(e2,-1)},x*y); + isWellDefined e1 + isWellDefined e2 + isWellDefined f + SeeAlso + (isWellDefined,CellComplex) + Caveat + Text + This function does not check that the cells contained in the boundary are + themselves well defined. For checking if a whole cell complex is + well defined, see @TO (isWellDefined,CellComplex)@. +/// + +doc /// + Key + (isWellDefined,CellComplex) + Headline + checks if a cell complex is well defined + Usage + isWellDefined C + Inputs + C : CellComplex + Outputs + : Boolean + Description + Text + This function checks two conditions. First, it checks that all cells + in the cell complex are well defined in the sense of + @TO (isWellDefined,Cell)@. Second, it checks that all of the labels + in the cell complex are in a common ambient module. + Text + The following example demonstrates that unlike @TO (isWellDefined,Cell)@, + cells at all dimensions are checked, and not just the cells in the boundary. + The cell complex in the example is not well defined since the edges + have labels that aren't divisible by the vertices in their boundary. + Example + R = QQ[x,y]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},y); + e1 = newSimplexCell({v1,v2},x); + e2 = newSimplexCell({v1,v2},y); + f = newCell({(e1,1),(e2,-1)},x*y); + isWellDefined f + isWellDefined cellComplex(R,{f}) + Text + Another important way that cell complexes can fail to be well defined is + if they have labels from different rings + Example + R = QQ[x,y]; + S = ZZ[a,b]; + v1 = newSimplexCell({},x); + v2 = newSimplexCell({},a); + isWellDefined cellComplex(R,{v1,v2}) + SeeAlso + (isWellDefined,Cell) + Caveat + This function does not check that the cell complex corresponds to + a topologically realizable cell complex. +/// + + +doc /// + Key + subcomplex + LabelRing + [subcomplex,LabelRing] + (subcomplex,CellComplex,RingElement) + (subcomplex,CellComplex,List) + (subcomplex,CellComplex,ZZ) + (symbol _,CellComplex,RingElement) + (symbol _,CellComplex,List) + (symbol _,CellComplex,ZZ) + Headline + the subcomplex induced by a degree or monomial + Usage + subcomplex(C,m) + subcomplex(C,d) + C_m + C_d + Inputs + C : CellComplex + m : RingElement + a monomial + d : {ZZ,List} + a degree + LabelRing => Ring + for the labels on the output + Outputs + : CellComplex + Description + Text + This function computes the subcomplex of the input cell complex induced + by a monomial or a degree. The returned cell complex will be labeled + over the coefficient ring of the label ring of the original cell complex, + or the ring given by the LabelRing optional parameter if provided. + Text + When given a monomial, the function expects all labels to be monomials, + ideals, or submodules of the ring associated to the cell complex. In that case, + the returned cell complex will be the subcomplex of the original complex containing + the cells whose labels either divide or contain the provided monomial. + The labels on the cells in the returned cell complexes will be 1. + Example + R = QQ[x,y,z]; + C = taylorComplex monomialIdeal {x,y,z} + subcomplex(C,x*y) + subcomplex(C,x*y,LabelRing=>R) + Text + When given a degree, the function will use basis to find the appropriate free module + over the new label ring representing the elements of that degree in the module + associated to the label + Example + subcomplex(C,1) + Text + In general, the degree version is intended to be used with fine-graded polynomial rings + Example + S = QQ[a,b,c,DegreeRank=>3]; + D = taylorComplex monomialIdeal {a,b,c} + subcomplex(D,{1,1,0}) +/// diff --git a/M2/Macaulay2/packages/CellularResolutions/tests.m2 b/M2/Macaulay2/packages/CellularResolutions/tests.m2 new file mode 100644 index 00000000000..1e17a9107e8 --- /dev/null +++ b/M2/Macaulay2/packages/CellularResolutions/tests.m2 @@ -0,0 +1,521 @@ +-- This file is in the public domain + +TEST /// +e = cellComplex(QQ,{}); +assert(isWellDefined e); +assert(dim e === -infinity); +assert(#maxCells e == 0); +assert(#maxCells skeleton(0,e) == 0); +chainComplex e; +/// + + +TEST /// +v1 = newSimplexCell {}; +v2 = newSimplexCell {}; +assert(isSimplex v1); +assert(isSimplex v2); +assert(dim v1===0); +assert(dim v2===0); +assert(v1 =!= v2); +assert(cellLabel v1 === 1) +l1 = newSimplexCell {(v1,1),(v2,-1)}; +l2 = newSimplexCell {v1,v2}; +assert(isSimplex l1); +assert(isSimplex l2); +assert(l1=!=l2); +assert(dim l1==1); +assert(dim l2==1); +C = cellComplex(QQ,{l1,l2}); +CchainComplex = chainComplex C; +assert(HH_0(CchainComplex)==0); +assert(prune HH_1(CchainComplex)==QQ^1); +assert(HH_2(CchainComplex)==0); +f1 = newCell {l1,l2}; +C = cellComplex(QQ,{f1}); +assert(dim C==2); +assert(dim f1==2); +assert(cellLabel f1 == 1); +del0 = boundaryMap(0,C); +del1 = boundaryMap(1,C); +del2 = boundaryMap(2,C); +del100 = boundaryMap(100,C); +assert(del0 == map(QQ^1,QQ^2, {{1,1}})); +assert(rank source del1 == 2); +assert(rank target del1 == 2); +assert(rank del1 == 1); +assert(rank source del2 == 1); +assert(rank target del2 == 2); +assert(rank del2 == 1); +assert(del100 == map(QQ^0,QQ^0,{})); +CchainComplex = chainComplex C; +assert(HH_0(CchainComplex)==0); +assert(HH_1(CchainComplex)==0); +assert(HH_2(CchainComplex)==0); +assert(HH C == HH CchainComplex); +assert(isFree(C)); +/// + +-- RP2 +TEST /// +v = newCell {}; +l = newCell {v,v}; +f = newCell {(l,1),(l,1)}; +C = cellComplex(ZZ,{f}); +assert(dim C===2); +prune HH chainComplex C +assert(HH_0 C == 0); +assert(homology(0,chainComplex(C,Reduced=>false))==ZZ^1); +assert(homology(1,chainComplex(C,Reduced=>false))==ZZ^1/(2*ZZ^1)); +assert(HH_1 chainComplex C == cokernel matrix {{2}}) +assert(HH^2 C == cokernel matrix {{2}}); +assert(HH^1 C == 0); +assert(dim skeleton_1 C == 1); +/// + +TEST /// +a = newCell {}; +b1 = newCell {(a,1),(a,-1)}; +b2 = newCell {(a,1),(a,-1)}; +D = cellComplex(QQ[x],{b1,b2}); +assert(dim D == 1); +assert(isSimplex a); +assert(not isSimplex b1); +assert(not isSimplex b2); +DchainComplex = chainComplex D; +assert(HH_0(DchainComplex)==0); +R = ring D; +assert(prune HH_1(DchainComplex)==R^2); +assert(HH_2(DchainComplex)==0); +/// + + +TEST /// +R = QQ[w,x,y,z] +C = simplicialComplex monomialIdeal(w*x,w*y); +dim C +D = cellComplex(QQ,C); +D +assert(dim D==2); +assert(#cells(2,D)==1); +assert(#cells(1,D)==4); +assert(#cells(0,D)==4); +assert(#cells(-1,D)==0); +/// + +TEST /// +S = QQ[x,y,z]; +R = QQ[a,b,c]; +f = map(R,S,matrix{{a,b,c^2}}); +v1 = newCell({},x); +v2 = newCell({},y); +v3 = newCell({},z); +e12 = newCell({v1,v2}); +e23 = newCell({v2,v3}); +C = cellComplex(S,{e12,e23}); +assert(ring C === S); +D = f ** C; +assert(ring D === R); +assert(#cells(1,D) == #cells(1,C)); +assert(set (cells(1,D)/cellLabel) === set {a*b, b*c^2}); +/// + + +--Koszul Complex via Taylor resolutions +TEST /// +R = QQ[x,y,z]; +vx = newSimplexCell({},x); +vy = newSimplexCell({},y); +vz = newSimplexCell({},z); +lxy = newSimplexCell({vx,vy}); +lyz = newSimplexCell({vy,vz}); +lxz = newSimplexCell({vx,vz}); +fxyz = newSimplexCell({lxy,lyz,lxz}); +assert(cellLabel fxyz === x*y*z); +D = cellComplex(R,{fxyz}); +C = (chainComplex D); +assert(HH_(-1)(C)==cokernel matrix {{x,y,z}}); +assert(C.dd^2==0); +assert(degrees C_0 == {{1}, {1}, {1}}); +/// + +--Monomial ideal labels +TEST /// +R = QQ[x,y,z]; +vx = newSimplexCell({},ideal(x)); +vy = newSimplexCell({},ideal(y)); +vz = newSimplexCell({},ideal(z)); +lxy = newSimplexCell({vx,vy}); +lyz = newSimplexCell({vy,vz}); +lxz = newSimplexCell({vx,vz}); +fxyz = newSimplexCell({lxy,lyz,lxz}); +D = cellComplex(R,{fxyz}); +C = (chainComplex D)[-1]; +assert(HH_0(C)==R^1/module ideal(x,y,z)) +assert(HH_1(C)==0) +assert(C.dd^2==0); +/// + +--Non principal labels +TEST /// +R = QQ[x,y,z]; +vx = newSimplexCell({},ideal(x,y)); +vy = newSimplexCell({},ideal(y,z)); +vz = newSimplexCell({},ideal(x,z)); +lxy = newSimplexCell {vx,vy}; +lyz = newSimplexCell {vy,vz}; +lxz = newSimplexCell {vx,vz}; +fxyz = newSimplexCell {lxy,lyz,lxz}; +D = cellComplex(R,{fxyz}); +C = (chainComplex D)[-1]; +assert(C.dd^2==0); +assert(not isFree(D)); +/// + +--Relabel test +TEST /// +R = QQ[a,b,c]; +v1 = newSimplexCell {}; +v2 = newSimplexCell {}; +v3 = newSimplexCell {}; +v4 = newSimplexCell {}; +e12 = newSimplexCell {v1,v2}; +e23 = newSimplexCell {v2,v3}; +e34 = newSimplexCell {v3,v4}; +e14 = newSimplexCell {v1,v4}; +e24 = newSimplexCell {v2,v4}; +f124 = newSimplexCell {e12,e24,e14}; +f234 = newSimplexCell {e23,e34,e24}; +C = cellComplex(R,{f124,f234}); +T = new HashTable from {v1 => a^2*b, v2 => a*c, v3 => b*c^2, v4 => b^2}; +D = relabelCellComplex(C,T); +assert(HH_(-1) D == R^1/ideal(a^2*b,a*c,b*c^2,b^2)); +assert(HH_0 D == 0); +assert(HH_1 D == 0); +assert(HH_2 D == 0); +labelsD2 = for c in cells(2,D) list cellLabel(c); +assert(set labelsD2 === set {a^2*b^2*c, a*b^2*c^2}); +/// + +--Polytope test 1 +TEST /// +R = QQ; +P = hypercube 3; +C = cellComplex(R,P); +assert(dim C==3); +assert(# cells(0,C)==8); +assert(# cells(1,C)==12); +assert(# cells(2,C)==6); +assert(# cells(3,C)==1); +assert(HH_1(C)==0); +assert(HH_2(C)==0); +assert(HH_3(C)==0); +assert((chainComplex C).dd^2==0); +C1 = skeleton(1,C); +assert(dim C1 == 1); +assert(rank HH_1 C1 == 5); +assert(rank HH_2 C1 == 0); +C2 = skeleton(2,C); +assert(dim C2 == 2); +assert(HH_1 C2 == 0); +assert(rank HH_2 C2 == 1); +/// + +--Polytope test 2 +TEST /// +R = QQ[x]; +M = transpose matrix {{0,0},{1,0},{2,1},{2,2},{1,2},{0,1}}; -- this is a hexagon +P = convexHull M; +C = cellComplex(R,P); +assert(dim C==2); +assert(# cells(0,C)==6); +assert(# cells(1,C)==6); +assert(# cells(2,C)==1); +assert(# cells(3,C)==0); +for i to 3 do assert(HH_i C==0); +C1 = skeleton(1,C); +assert(rank HH_1 C1 == 1); +/// + +--Polytope test 3 +TEST /// +R = ZZ[x]; +M = transpose matrix {{0,0,0},{0,1,0},{0,0,1},{1,0,0},{1,1,0},{1,0,1}}; +P = convexHull M; +C = cellComplex(R,P); +assert(dim C==3); +assert(# cells(0,C)==6); +assert(# cells(1,C)==9); +assert(# cells(2,C)==5); +assert(# cells(3,C)==1); +assert(# cells(4,C)==0); +/// + +--Polytope test 4 (LabelFunction) test +TEST /// +R = ZZ[x,y,z]; +M = transpose matrix {{0,0,0},{0,1,0},{0,0,1},{1,0,0},{1,1,0},{1,0,1}}; +P = convexHull M; +V = vertices P; +H = hashTable apply(numColumns V, i -> (v := V_i;(v,x^(lift(v_0,ZZ))*y^(lift(v_1,ZZ))*z^(lift(v_2,ZZ))))); +C = cellComplex(R,P,Labels => H); +assert(dim C==3); +assert(# cells(0,C)==6); +assert(# cells(1,C)==9); +assert(# cells(2,C)==5); +assert(# cells(3,C)==1); +assert(# cells(4,C)==0); +assert(sort apply(cells(0,C),cellLabel) == sort {x*z,x*y,x,z,y,1}); +/// + +--Polyhedral complex test 1 +TEST /// +R = QQ[x]; +P1 = convexHull matrix {{2,2,0},{1,-1,0}}; +P2 = convexHull matrix {{2,-2,0},{1,1,0}}; +P3 = convexHull matrix {{-2,-2,0},{1,-1,0}}; +P4 = convexHull matrix {{-2,2,0},{-1,-1,0}}; +F = polyhedralComplex {P1,P2,P3,P4}; +C = cellComplex(R,F); +assert(# cells(0,C)==5); +assert(# cells(1,C)==8); +assert(# cells(2,C)==4); +assert(# cells(3,C)==0); +for i to 2 do assert(HH_i C==0); +C1 = skeleton(1,C); +assert(rank HH_1 C1 == 4); +/// + + +--Polyhedral complex test 2 +TEST /// +R = frac(ZZ[x,y]); +P1 = convexHull matrix {{2,2,0},{1,-1,0}}; +P2 = convexHull matrix {{2,-2,0},{1,1,0}}; +P3 = convexHull matrix {{-2,-2,0},{1,-1,0}}; +P4 = convexHull matrix {{-2,2,0},{-1,-1,0}}; +F = polyhedralComplex {P1,P2,P3,P4}; +V = vertices F; +H = hashTable apply(numColumns V, i -> (v := V_i;(v,x^(lift(v_0,ZZ))*y^(lift(v_1,ZZ))))); +C = cellComplex(R,F,Labels=>H); +assert(# cells(0,C)==5); +assert(# cells(1,C)==8); +assert(# cells(2,C)==4); +assert(# cells(3,C)==0); +assert(sort apply(cells(0,C),cellLabel) == sort {x^2*y,x^2/y,1,y/x^2,1/(x^2*y)}); +/// + + +--Face poset +TEST /// +R = QQ; +v1 = newCell {}; +v2 = newCell {}; +v3 = newCell {}; +v4 = newCell {}; +e12 = newCell({v1,v2}); +e23 = newCell({v2,v3}); +e34 = newCell({v3,v4}); +e41 = newCell({v4,v1}); +f = newCell({e12,e23,e34,e41}); +C = cellComplex(R,{f}); -- C is a square +assert(dim C == 2); +P = facePoset C; +assert(compare(P,v1,e12)); +assert(not compare(P,v1,e23)); +assert(compare(P,P_*#0,f)); +assert(isGraded P); +assert(maximalElements P === {f}); +/// + +--Minimality check +TEST /// +R = QQ[x,y,z]; +v1 = newCell({},x^2*y); +v2 = newCell({},y*z); +v3 = newCell({},z^3); +e12 = newCell({v1,v2}); +e13 = newCell({v1,v3}); +e23 = newCell({v2,v3}); +f123 = newCell({e12,e13,e23}); +Cnonmin = cellComplex(R,{f123}); +assert(not isMinimal(Cnonmin)); +Cmin = cellComplex(R,{e12,e23}); +assert(isMinimal(Cmin)); +/// + +--RingMap**CellComplex check +TEST /// +R = ZZ; +M = transpose matrix {{0,0,0},{0,1,0},{0,0,1},{1,0,0},{1,1,0},{1,0,1}}; +P = convexHull M; +C = cellComplex(R,P); +S = ZZ[x]; +f = map(S,R,{}); +D = f**C; +chainD = chainComplex D; +assert(ring chainD === S); +assert(HH_1(D)==0); +assert(HH_2(D)==0); +assert(HH_3(D)==0); +/// + +TEST /// +R = ZZ; +v = newCell({},1); +f = newCell({(v,0)},1,CellDimension=>2); +C = cellComplex(R,{f}); +assert(HH_0(C)==0); +assert(HH_1(C)==0); +assert(HH_2(C)==ZZ^1); +/// + +--Sphere test +TEST /// +C = cellComplexSphere(QQ,3); +assert(dim C==3); +for i in {1,2,4} do assert(# cells(i,C) == 0); +for i in {0,3} do assert(# cells(i,C) == 1); +for i in {-1,0,1,2,4,20} do assert(HH_i(C)==0); +assert(HH_3(C)== QQ^1); +/// + +--RPn test +TEST /// +C = cellComplexRPn(ZZ,4); +assert(dim C==4); +for i in (0..4) do assert(# cells(i,C) == 1); +for i in {-1,5} do assert(# cells(i,C) == 0); +for i in {-1,0,2,4} do assert(HH_i C == 0); +for i in {1,3} do assert(prune HH_i C == cokernel matrix {{2}}); +/// + +-- Scarf complex Test +TEST /// +R = QQ[w,x,y,z]; +I = monomialIdeal {w*x,x*y,y*z,w*z} +C = scarfComplex I +assert(dim C == 1); +assert(prune HH_1 C != 0); +/// + +TEST /// +S = QQ[x,y,z,w]; +v1 = newCell({},y*w); +v2 = newCell({},x*y*z); +v3 = newCell({},x^2*y); +v4 = newCell({},z^4*w); +e12 = newCell({v1,v2}); +e13 = newCell({v1,v3}); +e23 = newCell({v2,v3}); +e14 = newCell({v1,v4}); +f123 = newCell({e12,e13,e23}); +Delta = cellComplex(S, {f123,e14}); +C = chainComplex Delta; +assert (dim Delta == 2); +assert (length C == 3); +/// + + +TEST /// +R = QQ[x,y,z]; +vx = newSimplexCell({},x); +vy = newSimplexCell({},y); +vz = newSimplexCell({},z); +lxy = newSimplexCell({vx,vy}); +lyz = newSimplexCell({vy,vz}); +lxz = newSimplexCell({vx,vz}); +assert(isCycle {(lxy,1)} == false); +assert(isCycle {{lxy,1},{lyz,1},{lxz,-1}} == true); +assert(isCycle {{lxy,1},{lyz,1},{lxz,1}} == false); +assert(isCycle {} == true); +assert(isCycle {(vx,1)} == false); +assert(isCycle {(vx,1),(vy,-1)} == true); +assert(isCycle {(vx,0)} == true); +/// + + +--Irrelevant ideal of a toric variety +TEST /// +needsPackage "NormalToricVarieties" +X = toricProjectiveSpace(1) ** toricProjectiveSpace(2); +S = ring X; +B = ideal X; +Sigma = fan X; +P = polytope Sigma; +d = dim P; +--Faces knows the order of the vertices relative to the output of vertices P +F = faces_d P; +m = product(apply(numgens S, i -> S_i)); +G = apply(max X, l -> m//product(apply(l,i -> S_i))); +H = hashTable apply(#G, i -> (j := F#i#0#0;((vertices P)_j,G_i))) +C = cellComplex(S,P,Labels => H); +Cres = (chainComplex C)[-1]; +assert(betti (res B) == betti Cres); +assert(HH_0 Cres == S^1/B); +assert(HH_1 Cres == 0); +assert(HH_2 Cres == 0); +assert(HH_3 Cres == 0); +/// + +-- Hull complex test +TEST /// +R = QQ[x,y,z]; +I = monomialIdeal (x^2*z, x*y*z, y^2*z, x^3*y^5, x^4*y^4, x^5*y^3); +H = hullComplex I; +chainComplex H; +assert(isMinimal H); +assert(HH_(-1) chainComplex H == R^1/I); +assert((HH_0 chainComplex H)==0); +H2 = hullComplex (3/2,I) +assert((HH_0 chainComplex H2)!=0); +/// + +--isWellDefined test +TEST /// +R = QQ[x,y]; +S = ZZ[a,b]; +v1 = newCell({},x); +v2 = newCell({},y); +v3 = newCell({},a); +v4 = newCell({},b); +assert(isWellDefined v1); +assert(isWellDefined v2); +assert(isWellDefined v3); +assert(isWellDefined v4); +C1 = cellComplex(R,{v1,v3}); +--incompatible rings/two different rings +assert(not isWellDefined C1); +e1 = newCell({v1,v3},x); +assert(not isWellDefined e1); +C2 = cellComplex(R,{e1}); +assert(not isWellDefined C2); +--label is not divisble by the labels in the boundary +e2 = newSimplexCell({v1,v2},x); +assert(not isWellDefined e2); +C3 = cellComplex(R,{e2}); +assert(not isWellDefined C3); +e3 = newSimplexCell({v1,v2}); +assert(isWellDefined e3); +C4 = cellComplex(R,{e3}); +assert(isWellDefined C4); +/// + +--subcomplex test +TEST /// +R = QQ[x,y,z]; +v1 = newCell({},x*y); +v2 = newCell({},x*z); +v3 = newCell({},y*z); +e1 = newSimplexCell {v1,v2}; +e2 = newSimplexCell {v1,v3}; +e3 = newSimplexCell {v2,v3}; +f = newSimplexCell {e1,e2,e3}; +C = cellComplex(R,{f}); +Cxy = subcomplex(C,x*y); +assert(isWellDefined Cxy); +assert(#flatten values cells Cxy == 1); +Cxyz = subcomplex(C,x*y*z); +assert(isWellDefined Cxy); +assert(#flatten values cells Cxyz == #flatten values cells C) +///