One Hat Cyber Team
Your IP :
216.73.216.216
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 :
EagonResolution.m2
newPackage( "EagonResolution", Version => "1.0", Date => "September 23, 2020", Authors => {{Name => "David Eisenbud", Email => "de@msri.org", HomePage => "http://www.msri.org/~de"}, {Name => "Mike Stillman", Email => "mike@math.cornell.edu", HomePage => "http://pi.math.cornell.edu/~mike"}}, Headline => "Compute the Eagon Resolution of the residue field", Keywords => {"Commutative Algebra"}, PackageExports => {"Complexes"}, DebuggingMode => false ) export { "eagon", "eagonResolution", -- use res EagonData as a synonym "golodBetti", -- the betti table "as if" the module is Golod. "verticalStrand", --make a vertical strand of the Eagon complex "horizontalStrand", --make a vertical strand of the Eagon complex "eagonBeta", -- the maps responsible for any non-minimality. "picture", -- compressed display of labeled matrices; various options "mapComponent", --Symbols: "EagonData", -- the result of the eagon computation "Transpose", -- option for picture "DisplayBlocks", --option for picture "Display", -- option for picture and for eagonBeta "CompressBeta" --option for eagon } protect EagonLength --EagonData-- EagonData = new Type of HashTable net EagonData := E -> net ("EagonData in <ring>.cache computed to length "| toString(E.EagonLength)) --verticalStrand-- verticalStrand = method() verticalStrand (EagonData, ZZ) := Complex => (E,n) -> ( Kemax :=select(keys E, k -> (k_0 === "dVert" and E#k !=0)); maxn := max apply(Kemax, k->k_1); if n>maxn then error "That vertical strand wasn't defined"; Ke := select(keys E, k -> (k_0 === 0 and k_1 === n and E#k !=0)); b := max (Ke/(k-> k_2)); complex(apply(b, i-> E#{"dVert", n,i+1}))) --horizontalStrand-- horizontalStrand = method() horizontalStrand (EagonData, ZZ) := Complex => (E,i) -> ( Kemax :=select(keys E, k -> (k_0 === "dHor" and E#k !=0)); maxi := max apply(Kemax, k->k_2); if i>maxi then error "That horizontal strand wasn't defined"; Ke := select(keys E, k -> (k_0 === 0 and k_2 === i and E#k !=0)); b := max (Ke/(k-> k_1)); complex(apply(b-1, n -> E#{"dHor", n+1,i}))) /// restart loadPackage("EagonResolution", Reload => true) /// --tensoring a list of modules together: tensorL = (R,L) -> (if L === {} then return R^1; if #L === 1 then return L_0; L_0**tensorL(R,drop(L,1))) --golodBetti-- golodBetti = method() golodBetti (Complex, Complex, ZZ) := BettiTally => (F,G,b) ->( --F,G finite free complexes (resolutions) over a ring S. --Compute the Betti table of what should be the Eagon resolution of --the module resolved by G over the ring resolved by F --up to step b. symbs := apply(b+1, n->eagonSymbols(n,0)); mods := apply(symbs, s -> directSum apply(#s, i-> G_(s_i_0)**tensorL(ring F, apply(s_i_1, j->F_(j))))); betti complex apply(b,i->map(mods_i,mods_(i+1),0)) ) golodBetti (Module,ZZ) := BettiTally => (M,b) ->( --case where M is a module over a factor ring R = S/I, --MS is the same module over S --F = freeResolution I --K = freeResolution MS R := ring M; p := presentation R; S := ring p; phi1 := substitute(presentation M, S); phi := phi1 | target phi1 ** p; MS := prune coker phi; K := freeResolution MS; F := freeResolution coker p; golodBetti(F,K,b) ) /// restart loadPackage("EagonResolution", Reload =>true) S = ZZ/101[x,y,z] R = S/ideal(x^2,y^3) M = coker random(R^2, R^{-2,-2,-3}) b = 5 golodBetti(M,5) /// homologyCover = method() homologyCover(Complex,ZZ) := Matrix => (C,i) ->( --map from a free module to C_i giving a basis of HH_i C. --Note that HH_i C must be of finite length, or there's an error. B := basis HH_i C; (gens target B)*matrix B) homologyCover(ZZ, Complex) := List => (b,C) -> apply(b, i-> homologyCover(C,i)) homologyCover(Complex) := List => C -> homologyCover(1+length C, C) homologyIsomorphism = method() homologyIsomorphism(Module, Complex, ZZ) := Matrix => (M,C,i) ->( --If M is isomorphic to HH_i C then the matrix returned is a map -- target presentation M --> C_i --inducing the isomorphism. --else the function throws and error. Hi := HH_i C; H := prune Hi; p := H.cache.pruningMap; -- iso from H to HH_i C f := degreeZeroSurjection(M,H); if f === null then error "non-iso modules that should be iso"; -- g := degreeZeroSurjection(H, M); -- this is just a check; get rid of it eventually map(C_i,HH_i C, gens Hi)*p*f ) labeler = (L,F) -> directSum(1:(L=>F)); tensorWithComponents = method() tensorWithComponents(Module, Module, Function) := Module => (F, G, combineIndices) -> ( if F == 0 or G == 0 then return (ring F)^0; (compsF, indicesF) := componentsAndIndices F; (compsG, indicesG) := componentsAndIndices G; comps := flatten for f from 0 to #compsF-1 list ( for g from 0 to #compsG-1 list ( newindex := if indicesF#f === null or indicesG#g === null then null else combineIndices(indicesF#f, indicesG#g); newindex => directSum(1:(newindex=>(compsF#f ** compsG#g))) ) ); if #comps == 0 then (ring F)^0 else directSum comps ) tensorWithComponents(Module, Module) := Module => (F, G) -> tensorWithComponents(F, G, (a,b) -> a|b) tensorWithComponents(Matrix, Module, Function) := Matrix => (phi, G, combineIndices) -> ( src := tensorWithComponents(source phi, G, combineIndices); tar := tensorWithComponents(target phi, G, combineIndices); map(tar,src,phi**G)) eTensor = method() eTensor(Module,Module) := Module => (F, G) -> tensorWithComponents(F, G, (a,b) ->(a#0+b#0,a#1|b#1)) eTensor(Matrix,Module) := Matrix => (phi,G) -> tensorWithComponents(phi, G, (a,b) ->(a#0+b#0,a#1|b#1)) trimWithLabel = method() trimWithLabel ZZ := ZZ => n -> n trimWithLabel Symbol := Symbol => s -> s trimWithLabel Module := Module => M ->( if M == 0 then return (ring M)^0; ci := componentsAndIndices M; pci := positions(ci_0,M -> M!=0); if #pci ===0 then return (ring M)^0; if #pci === 1 then labeler(ci_1_pci_0, ci_0_pci_0) else directSum apply(pci, i->labeler(ci_1_i,ci_0_i)) ) trimWithLabel Matrix := Matrix => f ->( S := source f; T := target f; S':= trimWithLabel S; T':= trimWithLabel T; map(T',T,id_T')*f*map(S,S',id_S) ) trimWithLabel HashTable := HashTable => E -> hashTable apply(keys E, k-> (k,trimWithLabel E#k)) trimWithLabel Complex := Complex => F -> complex apply( length F, i-> trimWithLabel(F.dd_(i+1)) ) /// restart loadPackage("EagonResolution", Reload =>true) S = ZZ/101[x,y,z] R = S/ideal"x2,y2,z2" E = eagon(R,5) E eagonResolution E resolution E eagon(R,4) eagon(R,-1) /// --eagon-- eagon = method(Options => {CompressBeta => true, Verbose => false}) eagon(Ring, ZZ) := EagonData => o -> (R,b) ->( --compute the Eagon configuration up to and including column b; and thus --also the "Eagon Resolution" --Y^b_0 \to...\to Y^1_0 \to Y^0_0. --Let X_i be the free module R**H_i(K), where K is the Koszul complex on the variables of R. --We count X_i as having homological degree i+1. --The module Y^n_i = Eagon#{0,n,i} is described in Gulliksen-Levin as: --Y^0 = koszul vars R --Y^(n+1)_0 = Y^n_1; and --for i>0, Y^(n+1)_i = Y^n_(i+1) ++ Y^n_0**X_i --Note that Y^n_i == 0 for i>1+length koszul vars R, so we will carry computations out to that length. --Each Y^n is a complex whose i-th homology is H_i(Y^n) = H_0(Y^n)^0**X_i (proved in Gulliksen-Levin). --assuming that the differential of Y^n and the maps Y^n --> Y^(n-1) are known --To construct the differential of Y^(n+1) and the map Y^(n+1) \to Y^n, --this isomorphism must be made explicit. if b == -1 then ( remove(R.cache,symbol EagonData); <<"EagonData removed from "<<R<<".cache"<<endl; return null); if R.cache.?EagonData and R.cache.EagonData.EagonLength >= b then return R.cache.EagonData; Eagon := new MutableHashTable; g := numgens R; K0 := koszul vars R; Eagon#"numgens" = g; --now label the modules in the Koszul complex K := complex(for i from 1 to length K0 list map(labeler((i-1,{}), K0_(i-1)), labeler((i,{}), K0_(i)), K0.dd_i)); homologyCover' := (K,i) -> ( --Returns the map from X(i) to K_i phi := homologyCover(K,i); Xi := labeler((0,{i}),source phi); map(K_i, Xi, phi) ); ebasis := memoize homologyCover'; X := i -> if i<=g and (s := source ebasis(K,i))!=0 then labeler((0,{i}),s) else R^0; -- X(i) is the X_i of Gulliksen-Levin. --we made it a function so that it would be available for all integers i. pd := 0; while X(pd)!=0 do pd = pd+1; pd = pd-1; -- max i such that X(i)!=0 Eagon#"pd" = pd; --first make the free modules Y^n_i = Eagon#{0,n,i}. --The maps Y^(n+1)_j \to Y^n_j will be Eagon#{"dHor",n+1,j} west := "dHor"; --The differential verticaldiff of Y^n is the sum of maps eagon#{"dVert",n,i} and eagon#{"NW",n,i}. north := "dVert"; eagonBeta := "eagonBeta"; --Make the free modules Eagon#{0,n,i}. --two special cases: for i from 0 to g+1 do ( Eagon#{0,0,i} = K_i;-- print Eagon#{0,0,i}.cache.components); for n from 0 to b do( Eagon#{0,n,g+2} = directSum(1:R^0);-- R^0++R^0; )); -- cases: for n from 1 to b do ( for i from 0 to g+1 do( if i == 0 then ( Eagon#{0,n,i} = Eagon#{0,n-1,1} ; ) else ( Eagon#{0,n,i} = Eagon#{0,n-1,i+1}++eTensor(Eagon#{0,n-1,0},X(i)); ) )); --Now make the northward maps; the maps of the complexes Y^n = E#{0,n,*} --Note that the highest term in Y^n is in place b-n, so the top interesting homology is H_(b-n-1) --initialize: for i from 0 to g+2 do Eagon#{north,0,i} = K.dd_i; --Make the maps for n=1: --three special cases: Eagon#{north, 1, g+2} = map(Eagon#{0,1,g+1}, Eagon#{0,1,g+2},0); Eagon#{west,1,0} = Eagon#{north, 0,1}; Eagon#{north,1,1} = (Eagon#{north,0,2})*(Eagon#{0,1,1})^[0] + ebasis(K,1)*(Eagon#{0,1,1})^[1]; Eagon#{eagonBeta,1,1} = ebasis(K,1); Eagon#{west,1,1} = K.dd_2 | ebasis(K,1); for i from 2 to g+1 do( Eagon#{north,1,i} = (Eagon#{0,1,i-1})_[0]* ( (Eagon#{north,0,i+1})*(Eagon#{0,1,i})^[0] + ebasis(K,i)*(Eagon#{0,1,i})^[1] ); Eagon#{eagonBeta,1,i} = ebasis(K,i); Eagon#{west,1,i} = K.dd_(i+1) | ebasis(K,i); ); --now the induction, assuming that the Y^m have been defined for m<n: for n from 2 to b do( Eagon#{north, n, g+2} = map(Eagon#{0,n,g+1}, Eagon#{0,n,g+2},0); Eagon#{west,n,0} = Eagon#{north, n-1,1}; Eagon#{eagonBeta,n,0} = Eagon#{eagonBeta, n-1,1}; for i from 1 to g+1 do( --for eagonBeta: toLift := -(if #components Eagon#{0,n-2,i} ===1 then id_(Eagon#{0,n-2,i}) else Eagon#{0,n-2,i}_[0])* Eagon#{eagonBeta,n-1,i}* eTensor(Eagon#{north, n-2,1},X(i)); M := Eagon#{north,n-2,i+1}; --idea: when o.CompresBeta == true, then --try the initial segments of blocks until lifting becomes possible; then --set M = that lifting. We --know that a lifting is eventually possible. ind := indices flattenBlocks source M; if o.CompressBeta == true and ind =!=null then( numInd := #ind; if numInd != 0 then scan(numInd, p-> ( M' := extractBlocks(M,toList(0..p)); if toLift % M' == 0 then( (M = M'; if o.Verbose == true then <<"Used "<<p+1<<" of "<<numInd<<" blocks of eagonBeta "<<(n,i)<<endl; break))))); Eagon#{eagonBeta,n,i} = toLift//M; Eagon#{west,n,i} = Eagon#{0,n-1,i}_[0]*Eagon#{eagonBeta,n,i}*(Eagon#{0,n,i})^[1]+ Eagon#{0,n-1,i}_[1]* (Eagon#{west,n-1,0}**X(i)) *(Eagon#{0,n,i})^[1]+ (if Eagon#?{west,n-1,i+1} then Eagon#{0,n-1,i}_[0]*Eagon#{west,n-1,i+1}*Eagon#{0,n,i}^[0] else 0); if i == 1 then Eagon#{north,n,i} = -- special case because Y^n_0 is not a tensor product with Y^(n-1)_0 ( (Eagon#{north,n-1,i+1})*((Eagon#{0,n,i})^[0])+ Eagon#{0,n-1,i}_[0]*Eagon#{eagonBeta,n,i}*(Eagon#{0,n,i})^[1]+ Eagon#{0,n-1,i}_[1]*(Eagon#{north, n-2,1}**X(i))*(Eagon#{0,n,i}^[1]) ) else Eagon#{north,n,i} = Eagon#{0,n,i-1}_[0]* ( (Eagon#{north,n-1,i+1})*((Eagon#{0,n,i})^[0])+ Eagon#{0,n-1,i}_[0]*Eagon#{eagonBeta,n,i}*(Eagon#{0,n,i})^[1]+ Eagon#{0,n-1,i}_[1]*(Eagon#{north, n-2,1}**X(i))*(Eagon#{0,n,i}^[1]) ); )); H := trimWithLabel hashTable prepend((symbol EagonLength, b), pairs Eagon); E :=new EagonData from H; R.cache.EagonData = E; E ) --eagonBeta-- eagonBeta = method(Options => {Display => "picture", Verbose => false}) --There are no maps eagonBeta(E,0) or eagonBeta(E,1); the display starts with eagonBeta(E,2). eagonBeta(EagonData, ZZ) := o -> (E,n) -> ( if o.Display == "picture" then picture(E#{"eagonBeta",n,0},Verbose => o.Verbose) else if o.Display == "DisplayBlocks" then displayBlocks E#{"eagonBeta",n,0} else E#{"eagonBeta",n,0} ) eagonBeta EagonData := List => o-> E -> ( b := max apply(select(keys E, k-> k_0 === 0 and k_2 === 0), k->k_1); netList apply(b-1, n -> eagonBeta(E,n+2,Display => o.Display, Verbose => o.Verbose)) ) extractBlocks = method() extractBlocks(Matrix, List) := Matrix => (phi, L) -> ( --the map map(target phi, source phi, phi_[L]*(source phi)^[L], where L is a list --of integers representing blocks in source phi. phi1 := flattenBlocks phi; src := source phi1; ind := indices src; sum(L, i->phi1_[ind_i]*src^[ind_i]) ) --eagonResolution-- eagonResolution = method() eagonResolution EagonData := Complex => E ->( b := max apply( select(keys E, k-> k_0 === 0 and k_2 === 0), k->k_1); complex(apply(b,n->E#{"dHor",n+1,0})) ) eagonResolution(Ring,ZZ) := Complex => (R,b) ->( eagonResolution eagon(R,b) ) resolution EagonData := opts->E -> eagonResolution E --res-- --resolution-- /// restart loadPackage("EagonResolution", Reload=>true) S = ZZ/101[a,b,c] I = ideal"a3,b3,c3" R = S/I E = eagon(R,6) eagonResolution E -- works --resolution EagonData := E -> eagonResolution E resolution E -- fails eagonResolution E freeResolution R.cache.EagonData eagonResolution E /// eagonSymbols = method() eagonSymbols(ZZ,ZZ) := List => (n,i) ->( --symbol of the module Y^n_i, as a list of pairs, defined inductively from n-1,i+1 and n-1,0 --assumes large number of vars and pd. if n === 0 then return {(i,{})}; if i === 0 then return eagonSymbols(n-1,1); e' := eagonSymbols (n-1,0); e'1 := apply (e', L -> L_1|{i}); eagonSymbols(n-1,i+1)|apply (#e', j-> (e'_j_0,e'1_j)) ) componentsAndIndices = (F) -> ( if not F.cache.?components then ( -- F has no components ({F}, {null}) ) else if #F.cache.components == 1 then ( if F.cache.?indices then ({F}, F.cache.indices) else ({F}, {null}) ) else ( a := for f in F.cache.components list componentsAndIndices f; (flatten(a/first), flatten(a/last)) ) ) flattenBlocks = method() flattenBlocks Module := (F) -> ( if not isFreeModule F then error "expected a free module"; (comps, inds) := componentsAndIndices F; compsLabelled := for i from 0 to #comps-1 list ( inds#i => comps#i ); directSum compsLabelled ) flattenBlocks Matrix := (M) -> ( F := flattenBlocks target M; G := flattenBlocks source M; map(F,G,matrix M) ) --displayBlocks-- displayBlocks = method() displayBlocks Matrix := (M1) -> ( M := flattenBlocks M1; src := select(indices source M, i-> i =!= null); tar := select(indices target M, i-> i =!= null); netList (prepend( prepend("", src), for t in tar list prepend(t, for s in src list ( mts := M^[t]_[s]; h := if mts == 0 then "." else if (numrows mts == numcols mts and mts == 1) then "1" else net mts )) ), Alignment=>Center) ) displayBlocks Complex := List => C -> apply(length C, i -> displayBlocks(C.dd_(i+1))) displayBlocks EagonData := List => E -> displayBlocks eagonResolution E --pictureList-- pictureList = method(Options => {Verbose => false, Display => "picture", Transpose => false}) pictureList Matrix := o -> (M1) -> ( M := flattenBlocks M1; src := indices source M; tar := indices target M; kkk := ring M/(ideal gens ring M); L := prepend( prepend("", src), for t in tar list prepend(t, for s in src list ( mts := M^[t]_[s]; nums := toString(numrows mts,numcols mts); cont := ideal M^[t]_[s]; if o.Verbose == false then ( h := if mts == 0 then "." else if (numrows mts == numcols mts and mts == 1) then "id" else if cont == ideal(1_(ring mts)) then toString numrows M^[t]_[s]|","|toString rank(kkk**M^[t]_[s]) else "*" ) else ( h = if mts == 0 then nums|" ." else if (numrows mts == numcols mts and mts == 1) then nums|" id" else if cont == ideal(1_(ring mts)) then nums|","|toString rank(kkk**M^[t]_[s]) else "*" ) ))); if o.Transpose then transpose L else L ) pictureList Complex := List => o -> C -> apply(length C, i-> pictureList(C.dd_(i+1), o)) pictureList EagonData := List => o -> E -> pictureList (eagonResolution E, o) --picture-- picture = method(Options => options pictureList) picture Matrix := o -> (M1) -> ( if o.Display === "DisplayBlocks" then return displayBlocks M1; netList (pictureList(M1,o), Alignment => Center) ) picture Complex := Net => o -> C -> netList apply(length C, i-> picture(C.dd_(i+1), o)) picture EagonData := Net => o -> E -> picture (eagonResolution E, o) --mapComponent-- mapComponent = method() mapComponent(Matrix, Sequence, Sequence) := Matrix => (M,tar,src) -> ( --Matrix should be one with labeled components, such as produced by --E = eagon(R,n) --M = E#{"dVert",4,1} --or --M = (eagonResolution(R,n)).dd_4 M1 := flattenBlocks M; --use "member" and "componentsAndIndices" to check reasonableness? or try evaluating and catch error. try (M2 := M1^[tar]_[src]) then M2 else ( <<endl<<"*** bad source or target symbol; use `picture M1' to check ***"<<endl<<endl; error() ) ) degreeZeroSurjection = method() degreeZeroSurjection(Module,Module) := Matrix => (A,B) -> ( -- null if no surjection --creates a random degree 0 map f:A --> B and tests surjectivity. returns the map if true, else null. A' := prune A; pruningMapA := A'.cache.pruningMap; --from A' to A B' := prune B; pruningMapB := B'.cache.pruningMap; H := Hom(A',B'); B0 := basis(0,H); -- this seems to be total degree 0 in case of degreeLength>1 f := homomorphism(B0*random(source B0, (ring B0)^1)); t := coker f == 0; if t then pruningMapB * f * (pruningMapA)^-1 else null ) beginDocumentation() -* restart debug loadPackage("EagonResolution", Reload => true) uninstallPackage "EagonResolution" restart installPackage "EagonResolution" check "EagonResolution" viewHelp EagonResolution *- --docEagonResolution doc /// Key EagonResolution Headline Construct the Eagon double complex, which contains a resolution of the residue field Description Text This package implements Eagon's algorithm for producing a not-necessarily minimal resolution of the residue field of a ring R = S/I where S is a polynomial ring and I is an ideal. The resolution constructed is minimal if and only if R is Golod. The resolution constructed is sometimes called the Golod or Shamash or Eagon resolution. This resolution was described, in the special case where it is minimal, by E.S. Golod: Homology of some local rings, Uspekhi Mat. Nauk 33 (1978), no. 5(203), 177–178. A general construction was described by Jack Shamash: The Poincaré series of a local ring II, J. Algebra 17 (1971), 1–18 and, perhaps around the same time, by Jack Eagon. Eagon's construction, superficially different than Shamash' was not published by him, but is described in Ch. 4 of the notes by Gulliksen and Levin: Homology of local rings, Queen's Paper in Pure and Applied Mathematics, No. 20 Queen's University, Kingston, Ont. 1969. To get a glimpse of the construction, consider the first steps. Let K be the Koszul complex of S, which is the minimal S-free resolution of the residue field k. If numgens S = n, this begins K_1 = S^n -> K_0 = S -> k. Let F be the minimal S-free resolution of R. by the right-exactness of the tensor product, the complex R**K_1 -> R**K_0 -> k is a presentation of k, and of course R**K_2 maps to the kernel of R**K_1 -> R**K_0. But there are new elements of the kernel, obtained by writing the generators of I, which correspond to the generators of F_1, in terms of the generators of the maximal ideal. Thus we must add a map R**F_1 -> R**K_1, and it is easy to show that the resulting complex R**F_1 ++ R**K_2 -> R**K_1 -> R**K_0 -> k is exact. There are three important points to note: 1) F_0 does not occur 2) F_1 occurs in homological degree 2 3) There is a map F_1 -> K_1 that must be introduced and that does not come from either the complex F nor the complex K. Eagon showed how this complex can be continued to a resolution. The underlying graded module of the complex is K ** T(F'), where F' is the complex F, shifted by 1 in homological degree so that F_i is in homological degree i+1, and truncated by dropping F_0; and T(F') denotes the tensor algebra on the graded module F'. The differentials of the complex come from the differentials in the Koszul complex and various maps identifying the homology, at successive stages of the construction, with tensor products of modules already constructed. These are also the ingredients of the "Massey products" from topology, used by Golod to construct the complex in the special case where there are ``trivial Massey products'', and the resolution is therefore minimal. The command @TO eagon@ produces a type of hashTable called an @TO EagonData@, defined in the package. It contains all the data produced in Eagon's construction of the resolution: a double complex Y^n_i, and some internal maps. The vertical differential is called dVert: Y^n_i -> Y^n_{i+1} and the horizontal differential is dHor: Y^n_i -> Y^{n-1}_i. Thus for example if $R$ is a factor ring of a polynomial ring S, then E = eagon(R,5) @TO eagonResolution@ E or simply res E produces the first 5 steps of a (not necessarily minimal) R-free resolution of the residue field of R. The function picture gives alternate ways of viewing the innards of the resolution. Example S = ZZ/101[a,b,c] I = ideal(a,b,c)*ideal(b,c) R = S/I E = eagon(R,5) F = eagonResolution E assert(F == resolution E) Text As stated above, F = K\otimes T(F'), and one can see the maps between each pair of summands. We label the summand K_i**F_{j_1}**..**F_{j_m} with the symbol (i,\{j_1,..,j_m\}), and we can write out the differentials in block form with the function picture, with the option Display => "DisplayBlocks", including the labels: Example F.dd_3 picture(F.dd_3, Display => "DisplayBlocks") Text Since the matrices can be very large, it is sometimes better to know just whether a given block is zero or not, and this can be obtained with the function @TO picture@, with the default option Display => "picture". Example picture F.dd_3 picture (F, Verbose => true) picture (F, Verbose => true, Transpose => true) SeeAlso eagon picture eagonResolution DisplayBlocks Transpose /// --docEagonData doc /// Key EagonData Headline HashTable storing output of eagon Usage E = eagon(R,b) Inputs R:Ring b:ZZ Outputs E:EagonData Description Text The command E = eagon(R,b) puts the EagonData E in R.cache so that E== R.cache.EagonData, and causes the message "EagonData in <ring>.cache computed to length b" Example R = ZZ/101[x,y]/ideal"x2,xy,y2" E = eagon(R,3) E === R.cache.EagonData SeeAlso eagon /// --doceagon doc/// Key eagon (eagon, Ring, ZZ) [eagon,CompressBeta] [eagon,Verbose] Headline compute the Eagon double complex Usage E = eagon(R,b) Inputs R:Ring b:ZZ how far to carry the computation; -1 means "delete EagonData" Outputs E:EagonData Description Text eagon(R,b) computes the first b columns of the Eagon double complex Y^*_* of R, and caches them in a HashTable of class EagonData in of R.cache.EagonData. (The command eagon(R,-1) removes this.) Following Gulliksen-Levin we think of Y^n_* as the n-th column, and Y^*_i as the i-th row. The columns Y^n are not acyclic. The i-th row is a resolution of the i-th module of boundaries in the Koszul complex K of the variables of R; in particular, the "Eagon Resolution" is the 0-th row, Y^b_0 \to...\to Y^1_0 \to Y^0_0. Let X_i be the free module R**H_i(K), which is also the R**F_i, where F is a minimal free resolution of R as a module over the polynomial ring on the same set of variables. We count X_i as having homological degree i+1. With this convention, Y^*_0 has the form K\otimes T(F'), where T denotes the tensor algebra and F' is the F_1++F_2++... . The module Y^n_i = Eagon#{0,n,i} is described in Gulliksen-Levin as: Y^0 = koszul vars R Y^{n+1}_0 = Y^n_1; and for i>0, Y^{n+1}_i = Y^n_{i+1} ++ Y^n_0**X_i Note that Y^n_i == 0 for i>1+length koszul vars R - n, The i-th homology of Y^n_* is H_i(Y^n) = H_0(Y^n_*)**X_i (proved in Gulliksen-Levin). Part of the inductive construction will be a map inducing this isomorphism alpha^n_i = eagonBeta^n_i + dHor^n_0**1: Y^n_0**X_i \to Y^{n-1}_{i+1} ++ Y^{n-1}_0**X_i = Y^n Assume that the differential of Y^n and the maps dVert^n and alpha^n are known. We take dHor^{n+1}_0: Y^{n+1}_0 = Y^n_1 -> Y^n_0 to be dVert^n_1. The remaining horizontal differentials dHor^{n+1}_i: Y^{n+1} \to Y^n have source and target as follows: Y^{n+1}_i = Y^n_{i+1} ++ Y^n_0**X_i -> Y^n_i = Y^{n-1}_{i+1} ++ Y^{n-1}_0**X_i. We take dHor^{n+1}_i to be the sum of two maps: dVert^n_{i+1} Y^n_{i+1} -> Y^n_i ++ Y^{n-1}_0**X_i. and alpha^{n+1}_i = eagonBeta^{n+1}_i + dHor^n_0**1: Y^n_0**X_i \to Y^n_i ++ Y^{n-1}_0**X(i). It remains to define eagonBeta^{n+1}_i; we take this to be the negative of a lifting along the map from Y^{n+1}_{i-1} \subset Y^n_i to Y^n_{i-1} of the composite dVert^{n+1}_{i-1} * (dHor^n_0 ** X_i): Y^n_0**X_i -> Y^{n-1}_0. Example S = ZZ/101[a,b,c] I = ideal(a,b)*ideal"a3,b3,c3" R = S/I needsPackage "DGAlgebras"; isGolod R E = eagon(R,6) Text We can see the vertical and horizontal strands, and the eagonBeta maps Example verticalStrand(E,3) horizontalStrand(E,2) horizontalStrand (E,0) F = eagonResolution E eagonBeta E Text With the default option CompressBeta => true, only a subset of the components of Y^{n+1}_{i-1} are used. To see the effect of CompressBeta => true, consider: Example eagon(R,-1) E = eagon(R,6, Verbose =>true) eagon(R,-1) En = eagon(R,6,CompressBeta => false) eagonBeta (E,4), eagonBeta(E,5) eagonBeta (En,4), eagonBeta(En,5) Text There are also ways to investigate the components of dVert, dHor, and eagonBeta; see @TO picture@, @TO DisplayBlocks@, and @TO mapComponent@. SeeAlso verticalStrand horizontalStrand /// --doceagonResolution doc /// Key eagonResolution (eagonResolution, Ring, ZZ) (eagonResolution, EagonData) Headline computes a resolution of the residue field Usage F = eagonResolution(R,n) F = eagonResolution E Inputs R:Ring factor ring of a polynomial ring n:ZZ number of maps to compute E:EagonData computed by eagon(R,n) Outputs F:Complex possibly non-minimal R-free resolution of R/(ideal vars R) Description Text computes the Eagon resolution Example S = ZZ/101[a,b,c] I = ideal(a,b,c)*ideal(b,c) R = S/I eagonResolution(R,5) SeeAlso eagon /// --docpicture doc /// Key picture (picture, Matrix) (picture, Complex) (picture, EagonData) [picture, Display] [picture, Verbose] [picture, Transpose] Headline information about components of a labeled Matrix or Complex Usage N = picture M L = picture C L = picture E Inputs M:Matrix C:Complex E:EagonData produced by eagon; picture E is equivalent to picture @TO eagonResolution@ E and to picture res E Outputs N:Net L:List List of Nets, one for each map in the complex Description Text The free modules that are the sources and targets of the matrices defined in the EagonData eagon(R,b) generally have many components. These can be analyzed with the functions picture, and @TO mapComponent@. Each summand of one of these free modules has a label of the form (i, \{u_1..u_s\}) representing the tensor product K_i ** X_{u_1}**..**X_{u_s}, where 0\leq i \leq numvars R and 1\leq u_t \leq projective dimension over S of R. Thus a block is identified by a pair of such symbols in the order target, source. For any labeled matrix M, picture M (with the default option Display => "picture") prints a net showing which blocks of the matrix are 0 (represented by .); or nonzero and in the maximal ideal, represented by *; or contain a unit entry, represented by a pair of numbers, which are the rank of the target of the matrix and the rank of the matrix tensored with the residue field (the "nonminimal part"). Options: The default option is Display => "picture". With the option Display=>"DisplayBlocks", picture prints the matrices in each block. With any other assignment such as Display =>"", Display prints the whole matrix, without showing the block structure. With the option Verbose => true, picture prints (numrows,numcols) for each block. With the option Transpose => true, picture prints the data for the transposed matrix (possibly useful if there are many columns in the matrix). Applied to a complex of labeled matrices such as that produced by (res, EagonData) or applied to an instance of EagonData, picture prints a netList of the pictures of the maps in the complex Example S = ZZ/101[a,b,c] I = ideal(a,b)*ideal(a,b,c) R = S/I E = eagon(R,4); picture E picture E#{"eagonBeta",3,0} picture E picture verticalStrand(E,1) SeeAlso eagon "eagonBeta" eagonResolution DisplayBlocks mapComponent /// --docdisplayBlocks doc /// Key DisplayBlocks Headline Display => "DisplayBlocks" option for picture Usage N = picture(M, Display => "DisplayBlocks") Inputs M:Matrix Outputs N:Net prints a "picture" -- a net -- showing information about the blocks Description Text The free modules that are the sources and targets of the matrices defined in the EagonData eagon(R,b) generally have many components. These can be analyzed with the functions @TO picture@, and @TO mapComponent@. Each summand of one of these free modules has a label of the form (i, {u_1..u_s}) representing the tensor product K_i ** X_{u_1}**..**X_{u_s}, where 0\leq i \leq numvars R and 1\leq u_t \leq projective dimension R. Thus a block is identified by a pair of such symbols, representing source and target. Display => "picture"; with this option, @TO picture@ does not actually print the entries of the matrices. But picture(M, Display => "DisplayBlocks") prints a net with the matrices themselves. Example S = ZZ/101[a,b,c] I = ideal(a,b)*ideal(a,b,c) R = S/I E = eagon(R,4); C = horizontalStrand(E,0) picture C picture(C, Display => "DisplayBlocks") SeeAlso eagon eagonResolution picture mapComponent horizontalStrand verticalStrand /// --doceagonBeta doc/// Key eagonBeta (eagonBeta,EagonData) (eagonBeta,EagonData,ZZ) [eagonBeta, Display] [eagonBeta, Verbose] Headline print the eagonBeta maps in the Eagon resolution Usage N = eagonBeta E N = eagonBeta(E,n) Inputs E:EagonData created by eagon(R,b) n:ZZ which eagonBeta to show Outputs N:Net either a "Display" display (with Display => "picture", the default) or a "displayBlocks" display with Display => "DisplayBlocks" or a plain matrix if Display => <anything else>. With Verbose => true, the display includes (rank target eagonBeta,rank source eagonBeta) Description Text The eagonBeta maps are the components of the Eagon resolution, starting from the 2nd differential that may or may not be minimal, and are therefore most interesting. With the default option Display => "picture" the pictures (which blocks are 0,nonzero, nonminimal) are shown; or the displayBlocks output with Display => "DisplayBlocks" or a plain matrix if Display => <any other string>. In the notes of Gulliksen-Levin it is proven that R is Golod if and only if the maps eagonBeta can be taken with values in the Koszul complex; thus in particular, if R is Golod, then there are no "new" eagonBetas after eagonBeta(E,numgens R+1). Since R is Golod iff all the eagonBeta matrices have all entries in the maximal ideal, this proves in particular that R is Golod if and only if the Betti numbers of the resolution of coker vars R agree up to the step numgens R with the Betti numbers of the Eagon resolution. Example S = ZZ/101[a,b,c,d] I = ideal(a,b,c)*ideal(a,b,c,d) I = ideal"a3,b3,c3" R = S/I E = eagon(R,4); eagonBeta(E,4) eagonBeta(E,4,Display => "DisplayBlocks") eagonBeta(E,4,Display => "") eagonBeta E SeeAlso eagon picture DisplayBlocks /// --dochorizontalStrand doc/// Key horizontalStrand (horizontalStrand, EagonData, ZZ) Headline extracts one horizontal strand from an Eagon double complex Usage F = horizontalStrand(E,i) Inputs E:EagonData produced by eagon(R,b) i:ZZ which strand Outputs F:Complex beginning of the free resolution of the i-th boundary module of the Koszul complex Description Text The 0-th strand is a possibly non-minimal resolution of the residue field. More generally, the i-th strand resolves the i-th boundary module in the Koszul complex of R. These resolutions are all minimal iff R is Golod. Example S = ZZ/101[x,y,z] R = S/((ideal(x,y))^2+ideal(z^3)) E = eagon(R,5); F = horizontalStrand(E,2) picture F SeeAlso verticalStrand eagon picture /// --docverticalStrand doc /// Key verticalStrand (verticalStrand, EagonData, ZZ) Headline extracts one vertical strand from an Eagon double complex Usage F = verticalStrand(E,i) Inputs E:EagonData produced by eagon(R,b) i:ZZ which strand Outputs F:Complex beginning of the free resolution of the i-th boundary module of the Koszul complex Description Text The 0-th vertical strand is the Koszul complex of R. The vertical strands are never resolutions unless R is regular. The key lemma in Eagon's treatment identifies the i-th homology H_i of the n-th vertical strand with H_0**X_i. Example S = ZZ/101[x,y,z] R = S/((ideal(x,y))^2+ideal(z^3)) E = eagon(R,5); F = verticalStrand(E,3) picture F SeeAlso horizontalStrand eagon picture /// --docmapComponent doc /// Key mapComponent (mapComponent, Matrix, Sequence, Sequence) Headline extract a single component from a labeled map Usage N = mapComponent(M,tar,src) Inputs M:Matrix labeled map from eagon(R,b) tar:Sequence symbol of a free module components of the Eagon resolution src:Sequence symbol of a free module components of the Eagon resolution Outputs N:Matrix Description Text The source and target of a a map in the Eagon double complex, such as dVert, dHor, and eagonBeta, are direct sums of tensor products of the form K_i**X_{u_1}**..**X_{u_s} where K_i is a term of the Koszul complex and X_i is a term of the S-free resolution of R, all tensored with R. This tensor product is represented by a symbol that is a two element Sequence (i, \{u_1..u_s\}) The block structure of the matrix, together with the source and target Sequences, can be seen from picture M. The function mapComponent returns a single block. Example S = ZZ/101[a,b,c,d,e] R = S/(ideal(e^2,d*e^4)+(ideal"ab,ac")^2) --a non-Golod ring, generators in different degrees E = eagon (R,5); picture E#{"dHor",3,0} mapComponent(E#{"dHor",3,0}, (0,{1}),(1,{1})) picture E#{"dVert",3,1} mapComponent(E#{"dVert",3,1}, (0,{2}),(0,{1,1})) picture E#{"eagonBeta",3,1} mapComponent(E#{"eagonBeta",3,1}, (0,{2}),(0,{1,1})) SeeAlso picture DisplayBlocks eagon /// --docgolodBetti doc /// Key golodBetti (golodBetti, Complex, Complex, ZZ) (golodBetti, Module, ZZ) Headline list the ranks of the free modules in the resolution of a Golod module Usage B = golodBetti(F,K,b) B = golodBetti(M,b) Inputs F:Complex resolution, typically of (R = S/I)^1 over S K:Complex resolution, typically of an R-module M over S M:Module R-module b:ZZ homological degree to which to carry the computation Outputs B:BettiTally This would be betti table of the free res of M over R, if M were a Golod module over R Description Text Let S be a standard graded polynomial ring. A module M over R = S/I is Golod if the resolution H of M has maximal betti numbers given the betti numbers of the S-free resolutions F of R and K of M. This resolution, H, has underlying graded module H = R**K**T(F'), where F' is the truncated resolution F_1 <- F_2... and T(F') is the tensor algebra. Since the component modules of H are given, the computation only requires the computation of the minimal S-free resolution of M, and then is purely numeric; the differentials in the R-free resolution of M are not computed. In case M = coker vars R, the result is the Betti table of the Golod-Shamash-Eagon resolution of the residue field. We say that M is a Golod module (over R) if the ranks of the free modules in a minimal R-free resolution of M are equal to the numbers produced by golodBetti. Theorems of Levin and Lescot assert that if R has a Golod module, then R is a Golod ring; and that if R is Golod, then the d-th syzygy of any R-module M is Golod for all d greater than or equal to the projective dimension of M as an S-module (more generally, the co-depth of M) (Avramov, 6 lectures, 5.3.2). Example S = ZZ/101[a,b,c] I = (ideal(a,b,c^2))^2 F = freeResolution(S^1/I) K = freeResolution coker vars S R = S/I E = eagon(R,6); golodBetti(F,K,6) betti freeResolution (coker vars R, LengthLimit => 6) betti eagonResolution E SeeAlso eagon eagonResolution /// -* --doceagonSymbols doc /// Key eagonSymbols (eagonSymbols, ZZ, ZZ) Headline symbols of the components of a module in the Eagon double complex Usage L = eagonSymbols(n,i) Inputs n:ZZ column indix i:ZZ row index Outputs L:List list of symbols Description Text Each module in the Eagon double complex is a direct sum of tensor products of a component of the Koszul complex and a list of modules X_i. Such a tensor product is represented by a symbol that is a two element Sequence of the form (i, {u_1..u_s}) representing K_i ** X_{u_1} ** .. ** X_{u_s}. The function eagonSymbols(n,i) produces the list of symbols of the summands of the module Y^n_i in the n-th column, i-th row of the double complex. This is done arithmetically, that is, without computing the double complex. Example L = eagonSymbols(3,2) Text This is the list of symbols associated to the source of (for example) the 2-nd differential in the 3-rd vertical strand of the double complex. Example S = ZZ/101[a,b,c,d,e] R = S/(ideal vars S)^2 --a Golod ring E = eagon(R,3); V = verticalStrand(E,3) picture (V.dd_2) Text Compare the top row with Example L SeeAlso eagon picture DisplayBlocks mapComponent /// *- --docDisplay doc /// Key Display Headline Option for eagonBeta, default is "picture" Usage eagonBeta(E,Display => "picture") Inputs E:EagonData Description Text if Display=>"picture" then @TO picture@ is invoked; if Display =>"DisplayBlocks" then a net with the matrices (the "blocks") is produced. Example R = ZZ/101[x,y,z]/ideal"x3,y3,z3" E = eagon(R,5); eagonBeta(E,3) eagonBeta(E,3,Display =>"DisplayBlocks") SeeAlso eagon /// --docCompressBeta doc /// Key CompressBeta Headline CompressBeta is an option for eagon, default is true Usage E = eagon(R,b,CompressBeta =>bool1, Verbose =>bool2) Inputs R:Ring b:ZZ bool1: Boolean bool2: Boolean Outputs E:EagonData Description Text eagonBeta(E,i) := E#{"eagonBeta",n,0} is defined by lifting another map along the map dVert^n_i. If CompressBeta => true, the default, then the lifting uses the smallest initial subsequence of the blocks of source dVert as possible, and thus the columns of picture eagonBeta(E,i) will have fewer nonzero entries. if Verbose =>true, then data about the usage is printed. Example R = ZZ/101[x,y,z]/ideal"x3,y3,z3" E = eagon(R,4,CompressBeta =>true, Verbose =>true); SeeAlso eagon eagonBeta /// doc /// Key Transpose Headline Transpose => false, default option for picture Usage N = picture(M, Transpose => b) Inputs M:Matrix labeled matrix b:Boolean Outputs N: Net Description Text With the option Transpose => true, picture prints the picture of the transposed matrix; when the matrix has many more columns than rows this makes it easier to read. Example S = ZZ/101[a,b] R = S/ideal"a2,b2" E = eagon(R,3) picture resolution E picture(resolution E, Transpose => true) SeeAlso DisplayBlocks Verbose /// doc /// Key (net, EagonData) Headline prints the homological degree to which the EagonData has been computed Usage s = net E Inputs E:EagonData Outputs s:Net Description Text prints the homological degree to which the EagonData has been computed Example R = ZZ/101[a,b]/ideal"a2,b2" E = eagon(R,4) net E SeeAlso EagonData /// doc /// Key (resolution, EagonData) Headline outputs the resolution that is the 0th row of the Eagon double complex Usage C = resolution E Inputs E:EagonData Outputs C:Complex Description Text This command is equivalent to @TO (eagonResolution, EagonData)@. Example R = ZZ/101[a,b]/ideal"a2,b2" E = eagon(R,4) C = resolution E picture C SeeAlso eagon EagonData eagonResolution picture /// ----TESTS -* restart uninstallPackage "EagonResolution" installPackage "EagonResolution" check EagonResolution viewHelp EagonResolution loadPackage("EagonResolution", Reload => true) *- TEST/// S = ZZ/101[a,b,c] I = ideal(a,b)*ideal"a3,b3,c3" R = S/I E = eagon(R,6,CompressBeta =>true); En = eagon(R,6,CompressBeta => false); F = eagonResolution E Fn= eagonResolution En assert(F.dd^2 == 0) assert(Fn.dd^2 == 0) assert(all(5, i->prune HH_(i+1) F == 0)) assert(all(5, i->prune HH_(i+1) Fn == 0)) /// TEST/// debug EagonResolution S = ZZ/101[a,b,c]/ideal(b^2,c^2) -- complete intersection B = 3 E = eagon(S,B); F = eagonResolution E M = F.dd_3 M' = extractBlocks(M,{0,2}) M'' = map(target M', source M', matrix {{c, 0, -b, 0, 0, 0, 0, 0}, {-b, 0, 0, -c, 0, 0, 0, 0}, {a, 0, 0, 0, 0, -c, b, 0}, {0, 0, a, 0, b, 0, c, 0}, {0, 0, 0, a, 0, b, 0, c}}) assert(M' == M'') /// TEST/// S = ZZ/101[a,b,c] R = S/(ideal"ab,ac")^2 --a simple Golod ring on which to try this b = 6 E = eagon(R,b); F = eagonResolution E G = freeResolution (coker vars R, LengthLimit => b) assert(betti F == betti G) assert(F.dd^2== 0) assert(all(b-1, i-> prune (HH_(i+1) F) == 0)) /// TEST/// debug EagonResolution S = ZZ/101[a,b,c,d,e] A = S^1 B = S^2 A' = labeler((0,{1}),A) indices A' B' = labeler((0,{2}),B) assert(indices eTensor(A',B') == {(0, {1, 2})}) assert(indices (T = eTensor(B',A')) == {(0, {2, 1})}) assert(indices trimWithLabel T == {(0, {2, 1})}) /// TEST/// -- test of eagon S = ZZ/101[a,b,c] R = S/(ideal"ab,ac")^2 --a simple Golod ring on which to try this bound = 6 E = eagon(R,bound); Y = apply(bound, n-> verticalStrand(E,n)) assert(all(#Y, n->((Y_n).dd)^2 == 0)) assert all(#Y, n->isHomogeneous Y_n) F = eagonResolution(R,bound) F = eagonResolution E assert isHomogeneous F assert all(bound-1,i-> prune HH_(i+1) F == 0) assert(betti freeResolution(coker vars R,LengthLimit => bound) == betti F) S = ZZ/101[a,b,c,d,e] R = S/(ideal(e^2,d*e^4)+(ideal"ab,ac")^2) --a non-Golod ring, generators in different degrees E = eagon(R,5); Y = apply(5, n -> verticalStrand(E,n)); assert(all(#Y, n->((Y_n).dd)^2 == 0)) assert all(#Y, n->isHomogeneous Y_n) F = eagonResolution(R,5) assert isHomogeneous F assert all(4,i-> prune HH_(i+1) F == 0) /// TEST/// debug needsPackage "EagonResolution" assert(eagonSymbols(1,2) == {(3, {}), (0, {2})}) assert (eagonSymbols(2,1) == eagonSymbols(3,0)) assert(eagonSymbols(1,3) == {(4, {}), (0, {3})}) assert(eagonSymbols(2,2) == {(4, {}), (0, {3}), (1, {2})}) assert(eagonSymbols(2,0) == {(2, {}), (0, {1})}) assert(eagonSymbols(3,1) == {(4, {}), (0, {3}), (1, {2}), (2, {1}), (0, {1, 1})}) assert(eagonSymbols(3,2) == {(5, {}), (0, {4}), (1, {3}), (2, {2}), (0, {1, 2})}) /// /// restart loadPackage("EagonResolution",Reload => true) needsPackage "DGAlgebras" /// TEST/// S = ZZ/101[x,y,z] I = trim(ideal(x,y)*ideal"x,y2,z") R = S/I F = freeResolution I G = freeResolution coker vars S b = 6 H = freeResolution(coker vars R,LengthLimit =>6) E = eagon(R,b); assert(betti eagonResolution E == betti H) assert(golodBetti(F,G,b) == betti H) assert (golodBetti (coker vars R,b) == betti H) /// -- TODO: PLACE INTO M2 CORE --SOME USEFUL INTERNAL FUNCTIONS -- "compositions" -* compositions(ZZ,ZZ,ZZ) := (nparts, k, maxelem) -> ( -- nparts is the number of terms -- k is the sum of the elements -- each element is between 0 and maxelem. compositionn := (n,k) -> ( if n===0 or k < 0 then {} else if k===0 then {toList(n:0)} else ( set1 := apply(compositionn(n-1,k), s -> s | {0}); set2 := apply(compositionn(n,k-1), s -> s + (toList(n-1:0) | {1})); set2 = select(set2, s -> s#(n-1) <= maxelem); join(set1, set2) ) ); compositionn = memoize compositionn; result := compositionn(nparts,k); compositionn = null; result ); *- TEST/// --test of degreeZeroSurjection debug needsPackage "EagonResolution"--degreeZeroSurjection is not exported. S = ZZ/101[a,b] A = S^{1,0}; B = S^{0,1};B1 = B/((ideal a)*B) f = degreeZeroSurjection(A,B) assert(source f == A) assert(target f == B) assert(coker f == 0) f = degreeZeroSurjection(A,B1) assert(source f == A) assert(target f == B1) assert(coker f == 0) f = degreeZeroSurjection(B1,A) assert(f===null) /// end-- ------------------------------------ restart uninstallPackage "EagonResolution" restart installPackage "EagonResolution" check EagonResolution viewHelp EagonResolution