From cbc7e87e52e75989444cbcdf7bb7e79e5823486f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 21 Apr 2025 04:18:51 -0400 Subject: [PATCH 01/88] added DirectSummands to =distributed-packages --- M2/Macaulay2/packages/=distributed-packages | 1 + 1 file changed, 1 insertion(+) diff --git a/M2/Macaulay2/packages/=distributed-packages b/M2/Macaulay2/packages/=distributed-packages index 1f89d2ebaa2..9f96961d7ea 100644 --- a/M2/Macaulay2/packages/=distributed-packages +++ b/M2/Macaulay2/packages/=distributed-packages @@ -250,6 +250,7 @@ OnlineLookup MergeTeX Probability Isomorphism +DirectSummands CodingTheory WhitneyStratifications JSON From f5bdef620a384b5c47aefb27169e0d5dcff5628c Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 5 Oct 2023 14:14:58 -0500 Subject: [PATCH 02/88] added package draft --- M2/Macaulay2/packages/DirectSummands.m2 | 394 ++++++++++++++++++ .../packages/DirectSummands/idempotents.m2 | 64 +++ 2 files changed, 458 insertions(+) create mode 100644 M2/Macaulay2/packages/DirectSummands.m2 create mode 100644 M2/Macaulay2/packages/DirectSummands/idempotents.m2 diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 new file mode 100644 index 00000000000..868582e357e --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -0,0 +1,394 @@ +newPackage( + "DirectSummands", + Version => "0.1", + Date => "", + Headline => "", + Authors => { + { Name => "Devlin Mallory", Email => "malloryd@math.utah.edu", HomePage => "https://math.utah.edu/~malloryd/"}, + { Name => "Mahrud Sayrafi", Email => "mahrud@umn.edu", HomePage => "https://math.umn.edu/~mahrud/"} + }, + PackageImports => { + "RationalPoints2", -- for rationalPoints in findIdem + -- "PushForward", -- only for frobenius.m2 + "Polyhedra", -- for coneFromVData and coneComp + "Truncations", -- for effGenerators + }, + AuxiliaryFiles => false, + DebuggingMode => true + ) + +export { + "summands", + "Indecomposable", + "ExtendGroundField" + } + +----------------------------------------------------------------------------- +-* Code section *- +----------------------------------------------------------------------------- + +--needs "helpers.m2" +--needs "frobenius.m2" +needs "./DirectSummands/idempotents.m2" + +----------------------------------------------------------------------------- +-- Things to move to the Core +----------------------------------------------------------------------------- + +-* -- FIXME: what was this for? +importFrom_Core { "raw", "submatrixFree", "listZZ", "rawSubmatrix" }; +try ( +submatrixFree = (m, rows, cols) -> (if rows === null + then rawSubmatrix(raw cover m, listZZ cols) + else rawSubmatrix(raw cover m, listZZ rows, + if cols =!= null then listZZ cols else 0 .. numgens source m - 1)) +) +*- + +-- return the submatrix with given degrees of target and source +submatrixByDegrees(Matrix, Sequence) := (m, degs) -> ( + (tar, src) := degs; + col := if src =!= null then positions(degrees source m, deg -> member(deg, src)); + row := if tar =!= null then positions(degrees target m, deg -> member(deg, tar)); + submatrix(m, row, col)) + +-- TODO: perhaps also take degree into account +CoherentSheaf ? CoherentSheaf := +Module ? Module := (M, N) -> rank M ? rank N + +-- TODO: move to Core +position(ZZ, Function) := o -> (n, f) -> position(0..n-1, f) +-- TODO: this is different from position(List,List,Function) +position' = (B, C, f) -> for b in B do for c in C do if f(b, c) then return (b, c) + +-- TODO: what generality should this be in? +-- WANT: +-- R ** ZZ/101 to change characteristic +-- R ** S to change coefficient ring +-- TODO: can you change the ground field but keep the tower structure? +QuotientRing ** GaloisField := +PolynomialRing ** GaloisField := (R, L) -> ( + -- TODO: in general we may want to keep part of the ring tower + A := first flattenRing(R, CoefficientRing => null); + quotient sub(ideal A, L monoid A)) + +changeBaseField = (L, M) -> ( + S := first flattenRing(ring M, CoefficientRing => null); + R := quotient sub(ideal S, L monoid S); + coker sub(presentation M, R)) + +nonzero = x -> select(x, i -> i != 0) + +----------------------------------------------------------------------------- +----------------------------------------------------------------------------- + +importFrom_Truncations { "effGenerators" } + +coneComp = (C, u, v) -> ( + --if u == v then symbol== else + if contains(C, matrix vector(v - u)) then symbol <= else + if contains(C, matrix vector(u - v)) then symbol > else incomparable) + +-- TODO: add this as a strategy to basis +smartBasis = (deg, M) -> ( + if instance(deg, ZZ) then deg = {deg}; + degs := if #deg == 1 then {min(unique degrees M)} else ( + -- FIXME: in the multigraded case sometimes just using basis is faster: + return basis(deg, M); + -- in the multigraded case, coneMin and coneComp can be slow + -- but for sufficiently large modules they are still an improvement + -- TODO: make them faster + C := coneFromVData effGenerators ring M; + --elapsedTime compMin(C, unique degrees M) -- TODO: this is not the right thing + select(unique degrees M, d -> coneComp(C, d, deg) == symbol <=)); + if degs === {deg} then map(M, , submatrixByDegrees(gens cover M, (, degs))) + -- M_(positions(degrees M, d -> d == deg)) + -- FIXME: this line forgets the homomorphism information + --else basis(deg, image map(M, , submatrixByDegrees(gens cover M, (, degs))))) -- TODO: confirm that this is faster + else basis(deg, M)) -- caching this causes issues! + +----------------------------------------------------------------------------- +----------------------------------------------------------------------------- + +-- this is a kludge to handle the case when h^2 = ah +reduceScalar = m -> m // scan(unique flatten entries m | {1}, x -> if isConstant x and not zero x then break x) +isIdempotent = h -> reduceScalar(h^2) == reduceScalar h + +-- Note: M may need to be extended to a field extensions +-- TODO: cache the inclusion maps +-- Strategies: +-- 1 => use expanded hom +summands = method(Options => { Verbose => true, Strategy => 1, ExtendGroundField => null }) +summands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( + -- Note: rank does weird stuff if R is not a domain + if 0 < debugLevel then printerr("splitting module of rank: ", toString rank M); + if opts.ExtendGroundField =!= null then ( + L := opts.ExtendGroundField; + L = if instance(L, ZZ) then GF(char ring M, L) + else if instance(L, Ring) then L else error "expected an integer or a ground field"; + M = changeBaseField(L, M)); + if 1 < #components M then return flatten apply(components M, summands); + zdeg := degree 1_(ring M); + --TODO: make "elapsedTime" contingent on verbosity + A := if opts.Strategy == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step + B := smartBasis(zdeg, A); + K := coker vars ring M; + -- FIXME: this currently does not find _all_ idempotents + -- FIXME: why is B_{0} so slow for the Horrocks-Mumford example?! + flag := true; -- whether all homomorphisms are zero mod m; + idem := position(numcols B, c -> ( + h := homomorphism B_{c}; + if h == id_M then false else ( + if flag and K ** h != 0 then flag = false; + isIdempotent h)) + ); + -- check if M is certifiably indecomposable + -- TODO: is homomorphism cached? + if flag then ( + -- this is the same as checking: + -- all(numcols B, i -> homomorphism B_{i} == id_M or K ** homomorphism B_{i} == 0) + if 0 < debugLevel then printerr("\t... certified indecomposable!"); + M.cache.Indecomposable = true; {M} ); + -- TODO: parallelize + h := if idem =!= null then homomorphism B_{idem} else try findIdem M; + if h === null then {M} else nonzero flatten join( + -- TODO: restrict End M to each summand and pass it on + M1 := prune image h; + M2 := prune coker h; + -- Projection maps to the summands + --p1 := inverse M1.cache.pruningMap * map(image h, M, h); + --p2 := inverse M2.cache.pruningMap * map(coker h, M, h); + --B1.cache.homomorphism = f -> map(M1, M1, adjoint'(p1 * f * inverse p1, M1, M1), Degree => first degrees source f + degree f); + summands(M1, opts), + summands(M2, opts)) + )) + +-- TODO: if ExtendGroundField is given, change variety +summands CoherentSheaf := List => opts -> F -> apply(summands(module F, opts), N -> sheaf(-*variety F,*- N)) +--summands Matrix := List => opts -> f -> () -- TODO: should be functorial +--summands Complex := List => opts -> C -> () -- TODO: should be functorial + +isDefinitelyIndecomposable = method() +isDefinitelyIndecomposable Module := M -> M.cache.?Indecomposable +isDefinitelyIndecomposable CoherentSheaf := M -> isDefinitelyIndecomposable module M + +-* TODO: not done yet +diagonalize = M -> ( + m := presentation M; + elapsedTime A := End M; -- most time consuming step + elapsedTime B := basis(degree 1_(ring M), A); + h = generalEndomorphism M; + N0 = image homomorphism B_{idem}; + N = prune N0; + psi = inverse N.cache.pruningMap; -- map N0 --> N + phi = map(N0, M, homomorphism B_{idem}); -- map M --> N0 + f = psi * phi; -- map M --> N + h' = f * h * inverse f; + source h' == N; + target h' == N; + h' + ) +*- + +----------------------------------------------------------------------------- +-- Hom in specific degrees +----------------------------------------------------------------------------- +-- TODO: add DegreeLimit as an option to Hom instead + +-- Hom(Module, Module) := Module => (M, N) -> ( +-- Y := youngest(M.cache.cache, N.cache.cache); +-- if Y#?(Hom, M, N) then return Y#(Hom, M, N); +-- H := trim kernel (transpose presentation M ** N); +-- H.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); +-- H.cache.formation = FunctionApplication { Hom, (M, N) }; +-- Y#(Hom, M, N) = H) -- a hack: we really want to type "Hom(M, N) = ..." +-- finds submodule of Hom containing at least the homomorphisms of degree e +Hom(Module, Module, ZZ) := Module => (M, N, e) -> Hom(M, N, {e}) +Hom(Module, Module, List) := Module => (M, N, e) -> ( + Y := youngest(M.cache.cache, N.cache.cache); + if Y#?(Hom, M, N, e) then return Y#(Hom, M, N, e); + A := presentation M; + B := presentation N; + (G, F, piM) := (target A, source A, inducedMap(M, G, gens M)); -- not used + (K, H, piN) := (target B, source B, inducedMap(N, K, gens N)); + Psi := (Hom(A, N) * Hom(G, piN)) // Hom(F, piN); + L := syz(Psi | (-Hom(F, B)), DegreeLimit => e); + p := map(Hom(G, K), Hom(G, K) ++ Hom(F, H), (id_(Hom(G, K)) | map(Hom(G, K), Hom(F, H), 0))); + HMN := trim image(Hom(G, piN) * p * L); + HMN.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); + Y#(Hom, M, N, e) = HMN; -- a hack: we really want to type "Hom(M, N) = ..." + HMN.cache.formation = FunctionApplication { Hom, (M, N, e) }; + HMN) + + +----------------------------------------------------------------------------- +-* Documentation section *- +----------------------------------------------------------------------------- + +beginDocumentation() + +/// +Node + Key + DirectSummands + Headline + Description + Text + Tree + Example + CannedExample + Acknowledgement + Contributors + References + Caveat + SeeAlso + Subnodes +/// + +doc /// +Node + Key + summands + Headline + summands of a module or coherent sheaf + Usage + summands M + Inputs + M:{Module,CoherentSheaf} + Outputs + :List + containing modules or coherent sheaves which are summands of $M$ + Description + Text + This function attempts to find the indecomposable summands of a module or coherent sheaf $M$. + Example + S = QQ[x,y] + M = coker matrix{{x,y},{x,x}} + L = summands M + assert isIsomorphic(M, directSum L) + SeeAlso + findIdem +/// + +-- Template: +/// +Node + Key + Headline + Usage + Inputs + Outputs + Consequences + Item + Description + Text + Example + CannedExample + Code + Pre + ExampleFiles + Contributors + References + Caveat + SeeAlso +/// + +----------------------------------------------------------------------------- +-* Test section *- +----------------------------------------------------------------------------- + +TEST /// -* [insert short title for this test] *- + S = QQ[x,y] + M = coker matrix{{1,0},{1,y}} + A = summands M + B = summands prune M + C = summands trim M + assert same({prune M}, A, B, prune \ C) +/// + +TEST /// -- Direct Summands of a ring + S = kk[x,y,z] + R = kk[x,y,z,w]/(x^3+y^3+z^3+w^3) + f = map(R, S) + M = pushForward(f, module R) + assert(M == S^{0,-1,-2}) +/// + +end-- + +----------------------------------------------------------------------------- +-* Development section *- +----------------------------------------------------------------------------- + +restart +debug needsPackage "DirectSummands" +check "DirectSummands" + +uninstallPackage "DirectSummands" +restart +installPackage "DirectSummands" +viewHelp "DirectSummands" + +end-- +restart +needsPackage "DirectSummands" + +-- basic free module test +R = ZZ/2[x_0..x_5] +M = R^{2:-2,2:0,2:2} +debugLevel=1 +elapsedTime summands M +elapsedTime summands(M, ExtendGroundField => 2) +elapsedTime summands(M, ExtendGroundField => 4) + +summands(M, ExtendGroundField => ZZ/101) +summands(M, ExtendGroundField => GF(2,2)) + +-- basic multigraded test +R = kk[x,y,z] ** kk[a,b,c] +M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} +assert isIsomorphic(directSum elapsedTime summands M, M) + +-- presentation of the Horrocks-Mumford bundle +restart +needsPackage "DirectSummands" +needsPackage "BGG" +S = ZZ/32003[x_0..x_4]; +E = ZZ/32003[e_0..e_4,SkewCommutative=>true]; +alphad = matrix{{e_4*e_1, e_2*e_3},{e_0*e_2, e_3*e_4}, + {e_1*e_3, e_4*e_0},{e_2*e_4, e_0*e_1}, + {e_3*e_0, e_1*e_2}}; +alphad=map(E^5,E^{-2,-2},alphad) +alpha=syz alphad +alphad=beilinson(alphad,S); +alpha=beilinson(alpha,S); + +M = FHM = prune homology(alphad,alpha); +assert(rank FHM == 2) +elapsedTime assert(summands FHM == {FHM}) -- ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~11 + +-- is smartBasis useful? yes! +restart +needsPackage "DirectSummands" +R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); +X = Proj R; +M = frobeniusPushforward(OO_X,1); +A = End M; +-- 0.39809 seconds elapsed +elapsedTime basis({3}, A); +-- 0.171702 seconds elapsed +elapsedTime smartBasis({3}, A); + +assert({1, 2, 2, 2} == rank \ summands M) +assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) +assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) +assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) + +restart +needsPackage "DirectSummands" + + +-------- +-- summand of 4th syzygy of residue field of ring defined by +-- ideal(y*z,x*z,y^3,x*y^2+z^3,x^2*y,x^3) is indecomposable, +-- but the current method doesn't really show that definitively diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 new file mode 100644 index 00000000000..1c0296bb36b --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -0,0 +1,64 @@ +--needsPackage "RationalPoints2" + +generalEndomorphism = method() +generalEndomorphism Module := M -> ( + R := ring M; + EndM := Hom(M, M, degree 1_R); + --EndM := End(M); + B := smartBasis(0, EndM); + --r := rank source B; + --if r == 1 then return "warning: End(M)_0 is 1-dimensional"; + --rm=length select(for i from 0 to r-1 list( unique apply(flatten entries homomorphism B_{i},j->j% ideal gens R) == {0}) ,k->k==false); + --if rm< 2 then return "warning: End(M)_0/maximal ideal is 1-dimensional"; + homomorphism(B * random(ZZ^(rank source B),ZZ^1))) +generalEndomorphism CoherentSheaf := M -> generalEndomorphism module M + +charMatrix = A -> ( + R := ring A; + m := ideal gens R; + k := coefficientRing R; + local t; + T := k[t]; + AT := sub(cover A, T); + n := rank source AT; + AT-t*id_(T^n) + ) + +protect Tries +findIdem = method(Options => { Tries => 50 }) +findIdem Module := opts -> M -> findIdem(M, fieldExponent coefficientRing ring M) +findIdem(Module, ZZ) := opts -> (M, e) -> ( + L := coefficientRing ring M; + p := char L; + tries:=0; + r:=numgens M; + er:=max(for i from 1 to r do if p^i>r then break i , e); + for i to opts.Tries - 1 do ( + tries =tries +1; + f := generalEndomorphism M; + eigen := flatten rationalPoints ideal det charMatrix(f); + opers := flatten for i in eigen list if p == 0 then (f-i*id_M) else for j from 0 to e list (((f-i*id_M)^(p^er))^(p^(j+1)-1)); + idem := position(opers, g -> isIdempotent( g**(quotient ideal gens ring g)) and g != id_M and g != 0); + if idem =!= null then ( + print ("it took "|tries|" tries"); + break opers_idem))) +findIdem CoherentSheaf := opts -> M -> findIdem module M + +testSplitting = (L, M0)->( + B := smartBasis(0, module sheafHom(L, M0)); + b := rank source B; + C := smartBasis(0, module sheafHom(M0, L)); + c := rank source C; + isSplitting := (i,j) -> reduceScalar(homomorphism C_{j} * homomorphism B_{i}) == id_(module L); + l := if (P := position'(0..b-1, 0..c-1, isSplitting)) === null then return else first P; + sheaf coker homomorphism B_{l} + ) + +fieldExponent = L -> ( + p := char L; + if p == 0 then return 1 else ( + a := L_0; + e := 1; + while a^(p^e) != a do (e = e + 1); + e) + ) From 9565f977a2391f60f9e981fd42e680261606bc67 Mon Sep 17 00:00:00 2001 From: Devlin-Mallory Date: Wed, 18 Oct 2023 16:55:17 -0600 Subject: [PATCH 03/88] exported "findIdem" command, though we should rename it at some point --- M2/Macaulay2/packages/DirectSummands.m2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 868582e357e..5a1957e81c3 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -20,7 +20,8 @@ newPackage( export { "summands", "Indecomposable", - "ExtendGroundField" + "ExtendGroundField", + "findIdem" } ----------------------------------------------------------------------------- From 85a830ecd9475071646d20542a8192d873026d69 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 25 Oct 2023 18:03:24 -0500 Subject: [PATCH 04/88] all checks are green --- M2/Macaulay2/packages/DirectSummands.m2 | 193 +++++++++++------- .../packages/DirectSummands/frobenius.m2 | 156 ++++++++++++++ .../packages/DirectSummands/idempotents.m2 | 80 ++++---- 3 files changed, 307 insertions(+), 122 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/frobenius.m2 diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 5a1957e81c3..6bb88972f47 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -1,35 +1,50 @@ +--------------------------------------------------------------------------- +-- PURPOSE : to compute direct summands of modules and coherent sheaves +-- +-- UPDATE HISTORY : created Oct 2023 +-- +-- TODO : +-- 1. implement over Dmodules and other non-commutative rings +-- 2. try to find all irreducible idempotents from End M using representation theory +--------------------------------------------------------------------------- newPackage( "DirectSummands", Version => "0.1", - Date => "", - Headline => "", + Date => "25 Oct 2023", + Headline => "direct summands of modules and coherent sheaves", Authors => { { Name => "Devlin Mallory", Email => "malloryd@math.utah.edu", HomePage => "https://math.utah.edu/~malloryd/"}, { Name => "Mahrud Sayrafi", Email => "mahrud@umn.edu", HomePage => "https://math.umn.edu/~mahrud/"} }, PackageImports => { - "RationalPoints2", -- for rationalPoints in findIdem - -- "PushForward", -- only for frobenius.m2 + "RationalPoints2", -- for rationalPoints in findIdempotent + "PushForward", -- only for frobenius.m2 "Polyhedra", -- for coneFromVData and coneComp "Truncations", -- for effGenerators + -- "Varieties", }, AuxiliaryFiles => false, DebuggingMode => true ) export { - "summands", + "directSummands", "Indecomposable", "ExtendGroundField", - "findIdem" + "findIdempotent", + -- aliases + "summands" => "directSummands", + "findIdem" => "findIdempotent", } ----------------------------------------------------------------------------- -* Code section *- ----------------------------------------------------------------------------- ---needs "helpers.m2" ---needs "frobenius.m2" +-- helpers for computing Frobenius pushforwards of modules and sheaves +-- TODO: move to PushForward package? +needs "./DirectSummands/frobenius.m2" +-- helpers for finding random idempotents of a module needs "./DirectSummands/idempotents.m2" ----------------------------------------------------------------------------- @@ -76,7 +91,8 @@ PolynomialRing ** GaloisField := (R, L) -> ( changeBaseField = (L, M) -> ( S := first flattenRing(ring M, CoefficientRing => null); R := quotient sub(ideal S, L monoid S); - coker sub(presentation M, R)) + directSum apply(components M, + N -> coker sub(presentation N, R))) nonzero = x -> select(x, i -> i != 0) @@ -115,27 +131,36 @@ smartBasis = (deg, M) -> ( reduceScalar = m -> m // scan(unique flatten entries m | {1}, x -> if isConstant x and not zero x then break x) isIdempotent = h -> reduceScalar(h^2) == reduceScalar h +-- TODO: can we return cached summands from the closest field extension? +-- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) +cachedSummands = M -> if M.cache#?(symbol summands => null) then M.cache#(symbol summands => null) else {M} + -- Note: M may need to be extended to a field extensions +-- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps -- Strategies: -- 1 => use expanded hom -summands = method(Options => { Verbose => true, Strategy => 1, ExtendGroundField => null }) -summands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( +directSummands = method(Options => { Verbose => true, Strategy => 1, ExtendGroundField => null }) +directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( -- Note: rank does weird stuff if R is not a domain + -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis + R := ring M; + if not isCommutative R and not isWeylAlgebra R then error "expected a commutative base ring"; if 0 < debugLevel then printerr("splitting module of rank: ", toString rank M); + if isFreeModule M then return apply(-degrees M, d -> R^{d}); + if 1 < #components M then return flatten apply(components M, directSummands_opts); -- TODO: parallelize if opts.ExtendGroundField =!= null then ( L := opts.ExtendGroundField; - L = if instance(L, ZZ) then GF(char ring M, L) + L = if instance(L, ZZ) then GF(char R, L) else if instance(L, Ring) then L else error "expected an integer or a ground field"; - M = changeBaseField(L, M)); - if 1 < #components M then return flatten apply(components M, summands); - zdeg := degree 1_(ring M); + M = changeBaseField(L, directSum cachedSummands M); + R = ring M); + K := coker vars R; + zdeg := toList(degreeLength R : 0); --TODO: make "elapsedTime" contingent on verbosity A := if opts.Strategy == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step B := smartBasis(zdeg, A); - K := coker vars ring M; -- FIXME: this currently does not find _all_ idempotents - -- FIXME: why is B_{0} so slow for the Horrocks-Mumford example?! flag := true; -- whether all homomorphisms are zero mod m; idem := position(numcols B, c -> ( h := homomorphism B_{c}; @@ -144,30 +169,39 @@ summands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGr isIdempotent h)) ); -- check if M is certifiably indecomposable - -- TODO: is homomorphism cached? if flag then ( - -- this is the same as checking: - -- all(numcols B, i -> homomorphism B_{i} == id_M or K ** homomorphism B_{i} == 0) if 0 < debugLevel then printerr("\t... certified indecomposable!"); - M.cache.Indecomposable = true; {M} ); + M.cache.Indecomposable = true; return {M} ); -- TODO: parallelize - h := if idem =!= null then homomorphism B_{idem} else try findIdem M; + h := if idem =!= null then homomorphism B_{idem} else try findIdempotent M; if h === null then {M} else nonzero flatten join( - -- TODO: restrict End M to each summand and pass it on + -- TODO: restrict End M to each summand and pass it on M1 := prune image h; M2 := prune coker h; -- Projection maps to the summands --p1 := inverse M1.cache.pruningMap * map(image h, M, h); --p2 := inverse M2.cache.pruningMap * map(coker h, M, h); --B1.cache.homomorphism = f -> map(M1, M1, adjoint'(p1 * f * inverse p1, M1, M1), Degree => first degrees source f + degree f); - summands(M1, opts), - summands(M2, opts)) + directSummands(M1, opts), + directSummands(M2, opts)) )) -- TODO: if ExtendGroundField is given, change variety -summands CoherentSheaf := List => opts -> F -> apply(summands(module F, opts), N -> sheaf(-*variety F,*- N)) ---summands Matrix := List => opts -> f -> () -- TODO: should be functorial ---summands Complex := List => opts -> C -> () -- TODO: should be functorial +directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf(-*variety F,*- N)) +--directSummands Matrix := List => opts -> f -> () -- TODO: should be functorial +--directSummands Complex := List => opts -> C -> () -- TODO: should be functorial + +-- TODO: export and document +-- given sheaves L and M0, tests whether L is a summand of M0 +testSplitting = (L, M0)->( + B := smartBasis(0, module sheafHom(L, M0)); + b := rank source B; + C := smartBasis(0, module sheafHom(M0, L)); + c := rank source C; + isSplitting := (i,j) -> reduceScalar(homomorphism C_{j} * homomorphism B_{i}) == id_(module L); + l := if (P := position'(0..b-1, 0..c-1, isSplitting)) === null then return else first P; + sheaf coker homomorphism B_{l} + ) isDefinitelyIndecomposable = method() isDefinitelyIndecomposable Module := M -> M.cache.?Indecomposable @@ -249,16 +283,16 @@ Node doc /// Node Key - summands + directSummands Headline - summands of a module or coherent sheaf + direct summands of a module or coherent sheaf Usage summands M Inputs M:{Module,CoherentSheaf} Outputs :List - containing modules or coherent sheaves which are summands of $M$ + containing modules or coherent sheaves which are direct summands of $M$ Description Text This function attempts to find the indecomposable summands of a module or coherent sheaf $M$. @@ -268,7 +302,7 @@ Node L = summands M assert isIsomorphic(M, directSum L) SeeAlso - findIdem + findIdempotent /// -- Template: @@ -298,7 +332,7 @@ Node -* Test section *- ----------------------------------------------------------------------------- -TEST /// -* [insert short title for this test] *- +TEST /// -- basic test S = QQ[x,y] M = coker matrix{{1,0},{1,y}} A = summands M @@ -307,14 +341,49 @@ TEST /// -* [insert short title for this test] *- assert same({prune M}, A, B, prune \ C) /// -TEST /// -- Direct Summands of a ring - S = kk[x,y,z] - R = kk[x,y,z,w]/(x^3+y^3+z^3+w^3) +TEST /// -- direct summands of a free module + R = ZZ/2[x_0..x_5] + M = R^{100:-2,100:0,100:2} + A = summands M; + B = summands(M, ExtendGroundField => 2); + C = summands(M, ExtendGroundField => 4); + D = summands(M, ExtendGroundField => ZZ/101); + E = summands(M, ExtendGroundField => GF(2,2)); + assert same(M, directSum A) + assert same(A, B, C, D, E) +/// + +TEST /// -- direct summands of a multigraded free module + R = QQ[x,y,z] ** QQ[a,b,c] + M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} + assert same(M, directSum summands M) + --assert first isIsomorphic(directSum elapsedTime summands M, M) +/// + +TEST /// -- direct summands of a ring + S = ZZ/3[x,y,z] + R = ZZ/3[x,y,z,w]/(x^3+y^3+z^3+w^3) f = map(R, S) M = pushForward(f, module R) assert(M == S^{0,-1,-2}) /// +TEST /// -- direct summands over field extensions + debug needsPackage "DirectSummands" + R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); + X = Proj R; + M = module frobeniusPushforward(OO_X, 1); + -* is smartBasis useful? yes! + elapsedTime A = End M; -- ~0.65s + elapsedTime B = basis({0}, A); -- ~0.23s + elapsedTime B = smartBasis({0}, A); -- ~0.03s + *- + elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s + elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -- 2.87s -> 2.05 + elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 + elapsedTime assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) -- 2.18s -> 0.47 +/// + end-- ----------------------------------------------------------------------------- @@ -334,56 +403,24 @@ end-- restart needsPackage "DirectSummands" --- basic free module test -R = ZZ/2[x_0..x_5] -M = R^{2:-2,2:0,2:2} -debugLevel=1 -elapsedTime summands M -elapsedTime summands(M, ExtendGroundField => 2) -elapsedTime summands(M, ExtendGroundField => 4) - -summands(M, ExtendGroundField => ZZ/101) -summands(M, ExtendGroundField => GF(2,2)) - --- basic multigraded test -R = kk[x,y,z] ** kk[a,b,c] -M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} -assert isIsomorphic(directSum elapsedTime summands M, M) - +-- TODO: turn into documentation -- presentation of the Horrocks-Mumford bundle restart needsPackage "DirectSummands" needsPackage "BGG" S = ZZ/32003[x_0..x_4]; E = ZZ/32003[e_0..e_4,SkewCommutative=>true]; -alphad = matrix{{e_4*e_1, e_2*e_3},{e_0*e_2, e_3*e_4}, - {e_1*e_3, e_4*e_0},{e_2*e_4, e_0*e_1}, - {e_3*e_0, e_1*e_2}}; -alphad=map(E^5,E^{-2,-2},alphad) -alpha=syz alphad -alphad=beilinson(alphad,S); -alpha=beilinson(alpha,S); - -M = FHM = prune homology(alphad,alpha); +alphad = map(E^5, E^{-2,-2}, transpose matrix{ + { e_1*e_4, -e_0*e_2, -e_1*e_3, -e_2*e_4, e_0*e_3}, + {-e_2*e_3, -e_3*e_4, e_0*e_4, -e_0*e_1, -e_1*e_2}}) +alpha = syz alphad +alphad = beilinson(alphad, S); +alpha = beilinson(alpha, S); +FHM = prune homology(alphad, alpha); assert(rank FHM == 2) -elapsedTime assert(summands FHM == {FHM}) -- ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~11 +elapsedTime assert(summands FHM == {FHM}) -- ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~1s + --- is smartBasis useful? yes! -restart -needsPackage "DirectSummands" -R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); -X = Proj R; -M = frobeniusPushforward(OO_X,1); -A = End M; --- 0.39809 seconds elapsed -elapsedTime basis({3}, A); --- 0.171702 seconds elapsed -elapsedTime smartBasis({3}, A); - -assert({1, 2, 2, 2} == rank \ summands M) -assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) restart needsPackage "DirectSummands" diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 new file mode 100644 index 00000000000..f8c2e9f3530 --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -0,0 +1,156 @@ +--needsPackage "PushForward" +--needsPackage "Polyhedra" -- for lattice points +--needsPackage "Complexes" + +myPushForward = (f, M) -> ( + pushFwd(f, M) + -- doesn't work for matrices: + -- pushForward(f, M) + -- pushForward(f, M, UseHilbertFunction => false) + ) + +protect FrobeniusRing +protect FrobeniusFormation +frobeniusRing = method(TypicalValue => Ring) +frobeniusRing(ZZ, Ring) := (e, R) -> ( + if not R.?cache then R.cache = new CacheTable; + (Rp0, e0) := if R.cache.?FrobeniusFormation then R.cache.FrobeniusFormation else (R, 0); + if Rp0.cache#?(symbol FrobeniusRing, e0 + e) then Rp0.cache#(symbol FrobeniusRing, e0 + e) + else Rp0.cache#(symbol FrobeniusRing, e0 + e) = ( + Rpe := newRing(Rp0, Degrees => (char Rp0)^(e0 + e) * degrees Rp0); + Rpe.cache = new CacheTable; + Rpe.cache.FrobeniusFormation = (Rp0, e0 + e); + Rpe) + ) + +frobeniusMap = method(TypicalValue => RingMap) +frobeniusMap(Ring, ZZ) := (R, e) -> frobeniusMap(e, R) +frobeniusMap(ZZ, Ring) := (e, R) -> map(R, frobeniusRing(e, R), apply(gens R, g -> g^((char R)^e))) + +decomposeFrobeniusPresentation = (e, f) -> ( + p := char ring f; + tardegrees := degrees target f; + srcdegrees := degrees source f; + cube := flatten \ entries \ latticePoints hypercube(degreeLength ring f, 0, p^e - 1); + tarclasses := apply(cube, i -> positions(tardegrees, deg -> deg % p^e == i)); + srcclasses := apply(cube, i -> positions(srcdegrees, deg -> deg % p^e == i)); + -- sorts the degrees of source and column + tarclasses = apply(tarclasses, ell -> ell_(last \ sort \\ reverse \ toList pairs tardegrees_ell)); + srcclasses = apply(srcclasses, ell -> ell_(last \ sort \\ reverse \ toList pairs srcdegrees_ell)); + apply(tarclasses, srcclasses, (tarclass, srcclass) -> submatrix(f, tarclass, srcclass))) + +protect FrobeniusPushforward +frobeniusPushforward = method() +frobeniusPushforward(Thing, ZZ) := (T, e) -> frobeniusPushforward(e, T) +frobeniusPushforward(ZZ, Ring) := (e, R) -> frobeniusPushforward(e, module R) +frobeniusPushforward(ZZ, Ideal) := (e, I) -> frobeniusPushforward(e, quotient I) +-- TODO: cache in a way that the second pushforward is the same as applying pushforward twice +frobeniusPushforward(ZZ, Module) := (e, M) -> ( + if M.cache#?(FrobeniusPushforward, e) + then M.cache#(FrobeniusPushforward, e) + else M.cache#(FrobeniusPushforward, e) = ( + f := presentation myPushForward(frobeniusMap(e, ring M), M); + if not isHomogeneous f then coker f + else directSum apply(decomposeFrobeniusPresentation(e, f), coker))) +-- +frobeniusPushforward(ZZ, Matrix) := (e, f) -> ( + if f.cache#?(FrobeniusPushforward, e) + then f.cache#(FrobeniusPushforward, e) + else f.cache#(FrobeniusPushforward, e) = ( + g := myPushForward(frobeniusMap(e, ring f), f); + if not isHomogeneous g then g + else directSum decomposeFrobeniusPresentation(e, g))) +--frobeniusPushforward(ZZ, Complex) := (e, C) -> () -- TODO + +frobeniusPushforward(ZZ, SheafOfRings) := (e, N0) -> frobeniusPushforward(e, N0^1) -- TODO: is this cached? +frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> ( + R := ring N; + p := char R; + FN := first components frobeniusPushforward(e, module N); + -- slow alternative: + -- FN = myPushForward(frobeniusMap(e, R), module N); + -- prune sheaf image basis(p^e * (max degrees FN // p^e), FN) + Fmatrix := sub(presentation FN, R); + (tardegs, srcdegs) := toSequence(-degrees Fmatrix // p^e); + -- TODO: how long does this take? is it worth caching? + sheaf prune coker map(R^tardegs, R^srcdegs, Fmatrix)) + +protect FrobeniusPullback +frobeniusPullback = method() +frobeniusPullback(Thing, ZZ) := (T, e) -> frobeniusPullback(e, T) +frobeniusPullback(ZZ, Module) := (e, M) -> ( + if M.cache#?(FrobeniusPullback, e) + then M.cache#(FrobeniusPullback, e) + else M.cache#(FrobeniusPullback, e) = ( + R := ring M; + p := char R; + F := frobeniusMap(R, e); + R0 := source F; + A := presentation M; + A0 := sub(A, R0); + coker(F ** map(R0^(-(p^e) * degrees target A0), , A0)))) +frobeniusPullback(ZZ, CoherentSheaf) := (e, F) -> sheaf frobeniusPullback(e, module F) + +end-- +restart +needs "frobenius.m2" +needs "splitting.m2" + +-- Two cubics on P^2_(ZZ/2) +X = toricProjectiveSpace(2, CoefficientRing => ZZ/2) +S = ring X +I = ideal(x_0^3+x_1^3+x_2^3) +J = ideal(x_0^3+x_0^2*x_1+x_1^3+x_0*x_1*x_2+x_2^3) + +R = quotient I +assert(rank \ summands frobeniusPushforward(1, OO_(Proj R)) == {2}) +assert(rank \ summands frobeniusPushforward(1, R) == {2,2}) +--M = coker frobeniusPushforward(char S, I) -- TODO: consolidate with toric version + +R = quotient J +assert(rank \ summands frobeniusPushforward(1, OO_(Proj R)) == {1,1}) +assert(rank \ summands frobeniusPushforward(1, R) == {1, 1, 2}) -- FIXME: this is not correct +--M = coker frobeniusPushforward(char S, J) -- TODO: consolidate with toric version + +-- +R = quotient I +M = frobeniusPushforward(1, R); +N1 = frobeniusPushforward(2, R) +N2 = frobeniusPushforward(1, M) +assert(N1 == N2) -- FIXME: why is this different? +N2' = prune coker frobeniusPushforward(1, presentation M) +assert(N2 == N2') + + +-- +S=(ZZ/2)[x_0..x_2,y_0..y_2,Degrees=>{{1,0},{1,0},{1,0},{0,1},{0,1},{0,1}}]; +J=ideal(x_0*y_0+x_1*y_1+x_2*y_2); +B=S/J; +Y=Proj B; + frobeniusPushforward(B,1) +--why is this giving 0? (if the degrees are standard it doesn't) +--this is now fixed with the new code for decomposeFrobeniusPresentation -DM + + +--- +restart +needs "frobenius.m2" +debug PushForward +S=(ZZ/2)[x_0,x_1,x_2,y_0,y_1,y_2,Degrees=>{{1,0},{1,0},{1,0},{0,1},{0,1},{0,1}}] +S0=(ZZ/2)[x_0,x_1,x_2]**(ZZ/2)[y_0,y_1,y_2]; +S0=tensor((ZZ/2)[x_0,x_1,x_2], (ZZ/2)[y_0,y_1,y_2], DegreeMap => null) +e=1 +errorDepth=1 +target presentation myPushForward(frobeniusMap(e, ring S^1), S^1) +target presentation myPushForward(frobeniusMap(e, ring S0^1), S0^1) + +g' = g +peek g + +degrees (pushAuxHgs g')_0 +degrees (pushAuxHgs g'')_0 +--why are the degrees right for S but not S0? + +RB = RA = S0 +tensor(RB, RA, Join => false) +tensor(RB, RA, Join => true) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 1c0296bb36b..92f344066df 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -13,52 +13,44 @@ generalEndomorphism Module := M -> ( homomorphism(B * random(ZZ^(rank source B),ZZ^1))) generalEndomorphism CoherentSheaf := M -> generalEndomorphism module M -charMatrix = A -> ( - R := ring A; - m := ideal gens R; - k := coefficientRing R; - local t; - T := k[t]; - AT := sub(cover A, T); - n := rank source AT; - AT-t*id_(T^n) - ) +-- TODO: this needs improvement to work over RR, QQ, GF, FractionField, etc. +-- e.g. given a tower such as K[x][y]/I, returns K +baseField = method() +baseField GaloisField := identity +baseField FractionField := identity -- FIXME: does this always work? +baseField Ring := R -> try ( coefficientRing first flattenRing R ) else R + +-- e.g. given a field isomorphic to GF(p,e), returns e +fieldExponent = R -> ( + L := baseField R; + (p, e) := (char L, 1); + if p == 0 then return e; + a := L_0; -- primitive element of L + while a^(p^e) != a do (e = e + 1); + e) + +-- finds the characteristic polynomial of a matrix +char Matrix := A -> ( + if numRows A != numColumns A then error "expected a square matrix"; + T := (baseField ring A)(monoid[vars 0]); + B := sub(cover A, T); + I := id_(source B); + det(B - T_0 * I)) protect Tries -findIdem = method(Options => { Tries => 50 }) -findIdem Module := opts -> M -> findIdem(M, fieldExponent coefficientRing ring M) -findIdem(Module, ZZ) := opts -> (M, e) -> ( - L := coefficientRing ring M; - p := char L; - tries:=0; - r:=numgens M; - er:=max(for i from 1 to r do if p^i>r then break i , e); +findIdempotent = method(Options => { Tries => 50 }) +findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent ring M) +findIdempotent(Module, ZZ) := opts -> (M, e) -> ( + p := char ring M; + K := quotient ideal gens ring M; + l := if p == 0 then e else max(e, ceiling log_p numgens M); for i to opts.Tries - 1 do ( - tries =tries +1; f := generalEndomorphism M; - eigen := flatten rationalPoints ideal det charMatrix(f); - opers := flatten for i in eigen list if p == 0 then (f-i*id_M) else for j from 0 to e list (((f-i*id_M)^(p^er))^(p^(j+1)-1)); - idem := position(opers, g -> isIdempotent( g**(quotient ideal gens ring g)) and g != id_M and g != 0); + eigen := flatten rationalPoints ideal char f; + opers := flatten for i in eigen list if p == 0 then (f - i*id_M) else for j from 0 to e list (((f-i*id_M)^(p^l))^(p^(j+1)-1)); + idem := position(opers, g -> isIdempotent(K ** g) and g != id_M and K ** g != 0); if idem =!= null then ( - print ("it took "|tries|" tries"); - break opers_idem))) -findIdem CoherentSheaf := opts -> M -> findIdem module M - -testSplitting = (L, M0)->( - B := smartBasis(0, module sheafHom(L, M0)); - b := rank source B; - C := smartBasis(0, module sheafHom(M0, L)); - c := rank source C; - isSplitting := (i,j) -> reduceScalar(homomorphism C_{j} * homomorphism B_{i}) == id_(module L); - l := if (P := position'(0..b-1, 0..c-1, isSplitting)) === null then return else first P; - sheaf coker homomorphism B_{l} - ) - -fieldExponent = L -> ( - p := char L; - if p == 0 then return 1 else ( - a := L_0; - e := 1; - while a^(p^e) != a do (e = e + 1); - e) - ) + if 1 < debugLevel then printerr("found idempotent after ", toString i, " attempts."); + return opers_idem)); + error("no idempotent found after ", toString opts.Tries, " attempts. Try extending the base field.")) +findIdempotent CoherentSheaf := opts -> M -> findIdempotent module M From 4b79a111fa23726e5f34695431ee52967ce096b5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 25 Oct 2023 18:24:47 -0500 Subject: [PATCH 05/88] changed Hom of sheaves to use Hom in degree zero --- M2/Macaulay2/packages/DirectSummands.m2 | 16 ++++++++++++++-- .../packages/DirectSummands/frobenius.m2 | 4 ++-- .../packages/DirectSummands/idempotents.m2 | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 6bb88972f47..b8e55ddff48 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -29,9 +29,15 @@ newPackage( export { "directSummands", + "findIdempotent", + -- "Indecomposable", "ExtendGroundField", - "findIdempotent", + -- + "frobeniusMap", + "frobeniusRing", + "frobeniusPullback", + "frobeniusPushforward", -- aliases "summands" => "directSummands", "findIdem" => "findIdempotent", @@ -95,6 +101,7 @@ changeBaseField = (L, M) -> ( N -> coker sub(presentation N, R))) nonzero = x -> select(x, i -> i != 0) +nonnull = x -> select(x, i -> i =!= null) ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- @@ -238,7 +245,7 @@ diagonalize = M -> ( -- H.cache.formation = FunctionApplication { Hom, (M, N) }; -- Y#(Hom, M, N) = H) -- a hack: we really want to type "Hom(M, N) = ..." -- finds submodule of Hom containing at least the homomorphisms of degree e -Hom(Module, Module, ZZ) := Module => (M, N, e) -> Hom(M, N, {e}) +Hom(Module, Module, ZZ) := Module => (M, N, e) -> Hom(M, N, if e == 0 then degree 1_(ring M) else {e}) Hom(Module, Module, List) := Module => (M, N, e) -> ( Y := youngest(M.cache.cache, N.cache.cache); if Y#?(Hom, M, N, e) then return Y#(Hom, M, N, e); @@ -255,6 +262,11 @@ Hom(Module, Module, List) := Module => (M, N, e) -> ( HMN.cache.formation = FunctionApplication { Hom, (M, N, e) }; HMN) +sameVariety := Fs -> if not same apply(Fs, variety) then error "expected coherent sheaves on the same variety" + +-- TODO: confirm this; also: can sheafHom be improved? +Hom(CoherentSheaf, CoherentSheaf) := Module => (F, G) -> ( + sameVariety(F, G); HH^0 sheaf(variety F, Hom(module F, module G, 0))) ----------------------------------------------------------------------------- -* Documentation section *- diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 index f8c2e9f3530..c0189de196f 100644 --- a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -93,8 +93,8 @@ frobeniusPullback(ZZ, CoherentSheaf) := (e, F) -> sheaf frobeniusPullback(e, mod end-- restart -needs "frobenius.m2" -needs "splitting.m2" +needsPackage "DirectSummands" +needsPackage "NormalToricVarieties" -- Two cubics on P^2_(ZZ/2) X = toricProjectiveSpace(2, CoefficientRing => ZZ/2) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 92f344066df..bf37c2b3ce7 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -3,7 +3,7 @@ generalEndomorphism = method() generalEndomorphism Module := M -> ( R := ring M; - EndM := Hom(M, M, degree 1_R); + EndM := Hom(M, M, 0); --EndM := End(M); B := smartBasis(0, EndM); --r := rank source B; From e8ef5020681927adf552d6f6d1ca76d39c0f7cfb Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 27 Oct 2023 16:41:50 -0500 Subject: [PATCH 06/88] moved tests and docs to aux directory --- M2/Macaulay2/packages/DirectSummands.m2 | 146 +----------------- M2/Macaulay2/packages/DirectSummands/docs.m2 | 81 ++++++++++ M2/Macaulay2/packages/DirectSummands/tests.m2 | 51 ++++++ 3 files changed, 138 insertions(+), 140 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/docs.m2 create mode 100644 M2/Macaulay2/packages/DirectSummands/tests.m2 diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index b8e55ddff48..e8dc9bc90a8 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -6,6 +6,7 @@ -- TODO : -- 1. implement over Dmodules and other non-commutative rings -- 2. try to find all irreducible idempotents from End M using representation theory +-- 3. cache maps to the summands --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -23,7 +24,7 @@ newPackage( "Truncations", -- for effGenerators -- "Varieties", }, - AuxiliaryFiles => false, + AuxiliaryFiles => true, DebuggingMode => true ) @@ -274,127 +275,13 @@ Hom(CoherentSheaf, CoherentSheaf) := Module => (F, G) -> ( beginDocumentation() -/// -Node - Key - DirectSummands - Headline - Description - Text - Tree - Example - CannedExample - Acknowledgement - Contributors - References - Caveat - SeeAlso - Subnodes -/// - -doc /// -Node - Key - directSummands - Headline - direct summands of a module or coherent sheaf - Usage - summands M - Inputs - M:{Module,CoherentSheaf} - Outputs - :List - containing modules or coherent sheaves which are direct summands of $M$ - Description - Text - This function attempts to find the indecomposable summands of a module or coherent sheaf $M$. - Example - S = QQ[x,y] - M = coker matrix{{x,y},{x,x}} - L = summands M - assert isIsomorphic(M, directSum L) - SeeAlso - findIdempotent -/// - --- Template: -/// -Node - Key - Headline - Usage - Inputs - Outputs - Consequences - Item - Description - Text - Example - CannedExample - Code - Pre - ExampleFiles - Contributors - References - Caveat - SeeAlso -/// +needs "./DirectSummands/docs.m2" ----------------------------------------------------------------------------- -* Test section *- ----------------------------------------------------------------------------- -TEST /// -- basic test - S = QQ[x,y] - M = coker matrix{{1,0},{1,y}} - A = summands M - B = summands prune M - C = summands trim M - assert same({prune M}, A, B, prune \ C) -/// - -TEST /// -- direct summands of a free module - R = ZZ/2[x_0..x_5] - M = R^{100:-2,100:0,100:2} - A = summands M; - B = summands(M, ExtendGroundField => 2); - C = summands(M, ExtendGroundField => 4); - D = summands(M, ExtendGroundField => ZZ/101); - E = summands(M, ExtendGroundField => GF(2,2)); - assert same(M, directSum A) - assert same(A, B, C, D, E) -/// - -TEST /// -- direct summands of a multigraded free module - R = QQ[x,y,z] ** QQ[a,b,c] - M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} - assert same(M, directSum summands M) - --assert first isIsomorphic(directSum elapsedTime summands M, M) -/// - -TEST /// -- direct summands of a ring - S = ZZ/3[x,y,z] - R = ZZ/3[x,y,z,w]/(x^3+y^3+z^3+w^3) - f = map(R, S) - M = pushForward(f, module R) - assert(M == S^{0,-1,-2}) -/// - -TEST /// -- direct summands over field extensions - debug needsPackage "DirectSummands" - R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); - X = Proj R; - M = module frobeniusPushforward(OO_X, 1); - -* is smartBasis useful? yes! - elapsedTime A = End M; -- ~0.65s - elapsedTime B = basis({0}, A); -- ~0.23s - elapsedTime B = smartBasis({0}, A); -- ~0.03s - *- - elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s - elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -- 2.87s -> 2.05 - elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 - elapsedTime assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) -- 2.18s -> 0.47 -/// +needs "./DirectSummands/tests.m2" end-- @@ -408,32 +295,11 @@ check "DirectSummands" uninstallPackage "DirectSummands" restart +needsPackage "DirectSummands" installPackage "DirectSummands" -viewHelp "DirectSummands" +viewHelp DirectSummands end-- -restart -needsPackage "DirectSummands" - --- TODO: turn into documentation --- presentation of the Horrocks-Mumford bundle -restart -needsPackage "DirectSummands" -needsPackage "BGG" -S = ZZ/32003[x_0..x_4]; -E = ZZ/32003[e_0..e_4,SkewCommutative=>true]; -alphad = map(E^5, E^{-2,-2}, transpose matrix{ - { e_1*e_4, -e_0*e_2, -e_1*e_3, -e_2*e_4, e_0*e_3}, - {-e_2*e_3, -e_3*e_4, e_0*e_4, -e_0*e_1, -e_1*e_2}}) -alpha = syz alphad -alphad = beilinson(alphad, S); -alpha = beilinson(alpha, S); -FHM = prune homology(alphad, alpha); -assert(rank FHM == 2) -elapsedTime assert(summands FHM == {FHM}) -- ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~1s - - - restart needsPackage "DirectSummands" diff --git a/M2/Macaulay2/packages/DirectSummands/docs.m2 b/M2/Macaulay2/packages/DirectSummands/docs.m2 new file mode 100644 index 00000000000..8c3ab1edc83 --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/docs.m2 @@ -0,0 +1,81 @@ +doc /// +Node + Key + DirectSummands + Headline + computing direct summands of modules and coherent sheaves + Description + Text + As an example, we prove the indecomposability of the @wikipedia "Horrocks–Mumford bundle"@ on $\PP4$. + Example + needsPackage "BGG" + S = ZZ/32003[x_0..x_4]; + E = ZZ/32003[e_0..e_4, SkewCommutative => true]; + alphad = map(E^5, E^{-2,-2}, transpose matrix{ + { e_1*e_4, -e_0*e_2, -e_1*e_3, -e_2*e_4, e_0*e_3}, + {-e_2*e_3, -e_3*e_4, e_0*e_4, -e_0*e_1, -e_1*e_2}}); + alpha = syz alphad; + alphad = beilinson(alphad, S); + alpha = beilinson(alpha, S); + FHM = prune homology(alphad, alpha) + assert(2 == rank FHM) + assert({FHM} == summands FHM) -- ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~1s + assert FHM.cache.Indecomposable +-- Tree +-- CannedExample +-- Acknowledgement +-- Contributors +-- References +-- Caveat +-- SeeAlso + Subnodes + directSummands + +Node + Key + directSummands + (directSummands, Module) + (directSummands, CoherentSheaf) + Headline + direct summands of a module or coherent sheaf + Usage + summands M + Inputs + M:{Module,CoherentSheaf} + Outputs + :List + containing modules or coherent sheaves which are direct summands of $M$ + Description + Text + This function attempts to find the indecomposable summands of a module or coherent sheaf $M$. + Example + S = QQ[x,y] + M = coker matrix{{x,y},{x,x}} + L = summands M + assert first isIsomorphic(M, directSum L) + SeeAlso + findIdempotent +/// + +-- Template: +/// +Node + Key + Headline + Usage + Inputs + Outputs + Consequences + Item + Description + Text + Example + CannedExample + Code + Pre + ExampleFiles + Contributors + References + Caveat + SeeAlso +/// diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 new file mode 100644 index 00000000000..eba6d09ca2c --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -0,0 +1,51 @@ +TEST /// -- basic test + S = QQ[x,y] + M = coker matrix{{1,0},{1,y}} + A = summands M + B = summands prune M + C = summands trim M + assert same({prune M}, A, B, prune \ C) +/// + +TEST /// -- direct summands of a free module + R = ZZ/2[x_0..x_5] + M = R^{100:-2,100:0,100:2} + A = summands M; + B = summands(M, ExtendGroundField => 2); + C = summands(M, ExtendGroundField => 4); + D = summands(M, ExtendGroundField => ZZ/101); + E = summands(M, ExtendGroundField => GF(2,2)); + assert same(M, directSum A) + assert same(A, B, C, D, E) +/// + +TEST /// -- direct summands of a multigraded free module + R = QQ[x,y,z] ** QQ[a,b,c] + M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} + assert same(M, directSum summands M) + --assert first isIsomorphic(directSum elapsedTime summands M, M) +/// + +TEST /// -- direct summands of a ring + S = ZZ/3[x,y,z] + R = ZZ/3[x,y,z,w]/(x^3+y^3+z^3+w^3) + f = map(R, S) + M = pushForward(f, module R) + assert(M == S^{0,-1,-2}) +/// + +TEST /// -- direct summands over field extensions + debug needsPackage "DirectSummands" + R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); + X = Proj R; + M = module frobeniusPushforward(OO_X, 1); + -* is smartBasis useful? yes! + elapsedTime A = End M; -- ~0.65s + elapsedTime B = basis({0}, A); -- ~0.23s + elapsedTime B = smartBasis({0}, A); -- ~0.03s + *- + elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s + elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -- 2.87s -> 2.05 + elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 + elapsedTime assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) -- 2.18s -> 0.47 +/// From 89df4f80349b20e203422c6002bfd282b807435e Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 27 Oct 2023 16:45:17 -0500 Subject: [PATCH 07/88] added directSummands(Matrix) --- M2/Macaulay2/packages/DirectSummands.m2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index e8dc9bc90a8..1c034c7548a 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -196,7 +196,7 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex -- TODO: if ExtendGroundField is given, change variety directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf(-*variety F,*- N)) ---directSummands Matrix := List => opts -> f -> () -- TODO: should be functorial +--directSummands Matrix := List => opts -> f -> apply(directSummands(coker f, opts), presentation) --directSummands Complex := List => opts -> C -> () -- TODO: should be functorial -- TODO: export and document From 19193ecf762ff035c46f1d8275d34241b22aa2c2 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 27 Oct 2023 17:46:09 -0500 Subject: [PATCH 08/88] added a main documentation page --- M2/Macaulay2/packages/DirectSummands.m2 | 26 ++++++++++---------- M2/Macaulay2/packages/DirectSummands/docs.m2 | 25 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 1c034c7548a..7ef645841cf 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -12,11 +12,12 @@ newPackage( "DirectSummands", Version => "0.1", Date => "25 Oct 2023", - Headline => "direct summands of modules and coherent sheaves", + Headline => "decompositions of modules and coherent sheaves", Authors => { { Name => "Devlin Mallory", Email => "malloryd@math.utah.edu", HomePage => "https://math.utah.edu/~malloryd/"}, { Name => "Mahrud Sayrafi", Email => "mahrud@umn.edu", HomePage => "https://math.umn.edu/~mahrud/"} }, + Keywords => { "Commutative Algebra" }, PackageImports => { "RationalPoints2", -- for rationalPoints in findIdempotent "PushForward", -- only for frobenius.m2 @@ -270,18 +271,18 @@ Hom(CoherentSheaf, CoherentSheaf) := Module => (F, G) -> ( sameVariety(F, G); HH^0 sheaf(variety F, Hom(module F, module G, 0))) ----------------------------------------------------------------------------- --* Documentation section *- +-* Test section *- ----------------------------------------------------------------------------- -beginDocumentation() - -needs "./DirectSummands/docs.m2" +needs "./DirectSummands/tests.m2" ----------------------------------------------------------------------------- --* Test section *- +-* Documentation section *- ----------------------------------------------------------------------------- -needs "./DirectSummands/tests.m2" +beginDocumentation() + +needs "./DirectSummands/docs.m2" end-- @@ -290,18 +291,17 @@ end-- ----------------------------------------------------------------------------- restart -debug needsPackage "DirectSummands" -check "DirectSummands" +check needsPackage "DirectSummands" uninstallPackage "DirectSummands" restart -needsPackage "DirectSummands" -installPackage "DirectSummands" -viewHelp DirectSummands +viewHelp installPackage "DirectSummands" +viewHelp directSummands +viewHelp end-- restart -needsPackage "DirectSummands" +debug needsPackage "DirectSummands" -------- diff --git a/M2/Macaulay2/packages/DirectSummands/docs.m2 b/M2/Macaulay2/packages/DirectSummands/docs.m2 index 8c3ab1edc83..2da6ed7b2f5 100644 --- a/M2/Macaulay2/packages/DirectSummands/docs.m2 +++ b/M2/Macaulay2/packages/DirectSummands/docs.m2 @@ -3,10 +3,10 @@ Node Key DirectSummands Headline - computing direct summands of modules and coherent sheaves + decompositions of graded modules and coherent sheaves Description Text - As an example, we prove the indecomposability of the @wikipedia "Horrocks–Mumford bundle"@ on $\PP4$. + As an example, we prove the indecomposability of the @wikipedia "Horrocks–Mumford bundle"@ on $\PP^4$. Example needsPackage "BGG" S = ZZ/32003[x_0..x_4]; @@ -19,15 +19,12 @@ Node alpha = beilinson(alpha, S); FHM = prune homology(alphad, alpha) assert(2 == rank FHM) - assert({FHM} == summands FHM) -- ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~1s + -- initially ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~1s total! + assert({FHM} == summands FHM) assert FHM.cache.Indecomposable --- Tree --- CannedExample --- Acknowledgement --- Contributors --- References --- Caveat --- SeeAlso + Acknowledgement + The authors thank the organizers of the @HREF{"https://aimath.org/pastworkshops/macaulay2efie.html", + "Macaulay2 workshop at AIM"}@, where significant progress on this package was made. Subnodes directSummands @@ -37,7 +34,7 @@ Node (directSummands, Module) (directSummands, CoherentSheaf) Headline - direct summands of a module or coherent sheaf + computes the direct summands of a graded module or coherent sheaf Usage summands M Inputs @@ -78,4 +75,10 @@ Node References Caveat SeeAlso +-- Tree +-- CannedExample +-- Contributors +-- References +-- Caveat +-- SeeAlso /// From 01a89e6ff348f76924a5e82588361871e6130923 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 3 Dec 2023 03:49:34 -0600 Subject: [PATCH 09/88] removed trim from Hom for speedups --- M2/Macaulay2/packages/DirectSummands.m2 | 59 +++++++++++-------- M2/Macaulay2/packages/DirectSummands/tests.m2 | 3 +- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 7ef645841cf..6c659d3ba13 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -7,6 +7,7 @@ -- 1. implement over Dmodules and other non-commutative rings -- 2. try to find all irreducible idempotents from End M using representation theory -- 3. cache maps to the summands +-- 4. rewrite (isDirectSum, Module) --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -118,7 +119,7 @@ coneComp = (C, u, v) -> ( -- TODO: add this as a strategy to basis smartBasis = (deg, M) -> ( if instance(deg, ZZ) then deg = {deg}; - degs := if #deg == 1 then {min(unique degrees M)} else ( + degs := if #deg == 1 then select(unique degrees M, d -> d <= deg) else ( -- FIXME: in the multigraded case sometimes just using basis is faster: return basis(deg, M); -- in the multigraded case, coneMin and coneComp can be slow @@ -127,11 +128,12 @@ smartBasis = (deg, M) -> ( C := coneFromVData effGenerators ring M; --elapsedTime compMin(C, unique degrees M) -- TODO: this is not the right thing select(unique degrees M, d -> coneComp(C, d, deg) == symbol <=)); - if degs === {deg} then map(M, , submatrixByDegrees(gens cover M, (, degs))) - -- M_(positions(degrees M, d -> d == deg)) - -- FIXME: this line forgets the homomorphism information - --else basis(deg, image map(M, , submatrixByDegrees(gens cover M, (, degs))))) -- TODO: confirm that this is faster - else basis(deg, M)) -- caching this causes issues! + if degs === {deg} then return map(M, , submatrixByDegrees(gens cover M, (, degs))); + M' := subquotient(ambient M, + if M.?generators then submatrixByDegrees(gens M, (, degs)), + if M.?relations then relations M); + M'.cache.homomorphism = M.cache.homomorphism; + basis(deg, M')) -- caching this globally causes issues! ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- @@ -167,8 +169,8 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex K := coker vars R; zdeg := toList(degreeLength R : 0); --TODO: make "elapsedTime" contingent on verbosity - A := if opts.Strategy == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step - B := smartBasis(zdeg, A); + elapsedTime A := if opts.Strategy == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step + elapsedTime B := smartBasis(zdeg, A); -- FIXME: this currently does not find _all_ idempotents flag := true; -- whether all homomorphisms are zero mod m; idem := position(numcols B, c -> ( @@ -237,15 +239,21 @@ diagonalize = M -> ( ----------------------------------------------------------------------------- -- Hom in specific degrees ----------------------------------------------------------------------------- --- TODO: add DegreeLimit as an option to Hom instead --- Hom(Module, Module) := Module => (M, N) -> ( --- Y := youngest(M.cache.cache, N.cache.cache); --- if Y#?(Hom, M, N) then return Y#(Hom, M, N); --- H := trim kernel (transpose presentation M ** N); --- H.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); --- H.cache.formation = FunctionApplication { Hom, (M, N) }; --- Y#(Hom, M, N) = H) -- a hack: we really want to type "Hom(M, N) = ..." +-- same, but without trim, which seems to be unnecessary if at least M is free!! +Hom(Module, Module) := Module => (M,N) -> ( + Y := youngest(M.cache.cache,N.cache.cache); + if Y#?(Hom,M,N) then return Y#(Hom,M,N); + H := -* trim *- kernel (transpose presentation M ** N); + H.cache.homomorphism = (f) -> map(N,M,adjoint'(f,M,N), Degree => first degrees source f + degree f); + Y#(Hom,M,N) = H; -- a hack: we really want to type "Hom(M,N) = ..." + H.cache.formation = FunctionApplication { Hom, (M,N) }; + H) + +Hom(Matrix,Module) := Matrix => (f,N) -> inducedMap(Hom(source f,N),Hom(target f,N),transpose cover f ** N,Verify=>false) + +-- TODO: add DegreeLimit as an option to Hom instead +--Hom = method(Options => { DegreeLimit => null, MinimalGenerators => null }) -- finds submodule of Hom containing at least the homomorphisms of degree e Hom(Module, Module, ZZ) := Module => (M, N, e) -> Hom(M, N, if e == 0 then degree 1_(ring M) else {e}) Hom(Module, Module, List) := Module => (M, N, e) -> ( @@ -254,15 +262,18 @@ Hom(Module, Module, List) := Module => (M, N, e) -> ( A := presentation M; B := presentation N; (G, F, piM) := (target A, source A, inducedMap(M, G, gens M)); -- not used - (K, H, piN) := (target B, source B, inducedMap(N, K, gens N)); + (L, K, piN) := (target B, source B, inducedMap(N, L, gens N)); + -- TODO: the trim in Hom(target A, N) takes 2/3 of total time Psi := (Hom(A, N) * Hom(G, piN)) // Hom(F, piN); - L := syz(Psi | (-Hom(F, B)), DegreeLimit => e); - p := map(Hom(G, K), Hom(G, K) ++ Hom(F, H), (id_(Hom(G, K)) | map(Hom(G, K), Hom(F, H), 0))); - HMN := trim image(Hom(G, piN) * p * L); - HMN.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); - Y#(Hom, M, N, e) = HMN; -- a hack: we really want to type "Hom(M, N) = ..." - HMN.cache.formation = FunctionApplication { Hom, (M, N, e) }; - HMN) + p := id_(Hom(G, L)) | map(Hom(G, L), Hom(F, K), 0); + r := syz(Psi | -Hom(F, B), DegreeLimit => e); + --trim := if opts.MinimalGenerators then trim else identity; + Y#(Hom, M, N, e) = + -- TODO: trim takes 1/3 of total time here + H := -*trim*- image(Hom(G, piN) * p * r); + H.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); + H.cache.formation = FunctionApplication { Hom, (M, N, e) }; + H) sameVariety := Fs -> if not same apply(Fs, variety) then error "expected coherent sheaves on the same variety" diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index eba6d09ca2c..099130f7702 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -4,7 +4,8 @@ TEST /// -- basic test A = summands M B = summands prune M C = summands trim M - assert same({prune M}, A, B, prune \ C) + assert(A === {M}) + assert same({prune M}, B, prune \ C) /// TEST /// -- direct summands of a free module From b02161eb6857ae6b03d880c069722bf9e6643907 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 4 Dec 2023 01:01:13 -0600 Subject: [PATCH 10/88] improved summands for Gr(2,4) --- M2/Macaulay2/packages/DirectSummands.m2 | 84 +++++++++++++------ M2/Macaulay2/packages/DirectSummands/bench.m2 | 36 ++++++++ 2 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/bench.m2 diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 6c659d3ba13..ebf169dd96c 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -36,11 +36,14 @@ export { -- "Indecomposable", "ExtendGroundField", + "Random", + "Tries", -- "frobeniusMap", "frobeniusRing", "frobeniusPullback", "frobeniusPushforward", + "frobeniusTwist", -- aliases "summands" => "directSummands", "findIdem" => "findIdempotent", @@ -144,14 +147,14 @@ isIdempotent = h -> reduceScalar(h^2) == reduceScalar h -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) -cachedSummands = M -> if M.cache#?(symbol summands => null) then M.cache#(symbol summands => null) else {M} +cachedSummands = M -> if M.cache#?(symbol summands => null) then M.cache#(symbol summands => null) else components M -- Note: M may need to be extended to a field extensions -- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps -- Strategies: -- 1 => use expanded hom -directSummands = method(Options => { Verbose => true, Strategy => 1, ExtendGroundField => null }) +directSummands = method(Options => { Tries => 0, Verbose => true, Strategy => 3, ExtendGroundField => null }) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( -- Note: rank does weird stuff if R is not a domain -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis @@ -166,17 +169,27 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex else if instance(L, Ring) then L else error "expected an integer or a ground field"; M = changeBaseField(L, directSum cachedSummands M); R = ring M); + -- Attempt to peel off line bundles by observing the degrees of generators + if opts.Strategy & 2 == 2 then ( + opts = opts ++ { Strategy => opts.Strategy ^^ 2 }; -- so we only try this once + if 0 < debugLevel then stderr << " -- peeling off rank 1 summands: " << flush; + L = directSummands(apply(-unique degrees M, d -> R^{d}), M, opts); + if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; + if 1 < #L then return join(drop(L, -1), directSummands(last L, opts))); + -- K := coker vars R; zdeg := toList(degreeLength R : 0); --TODO: make "elapsedTime" contingent on verbosity - elapsedTime A := if opts.Strategy == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step + elapsedTime A := if opts.Strategy & 1 == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step elapsedTime B := smartBasis(zdeg, A); -- FIXME: this currently does not find _all_ idempotents - flag := true; -- whether all homomorphisms are zero mod m; + flag := true; -- whether all non-identity homomorphisms are zero mod m + -- TODO: 10k columns for F_*(OO_X) on Gr(2,4) over ZZ/3 take a long time idem := position(numcols B, c -> ( h := homomorphism B_{c}; if h == id_M then false else ( - if flag and K ** h != 0 then flag = false; + if flag and K ** h != 0 + then flag = false; isIdempotent h)) ); -- check if M is certifiably indecomposable @@ -185,8 +198,12 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex M.cache.Indecomposable = true; return {M} ); -- TODO: parallelize h := if idem =!= null then homomorphism B_{idem} else try findIdempotent M; + -- TODO: add this when the maps M_[i] and M^[i] also work + -- M.cache.components = if h === null then {M} else nonzero flatten join( -- TODO: restrict End M to each summand and pass it on + -- TODO: could use 'compose' perhaps + -- TODO: can we check if M has multiple copies of M1 or M2 quickly? M1 := prune image h; M2 := prune coker h; -- Projection maps to the summands @@ -202,17 +219,37 @@ directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module --directSummands Matrix := List => opts -> f -> apply(directSummands(coker f, opts), presentation) --directSummands Complex := List => opts -> C -> () -- TODO: should be functorial --- TODO: export and document --- given sheaves L and M0, tests whether L is a summand of M0 -testSplitting = (L, M0)->( - B := smartBasis(0, module sheafHom(L, M0)); - b := rank source B; - C := smartBasis(0, module sheafHom(M0, L)); - c := rank source C; - isSplitting := (i,j) -> reduceScalar(homomorphism C_{j} * homomorphism B_{i}) == id_(module L); - l := if (P := position'(0..b-1, 0..c-1, isSplitting)) === null then return else first P; - sheaf coker homomorphism B_{l} - ) +random Module := Vector => o -> M -> vector(gens M * random(cover M, module ring M, o)) +homomorphism Vector := v -> homomorphism matrix v + +-- tests whether L (perhaps a line bundle) is a summand of M +-- FIXME: it's not guaranteed to work, e.g. on X_4 over ZZ/2 +directSummands(Module, Module) := List => opts -> (L, M) -> sort( + if ring L =!= ring M then error "expected objects over the same ring"; + if rank L >= rank M then return {M}; + zdeg := toList(degreeLength ring L : 0); + -- we look for a composition L -> M -> L which is the identity + B := smartBasis(zdeg, Hom(L, M, zdeg)); + C := smartBasis(zdeg, Hom(M, L, zdeg)); + -- attempt to find a random isomorphism + for i to opts.Tries - 1 do ( + b := homomorphism random image B; + c := homomorphism random image C; + if c * b != 0 and isIsomorphism(c * b) then + return flatten join({L}, directSummands(L, coker b, opts))); + -- TODO: is precomputing the homs too memory intensive? + Bhoms := apply(numcols B, i -> homomorphism B_{i}); + Choms := apply(numcols C, i -> homomorphism C_{i}); + P := position'(Choms, Bhoms, (c, b) -> id_L == reduceScalar(c * b)); + if P === null then return {M}; + if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; + -- TODO: can we detect multiple summands of L at once? + flatten join({L}, directSummands(L, coker last P, opts))) + +-- attempt to peel off summands from a given list of modules +directSummands(List, Module) := List => opts -> (Ls, M) -> fold(Ls, {M}, + (L, LL) -> join(drop(LL, -1), directSummands(L, last LL))) + isDefinitelyIndecomposable = method() isDefinitelyIndecomposable Module := M -> M.cache.?Indecomposable @@ -259,17 +296,16 @@ Hom(Module, Module, ZZ) := Module => (M, N, e) -> Hom(M, N, if e == 0 then deg Hom(Module, Module, List) := Module => (M, N, e) -> ( Y := youngest(M.cache.cache, N.cache.cache); if Y#?(Hom, M, N, e) then return Y#(Hom, M, N, e); - A := presentation M; - B := presentation N; - (G, F, piM) := (target A, source A, inducedMap(M, G, gens M)); -- not used - (L, K, piN) := (target B, source B, inducedMap(N, L, gens N)); - -- TODO: the trim in Hom(target A, N) takes 2/3 of total time - Psi := (Hom(A, N) * Hom(G, piN)) // Hom(F, piN); + A := presentation M; (G, F) := (target A, source A); -- M <-- G <-- F + B := presentation N; (L, K) := (target B, source B); -- N <-- L <-- K + piN := inducedMap(N, L); + -- TODO: the trim in Hom(target A, N) used to take 2/3 of total time + psi := (Hom(A, N) * Hom(G, piN)) // Hom(F, piN); p := id_(Hom(G, L)) | map(Hom(G, L), Hom(F, K), 0); - r := syz(Psi | -Hom(F, B), DegreeLimit => e); + r := syz(psi | -Hom(F, B), DegreeLimit => e); --trim := if opts.MinimalGenerators then trim else identity; Y#(Hom, M, N, e) = - -- TODO: trim takes 1/3 of total time here + -- TODO: trim used to take 1/3 of total time here H := -*trim*- image(Hom(G, piN) * p * r); H.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); H.cache.formation = FunctionApplication { Hom, (M, N, e) }; diff --git a/M2/Macaulay2/packages/DirectSummands/bench.m2 b/M2/Macaulay2/packages/DirectSummands/bench.m2 new file mode 100644 index 00000000000..8b336fded5e --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/bench.m2 @@ -0,0 +1,36 @@ +restart +debug needsPackage "DirectSummands" +debugLevel = 1 + +kk = ZZ/3 +R = quotient Grassmannian(1,3, CoefficientRing => kk) +X = Proj R +elapsedTime M = module frobeniusPushforward(1, OO_X); -- <1s in char 2 & 3 +elapsedTime L = summands M; + +tally apply(L, N -> (rank N, degrees N)) + +-* +Timing results: + q | F_* | Tot | End |basis| misc +----------------------------------- + 2 | <1s | <1s | | | + 4 | ~6s | | | | + 3 | <1s | 43s | 13s | 2.3 | + 5 | 64s | | | | + +ZZ/2 q = 2^1 rk 16: 1:0 14:1 1:2 +ZZ/2 q = 2^2 rk 256 + +ZZ/3 q = 3^1 rk 81: 1:0 44:1 20:2 8:rk 2 bundles + + +*- + + +kk = ZZ/3 +R = quotient Grassmannian(2,5, CoefficientRing => kk) +X = Proj R +elapsedTime M = module frobeniusPushforward(1, OO_X); -- <1s +rank M +elapsedTime L = summands M; From 8b21089e65e86fa287e68ae78fe0bb18481ccbd5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 5 Dec 2023 19:29:07 -0600 Subject: [PATCH 11/88] added frobeniusTwist, X5 / GF(4) works now --- M2/Macaulay2/packages/DirectSummands.m2 | 6 +- .../packages/DirectSummands/frobenius.m2 | 62 +++++++++++++++++-- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index ebf169dd96c..ed486fa3a62 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -233,9 +233,9 @@ directSummands(Module, Module) := List => opts -> (L, M) -> sort( C := smartBasis(zdeg, Hom(M, L, zdeg)); -- attempt to find a random isomorphism for i to opts.Tries - 1 do ( - b := homomorphism random image B; - c := homomorphism random image C; - if c * b != 0 and isIsomorphism(c * b) then + b := homomorphism(B * random source B); + c := homomorphism(C * random source C); + if isIsomorphism(c * b) then return flatten join({L}, directSummands(L, coker b, opts))); -- TODO: is precomputing the homs too memory intensive? Bhoms := apply(numcols B, i -> homomorphism B_{i}); diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 index c0189de196f..459ddda4e4a 100644 --- a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -3,7 +3,8 @@ --needsPackage "Complexes" myPushForward = (f, M) -> ( - pushFwd(f, M) + directSum apply(cachedSummands M, + N -> pushFwd(f, N)) -- doesn't work for matrices: -- pushForward(f, M) -- pushForward(f, M, UseHilbertFunction => false) @@ -23,9 +24,54 @@ frobeniusRing(ZZ, Ring) := (e, R) -> ( Rpe) ) +-- FIXMEEE: +quotient Ideal := QuotientRing => opts -> (cacheValue symbol quotient) (I -> (ring I) / I) +--ideal Ring := (cacheValue symbol ideal) (R -> ideal map(R^1, R^0, 0)) + +-- FIXME: this might forget some information +RingMap ** Module := Module => (f, M) -> directSum apply(cachedSummands M, N -> tensor(f, N)) + +-- TODO: should also work if S is a finite field +-- defined as a QuotientRing rather than GaloisField +frobeniusTwistMap = (e, S) -> ( + k := coefficientRing S; + if char S == 0 or k_0 == 1 then return map(S, S); + a := k_0; + p := char k; + map(S, S, gens S | {a^(p^e)})) + +/// -- TODO: add as test: + R = QQ[x,y] -- or ZZ/p + assert(R === frobeniusTwist(1, R)) +/// + +protect FrobeniusTwist +frobeniusTwist = method() +frobeniusTwist(ZZ, Ring) := Ring => (e, S) -> ( + k := coefficientRing S; + if char S == 0 or k_0 == 1 then return S; + if not S.?cache then S.cache = new CacheTable; + -- FIMXE: towers of pushforwards + if S.cache#?(symbol FrobeniusTwist, e) then S.cache#(symbol FrobeniusTwist, e) + else S.cache#(symbol FrobeniusTwist, e) = ( + F := frobeniusTwistMap(e, ring ideal S); + quotient F ideal S)) +frobeniusTwist(ZZ, Ideal) := Ideal => (e, I) -> frobeniusTwist(e, module I) +frobeniusTwist(ZZ, Module) := Module => (e, M) -> ( + S := ring M; + R := frobeniusTwist(e, S); + F := frobeniusTwistMap(e, S); + -- TODO: is this correct? + F ** M ** R) +frobeniusTwist(ZZ, Matrix) := Matrix => (e, f) -> ( + map(frobeniusTwist(e, target f), frobeniusTwist(e, source f), + frobeniusTwistMap(e, ring f) ** f)) + frobeniusMap = method(TypicalValue => RingMap) frobeniusMap(Ring, ZZ) := (R, e) -> frobeniusMap(e, R) -frobeniusMap(ZZ, Ring) := (e, R) -> map(R, frobeniusRing(e, R), apply(gens R, g -> g^((char R)^e))) +frobeniusMap(ZZ, Ring) := (e, R) -> ( + map(Re := frobeniusTwist(e, R), frobeniusRing(e, R), + apply(gens Re, g -> g^((char R)^e)))) decomposeFrobeniusPresentation = (e, f) -> ( p := char ring f; @@ -49,7 +95,9 @@ frobeniusPushforward(ZZ, Module) := (e, M) -> ( if M.cache#?(FrobeniusPushforward, e) then M.cache#(FrobeniusPushforward, e) else M.cache#(FrobeniusPushforward, e) = ( - f := presentation myPushForward(frobeniusMap(e, ring M), M); + f := presentation myPushForward( + frobeniusMap(e, ring M), + frobeniusTwist(e, M)); if not isHomogeneous f then coker f else directSum apply(decomposeFrobeniusPresentation(e, f), coker))) -- @@ -57,7 +105,9 @@ frobeniusPushforward(ZZ, Matrix) := (e, f) -> ( if f.cache#?(FrobeniusPushforward, e) then f.cache#(FrobeniusPushforward, e) else f.cache#(FrobeniusPushforward, e) = ( - g := myPushForward(frobeniusMap(e, ring f), f); + g := myPushForward( + frobeniusMap(e, ring f), + frobeniusTwist(e, f)); if not isHomogeneous g then g else directSum decomposeFrobeniusPresentation(e, g))) --frobeniusPushforward(ZZ, Complex) := (e, C) -> () -- TODO @@ -141,8 +191,8 @@ S0=(ZZ/2)[x_0,x_1,x_2]**(ZZ/2)[y_0,y_1,y_2]; S0=tensor((ZZ/2)[x_0,x_1,x_2], (ZZ/2)[y_0,y_1,y_2], DegreeMap => null) e=1 errorDepth=1 -target presentation myPushForward(frobeniusMap(e, ring S^1), S^1) -target presentation myPushForward(frobeniusMap(e, ring S0^1), S0^1) +target presentation myPushForward(frobeniusMap(e, ring S^1), frobeniusTwist(e, S^1)) +target presentation myPushForward(frobeniusMap(e, ring S0^1), frobeniusTwist(e, S0^1)) g' = g peek g From c0713a709ac03ee323e8d5224ebcc960f4e88456 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 21 Jan 2024 18:06:02 -0800 Subject: [PATCH 12/88] added Recursive option, several minor improvements --- M2/Macaulay2/packages/DirectSummands.m2 | 94 +++++++++++-------- .../packages/DirectSummands/idempotents.m2 | 5 +- M2/Macaulay2/packages/DirectSummands/tests.m2 | 3 +- 3 files changed, 61 insertions(+), 41 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index ed486fa3a62..aa04434a52d 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -31,22 +31,20 @@ newPackage( ) export { - "directSummands", - "findIdempotent", - -- - "Indecomposable", + -- methods + "directSummands", "summands" => "directSummands", + "findIdempotent", "findIdem" => "findIdempotent", + -- symbols "ExtendGroundField", - "Random", + "Indecomposable", + "Recursive", "Tries", - -- + -- frobenius methods "frobeniusMap", "frobeniusRing", "frobeniusPullback", "frobeniusPushforward", "frobeniusTwist", - -- aliases - "summands" => "directSummands", - "findIdem" => "findIdempotent", } ----------------------------------------------------------------------------- @@ -87,7 +85,9 @@ Module ? Module := (M, N) -> rank M ? rank N -- TODO: move to Core position(ZZ, Function) := o -> (n, f) -> position(0..n-1, f) -- TODO: this is different from position(List,List,Function) -position' = (B, C, f) -> for b in B do for c in C do if f(b, c) then return (b, c) +position' = method() +position'(VisibleList, VisibleList, Function) := (B, C, f) -> for b in B do for c in C do if f(b, c) then return (b, c) +position'(ZZ, ZZ, Function) := (B, C, f) -> position'(0..B-1, 0..C-1, f) -- TODO: what generality should this be in? -- WANT: @@ -109,6 +109,12 @@ changeBaseField = (L, M) -> ( nonzero = x -> select(x, i -> i != 0) nonnull = x -> select(x, i -> i =!= null) +random Module := Vector => o -> M -> vector(gens M * random(cover M, module ring M, o)) +homomorphism Vector := v -> homomorphism matrix v + +checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then printerr( + "Warning: the recursion depth limit may need to be extended; use `recursionLimit = N`") + ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- @@ -147,15 +153,19 @@ isIdempotent = h -> reduceScalar(h^2) == reduceScalar h -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) -cachedSummands = M -> if M.cache#?(symbol summands => null) then M.cache#(symbol summands => null) else components M +cachedSummands = { ExtendGroundField => null } >> o -> M -> ( + if M.cache#?(symbol summands => o.ExtendGroundField) + then M.cache#(symbol summands => o.ExtendGroundField) else components M) -- Note: M may need to be extended to a field extensions -- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps -- Strategies: -- 1 => use expanded hom -directSummands = method(Options => { Tries => 0, Verbose => true, Strategy => 3, ExtendGroundField => null }) +-- 2 => use degrees of generators as heuristic to peel off line bundles first +directSummands = method(Options => { Recursive => true, Tries => 0, Verbose => true, Strategy => 3, ExtendGroundField => null }) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( + checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis R := ring M; @@ -175,11 +185,11 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if 0 < debugLevel then stderr << " -- peeling off rank 1 summands: " << flush; L = directSummands(apply(-unique degrees M, d -> R^{d}), M, opts); if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; - if 1 < #L then return join(drop(L, -1), directSummands(last L, opts))); + if 1 < #L then return directSummands(directSum L, opts)); -- K := coker vars R; - zdeg := toList(degreeLength R : 0); - --TODO: make "elapsedTime" contingent on verbosity + zdeg := degree 0_M; + -- TODO: make "elapsedTime" contingent on verbosity elapsedTime A := if opts.Strategy & 1 == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step elapsedTime B := smartBasis(zdeg, A); -- FIXME: this currently does not find _all_ idempotents @@ -187,7 +197,7 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex -- TODO: 10k columns for F_*(OO_X) on Gr(2,4) over ZZ/3 take a long time idem := position(numcols B, c -> ( h := homomorphism B_{c}; - if h == id_M then false else ( + if h == id_M or h == 0 then false else ( if flag and K ** h != 0 then flag = false; isIdempotent h)) @@ -216,45 +226,48 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex -- TODO: if ExtendGroundField is given, change variety directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf(-*variety F,*- N)) ---directSummands Matrix := List => opts -> f -> apply(directSummands(coker f, opts), presentation) ---directSummands Complex := List => opts -> C -> () -- TODO: should be functorial - -random Module := Vector => o -> M -> vector(gens M * random(cover M, module ring M, o)) -homomorphism Vector := v -> homomorphism matrix v -- tests whether L (perhaps a line bundle) is a summand of M +-- Recursive => true (default) attempts to peel as many copies of L as possible -- FIXME: it's not guaranteed to work, e.g. on X_4 over ZZ/2 -directSummands(Module, Module) := List => opts -> (L, M) -> sort( +-- TODO: cache this +directSummands(Module, Module) := List => opts -> (L, M) -> ( + checkRecursionDepth(); if ring L =!= ring M then error "expected objects over the same ring"; if rank L >= rank M then return {M}; - zdeg := toList(degreeLength ring L : 0); + if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); + zdeg := degree 0_M; -- we look for a composition L -> M -> L which is the identity B := smartBasis(zdeg, Hom(L, M, zdeg)); C := smartBasis(zdeg, Hom(M, L, zdeg)); -- attempt to find a random isomorphism - for i to opts.Tries - 1 do ( + h := for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); c := homomorphism(C * random source C); - if isIsomorphism(c * b) then - return flatten join({L}, directSummands(L, coker b, opts))); - -- TODO: is precomputing the homs too memory intensive? - Bhoms := apply(numcols B, i -> homomorphism B_{i}); - Choms := apply(numcols C, i -> homomorphism C_{i}); - P := position'(Choms, Bhoms, (c, b) -> id_L == reduceScalar(c * b)); - if P === null then return {M}; + if isIsomorphism(c * b) then break b); + -- precomputing the Homs can be too memory intensive + P := if h === null then position'(numcols C, numcols B, (c, b) -> isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); + if P =!= null then h = homomorphism B_(last P); + if h === null then return {M}; if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; -- TODO: can we detect multiple summands of L at once? - flatten join({L}, directSummands(L, coker last P, opts))) + sort flatten join({L}, if not opts.Recursive then {coker h} + else directSummands(L, coker h, opts))) + +directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( + directSummands(module L, module G, opts), N -> sheaf(variety L, N)) -- attempt to peel off summands from a given list of modules +directSummands(List, CoherentSheaf) := directSummands(List, Module) := List => opts -> (Ls, M) -> fold(Ls, {M}, - (L, LL) -> join(drop(LL, -1), directSummands(L, last LL))) - + (L, LL) -> join(drop(LL, -1), directSummands(L, last LL, opts))) +-- isDefinitelyIndecomposable = method() isDefinitelyIndecomposable Module := M -> M.cache.?Indecomposable isDefinitelyIndecomposable CoherentSheaf := M -> isDefinitelyIndecomposable module M +--directSummands Matrix := List => opts -> f -> apply(directSummands(coker f, opts), presentation) -* TODO: not done yet diagonalize = M -> ( m := presentation M; @@ -273,6 +286,8 @@ diagonalize = M -> ( ) *- +--directSummands Complex := List => opts -> C -> () -- TODO: should be functorial + ----------------------------------------------------------------------------- -- Hom in specific degrees ----------------------------------------------------------------------------- @@ -287,8 +302,6 @@ Hom(Module, Module) := Module => (M,N) -> ( H.cache.formation = FunctionApplication { Hom, (M,N) }; H) -Hom(Matrix,Module) := Matrix => (f,N) -> inducedMap(Hom(source f,N),Hom(target f,N),transpose cover f ** N,Verify=>false) - -- TODO: add DegreeLimit as an option to Hom instead --Hom = method(Options => { DegreeLimit => null, MinimalGenerators => null }) -- finds submodule of Hom containing at least the homomorphisms of degree e @@ -298,7 +311,7 @@ Hom(Module, Module, List) := Module => (M, N, e) -> ( if Y#?(Hom, M, N, e) then return Y#(Hom, M, N, e); A := presentation M; (G, F) := (target A, source A); -- M <-- G <-- F B := presentation N; (L, K) := (target B, source B); -- N <-- L <-- K - piN := inducedMap(N, L); + piN := inducedMap(N, L, gens N); -- TODO: the trim in Hom(target A, N) used to take 2/3 of total time psi := (Hom(A, N) * Hom(G, piN)) // Hom(F, piN); p := id_(Hom(G, L)) | map(Hom(G, L), Hom(F, K), 0); @@ -351,6 +364,13 @@ restart debug needsPackage "DirectSummands" +R = kk[x,y,z]; +n = 1000 +d = {100} +elapsedTime smartBasis(0, Hom(R^n, R^d)); +elapsedTime smartBasis(0, Hom(R^n, R^d, 0)); + + -------- -- summand of 4th syzygy of residue field of ring defined by -- ideal(y*z,x*z,y^3,x*y^2+z^3,x^2*y,x^3) is indecomposable, diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index bf37c2b3ce7..cc97f0c2e0a 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -3,9 +3,10 @@ generalEndomorphism = method() generalEndomorphism Module := M -> ( R := ring M; - EndM := Hom(M, M, 0); + zdeg := toList(degreeLength R : 0); + EndM := Hom(M, M, zdeg); --EndM := End(M); - B := smartBasis(0, EndM); + B := smartBasis(zdeg, EndM); --r := rank source B; --if r == 1 then return "warning: End(M)_0 is 1-dimensional"; --rm=length select(for i from 0 to r-1 list( unique apply(flatten entries homomorphism B_{i},j->j% ideal gens R) == {0}) ,k->k==false); diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 099130f7702..26379af6682 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -4,8 +4,7 @@ TEST /// -- basic test A = summands M B = summands prune M C = summands trim M - assert(A === {M}) - assert same({prune M}, B, prune \ C) + assert same(A, {prune M}, B, prune \ C) /// TEST /// -- direct summands of a free module From 120dafdc51c8486265ad77bfe3f8404f927c9ed6 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 22 Jan 2024 00:48:04 -0800 Subject: [PATCH 13/88] updated package for M2 v1.23 --- M2/Macaulay2/packages/DirectSummands.m2 | 84 +++++-------------- .../packages/DirectSummands/idempotents.m2 | 14 ++-- M2/Macaulay2/packages/DirectSummands/tests.m2 | 1 + 3 files changed, 25 insertions(+), 74 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index aa04434a52d..55e154f3e62 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -8,6 +8,9 @@ -- 2. try to find all irreducible idempotents from End M using representation theory -- 3. cache maps to the summands -- 4. rewrite (isDirectSum, Module) +-- 5. implement diagonalize for matrices (and later, complexes) +-- 6. restrict and pass End to the summands +-- 7. check for isomorphic summands --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -20,11 +23,11 @@ newPackage( }, Keywords => { "Commutative Algebra" }, PackageImports => { + "Polyhedra", -- for coneFromVData and coneComp + "PushForward", -- only for frobenius.m2 "RationalPoints2", -- for rationalPoints in findIdempotent - "PushForward", -- only for frobenius.m2 - "Polyhedra", -- for coneFromVData and coneComp - "Truncations", -- for effGenerators - -- "Varieties", + "Truncations", -- for effGenerators + "Varieties", }, AuxiliaryFiles => true, DebuggingMode => true @@ -61,16 +64,6 @@ needs "./DirectSummands/idempotents.m2" -- Things to move to the Core ----------------------------------------------------------------------------- --* -- FIXME: what was this for? -importFrom_Core { "raw", "submatrixFree", "listZZ", "rawSubmatrix" }; -try ( -submatrixFree = (m, rows, cols) -> (if rows === null - then rawSubmatrix(raw cover m, listZZ cols) - else rawSubmatrix(raw cover m, listZZ rows, - if cols =!= null then listZZ cols else 0 .. numgens source m - 1)) -) -*- - -- return the submatrix with given degrees of target and source submatrixByDegrees(Matrix, Sequence) := (m, degs) -> ( (tar, src) := degs; @@ -161,9 +154,10 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps -- Strategies: --- 1 => use expanded hom --- 2 => use degrees of generators as heuristic to peel off line bundles first -directSummands = method(Options => { Recursive => true, Tries => 0, Verbose => true, Strategy => 3, ExtendGroundField => null }) +-- 1 => use degrees of generators as heuristic to peel off line bundles first +-- 2 => use Hom option DegreeLimit => 0 +-- 4 => use Hom option MinimalGenerators => false +directSummands = method(Options => { Recursive => true, Tries => 0, Verbose => true, Strategy => 7, ExtendGroundField => null }) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain @@ -180,8 +174,8 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex M = changeBaseField(L, directSum cachedSummands M); R = ring M); -- Attempt to peel off line bundles by observing the degrees of generators - if opts.Strategy & 2 == 2 then ( - opts = opts ++ { Strategy => opts.Strategy ^^ 2 }; -- so we only try this once + if opts.Strategy & 1 == 1 then ( + opts = opts ++ { Strategy => opts.Strategy ^^ 1 }; -- so we only try this once if 0 < debugLevel then stderr << " -- peeling off rank 1 summands: " << flush; L = directSummands(apply(-unique degrees M, d -> R^{d}), M, opts); if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; @@ -190,7 +184,9 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex K := coker vars R; zdeg := degree 0_M; -- TODO: make "elapsedTime" contingent on verbosity - elapsedTime A := if opts.Strategy & 1 == 1 then Hom(M, M, zdeg) else End M; -- most time consuming step + elapsedTime A := Hom(M, M, -- most time consuming step + DegreeLimit => if opts.Strategy & 2 == 2 then zdeg, + MinimalGenerators => if opts.Strategy & 4 == 4 then false); elapsedTime B := smartBasis(zdeg, A); -- FIXME: this currently does not find _all_ idempotents flag := true; -- whether all non-identity homomorphisms are zero mod m @@ -238,8 +234,8 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); zdeg := degree 0_M; -- we look for a composition L -> M -> L which is the identity - B := smartBasis(zdeg, Hom(L, M, zdeg)); - C := smartBasis(zdeg, Hom(M, L, zdeg)); + B := smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); + C := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); -- attempt to find a random isomorphism h := for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); @@ -288,48 +284,6 @@ diagonalize = M -> ( --directSummands Complex := List => opts -> C -> () -- TODO: should be functorial ------------------------------------------------------------------------------ --- Hom in specific degrees ------------------------------------------------------------------------------ - --- same, but without trim, which seems to be unnecessary if at least M is free!! -Hom(Module, Module) := Module => (M,N) -> ( - Y := youngest(M.cache.cache,N.cache.cache); - if Y#?(Hom,M,N) then return Y#(Hom,M,N); - H := -* trim *- kernel (transpose presentation M ** N); - H.cache.homomorphism = (f) -> map(N,M,adjoint'(f,M,N), Degree => first degrees source f + degree f); - Y#(Hom,M,N) = H; -- a hack: we really want to type "Hom(M,N) = ..." - H.cache.formation = FunctionApplication { Hom, (M,N) }; - H) - --- TODO: add DegreeLimit as an option to Hom instead ---Hom = method(Options => { DegreeLimit => null, MinimalGenerators => null }) --- finds submodule of Hom containing at least the homomorphisms of degree e -Hom(Module, Module, ZZ) := Module => (M, N, e) -> Hom(M, N, if e == 0 then degree 1_(ring M) else {e}) -Hom(Module, Module, List) := Module => (M, N, e) -> ( - Y := youngest(M.cache.cache, N.cache.cache); - if Y#?(Hom, M, N, e) then return Y#(Hom, M, N, e); - A := presentation M; (G, F) := (target A, source A); -- M <-- G <-- F - B := presentation N; (L, K) := (target B, source B); -- N <-- L <-- K - piN := inducedMap(N, L, gens N); - -- TODO: the trim in Hom(target A, N) used to take 2/3 of total time - psi := (Hom(A, N) * Hom(G, piN)) // Hom(F, piN); - p := id_(Hom(G, L)) | map(Hom(G, L), Hom(F, K), 0); - r := syz(psi | -Hom(F, B), DegreeLimit => e); - --trim := if opts.MinimalGenerators then trim else identity; - Y#(Hom, M, N, e) = - -- TODO: trim used to take 1/3 of total time here - H := -*trim*- image(Hom(G, piN) * p * r); - H.cache.homomorphism = f -> map(N, M, adjoint'(f, M, N), Degree => first degrees source f); - H.cache.formation = FunctionApplication { Hom, (M, N, e) }; - H) - -sameVariety := Fs -> if not same apply(Fs, variety) then error "expected coherent sheaves on the same variety" - --- TODO: confirm this; also: can sheafHom be improved? -Hom(CoherentSheaf, CoherentSheaf) := Module => (F, G) -> ( - sameVariety(F, G); HH^0 sheaf(variety F, Hom(module F, module G, 0))) - ----------------------------------------------------------------------------- -* Test section *- ----------------------------------------------------------------------------- @@ -368,7 +322,7 @@ R = kk[x,y,z]; n = 1000 d = {100} elapsedTime smartBasis(0, Hom(R^n, R^d)); -elapsedTime smartBasis(0, Hom(R^n, R^d, 0)); +elapsedTime smartBasis(0, Hom(R^n, R^d, DegreeLimit => 0)); -------- diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index cc97f0c2e0a..992204a2e67 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -3,15 +3,11 @@ generalEndomorphism = method() generalEndomorphism Module := M -> ( R := ring M; - zdeg := toList(degreeLength R : 0); - EndM := Hom(M, M, zdeg); - --EndM := End(M); - B := smartBasis(zdeg, EndM); - --r := rank source B; - --if r == 1 then return "warning: End(M)_0 is 1-dimensional"; - --rm=length select(for i from 0 to r-1 list( unique apply(flatten entries homomorphism B_{i},j->j% ideal gens R) == {0}) ,k->k==false); - --if rm< 2 then return "warning: End(M)_0/maximal ideal is 1-dimensional"; - homomorphism(B * random(ZZ^(rank source B),ZZ^1))) + A := Hom(M, M, + DegreeLimit => zdeg := degree 0_M, + MinimalGenerators => false); + B := smartBasis(zdeg, A); + homomorphism(B * random(source B, R^1))) generalEndomorphism CoherentSheaf := M -> generalEndomorphism module M -- TODO: this needs improvement to work over RR, QQ, GF, FractionField, etc. diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 26379af6682..527bfbbb0c9 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -44,6 +44,7 @@ TEST /// -- direct summands over field extensions elapsedTime B = basis({0}, A); -- ~0.23s elapsedTime B = smartBasis({0}, A); -- ~0.03s *- + -- if this test fails, check if "try findIdempotent M" if hiding any unexpected errors elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -- 2.87s -> 2.05 elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 From 9167ca21c6bd574d96e5ee3ff6184b4a13df6edf Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 22 Jan 2024 02:31:18 -0800 Subject: [PATCH 14/88] added strategy for precomputing Homs --- M2/Macaulay2/packages/DirectSummands.m2 | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 55e154f3e62..147e5ae38be 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -157,6 +157,7 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- 1 => use degrees of generators as heuristic to peel off line bundles first -- 2 => use Hom option DegreeLimit => 0 -- 4 => use Hom option MinimalGenerators => false +-- 8 => precompute Homs before looking for idempotents directSummands = method(Options => { Recursive => true, Tries => 0, Verbose => true, Strategy => 7, ExtendGroundField => null }) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( checkRecursionDepth(); @@ -241,9 +242,19 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( b := homomorphism(B * random source B); c := homomorphism(C * random source C); if isIsomorphism(c * b) then break b); - -- precomputing the Homs can be too memory intensive - P := if h === null then position'(numcols C, numcols B, (c, b) -> isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); - if P =!= null then h = homomorphism B_(last P); + if h === null then h = ( + if opts.Strategy & 8 == 8 then ( + -- precomputing the Homs can sometimes be a good strategy + Bhoms := apply(numcols B, i -> homomorphism B_{i}); + Choms := apply(numcols C, i -> homomorphism C_{i}); + pos := position'(Choms, Bhoms, + (c, b) -> isIsomorphism(c * b)); + if pos =!= null then last pos) + else ( + -- and sometimes too memory intensive + ind := position'(numcols C, numcols B, + (c, b) -> isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); + if ind =!= null then homomorphism B_{last ind})); if h === null then return {M}; if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; -- TODO: can we detect multiple summands of L at once? From b635faca48aa70266c13b985bed2ec0652b73a30 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 23 Jan 2024 14:38:03 -0800 Subject: [PATCH 15/88] cached frobeniusPushforward for sheaves and removed frobeniusPushforward(X, e) --- M2/Macaulay2/packages/DirectSummands/frobenius.m2 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 index 459ddda4e4a..d91b6fc6384 100644 --- a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -87,7 +87,7 @@ decomposeFrobeniusPresentation = (e, f) -> ( protect FrobeniusPushforward frobeniusPushforward = method() -frobeniusPushforward(Thing, ZZ) := (T, e) -> frobeniusPushforward(e, T) +--frobeniusPushforward(Thing, ZZ) := (T, e) -> frobeniusPushforward(e, T) frobeniusPushforward(ZZ, Ring) := (e, R) -> frobeniusPushforward(e, module R) frobeniusPushforward(ZZ, Ideal) := (e, I) -> frobeniusPushforward(e, quotient I) -- TODO: cache in a way that the second pushforward is the same as applying pushforward twice @@ -114,6 +114,9 @@ frobeniusPushforward(ZZ, Matrix) := (e, f) -> ( frobeniusPushforward(ZZ, SheafOfRings) := (e, N0) -> frobeniusPushforward(e, N0^1) -- TODO: is this cached? frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> ( + if N.cache#?(FrobeniusPushforward, e) + then N.cache#(FrobeniusPushforward, e) + else N.cache#(FrobeniusPushforward, e) = ( R := ring N; p := char R; FN := first components frobeniusPushforward(e, module N); @@ -124,10 +127,11 @@ frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> ( (tardegs, srcdegs) := toSequence(-degrees Fmatrix // p^e); -- TODO: how long does this take? is it worth caching? sheaf prune coker map(R^tardegs, R^srcdegs, Fmatrix)) + ) protect FrobeniusPullback frobeniusPullback = method() -frobeniusPullback(Thing, ZZ) := (T, e) -> frobeniusPullback(e, T) +--frobeniusPullback(Thing, ZZ) := (T, e) -> frobeniusPullback(e, T) frobeniusPullback(ZZ, Module) := (e, M) -> ( if M.cache#?(FrobeniusPullback, e) then M.cache#(FrobeniusPullback, e) From 5a5d946fefe7f0eb9394d8df753ef5b4c8139713 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 14 Feb 2024 02:34:18 -0600 Subject: [PATCH 16/88] improved changeBaseField --- M2/Macaulay2/packages/DirectSummands.m2 | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 147e5ae38be..1c9e1cd09e5 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -48,6 +48,8 @@ export { "frobeniusPullback", "frobeniusPushforward", "frobeniusTwist", + "potentialExtension", + "changeBaseField" } ----------------------------------------------------------------------------- @@ -93,11 +95,22 @@ PolynomialRing ** GaloisField := (R, L) -> ( A := first flattenRing(R, CoefficientRing => null); quotient sub(ideal A, L monoid A)) -changeBaseField = (L, M) -> ( +changeBaseField = method() +changeBaseField(GaloisField, Module) := (L, M) -> ( S := first flattenRing(ring M, CoefficientRing => null); - R := quotient sub(ideal S, L monoid S); - directSum apply(components M, - N -> coker sub(presentation N, R))) + K := coefficientRing S; + if class K =!= GaloisField then ( + R0 := quotient sub(ideal S, L monoid S); + return directSum apply(components M, + N -> coker sub(presentation N, R0))); + i0 := map(L, K); + LS := L(monoid S); + i1 := map(LS, ring ideal S, gens LS | { i0 K_0 }); + R := quotient i1 ideal S; + i := map(R, S, i1); + directSum apply(components M, N -> i ** N)) + +changeBaseField(GaloisField, CoherentSheaf) := (L, F) -> changeBaseField(L, module F) nonzero = x -> select(x, i -> i != 0) nonnull = x -> select(x, i -> i =!= null) @@ -151,6 +164,7 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( then M.cache#(symbol summands => o.ExtendGroundField) else components M) -- Note: M may need to be extended to a field extensions +-- TODO: add option to provide a general endomorphism or idempotent -- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps -- Strategies: From de74dbdea5da4fdc51fe085ec6f63e904b15aacc Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 15 Feb 2024 21:05:32 -0600 Subject: [PATCH 17/88] added test for modules with Schreyer order --- M2/Macaulay2/packages/DirectSummands/tests.m2 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 527bfbbb0c9..d1ffed27e81 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -50,3 +50,11 @@ TEST /// -- direct summands over field extensions elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 elapsedTime assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) -- 2.18s -> 0.47 /// + +TEST /// + R = ZZ/101[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) + C = res(coker vars R, LengthLimit => 3) + D = res(coker transpose C.dd_3, LengthLimit => 3) + M = coker D.dd_3 + summands M +/// From 685a1d23baeaa7f1a80ea274b519483719c21b62 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 15 Feb 2024 21:05:50 -0600 Subject: [PATCH 18/88] added test for potentialExtension and changeBaseField --- M2/Macaulay2/packages/DirectSummands/tests.m2 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index d1ffed27e81..a5e2bdc4fd5 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -58,3 +58,14 @@ TEST /// M = coker D.dd_3 summands M /// + +TEST /// + --tests largepowers + K = ZZ/7 + R = K[x,y,z]/(x^3+y^3+z^3) + X = Proj R + M1 = summands(frobeniusPushforward(1, OO_X), ExtendGroundField => 2) + M2 = frobeniusPushforward(1, M1#1) + L = potentialExtension M2 + findIdem changeBaseField(L, M2) +/// From 54e63c910b24bec3a5cc91e8d05c8e4a60a58763 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 15 Feb 2024 21:47:37 -0600 Subject: [PATCH 19/88] added support for larger powers --- .../packages/DirectSummands/idempotents.m2 | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 992204a2e67..55291cb18e9 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -29,25 +29,45 @@ fieldExponent = R -> ( -- finds the characteristic polynomial of a matrix char Matrix := A -> ( if numRows A != numColumns A then error "expected a square matrix"; - T := (baseField ring A)(monoid[vars 0]); + b := symbol b; + T := (baseField ring A)(monoid[b]); B := sub(cover A, T); I := id_(source B); det(B - T_0 * I)) -protect Tries +largePower = (p,l,M) -> ( + if p^l < 2^30 then return M^(p^l); + --should have this line check for monomial size of ambient ring also + N := M; + for i from 1 to l do N = N^p; + N) + +largePower' = (p,l,M) -> ( + if p^l < 2^30 then return M^(p^l-1); + --should have this line check for monomial size of ambient ring also + N := M; + (largePower(p,l-1,M))^(p-1) * largePower'(p,l-1,M)) + findIdempotent = method(Options => { Tries => 50 }) -findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent ring M) +findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent ring M,opts) findIdempotent(Module, ZZ) := opts -> (M, e) -> ( p := char ring M; K := quotient ideal gens ring M; l := if p == 0 then e else max(e, ceiling log_p numgens M); + q := infinity; for i to opts.Tries - 1 do ( f := generalEndomorphism M; - eigen := flatten rationalPoints ideal char f; - opers := flatten for i in eigen list if p == 0 then (f - i*id_M) else for j from 0 to e list (((f-i*id_M)^(p^l))^(p^(j+1)-1)); + q = min(q,(extField {chi:=char f}).order); + eigen := flatten rationalPoints ideal chi; + opers := flatten for i in eigen list if p == 0 then (f - i*id_M) else for j from 0 to e list largePower'(p,j+1, largePower(p,l,f-i*id_M)); idem := position(opers, g -> isIdempotent(K ** g) and g != id_M and K ** g != 0); if idem =!= null then ( if 1 < debugLevel then printerr("found idempotent after ", toString i, " attempts."); return opers_idem)); - error("no idempotent found after ", toString opts.Tries, " attempts. Try extending the base field.")) -findIdempotent CoherentSheaf := opts -> M -> findIdempotent module M + error("no idempotent found after ", toString opts.Tries, " attempts. Try extending the base field to GF ",toString q)) + +findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M,opts) + +potentialExtension = method() +potentialExtension CoherentSheaf := opts -> M -> potentialExtension(module M, opts) +potentialExtension Module := opts -> M -> extField {char generalEndomorphism M} From 9d72700bee01b4b5a6f642e6d412b823a49f8624 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 15 Feb 2024 21:47:53 -0600 Subject: [PATCH 20/88] wip: added tally' and unique' --- M2/Macaulay2/packages/DirectSummands.m2 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 1c9e1cd09e5..5a72e9db52a 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -23,6 +23,7 @@ newPackage( }, Keywords => { "Commutative Algebra" }, PackageImports => { + "Isomorphism", -- for isIsomorphic "Polyhedra", -- for coneFromVData and coneComp "PushForward", -- only for frobenius.m2 "RationalPoints2", -- for rationalPoints in findIdempotent @@ -121,6 +122,19 @@ homomorphism Vector := v -> homomorphism matrix v checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then printerr( "Warning: the recursion depth limit may need to be extended; use `recursionLimit = N`") +module Module := identity +-- TODO: speed this up +-- TODO: implement isIsomorphic for sheaves +unique' = L -> ( + L = new MutableList from module \ L; + b := new MutableList from #L : true; + for k to 5 do -- arbitrary, just to make sure isIsomorphic doesn't fail because of bad randomness + for i to #L-2 do for j from i+1 to #L-1 do if b#j then ( + if isIsomorphic(L#i, L#j) then ( b#j = false; L#j = L#i )); + new List from L) + +tally' := L -> tally unique' L + ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- From b309a49c1131848321e73422f88de7eafcb0769e Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 10 Mar 2024 23:39:17 -0700 Subject: [PATCH 21/88] fixed a broken test --- M2/Macaulay2/packages/DirectSummands/tests.m2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index a5e2bdc4fd5..21bf2cc2541 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -38,7 +38,7 @@ TEST /// -- direct summands over field extensions debug needsPackage "DirectSummands" R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); X = Proj R; - M = module frobeniusPushforward(OO_X, 1); + M = module frobeniusPushforward(1, OO_X); -* is smartBasis useful? yes! elapsedTime A = End M; -- ~0.65s elapsedTime B = basis({0}, A); -- ~0.23s From 3cc9b8143d2649c04fda6db9cda4ca340261d6b3 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 11 Mar 2024 00:00:05 -0700 Subject: [PATCH 22/88] fixed a bug in potentialExtension --- M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 55291cb18e9..a3f28e3a944 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -69,5 +69,5 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M,opts) potentialExtension = method() -potentialExtension CoherentSheaf := opts -> M -> potentialExtension(module M, opts) -potentialExtension Module := opts -> M -> extField {char generalEndomorphism M} +potentialExtension Module := M -> extField {char generalEndomorphism M} +potentialExtension CoherentSheaf := M -> potentialExtension module M From a04374095834669ebc9a9d062197a07376895a39 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 11 Mar 2024 00:02:37 -0700 Subject: [PATCH 23/88] added temporary fix for extending ground field of sheaves --- M2/Macaulay2/packages/DirectSummands.m2 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 5a72e9db52a..529692c64c5 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -111,7 +111,14 @@ changeBaseField(GaloisField, Module) := (L, M) -> ( i := map(R, S, i1); directSum apply(components M, N -> i ** N)) -changeBaseField(GaloisField, CoherentSheaf) := (L, F) -> changeBaseField(L, module F) +-- TODO: come up with a better way to extend ground field of a variety +-- TODO: does this also need to be used for frobenius pushforward? +sheaf' = (X, M) -> try sheaf(X, M) else ( + if instance(X, ProjectiveVariety) then sheaf(Proj ring M, M) else + if instance(X, AffineVariety) then sheaf(Spec ring M, M) else + error "extension of the coefficient field of the base variety is not implemented") + +changeBaseField(GaloisField, CoherentSheaf) := (L, F) -> sheaf'(variety F, changeBaseField(L, module F)) nonzero = x -> select(x, i -> i != 0) nonnull = x -> select(x, i -> i =!= null) @@ -250,7 +257,8 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex )) -- TODO: if ExtendGroundField is given, change variety -directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf(-*variety F,*- N)) +-- TODO: when ExtendGroundField is given, the variety will change! +directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf'(variety F, N)) -- tests whether L (perhaps a line bundle) is a summand of M -- Recursive => true (default) attempts to peel as many copies of L as possible From 2c664c34d3d5a7561775add67b01dd1c2723f678 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Apr 2024 22:20:16 -0500 Subject: [PATCH 24/88] changed needs to load --- M2/Macaulay2/packages/DirectSummands.m2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 529692c64c5..03c75f61756 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -59,9 +59,9 @@ export { -- helpers for computing Frobenius pushforwards of modules and sheaves -- TODO: move to PushForward package? -needs "./DirectSummands/frobenius.m2" +load "./DirectSummands/frobenius.m2" -- helpers for finding random idempotents of a module -needs "./DirectSummands/idempotents.m2" +load "./DirectSummands/idempotents.m2" ----------------------------------------------------------------------------- -- Things to move to the Core @@ -335,7 +335,7 @@ diagonalize = M -> ( -* Test section *- ----------------------------------------------------------------------------- -needs "./DirectSummands/tests.m2" +load "./DirectSummands/tests.m2" ----------------------------------------------------------------------------- -* Documentation section *- @@ -343,7 +343,7 @@ needs "./DirectSummands/tests.m2" beginDocumentation() -needs "./DirectSummands/docs.m2" +load "./DirectSummands/docs.m2" end-- From 2e917814600c67c4d777d9136546ae97e19b1a26 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 11 May 2024 12:26:58 -0500 Subject: [PATCH 25/88] added faster reducedScalar --- M2/Macaulay2/packages/DirectSummands.m2 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 03c75f61756..00c4fb6af1c 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -53,6 +53,12 @@ export { "changeBaseField" } +importFrom_Core { + "raw", "rawReshape", + "rawNumberOfColumns", + "rawNumberOfRows", + } + ----------------------------------------------------------------------------- -* Code section *- ----------------------------------------------------------------------------- @@ -76,7 +82,7 @@ submatrixByDegrees(Matrix, Sequence) := (m, degs) -> ( -- TODO: perhaps also take degree into account CoherentSheaf ? CoherentSheaf := -Module ? Module := (M, N) -> rank M ? rank N +Module ? Module := (M, N) -> if rank M != rank N then rank M ? rank N else degrees M ? degrees N -- TODO: move to Core position(ZZ, Function) := o -> (n, f) -> position(0..n-1, f) @@ -174,8 +180,11 @@ smartBasis = (deg, M) -> ( ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- +flatten' = m -> map(R := ring m, rawReshape(m = raw m, raw R^1, raw R^(rawNumberOfColumns m * rawNumberOfRows m))) + -- this is a kludge to handle the case when h^2 = ah -reduceScalar = m -> m // scan(unique flatten entries m | {1}, x -> if isConstant x and not zero x then break x) +reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // scan( + rawNumberOfColumns raw(mons := flatten' m), i -> if not zero mons_(0,i) then break leadCoefficient mons_(0,i))) isIdempotent = h -> reduceScalar(h^2) == reduceScalar h -- TODO: can we return cached summands from the closest field extension? From c92f2f9ebe35f050c2aa372c32af9dba30f3a168 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 15 May 2024 15:59:07 -0500 Subject: [PATCH 26/88] added faster reduceScalar --- M2/Macaulay2/packages/DirectSummands.m2 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 00c4fb6af1c..87605d15cb8 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -180,11 +180,15 @@ smartBasis = (deg, M) -> ( ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- +-- same as flatten(Matrix), but doesn't bother homogenizing the result flatten' = m -> map(R := ring m, rawReshape(m = raw m, raw R^1, raw R^(rawNumberOfColumns m * rawNumberOfRows m))) +-- not strictly speaking the "lead" coefficient +leadCoefficient Matrix := RingElement => m -> for c to numcols m - 1 do for r to numrows m - 1 do ( + if not zero m_(r,c) then return leadCoefficient m_(r,c)) + -- this is a kludge to handle the case when h^2 = ah -reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // scan( - rawNumberOfColumns raw(mons := flatten' m), i -> if not zero mons_(0,i) then break leadCoefficient mons_(0,i))) +reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // leadCoefficient m) isIdempotent = h -> reduceScalar(h^2) == reduceScalar h -- TODO: can we return cached summands from the closest field extension? From 2ee7f5fd179aa94fd06e862cb613545d171bc651 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 15 May 2024 16:00:21 -0500 Subject: [PATCH 27/88] added changes for 1.24.05 release --- M2/Macaulay2/packages/DirectSummands.m2 | 3 --- 1 file changed, 3 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 87605d15cb8..1b36403d228 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -129,9 +129,6 @@ changeBaseField(GaloisField, CoherentSheaf) := (L, F) -> sheaf'(variety F, chang nonzero = x -> select(x, i -> i != 0) nonnull = x -> select(x, i -> i =!= null) -random Module := Vector => o -> M -> vector(gens M * random(cover M, module ring M, o)) -homomorphism Vector := v -> homomorphism matrix v - checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then printerr( "Warning: the recursion depth limit may need to be extended; use `recursionLimit = N`") From 0a067d5df21a8fcc3a11bcbdded440695247a020 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 13 Aug 2024 08:24:42 +0200 Subject: [PATCH 28/88] fixed diagonalizing {{x,y},{-y,x}} --- M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index a3f28e3a944..4f7d51d79ba 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -57,14 +57,18 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( q := infinity; for i to opts.Tries - 1 do ( f := generalEndomorphism M; - q = min(q,(extField {chi:=char f}).order); + chi := char f; + K' := extField {chi}; + if p != 0 then q = min(q, K'.order) else q = K'; eigen := flatten rationalPoints ideal chi; opers := flatten for i in eigen list if p == 0 then (f - i*id_M) else for j from 0 to e list largePower'(p,j+1, largePower(p,l,f-i*id_M)); idem := position(opers, g -> isIdempotent(K ** g) and g != id_M and K ** g != 0); if idem =!= null then ( if 1 < debugLevel then printerr("found idempotent after ", toString i, " attempts."); return opers_idem)); - error("no idempotent found after ", toString opts.Tries, " attempts. Try extending the base field to GF ",toString q)) + -- TODO: skip the "Try passin" line if the field is large enough, e.g. q === K + error("no idempotent found after ", toString opts.Tries, " attempts. Try passing + ExtendGroundField => ", if p != 0 then ("GF " | toString q) else toString q)) findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M,opts) From 6beec57a19364c924d03261ac44cb81fd2f011ea Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 13 Aug 2024 08:48:17 +0200 Subject: [PATCH 29/88] switched to using isWeakIdempotent, added test --- M2/Macaulay2/packages/DirectSummands.m2 | 1 + .../packages/DirectSummands/idempotents.m2 | 12 ++++++++---- M2/Macaulay2/packages/DirectSummands/tests.m2 | 17 ++++++++++++++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 1b36403d228..4c3417b0e07 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -187,6 +187,7 @@ leadCoefficient Matrix := RingElement => m -> for c to numcols m - 1 do for r to -- this is a kludge to handle the case when h^2 = ah reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // leadCoefficient m) isIdempotent = h -> reduceScalar(h^2) == reduceScalar h +isWeakIdempotent = h -> 0 == det cover(reduceScalar(h^2) - reduceScalar h) -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 4f7d51d79ba..537bec14c7e 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -55,16 +55,20 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( K := quotient ideal gens ring M; l := if p == 0 then e else max(e, ceiling log_p numgens M); q := infinity; - for i to opts.Tries - 1 do ( + for c to opts.Tries - 1 do ( f := generalEndomorphism M; chi := char f; K' := extField {chi}; if p != 0 then q = min(q, K'.order) else q = K'; eigen := flatten rationalPoints ideal chi; - opers := flatten for i in eigen list if p == 0 then (f - i*id_M) else for j from 0 to e list largePower'(p,j+1, largePower(p,l,f-i*id_M)); - idem := position(opers, g -> isIdempotent(K ** g) and g != id_M and K ** g != 0); + -- if at most one eigenvalue is found then the module is probably indecomposable + if #eigen <= 1 then continue; + opers := flatten for y in eigen list ( + if p == 0 then (f - y*id_M) else ( + for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); + idem := position(opers, g -> isWeakIdempotent(K ** g) and g != id_M and K ** g != 0); if idem =!= null then ( - if 1 < debugLevel then printerr("found idempotent after ", toString i, " attempts."); + if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); return opers_idem)); -- TODO: skip the "Try passin" line if the field is large enough, e.g. q === K error("no idempotent found after ", toString opts.Tries, " attempts. Try passing diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 21bf2cc2541..4fae27ba0da 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -56,7 +56,22 @@ TEST /// C = res(coker vars R, LengthLimit => 3) D = res(coker transpose C.dd_3, LengthLimit => 3) M = coker D.dd_3 - summands M + assert(8 == #summands M) +/// + +TEST /// -- testing in char 0 + -- FIXME: + --S = ZZ[x,y]; + --assert(2 == #summands coker matrix "x,y;y,x") + S = QQ[x,y]; + assert(2 == #summands coker matrix "x,y; y,x") + assert(1 == #summands coker matrix "x,y;-y,x") + K = toField(QQ[i]/(i^2+1)); + S = K[x,y]; + assert(2 == #summands coker matrix "x,y; y,x") + assert(2 == #summands coker matrix "x,y;-y,x") + S = K[a,b,c,d]; + assert(4 == #summands coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a") /// TEST /// From c60227132685cfaf3af2eff0a54b9f69378b38b3 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 12 Nov 2024 15:31:12 +0100 Subject: [PATCH 30/88] changes until Oct 31st (probably) --- M2/Macaulay2/packages/DirectSummands.m2 | 21 +++- .../packages/DirectSummands/idempotents.m2 | 119 +++++++++++++++--- M2/Macaulay2/packages/DirectSummands/tests.m2 | 34 +++++ 3 files changed, 156 insertions(+), 18 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 4c3417b0e07..eb4d5deb550 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -11,6 +11,7 @@ -- 5. implement diagonalize for matrices (and later, complexes) -- 6. restrict and pass End to the summands -- 7. check for isomorphic summands +-- 8. make summands work over ZZ (currently rank fails) --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -184,10 +185,26 @@ flatten' = m -> map(R := ring m, rawReshape(m = raw m, raw R^1, raw R^(rawNumber leadCoefficient Matrix := RingElement => m -> for c to numcols m - 1 do for r to numrows m - 1 do ( if not zero m_(r,c) then return leadCoefficient m_(r,c)) +-- hacky things for CC +-- TODO: move to Core, also add conjugate Matrix, realPart, imaginaryPart, etc. +conjugate RingElement := x -> sum(listForm x, (e, c) -> conjugate c * (ring x)_e) +magnitude = x -> x * conjugate x +isZero = x -> if not instance(F := ultimate(coefficientRing, ring x), InexactField) then x == 0 else ( + leadCoefficient magnitude x < 2^(-precision F)) + +-- borrowed from Varieties as hack to get around +-- https://github.com/Macaulay2/M2/issues/3407 +flattenMorphism = f -> ( + g := presentation ring f; + S := ring g; + -- TODO: sometimes lifting to ring g is enough, how can we detect this? + -- TODO: why doesn't lift(f, ring g) do this automatically? + map(target f ** S, source f ** S, lift(cover f, S)) ** cokernel g) + -- this is a kludge to handle the case when h^2 = ah reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // leadCoefficient m) isIdempotent = h -> reduceScalar(h^2) == reduceScalar h -isWeakIdempotent = h -> 0 == det cover(reduceScalar(h^2) - reduceScalar h) +isWeakIdempotent = h -> all(flatten entries flattenMorphism(reduceScalar(h^2) - reduceScalar h), isZero) -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) @@ -238,6 +255,7 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex -- FIXME: this currently does not find _all_ idempotents flag := true; -- whether all non-identity homomorphisms are zero mod m -- TODO: 10k columns for F_*(OO_X) on Gr(2,4) over ZZ/3 take a long time + -- TODO: add option to skip this step idem := position(numcols B, c -> ( h := homomorphism B_{c}; if h == id_M or h == 0 then false else ( @@ -319,6 +337,7 @@ directSummands(List, Module) := List => opts -> (Ls, M) -> fold(Ls, {M}, -- isDefinitelyIndecomposable = method() isDefinitelyIndecomposable Module := M -> M.cache.?Indecomposable +-- TODO: check that the sheaf is pruned also isDefinitelyIndecomposable CoherentSheaf := M -> isDefinitelyIndecomposable module M --directSummands Matrix := List => opts -> f -> apply(directSummands(coker f, opts), presentation) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 537bec14c7e..a772152fb72 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -3,18 +3,82 @@ generalEndomorphism = method() generalEndomorphism Module := M -> ( R := ring M; - A := Hom(M, M, - DegreeLimit => zdeg := degree 0_M, - MinimalGenerators => false); - B := smartBasis(zdeg, A); - homomorphism(B * random(source B, R^1))) + if isHomogeneous M and isHomogeneous R then( + A := Hom(M, M, + DegreeLimit => zdeg := degree 0_M, + MinimalGenerators => false); + B := smartBasis(zdeg, A); + homomorphism(B * random(source B, R^1)) + --TODO: this is only "random" over the base field; is that okay? + ) + else( + --homomorphism sum for i from 0 to numgens A - 1 list sub(randomFieldElement K, R) * A_i + --line above is if we want random over the full field + randomHom(M,M) + ) +) generalEndomorphism CoherentSheaf := M -> generalEndomorphism module M +-----NEW STUFF FOR INHOMOGENEOUS CASE----- + +--does same thing as general(Endo)morphism, but in inhomgeneous case +randomHom = method() +randomHom(Module, Module) := (M,N) -> ( + R := ring M; + A := Hom(M, N, + MinimalGenerators => false); + homomorphism sum for i from 0 to numgens A - 1 list (random(R^1,R^1))_(0,0) * A_i +) + +isSplitSummand = method(Options => { Tries => 50 }) + +--tests if M is a split summand of N +isSplitSummand(Module,Module) := opts -> (M,N) -> ( + h := for i to opts.Tries - 1 do ( + b := randomHom(M,N); + c := randomHom(N,M); + if isIsomorphism(c * b) then break b); + if h === null then return "not known" else return h +) + +findIdem' = method(Options => { Tries=>500 }) +findIdem' Module := opts -> M -> findIdem'(M, fieldExponent ring M,opts) +findIdem'(Module,ZZ) := opts -> (M,e) -> ( + R := ring M; + p := char R; + F := ultimate(coefficientRing, R); + K := quotient ideal gens R; + l := if p == 0 then e else max(e, ceiling log_p numgens M); + L := infinity; + for c to opts.Tries - 1 do ( + f := generalEndomorphism M; + chi := char f; + eigen := if instance(F, InexactField) then roots chi else flatten rationalPoints ideal chi; + if #eigen <= 1 then continue; + opers := flatten for y in eigen list ( + if p == 0 then (f - y*id_M) else ( + for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); + idem := position(opers, g -> isSplitSummand(image g, source g) =!= null and g != id_M and K ** g != 0 and prune ker g != 0); + if idem =!= null then ( + if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); + return opers_idem)); +) + +--ONLY IF WE NEED THE FULL BASE FIELD: +--randomFieldElement = method() +--randomFieldElement(Ring) := K -> ( +-- gensK := numgens first flattenRing K; +-- (random(K^1,K^1))_(0,0) + sum for i from 0 to gensK - 1 list (random(K^1,K^1))_(0,0) * K_i +--) + +--------------------- + -- TODO: this needs improvement to work over RR, QQ, GF, FractionField, etc. -- e.g. given a tower such as K[x][y]/I, returns K baseField = method() baseField GaloisField := identity baseField FractionField := identity -- FIXME: does this always work? +baseField QuotientRing := R -> try baseField coefficientRing R else R baseField Ring := R -> try ( coefficientRing first flattenRing R ) else R -- e.g. given a field isomorphic to GF(p,e), returns e @@ -26,7 +90,7 @@ fieldExponent = R -> ( while a^(p^e) != a do (e = e + 1); e) --- finds the characteristic polynomial of a matrix +-- finds the characteristic polynomial of a matrix mod the maximal ideal char Matrix := A -> ( if numRows A != numColumns A then error "expected a square matrix"; b := symbol b; @@ -42,37 +106,58 @@ largePower = (p,l,M) -> ( for i from 1 to l do N = N^p; N) +-- TODO: use BinaryPowerMethod? largePower' = (p,l,M) -> ( if p^l < 2^30 then return M^(p^l-1); --should have this line check for monomial size of ambient ring also N := M; (largePower(p,l-1,M))^(p-1) * largePower'(p,l-1,M)) +coefficientRing' = K -> if isField K then K else coefficientRing K + +lift(CC, CC_*) := opts -> (r, C) -> numeric(precision C, r) + +-- adjust as needed LOL +findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) + +--TODO: findIdem right now will fail if K is not L[a]/f(a); in general, will need to find a primitive element first findIdempotent = method(Options => { Tries => 50 }) findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent ring M,opts) findIdempotent(Module, ZZ) := opts -> (M, e) -> ( - p := char ring M; - K := quotient ideal gens ring M; + R := ring M; + p := char R; + F := ultimate(coefficientRing', R); + K := quotient ideal gens R; l := if p == 0 then e else max(e, ceiling log_p numgens M); - q := infinity; + L := infinity; for c to opts.Tries - 1 do ( f := generalEndomorphism M; chi := char f; - K' := extField {chi}; - if p != 0 then q = min(q, K'.order) else q = K'; - eigen := flatten rationalPoints ideal chi; + K' := if instance(F, InexactField) then F else try extField {chi}; + --TODO: remember different L to extend to; right now the L you return at the end may not be large enough + if p != 0 then L = min(L, K'.order) else L = K'; + -- TODO: this seems too messy, what's the precise requirement? + -- maybe we should separate this in a different method + eigen := if instance(F, InexactField) or isMember(coefficientRing ring chi, {ZZ, QQ}) + then roots chi else flatten rationalPoints ideal chi; -- if at most one eigenvalue is found then the module is probably indecomposable - if #eigen <= 1 then continue; + if #eigen <= 1 then continue; -- TODO: check that eigen doesn't have higher multiplicity + -- we only want eigen values in F + eigen = nonnull apply(eigen, y -> try lift(y, F)); + if #eigen == 0 then continue; opers := flatten for y in eigen list ( - if p == 0 then (f - y*id_M) else ( + if p == 0 then reduceScalar(f - y*id_M) else ( for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); idem := position(opers, g -> isWeakIdempotent(K ** g) and g != id_M and K ** g != 0); if idem =!= null then ( if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); - return opers_idem)); - -- TODO: skip the "Try passin" line if the field is large enough, e.g. q === K + idem = opers_idem; + -- for inexact fields, we compose the idempotent until the determinant is zero + if instance(F, InexactField) then idem = idem ^ (findErrorMargin idem); + return idem)); + -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K error("no idempotent found after ", toString opts.Tries, " attempts. Try passing - ExtendGroundField => ", if p != 0 then ("GF " | toString q) else toString q)) + ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M,opts) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 4fae27ba0da..5ef332de364 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -4,6 +4,7 @@ TEST /// -- basic test A = summands M B = summands prune M C = summands trim M + -- FIXME: this keeps annoyingly breaking assert same(A, {prune M}, B, prune \ C) /// @@ -34,6 +35,7 @@ TEST /// -- direct summands of a ring assert(M == S^{0,-1,-2}) /// +-- FIXME: takes 13s, which is more than it used to TEST /// -- direct summands over field extensions debug needsPackage "DirectSummands" R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); @@ -66,14 +68,20 @@ TEST /// -- testing in char 0 S = QQ[x,y]; assert(2 == #summands coker matrix "x,y; y,x") assert(1 == #summands coker matrix "x,y;-y,x") + S = QQ[a,b,c,d]; + -- TODO: can we make sure the last block remains symmetric? + assert(3 == #summands coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a") K = toField(QQ[i]/(i^2+1)); S = K[x,y]; assert(2 == #summands coker matrix "x,y; y,x") assert(2 == #summands coker matrix "x,y;-y,x") S = K[a,b,c,d]; assert(4 == #summands coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a") + S = CC[x,y]; + scan(20, i -> assert(set summands coker matrix {{x,y},{-y,x}} == set {cokernel matrix {{x-ii*y}}, cokernel matrix {{x+ii*y}}})) /// +-- FIXME: takes 78s TEST /// --tests largepowers K = ZZ/7 @@ -84,3 +92,29 @@ TEST /// L = potentialExtension M2 findIdem changeBaseField(L, M2) /// + +/// + -- from David's email: reaches recursion limit overnight + needsPackage "DirectSummands" + kk = ZZ/101 + S = kk[x,y,z] + I = monomialIdeal(x^4,x*y,y^4,x*z,y^2*z,z^4) + R = S/I + F = res(coker vars R, LengthLimit => 5) + M = coker F.dd_5; + debugLevel = 1 + elapsedTime L5 = summands M; +/// + +/// +needsPackage "DirectSummands" + kk = ZZ/101 + S = kk[x,y,z] + P = Proj S + TP = tangentSheaf P + R = S/(x*y-z^2) + assert(length summands prune sheaf(module TP ** R) == rank TP) + assert(length summands sheaf(module TP ** R) == length summands prune sheaf(module TP ** R)) +/// + +load "./large-tests.m2" From e3f314caf45e2a019586022a9c28d68bef1e39cc Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 15 Nov 2024 17:09:10 +0100 Subject: [PATCH 31/88] added large-tests.m2 --- .../packages/DirectSummands/large-tests.m2 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 M2/Macaulay2/packages/DirectSummands/large-tests.m2 diff --git a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 new file mode 100644 index 00000000000..ed8007fc10f --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 @@ -0,0 +1,15 @@ +TEST /// + -- testing reduceScalar +-- restart + debug needsPackage "DirectSummands" + assert elapsedTime isIdempotent(2 * id_((ZZ/5[x])^500)) -- .03s + -- + R = (ZZ/3)[x,y,z,w]/(x^3*y+y^3*z+x*y*z*w+z^3*w+x*w^3) + m = map(subquotient(map(R^36,R^{{0}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, 0, 0, 0, 0, 0, 0, 0, y*z, 0, w^2, x*w, y^2, z*w, x*w, x*y+z^2, z^2, y*z, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, x*w, 0, 0, 0, 0, 0, 0, y*w}, {0, w, 0, z, 0, 0, y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, y^2, 0, x*w, 0, y*z, 0, 0}, {0, y, x, 0, 0, z, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, x*w, 0, y^2, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, 0, y^2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, y^2, z*w, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, z^2, x*z, x*w, x*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, w^2, 0, 0, x*z, 0, z^2, 0, 0, y^2, 0, 0}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, 0, 0, 0, 0, 0, 0, y*z, x*w, 0, z^2, 0, 0, 0, x*z, 0, x^2, 0, 0}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, x*z, y*z, 0, z^2, 0, 0, 0, 0, x^2, x*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, z*w, y*w, x^2, y*z, y*z, y^2+x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, 0, 0, 0, 0, 0, y*w, 0, z*w, 0, x^2, 0, 0, 0, 0, 0, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, x^2, 0, x*w, 0, w^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, y*w}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0}, {0, y, x, 0, 0, z, 0, y*z, x^2, 0, z*w, y*w, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, z*w}, {0, 0, z, x, w, 0, 0, 0, 0, 0, x*z, 0, x*y, y*z+w^2, z^2, z*w, z*w, y*w, x*w, x*w, 0, 0, z*w, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, y*z, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, y*z+w^2, 0, x*y, 0, z^2, 0, y*w, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, y*z+w^2, x*w, 0, 0, 0, 0, 0, 0, y*w, y*w, 0, 0, 0, 0, x*y, y^2, 0, y*z}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, x*w, 0, 0, 0, 0, 0, y*z, 0, z^2, 0, 0, 0, 0, 0, 0, 0, x*y, 0}, {0, w, 0, z, 0, 0, y, z*w, z^2, 0, z^2, y*z+w^2, 0, 0, 0, 0, 0, 0, z*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, x*z, y*w, x*y, 0, y^2, y^2, z*w, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, x*y, 0, y*w, y*z, 0, 0, z*w, 0, 0}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, y*z, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, x*y}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, 0, y^2, y*w, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, y*w, z^2, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, x*w, y*z, x*z, x*z, x*y, 0, 0, 0, 0, 0, 0, 0, w^2, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, w^2, y*z, 0, 0, 0, 0, y*w, 0, 0}, {0, 0, z, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, y*z, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}),map(R^36,R^{{0}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2}})),subquotient(map(R^36,R^{{0}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, 0, 0, 0, 0, 0, 0, 0, y*z, 0, w^2, x*w, y^2, z*w, x*w, x*y+z^2, z^2, y*z, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, x*w, 0, 0, 0, 0, 0, 0, y*w}, {0, w, 0, z, 0, 0, y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, y^2, 0, x*w, 0, y*z, 0, 0}, {0, y, x, 0, 0, z, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, x*w, 0, y^2, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, 0, y^2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, y^2, z*w, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, z^2, x*z, x*w, x*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, w^2, 0, 0, x*z, 0, z^2, 0, 0, y^2, 0, 0}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, 0, 0, 0, 0, 0, 0, y*z, x*w, 0, z^2, 0, 0, 0, x*z, 0, x^2, 0, 0}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, x*z, y*z, 0, z^2, 0, 0, 0, 0, x^2, x*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, z*w, y*w, x^2, y*z, y*z, y^2+x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, 0, 0, 0, 0, 0, y*w, 0, z*w, 0, x^2, 0, 0, 0, 0, 0, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, x^2, 0, x*w, 0, w^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, y*w}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0}, {0, y, x, 0, 0, z, 0, y*z, x^2, 0, z*w, y*w, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, z*w}, {0, 0, z, x, w, 0, 0, 0, 0, 0, x*z, 0, x*y, y*z+w^2, z^2, z*w, z*w, y*w, x*w, x*w, 0, 0, z*w, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, y*z, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, y*z+w^2, 0, x*y, 0, z^2, 0, y*w, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, y*z+w^2, x*w, 0, 0, 0, 0, 0, 0, y*w, y*w, 0, 0, 0, 0, x*y, y^2, 0, y*z}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, x*w, 0, 0, 0, 0, 0, y*z, 0, z^2, 0, 0, 0, 0, 0, 0, 0, x*y, 0}, {0, w, 0, z, 0, 0, y, z*w, z^2, 0, z^2, y*z+w^2, 0, 0, 0, 0, 0, 0, z*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, x*z, y*w, x*y, 0, y^2, y^2, z*w, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, x*y, 0, y*w, y*z, 0, 0, z*w, 0, 0}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, y*z, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, x*y}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, 0, y^2, y*w, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, y*w, z^2, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, x*w, y*z, x*z, x*z, x*y, 0, 0, 0, 0, 0, 0, 0, w^2, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, w^2, y*z, 0, 0, 0, 0, y*w, 0, 0}, {0, 0, z, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, y*z, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}),map(R^36,R^{{0}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2}})),{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z, 0, 0, 0, w, 0, 0, 0, 0, y, 0, z, z, 0, 0, 0, x, y, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, x, 0, 0, 0, 0, y, z, 0, w, 0, 0, 0, 0, 0, w, y, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, w, z, 0, 0, 0, y, 0, 0, 0, x, z, w, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w, 0, 0, 0, y, x, x, 0, w, z, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w, x, 0, z, x, 0, 0, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x, z, z, 0, 0, 0, 0, x, 0, 0, 0, y, 0, 0, w, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}); + assert try m == quotient(m, map(target m, cover target m, 1), Strategy => "Reflexive") else false + quotient(m, map(target m, cover target m, 1), DegreeLimit => 0) + elapsedTime(m // 1); + assert not elapsedTime isIdempotent m -- 0.002 + assert elapsedTime isIdempotent(2 * id_(target m)) + leadCoefficient (2 * id_(target m)) +/// From edee7aa49187b2e86440c763354de913d5955f0a Mon Sep 17 00:00:00 2001 From: Devlin Date: Mon, 25 Nov 2024 21:13:04 +0100 Subject: [PATCH 32/88] changes from last discussion, in preparation for changing matrix powers --- M2/Macaulay2/packages/DirectSummands.m2 | 53 +++++++++++++++---------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index eb4d5deb550..9aab7f734f7 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -111,6 +111,8 @@ changeBaseField(GaloisField, Module) := (L, M) -> ( R0 := quotient sub(ideal S, L monoid S); return directSum apply(components M, N -> coker sub(presentation N, R0))); + -- don't needlessly create new rings: + if K.order === L.order then return M; i0 := map(L, K); LS := L(monoid S); i1 := map(LS, ring ideal S, gens LS | { i0 K_0 }); @@ -201,10 +203,13 @@ flattenMorphism = f -> ( -- TODO: why doesn't lift(f, ring g) do this automatically? map(target f ** S, source f ** S, lift(cover f, S)) ** cokernel g) +leadCoefficient Number := identity + -- this is a kludge to handle the case when h^2 = ah reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // leadCoefficient m) isIdempotent = h -> reduceScalar(h^2) == reduceScalar h isWeakIdempotent = h -> all(flatten entries flattenMorphism(reduceScalar(h^2) - reduceScalar h), isZero) +--isWeakIdempotent = h -> isZero det cover flattenMorphism(reduceScalar(h^2) - reduceScalar h) -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) @@ -299,27 +304,33 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if rank L >= rank M then return {M}; if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); zdeg := degree 0_M; - -- we look for a composition L -> M -> L which is the identity - B := smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); - C := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); - -- attempt to find a random isomorphism - h := for i to opts.Tries - 1 do ( - b := homomorphism(B * random source B); - c := homomorphism(C * random source C); - if isIsomorphism(c * b) then break b); - if h === null then h = ( - if opts.Strategy & 8 == 8 then ( - -- precomputing the Homs can sometimes be a good strategy - Bhoms := apply(numcols B, i -> homomorphism B_{i}); - Choms := apply(numcols C, i -> homomorphism C_{i}); - pos := position'(Choms, Bhoms, - (c, b) -> isIsomorphism(c * b)); - if pos =!= null then last pos) - else ( - -- and sometimes too memory intensive - ind := position'(numcols C, numcols B, - (c, b) -> isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); - if ind =!= null then homomorphism B_{last ind})); + if isFreeModule L then( + B := smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); + h := for i to opts.Tries - 1 do ( + b := homomorphism(B * random source B); + if isSurjective b then break b);) + else( + -- we look for a composition L -> M -> L which is the identity + B = smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); + C := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); + -- attempt to find a random isomorphism + h = for i to opts.Tries - 1 do ( + b := homomorphism(B * random source B); + c := homomorphism(C * random source C); + if isIsomorphism(c * b) then break b); + if h === null then h = ( + if opts.Strategy & 8 == 8 then ( + -- precomputing the Homs can sometimes be a good strategy + Bhoms := apply(numcols B, i -> homomorphism B_{i}); + Choms := apply(numcols C, i -> homomorphism C_{i}); + pos := position'(Choms, Bhoms, + (c, b) -> isIsomorphism(c * b)); + if pos =!= null then last pos) + else ( + -- and sometimes too memory intensive + ind := position'(numcols C, numcols B, + (c, b) -> isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); + if ind =!= null then homomorphism B_{last ind}));); if h === null then return {M}; if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; -- TODO: can we detect multiple summands of L at once? From fe1cd7a3789061156b6df83eb50771cda99d7ed6 Mon Sep 17 00:00:00 2001 From: Devlin Date: Mon, 25 Nov 2024 21:13:52 +0100 Subject: [PATCH 33/88] changes from last discussion --- .../packages/DirectSummands/idempotents.m2 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index a772152fb72..918e0347a14 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -52,8 +52,8 @@ findIdem'(Module,ZZ) := opts -> (M,e) -> ( L := infinity; for c to opts.Tries - 1 do ( f := generalEndomorphism M; - chi := char f; - eigen := if instance(F, InexactField) then roots chi else flatten rationalPoints ideal chi; + Chi := char f; + eigen := if instance(F, InexactField) then roots Chi else flatten rationalPoints ideal Chi; if #eigen <= 1 then continue; opers := flatten for y in eigen list ( if p == 0 then (f - y*id_M) else ( @@ -132,21 +132,25 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( L := infinity; for c to opts.Tries - 1 do ( f := generalEndomorphism M; - chi := char f; - K' := if instance(F, InexactField) then F else try extField {chi}; + Chi := char f; + K' := if instance(F, InexactField) then F else try extField {Chi}; --TODO: remember different L to extend to; right now the L you return at the end may not be large enough if p != 0 then L = min(L, K'.order) else L = K'; -- TODO: this seems too messy, what's the precise requirement? -- maybe we should separate this in a different method - eigen := if instance(F, InexactField) or isMember(coefficientRing ring chi, {ZZ, QQ}) - then roots chi else flatten rationalPoints ideal chi; + --exactFlag := not( instance(F, InexactField) or isMember(coefficientRing ring Chi, {ZZ, QQ})); + exactFlag := not( instance(F, InexactField)); + eigen := if not exactFlag then roots Chi else flatten rationalPoints ideal Chi; -- if at most one eigenvalue is found then the module is probably indecomposable - if #eigen <= 1 then continue; -- TODO: check that eigen doesn't have higher multiplicity + if not exactFlag and #eigen <= 1 then continue; + if exactFlag and p!= 0 and #eigen <= 1 and L == F.order then continue; + --TODO: add check for when the field is QQ -- we only want eigen values in F eigen = nonnull apply(eigen, y -> try lift(y, F)); if #eigen == 0 then continue; opers := flatten for y in eigen list ( if p == 0 then reduceScalar(f - y*id_M) else ( + --TODO: should we just take powers of K**(f-y*id_M)? for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); idem := position(opers, g -> isWeakIdempotent(K ** g) and g != id_M and K ** g != 0); if idem =!= null then ( From da5cca838fa41885f251ea733da310fcafde2365 Mon Sep 17 00:00:00 2001 From: Devlin Date: Mon, 25 Nov 2024 21:15:05 +0100 Subject: [PATCH 34/88] frobenius pushforward of sheaf map --- .../packages/DirectSummands/frobenius.m2 | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 index d91b6fc6384..829dfa17edd 100644 --- a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -1,3 +1,4 @@ + --needsPackage "PushForward" --needsPackage "Polyhedra" -- for lattice points --needsPackage "Complexes" @@ -67,6 +68,7 @@ frobeniusTwist(ZZ, Matrix) := Matrix => (e, f) -> ( map(frobeniusTwist(e, target f), frobeniusTwist(e, source f), frobeniusTwistMap(e, ring f) ** f)) +--TODO: maybe export "frobeniusMap" frobeniusMap = method(TypicalValue => RingMap) frobeniusMap(Ring, ZZ) := (R, e) -> frobeniusMap(e, R) frobeniusMap(ZZ, Ring) := (e, R) -> ( @@ -110,6 +112,29 @@ frobeniusPushforward(ZZ, Matrix) := (e, f) -> ( frobeniusTwist(e, f)); if not isHomogeneous g then g else directSum decomposeFrobeniusPresentation(e, g))) + +frobeniusPushforward(ZZ, SheafMap) := (e, f) -> ( + if f.cache#?(FrobeniusPushforward, e) + then f.cache#(FrobeniusPushforward, e) + else f.cache#(FrobeniusPushforward, e) = ( + --if not(isFreeModule module source f and isFreeModule module target f) then error "expected a map between free modules"; + g := myPushForward( + frobeniusMap(e, ring matrix f), + frobeniusTwist(e, matrix f)); + if not isHomogeneous g then g + else Fg := first decomposeFrobeniusPresentation(e, g); + R := ring matrix f; + p := char R; + targetPres := presentation target Fg; + (tPrestardegs, tPressrcdegs) := toSequence(-degrees targetPres // p^e); + Fgtarget := sheaf coker map(R^tPrestardegs, R^tPressrcdegs, sub(targetPres, R)); + sourcePres := presentation source Fg; + (sPrestardegs, sPressrcdegs) := toSequence(-degrees sourcePres // p^e); + Fgsource := sheaf coker map(R^sPrestardegs, R^sPressrcdegs, sub(sourcePres, R)); + sheaf map(module Fgtarget, module Fgsource, sub(cover Fg, R))) +) + + --frobeniusPushforward(ZZ, Complex) := (e, C) -> () -- TODO frobeniusPushforward(ZZ, SheafOfRings) := (e, N0) -> frobeniusPushforward(e, N0^1) -- TODO: is this cached? @@ -117,7 +142,7 @@ frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> ( if N.cache#?(FrobeniusPushforward, e) then N.cache#(FrobeniusPushforward, e) else N.cache#(FrobeniusPushforward, e) = ( - R := ring N; + R := ring variety N; p := char R; FN := first components frobeniusPushforward(e, module N); -- slow alternative: From 7c311b954fb22d3566ad57a5192b1aabb20a61f9 Mon Sep 17 00:00:00 2001 From: Devlin Date: Mon, 25 Nov 2024 21:58:10 +0100 Subject: [PATCH 35/88] simplified matrix multiplication --- M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 918e0347a14..0afc4774405 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -128,10 +128,12 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( p := char R; F := ultimate(coefficientRing', R); K := quotient ideal gens R; + V := K ** M; l := if p == 0 then e else max(e, ceiling log_p numgens M); L := infinity; for c to opts.Tries - 1 do ( f := generalEndomorphism M; + fm := K ** f; Chi := char f; K' := if instance(F, InexactField) then F else try extField {Chi}; --TODO: remember different L to extend to; right now the L you return at the end may not be large enough @@ -149,13 +151,13 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( eigen = nonnull apply(eigen, y -> try lift(y, F)); if #eigen == 0 then continue; opers := flatten for y in eigen list ( - if p == 0 then reduceScalar(f - y*id_M) else ( - --TODO: should we just take powers of K**(f-y*id_M)? - for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); - idem := position(opers, g -> isWeakIdempotent(K ** g) and g != id_M and K ** g != 0); + if p == 0 then (1, f - y*id_M, fm - y*id_V) else ( + for j from 0 to e list (j, f - y*id_M, largePower'(p, j+1, largePower(p, l, fm - y*id_V))))); + idem := position(opers, (j, g, g') -> isWeakIdempotent g' and not isSurjective g' and g' != 0); if idem =!= null then ( + (j, g, g') := opers_idem; if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); - idem = opers_idem; + idem = (if p != 0 then largePower'(p, j+1, largePower(p, l, g)) else g); -- for inexact fields, we compose the idempotent until the determinant is zero if instance(F, InexactField) then idem = idem ^ (findErrorMargin idem); return idem)); From 95b70be90c410eb471931ed69adc63cc5934980c Mon Sep 17 00:00:00 2001 From: Devlin Delaney Mallory Date: Fri, 20 Dec 2024 15:23:03 +0100 Subject: [PATCH 36/88] fixed a bug I introduced in summands(L,M) when L is a free module --- M2/Macaulay2/packages/DirectSummands.m2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 9aab7f734f7..d2b05e09320 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -235,6 +235,7 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if not isCommutative R and not isWeylAlgebra R then error "expected a commutative base ring"; if 0 < debugLevel then printerr("splitting module of rank: ", toString rank M); if isFreeModule M then return apply(-degrees M, d -> R^{d}); + --TODO: is there an easy way to check if rank = 1 and M torsionfree? if 1 < #components M then return flatten apply(components M, directSummands_opts); -- TODO: parallelize if opts.ExtendGroundField =!= null then ( L := opts.ExtendGroundField; @@ -266,6 +267,7 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if h == id_M or h == 0 then false else ( if flag and K ** h != 0 then flag = false; + --TODO: is it worth asking if K**h is idempotent instead? (in graded/local case) isIdempotent h)) ); -- check if M is certifiably indecomposable @@ -305,7 +307,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); zdeg := degree 0_M; if isFreeModule L then( - B := smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); + B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); h := for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); if isSurjective b then break b);) From ba0ca2a51bfeca9e430f0cdcd2c3910096857ef8 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 31 Dec 2024 10:57:25 -0600 Subject: [PATCH 37/88] uncommitted changes --- M2/Macaulay2/packages/DirectSummands.m2 | 4 +-- .../packages/DirectSummands/idempotents.m2 | 33 ++++++++----------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index d2b05e09320..4d07d156769 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -226,7 +226,7 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- 2 => use Hom option DegreeLimit => 0 -- 4 => use Hom option MinimalGenerators => false -- 8 => precompute Homs before looking for idempotents -directSummands = method(Options => { Recursive => true, Tries => 0, Verbose => true, Strategy => 7, ExtendGroundField => null }) +directSummands = method(Options => { Recursive => true, Tries => 10, Verbose => true, Strategy => 7, ExtendGroundField => null }) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain @@ -310,7 +310,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); h := for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); - if isSurjective b then break b);) + if isSurjective b then break matrix{L_0}//b);) else( -- we look for a composition L -> M -> L which is the identity B = smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 0afc4774405..587e746b816 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -1,34 +1,27 @@ --needsPackage "RationalPoints2" +random Module := Vector => o -> M -> ( + K := try coefficientRing ring M else ring M; + v := random(cover M ** K, module K, o); + vector inducedMap(M, , generators M * v)) + generalEndomorphism = method() generalEndomorphism Module := M -> ( - R := ring M; - if isHomogeneous M and isHomogeneous R then( - A := Hom(M, M, - DegreeLimit => zdeg := degree 0_M, - MinimalGenerators => false); - B := smartBasis(zdeg, A); - homomorphism(B * random(source B, R^1)) - --TODO: this is only "random" over the base field; is that okay? - ) - else( - --homomorphism sum for i from 0 to numgens A - 1 list sub(randomFieldElement K, R) * A_i - --line above is if we want random over the full field - randomHom(M,M) - ) + zdeg := if isHomogeneous M then degree 0_M; + A := Hom(M, M, + DegreeLimit => zdeg, + MinimalGenerators => false); + B := if isHomogeneous M then smartBasis(zdeg, A) else inducedMap(A, , gens A); + homomorphism(B * random cover source B) ) + generalEndomorphism CoherentSheaf := M -> generalEndomorphism module M -----NEW STUFF FOR INHOMOGENEOUS CASE----- --does same thing as general(Endo)morphism, but in inhomgeneous case randomHom = method() -randomHom(Module, Module) := (M,N) -> ( - R := ring M; - A := Hom(M, N, - MinimalGenerators => false); - homomorphism sum for i from 0 to numgens A - 1 list (random(R^1,R^1))_(0,0) * A_i -) +randomHom(Module, Module) := (M,N) -> homomorphism random Hom(M, N, MinimalGenerators => false) isSplitSummand = method(Options => { Tries => 50 }) From 6da9eb5cb2d7b8211b97ed06dc2a42dce635e0ba Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 1 Feb 2025 20:17:03 -0500 Subject: [PATCH 38/88] added projection and inclusion maps --- M2/Macaulay2/packages/DirectSummands.m2 | 33 ++++++++++++------- M2/Macaulay2/packages/DirectSummands/tests.m2 | 30 +++++++++++++++++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 4d07d156769..e46692c993c 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -227,7 +227,7 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- 4 => use Hom option MinimalGenerators => false -- 8 => precompute Homs before looking for idempotents directSummands = method(Options => { Recursive => true, Tries => 10, Verbose => true, Strategy => 7, ExtendGroundField => null }) -directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> sort( +directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> ( checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis @@ -276,20 +276,31 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex M.cache.Indecomposable = true; return {M} ); -- TODO: parallelize h := if idem =!= null then homomorphism B_{idem} else try findIdempotent M; - -- TODO: add this when the maps M_[i] and M^[i] also work + -- TODO: add this when the maps M_[w] and M^[w] also work with subsets -- M.cache.components = - if h === null then {M} else nonzero flatten join( + if h === null then {M} else ( -- TODO: restrict End M to each summand and pass it on -- TODO: could use 'compose' perhaps - -- TODO: can we check if M has multiple copies of M1 or M2 quickly? - M1 := prune image h; - M2 := prune coker h; + -- TODO: can we check if M has multiple copies of M0 or M1 quickly? + M0 := prune image h; + M1 := prune coker h; + -- TODO: can we keep homomorphisms? + --B0.cache.homomorphism = f -> map(M0, M0, adjoint'(p1 * f * inverse p0, M0, M0), Degree => first degrees source f + degree f); + M0comps := directSummands(M0, opts); + M1comps := directSummands(M1, opts); -- Projection maps to the summands - --p1 := inverse M1.cache.pruningMap * map(image h, M, h); - --p2 := inverse M2.cache.pruningMap * map(coker h, M, h); - --B1.cache.homomorphism = f -> map(M1, M1, adjoint'(p1 * f * inverse p1, M1, M1), Degree => first degrees source f + degree f); - directSummands(M1, opts), - directSummands(M2, opts)) + c := -1; + p0 := inverse M0.cache.pruningMap * inducedMap(image h, M, h); + p1 := inverse M1.cache.pruningMap * inducedMap(coker h, M); + if #M0comps > 1 then apply(#M0comps, i -> M.cache#(symbol ^, [c += 1]) = M0^[i] * p0) else M.cache#(symbol ^, [c += 1]) = p0; + if #M1comps > 1 then apply(#M1comps, i -> M.cache#(symbol ^, [c += 1]) = M1^[i] * p1) else M.cache#(symbol ^, [c += 1]) = p1; + -- Inclusion maps from the summands + -- TODO: will this always work? + scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); + -- return the lists + -- TODO: why is this nonzero needed? + -- TODO: sort these, along with the projections + nonzero flatten join(M0comps, M1comps)) )) -- TODO: if ExtendGroundField is given, change variety diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 5ef332de364..7bb5d50e20f 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -61,6 +61,36 @@ TEST /// assert(8 == #summands M) /// +TEST /// + debug needsPackage "DirectSummands" + n = 4 + S = ZZ/11[x_0..x_(n-1)] + I = trim minors_2 matrix { S_*_{0..n-2}, S_*_{1..n-1}} + R = quotient I + C = res coker vars R + M = image C.dd_3 + needsPackage "RationalPoints2" + h = generalEndomorphism M + rationalPoints ideal char h + -- TODO: does the kernel and cokernel of this give summands? + h' = h - 1 * id_(target h) + prune ker h' + prune coker h' + summands M +/// + +TEST /// + n = 3 + S = (ZZ/2)[x_0..x_(n-1)] + R = quotient (ideal vars S)^3 + F = res coker vars R + M = image F.dd_3 + summands M + summands(image F.dd_1, M) + -- TODO: have a flag to test for twists of input summands as well + summands({image F.dd_1, coker vars R}, M) +/// + TEST /// -- testing in char 0 -- FIXME: --S = ZZ[x,y]; From f4ba01b02e933f8f5d21d8117936e97d0145d0b5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 14 Feb 2025 11:38:03 -0500 Subject: [PATCH 39/88] split random into homogeneous and inhomogeneous version --- .../packages/DirectSummands/idempotents.m2 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 587e746b816..a733e006465 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -1,9 +1,18 @@ --needsPackage "RationalPoints2" -random Module := Vector => o -> M -> ( - K := try coefficientRing ring M else ring M; - v := random(cover M ** K, module K, o); - vector inducedMap(M, , generators M * v)) +-- give a random vector in a module over a local ring +localRandom = (M, opts) -> ( + R := ring M; + -- TODO: which coefficient ring do we want? + K := try coefficientRing R else R; + v := random(cover M ** K, module K, opts); + -- TODO: sub should be unnecessary, but see https://github.com/Macaulay2/M2/issues/3638 + vector inducedMap(M, , generators M * sub(v, R))) + +random(ZZ, Module) := +random(List, Module) := Vector => o -> (d, M) -> vector map(M, , random(cover M, (ring M)^{-d}, o)) +random Module := Vector => o -> M -> ( + if isHomogeneous M then random(degree 1_(ring M), M, o) else localRandom(M, o)) generalEndomorphism = method() generalEndomorphism Module := M -> ( @@ -134,6 +143,7 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( -- TODO: this seems too messy, what's the precise requirement? -- maybe we should separate this in a different method --exactFlag := not( instance(F, InexactField) or isMember(coefficientRing ring Chi, {ZZ, QQ})); + --needsPackage "RationalPoints2" exactFlag := not( instance(F, InexactField)); eigen := if not exactFlag then roots Chi else flatten rationalPoints ideal Chi; -- if at most one eigenvalue is found then the module is probably indecomposable From 8dd88b6050aeda7c38907d872cf4f62c992b4d1f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 28 Mar 2025 09:51:37 -0400 Subject: [PATCH 40/88] added tallySummands and isomorphismTally --- M2/Macaulay2/packages/DirectSummands.m2 | 69 +++++++++++++++---- .../packages/DirectSummands/idempotents.m2 | 29 +++----- M2/Macaulay2/packages/DirectSummands/tests.m2 | 29 ++++++++ 3 files changed, 96 insertions(+), 31 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index e46692c993c..129a462110b 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -39,6 +39,8 @@ export { -- methods "directSummands", "summands" => "directSummands", "findIdempotent", "findIdem" => "findIdempotent", + "findSplitInclusion", + "isomorphismTally", -- symbols "ExtendGroundField", "Indecomposable", @@ -81,7 +83,7 @@ submatrixByDegrees(Matrix, Sequence) := (m, degs) -> ( row := if tar =!= null then positions(degrees target m, deg -> member(deg, tar)); submatrix(m, row, col)) --- TODO: perhaps also take degree into account +-- this defines sorting on modules and sheaves CoherentSheaf ? CoherentSheaf := Module ? Module := (M, N) -> if rank M != rank N then rank M ? rank N else degrees M ? degrees N @@ -136,17 +138,51 @@ checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then print "Warning: the recursion depth limit may need to be extended; use `recursionLimit = N`") module Module := identity + +-- FIXME in the Core: +isiso = lookup(isIsomorphic, Module, Module) +isIsomorphic(Module, Module) := Sequence => o -> (M, N) -> ( + -- TODO: in the free case, handle the Strict => false option, too + if isFreeModule M and isFreeModule N then ( + if set degrees M == set degrees N then return ( true, null )); -- FIXME: give the map + (isiso o)(M, N)) + +isIsomorphic' = method(Options => options isIsomorphic ++ { Tries => 10 }) +isIsomorphic'(Module, Module) := opts -> (M, N) -> ( + opts' := selectKeys(opts, k -> (options isIsomorphic)#?k); + -- TODO: parallelize + any(opts.Tries, i -> first isIsomorphic(M, N, opts'))) + -- TODO: speed this up -- TODO: implement isIsomorphic for sheaves -unique' = L -> ( - L = new MutableList from module \ L; +-- TODO: add strict option +tallySummands = L -> tally ( + opts := Homogeneous => all(L, isHomogeneous); + L = new MutableList from module \ L; b := new MutableList from #L : true; - for k to 5 do -- arbitrary, just to make sure isIsomorphic doesn't fail because of bad randomness - for i to #L-2 do for j from i+1 to #L-1 do if b#j then ( - if isIsomorphic(L#i, L#j) then ( b#j = false; L#j = L#i )); + for i to #L-2 do if b#i then for j from i+1 to #L-1 do if b#j then ( + if isIsomorphic'(L#i, L#j, opts) + then ( b#j = false; L#j = L#i )); new List from L) -tally' := L -> tally unique' L +isomorphismTally = method() +isomorphismTally List := L -> ( + if not uniform L then error "expected list of elements of the same type"; + if not (class L_0 === Module or class L_0 === CoherentSheaf ) then error "expected list of modules or sheaves"; + opts := Homogeneous => all(L, isHomogeneous); + --L = new MutableList from L; + j := 0; + while j < #L list ( + i := j + 1; + c := 1; + while i < #L do ( + if isIsomorphic'(L#j, L#i, opts) + then ( + L = drop(L, {i, i}); + c = c + 1) + else i = i + 1); + j = j + 1; + (L#(j-1), c))) ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- @@ -319,44 +355,51 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( zdeg := degree 0_M; if isFreeModule L then( B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); + --h := for i from 0 to numcols B - 1 do( if isSurjective(b := homomorphism B_{i}) then break matrix{L_0}//b); h := for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); if isSurjective b then break matrix{L_0}//b);) else( -- we look for a composition L -> M -> L which is the identity B = smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); + if numcols B == 0 then return {M}; C := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); + if numcols C == 0 then return {M}; -- attempt to find a random isomorphism h = for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); c := homomorphism(C * random source C); + --TODO: change isIsomorphism to isSurjective? if isIsomorphism(c * b) then break b); + --is it worth doing the following lines? when does the random strategy above fail? if h === null then h = ( if opts.Strategy & 8 == 8 then ( -- precomputing the Homs can sometimes be a good strategy Bhoms := apply(numcols B, i -> homomorphism B_{i}); - Choms := apply(numcols C, i -> homomorphism C_{i}); + Choms := select(apply(numcols C, i -> homomorphism C_{i}), j -> isSurjective j); pos := position'(Choms, Bhoms, (c, b) -> isIsomorphism(c * b)); if pos =!= null then last pos) else ( -- and sometimes too memory intensive ind := position'(numcols C, numcols B, - (c, b) -> isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); + --how to skip c if homomorphism C_{c} is not surjective? + (c, b) -> isSurjective homomorphism C_{c} and isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); if ind =!= null then homomorphism B_{last ind}));); if h === null then return {M}; if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; -- TODO: can we detect multiple summands of L at once? - sort flatten join({L}, if not opts.Recursive then {coker h} - else directSummands(L, coker h, opts))) + sort flatten join({L}, if not opts.Recursive then {prune coker h} + else directSummands(L, prune coker h, opts))) directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( directSummands(module L, module G, opts), N -> sheaf(variety L, N)) -- attempt to peel off summands from a given list of modules directSummands(List, CoherentSheaf) := -directSummands(List, Module) := List => opts -> (Ls, M) -> fold(Ls, {M}, - (L, LL) -> join(drop(LL, -1), directSummands(L, last LL, opts))) +directSummands(List, Module) := List => opts -> (Ls, M) -> sort ( + if 1 < #cachedSummands M then flatten apply(cachedSummands M, N -> directSummands(Ls, N, opts)) + else fold(Ls, {M}, (L, LL) -> join(drop(LL, -1), directSummands(L, last LL, opts)))) -- isDefinitelyIndecomposable = method() diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index a733e006465..19b5e9e5eba 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -14,34 +14,27 @@ random(List, Module) := Vector => o -> (d, M) -> vector map(M, , random(cover M, random Module := Vector => o -> M -> ( if isHomogeneous M then random(degree 1_(ring M), M, o) else localRandom(M, o)) -generalEndomorphism = method() -generalEndomorphism Module := M -> ( +generalEndomorphism = method(Options => options random) +generalEndomorphism Module := Matrix => o -> M -> ( zdeg := if isHomogeneous M then degree 0_M; A := Hom(M, M, DegreeLimit => zdeg, MinimalGenerators => false); B := if isHomogeneous M then smartBasis(zdeg, A) else inducedMap(A, , gens A); - homomorphism(B * random cover source B) -) - -generalEndomorphism CoherentSheaf := M -> generalEndomorphism module M + homomorphism(B * random(source B, o))) +-- the sheaf needs to be pruned to prevent missing endomorphisms +generalEndomorphism CoherentSheaf := SheafMap => o -> F -> sheaf generalEndomorphism(module prune F, o) -----NEW STUFF FOR INHOMOGENEOUS CASE----- ---does same thing as general(Endo)morphism, but in inhomgeneous case -randomHom = method() -randomHom(Module, Module) := (M,N) -> homomorphism random Hom(M, N, MinimalGenerators => false) - -isSplitSummand = method(Options => { Tries => 50 }) - +findSplitInclusion = method(Options => { Tries => 50 }) --tests if M is a split summand of N -isSplitSummand(Module,Module) := opts -> (M,N) -> ( +findSplitInclusion(Module, Module) := opts -> (M, N) -> ( h := for i to opts.Tries - 1 do ( - b := randomHom(M,N); - c := randomHom(N,M); + b := homomorphism random Hom(M, N, MinimalGenerators => false); + c := homomorphism random Hom(N, M, MinimalGenerators => false); if isIsomorphism(c * b) then break b); - if h === null then return "not known" else return h -) + if h === null then return "not known" else return h) findIdem' = method(Options => { Tries=>500 }) findIdem' Module := opts -> M -> findIdem'(M, fieldExponent ring M,opts) @@ -60,7 +53,7 @@ findIdem'(Module,ZZ) := opts -> (M,e) -> ( opers := flatten for y in eigen list ( if p == 0 then (f - y*id_M) else ( for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); - idem := position(opers, g -> isSplitSummand(image g, source g) =!= null and g != id_M and K ** g != 0 and prune ker g != 0); + idem := position(opers, g -> findSplitInclusion(image g, source g) =!= null and g != id_M and K ** g != 0 and prune ker g != 0); if idem =!= null then ( if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); return opers_idem)); diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 7bb5d50e20f..32f5a4caa52 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -98,7 +98,11 @@ TEST /// -- testing in char 0 S = QQ[x,y]; assert(2 == #summands coker matrix "x,y; y,x") assert(1 == #summands coker matrix "x,y;-y,x") + -- restart + debug needsPackage "DirectSummands" S = QQ[a,b,c,d]; + M = coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a" + findIdempotent M -- TODO: can we make sure the last block remains symmetric? assert(3 == #summands coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a") K = toField(QQ[i]/(i^2+1)); @@ -147,4 +151,29 @@ needsPackage "DirectSummands" assert(length summands sheaf(module TP ** R) == length summands prune sheaf(module TP ** R)) /// +/// + debug needsPackage "DirectSummands" + kk = ZZ/13 + S = kk[x,y,z] + R = S/(x*z-y^2) + L = summands frobeniusPushforward(1, R); + L = summands S^30000; + elapsedTime isomorphismTally L + elapsedTime tallySummands L + set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} +/// + + +/// + debug needsPackage "DirectSummands" + kk = ZZ/11 + S = kk[x,y,z,Degrees=>{5,1,5}] + R = S/(x*z-y^10) + L = summands frobeniusPushforward(1, R); + elapsedTime isomorphismTally L; + elapsedTime tallySummands L; + set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} +/// + + load "./large-tests.m2" From 142c0e3ac496af95a911d3f4a827d40ec131585e Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 30 Mar 2025 15:06:04 -0400 Subject: [PATCH 41/88] added failing GF exmaple and fixed free summand check --- M2/Macaulay2/packages/DirectSummands.m2 | 5 +++- M2/Macaulay2/packages/DirectSummands/tests.m2 | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 129a462110b..b4734bbcedf 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -196,6 +196,8 @@ coneComp = (C, u, v) -> ( -- TODO: add this as a strategy to basis smartBasis = (deg, M) -> ( + -- TODO: try splitting coker {{a, b^3}, {-b^3, a}} over ZZ/32003[a..b]/(a^2+b^6) + if M == 0 then return map(M, 0, 0); if instance(deg, ZZ) then deg = {deg}; degs := if #deg == 1 then select(unique degrees M, d -> d <= deg) else ( -- FIXME: in the multigraded case sometimes just using basis is faster: @@ -270,9 +272,10 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex R := ring M; if not isCommutative R and not isWeylAlgebra R then error "expected a commutative base ring"; if 0 < debugLevel then printerr("splitting module of rank: ", toString rank M); - if isFreeModule M then return apply(-degrees M, d -> R^{d}); --TODO: is there an easy way to check if rank = 1 and M torsionfree? if 1 < #components M then return flatten apply(components M, directSummands_opts); -- TODO: parallelize + if isFreeModule M then return apply(toList pairs(-degrees M), (i, d) -> ( + M.cache#(symbol ^, [i]) = transpose (M.cache#(symbol _, [i]) = matrix M_i); R^{d})); if opts.ExtendGroundField =!= null then ( L := opts.ExtendGroundField; L = if instance(L, ZZ) then GF(char R, L) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 32f5a4caa52..fa1b371d09d 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -175,5 +175,28 @@ needsPackage "DirectSummands" set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} /// +TEST /// + restart + errorDepth=2 + debug needsPackage "DirectSummands" + -- TODO: ARRGGAGGGHHHH GF is fucking up 'a' + R = ZZ/101[a,b, Degrees => {6,2}]/(a^2+b^6) + assert(2 == #summands coker matrix {{a, b^3}, {-b^3, a}}) + R = ZZ/32003[a,b, Degrees => {6,2}]/(a^2+b^6) + assert(1 == #summands coker matrix {{a, b^3}, {-b^3, a}}) + assert(2 == #summands(coker matrix {{a, b^3}, {-b^3, a}}, ExtendGroundField => 2)) + R = ZZ/32003[a,b]/(a^2+b^6) + assert(1 == #summands coker matrix {{a, b^3}, {-b^3, a}}) + assert(2 == #summands(coker matrix {{a, b^3}, {-b^3, a}}, ExtendGroundField => 2)) + R = GF(32003, 2)[a,b, Degrees => {6,2}]/(a^2+b^6) + assert(2 == #summands coker matrix {{a, b^3}, {-b^3, a}}) + + R = GF(32003, 2)[a,b]/(a^2+b^6) + assert(2 == #summands coker matrix {{a, b^3}, {-b^3, a}}) + + M = coker matrix {{a, b^3}, {-b^3, a}} + findIdempotent M + summands(M, ExtendGroundField => 2) +/// load "./large-tests.m2" From c393100949f77196c8fcba362df8a9ae38c2e525 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 1 Apr 2025 05:34:54 -0400 Subject: [PATCH 42/88] fixed a bug in isIsomorphic for free modules --- M2/Macaulay2/packages/DirectSummands.m2 | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index b4734bbcedf..3adebe205fe 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -60,6 +60,7 @@ importFrom_Core { "raw", "rawReshape", "rawNumberOfColumns", "rawNumberOfRows", + "sortBy", } ----------------------------------------------------------------------------- @@ -139,13 +140,25 @@ checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then print module Module := identity --- FIXME in the Core: +-- give an isomorphism between two free modules with same degrees +-- FIXME: because of https://github.com/Macaulay2/M2/issues/3719, +-- this might not give the most "natural" isomorphism +isisofree = o -> (M, N0) -> ( + (d1, d2) := (degrees M, degrees N0); + if #d1 =!= #d2 then return (false, null); + if o.Strict then N := N0 else d2 = degrees( + N = N0 ** (ring N0)^(min d2 - min d1)); + if sort d1 != sort d2 then return (false, null); + p1 := first \ (sortBy last) toList pairs d1; + p2 := first \ (sortBy last) toList pairs d2; + (true, map(M, N0, id_N^p2 // id_M^p1))) + +-- TODO: move to isIsomorphism isiso = lookup(isIsomorphic, Module, Module) isIsomorphic(Module, Module) := Sequence => o -> (M, N) -> ( - -- TODO: in the free case, handle the Strict => false option, too - if isFreeModule M and isFreeModule N then ( - if set degrees M == set degrees N then return ( true, null )); -- FIXME: give the map - (isiso o)(M, N)) + if isFreeModule M and isFreeModule N + then (isisofree o)(M, N) + else (isiso o)(M, N)) isIsomorphic' = method(Options => options isIsomorphic ++ { Tries => 10 }) isIsomorphic'(Module, Module) := opts -> (M, N) -> ( From 4072c498c226a8f39bfd1e102bd5b77d3e2ccacb Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 02:55:14 -0400 Subject: [PATCH 43/88] added groundField instead of baseField and coefficientRing' --- .../packages/DirectSummands/idempotents.m2 | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 19b5e9e5eba..902006e6674 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -41,7 +41,7 @@ findIdem' Module := opts -> M -> findIdem'(M, fieldExponent ring M,opt findIdem'(Module,ZZ) := opts -> (M,e) -> ( R := ring M; p := char R; - F := ultimate(coefficientRing, R); + F := groundField R; K := quotient ideal gens R; l := if p == 0 then e else max(e, ceiling log_p numgens M); L := infinity; @@ -68,17 +68,14 @@ findIdem'(Module,ZZ) := opts -> (M,e) -> ( --------------------- --- TODO: this needs improvement to work over RR, QQ, GF, FractionField, etc. -- e.g. given a tower such as K[x][y]/I, returns K -baseField = method() -baseField GaloisField := identity -baseField FractionField := identity -- FIXME: does this always work? -baseField QuotientRing := R -> try baseField coefficientRing R else R -baseField Ring := R -> try ( coefficientRing first flattenRing R ) else R +-- TODO: use in localRandom? +groundField = method() +groundField Ring := R -> ultimate(K -> if isField K then K else coefficientRing K, R) -- e.g. given a field isomorphic to GF(p,e), returns e fieldExponent = R -> ( - L := baseField R; + L := groundField R; (p, e) := (char L, 1); if p == 0 then return e; a := L_0; -- primitive element of L @@ -89,7 +86,7 @@ fieldExponent = R -> ( char Matrix := A -> ( if numRows A != numColumns A then error "expected a square matrix"; b := symbol b; - T := (baseField ring A)(monoid[b]); + T := (groundField ring A)(monoid[b]); B := sub(cover A, T); I := id_(source B); det(B - T_0 * I)) @@ -108,8 +105,6 @@ largePower' = (p,l,M) -> ( N := M; (largePower(p,l-1,M))^(p-1) * largePower'(p,l-1,M)) -coefficientRing' = K -> if isField K then K else coefficientRing K - lift(CC, CC_*) := opts -> (r, C) -> numeric(precision C, r) -- adjust as needed LOL @@ -121,7 +116,7 @@ findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent findIdempotent(Module, ZZ) := opts -> (M, e) -> ( R := ring M; p := char R; - F := ultimate(coefficientRing', R); + F := groundField R; K := quotient ideal gens R; V := K ** M; l := if p == 0 then e else max(e, ceiling log_p numgens M); From ffd5ba8c444516fc6808b49017913de6b98a0e31 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 04:36:20 -0400 Subject: [PATCH 44/88] added eigenvalues' --- .../packages/DirectSummands/idempotents.m2 | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 902006e6674..1b30c55d462 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -41,14 +41,12 @@ findIdem' Module := opts -> M -> findIdem'(M, fieldExponent ring M,opt findIdem'(Module,ZZ) := opts -> (M,e) -> ( R := ring M; p := char R; - F := groundField R; K := quotient ideal gens R; l := if p == 0 then e else max(e, ceiling log_p numgens M); L := infinity; for c to opts.Tries - 1 do ( f := generalEndomorphism M; - Chi := char f; - eigen := if instance(F, InexactField) then roots Chi else flatten rationalPoints ideal Chi; + eigen := eigenvalues' f; if #eigen <= 1 then continue; opers := flatten for y in eigen list ( if p == 0 then (f - y*id_M) else ( @@ -91,6 +89,12 @@ char Matrix := A -> ( I := id_(source B); det(B - T_0 * I)) +eigenvalues' = A -> ( + Chi := char A; + F := groundField ring A; + if instance(F, InexactField) then roots Chi + else flatten rationalPoints ideal Chi) + largePower = (p,l,M) -> ( if p^l < 2^30 then return M^(p^l); --should have this line check for monomial size of ambient ring also @@ -119,23 +123,24 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( F := groundField R; K := quotient ideal gens R; V := K ** M; + exactFlag := not instance(F, InexactField); l := if p == 0 then e else max(e, ceiling log_p numgens M); L := infinity; for c to opts.Tries - 1 do ( f := generalEndomorphism M; fm := K ** f; Chi := char f; - K' := if instance(F, InexactField) then F else try extField {Chi}; + K' := if not exactFlag then F else try extField {Chi}; --TODO: remember different L to extend to; right now the L you return at the end may not be large enough if p != 0 then L = min(L, K'.order) else L = K'; -- TODO: this seems too messy, what's the precise requirement? -- maybe we should separate this in a different method --exactFlag := not( instance(F, InexactField) or isMember(coefficientRing ring Chi, {ZZ, QQ})); --needsPackage "RationalPoints2" - exactFlag := not( instance(F, InexactField)); + -- TODO: replace with eigenvalues'? eigen := if not exactFlag then roots Chi else flatten rationalPoints ideal Chi; -- if at most one eigenvalue is found then the module is probably indecomposable - if not exactFlag and #eigen <= 1 then continue; + if not exactFlag and #eigen <= 1 then continue; if exactFlag and p!= 0 and #eigen <= 1 and L == F.order then continue; --TODO: add check for when the field is QQ -- we only want eigen values in F From c0ceb204a06e0c7297f23790cb9f5b63fc97ec45 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 16:50:15 -0400 Subject: [PATCH 45/88] added summandsFromProjectives --- M2/Macaulay2/packages/DirectSummands.m2 | 7 +- .../packages/DirectSummands/homogeneous.m2 | 87 +++++++++++++++++++ .../packages/DirectSummands/idempotents.m2 | 2 +- 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/homogeneous.m2 diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 3adebe205fe..0d999c52ff5 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -70,8 +70,10 @@ importFrom_Core { -- helpers for computing Frobenius pushforwards of modules and sheaves -- TODO: move to PushForward package? load "./DirectSummands/frobenius.m2" --- helpers for finding random idempotents of a module +-- helpers for finding random idempotents of a module for the local case load "./DirectSummands/idempotents.m2" +-- helpers for finding random projectors of a module for the graded case +load "./DirectSummands/homogeneous.m2" ----------------------------------------------------------------------------- -- Things to move to the Core @@ -303,6 +305,9 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; if 1 < #L then return directSummands(directSum L, opts)); -- + -- TODO: should this happen now, or after the indecomposability check? + if isHomogeneous M then return summandsFromProjectors M; + -- K := coker vars R; zdeg := degree 0_M; -- TODO: make "elapsedTime" contingent on verbosity diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 new file mode 100644 index 00000000000..638fc4bf90e --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -0,0 +1,87 @@ +needsPackage "RationalPoints2" + +findProjectors = method(Options => { Tries => 50 }) +findProjectors Module := opts -> M -> ( + R := ring M; + p := char R; + F := groundField R; + K := quotient ideal gens R; + n := numgens M; + L := null; + -- TODO: sort the degrees to make finding eigenvalues faster? + -- degs := unique sort degrees M; + for c to opts.Tries - 1 do ( + f := generalEndomorphism M; -- about 20% of computation + -- eigenvalues of f must be over the field, + -- and we can prove that f can be diagonalized over R + -- (i.e. without passing to frac R), hence we can + -- compute the eigenvalues by going to the field + f0 := sub(K ** f, F); + -- to be used as a suggestion in the error + -- TODO: expand for characteristic zero + L = extField { char f0 }; -- about 25% of computation + -- finding eigenvalues would be faster if the matrix + -- was put in Jordan form first, but this is easier... + eigen := eigenvalues' f0; -- about 25% of computation + if #eigen <= 1 then continue; + -- TODO: n is sufficient but usually too large + -- we need the maximum sum of multiplicities of an eigenvalue + -- TODO: worth using largePower? + -* + netList(projs = for y in eigen list (f - y * id_M)^n) + (f - eigen#0 * id_M)^n + (f - eigen#1 * id_M)^n + netList(injs = apply(projs, pr -> inducedMap(M, ker pr))) + projs#0 + projs#1 + image injs#0 + image injs#1 + *- + return for y in eigen list (f - y * id_M)^n + ); + -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K + error("no projector found after ", toString opts.Tries, " attempts. Try passing + ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) + +-- TODO: this should take the same options as summands +summandsFromProjectors = method(Options => { Tries => 50 }) +summandsFromProjectors Module := opts -> M -> ( + if degree M <= 1 then return {M}; + projs := try findProjectors(M, Tries => opts.Tries) else return {M}; + injs := apply(projs, pr -> inducedMap(M, ker pr)); + iota := matrix { injs }; + -- assert(0 == intersect apply(injs, image)); + -- assert same { numcols mingens M - numcols mingens coker iota, + -- sum(projs, f -> numcols mingens kernel f), + -- sum(injs, f -> numcols mingens image f) }; + L1 := flatten for pr in projs list ( + -- inducedMap(M, ker pr) + N := prune ker pr; + Ncomps := summandsFromProjectors(N, opts); + -- TODO: Projection maps to the summands + --c := -1; + --p := inverse N.cache.pruningMap * inducedMap(ker pr, N, ??); + --if #Ncomps > 1 then apply(#Ncomps, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) else M.cache#(symbol ^, [c += 1]) = p; + -- Inclusion maps from the summands + -- TODO: will this always work? + --scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); + Ncomps); + N := prune coker iota; + L2 := if N != 0 then summandsFromProjectors(N, opts) else {}; + join(L1, L2) +) + +end-- + +restart +debug needsPackage "DirectSummands" + -- ~2.2s + R = ZZ/101[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) + C = res(coker vars R, LengthLimit => 3) + D = res(coker transpose C.dd_3, LengthLimit => 3) + M = coker D.dd_3 + summands M + elapsedTime profile summands M; + profileSummary "DirectSum" + elapsedTime assert(8 == #summands M) + isIsomorphic(M, directSum summands M) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 1b30c55d462..e653b3a5da9 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -81,7 +81,7 @@ fieldExponent = R -> ( e) -- finds the characteristic polynomial of a matrix mod the maximal ideal -char Matrix := A -> ( +char Matrix := A -> A.cache.char ??= ( if numRows A != numColumns A then error "expected a square matrix"; b := symbol b; T := (groundField ring A)(monoid[b]); From b42bec7b332791ede74fefd5b32e887f0bef3d5d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 17:24:17 -0400 Subject: [PATCH 46/88] updated tests --- M2/Macaulay2/packages/DirectSummands/tests.m2 | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index fa1b371d09d..586680af805 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -1,14 +1,16 @@ TEST /// -- basic test + -- ~0.2s S = QQ[x,y] M = coker matrix{{1,0},{1,y}} A = summands M B = summands prune M C = summands trim M -- FIXME: this keeps annoyingly breaking - assert same(A, {prune M}, B, prune \ C) + assert same(prune \ A, {prune M}, B, prune \ C) /// TEST /// -- direct summands of a free module + -- ~1.1s R = ZZ/2[x_0..x_5] M = R^{100:-2,100:0,100:2} A = summands M; @@ -21,13 +23,15 @@ TEST /// -- direct summands of a free module /// TEST /// -- direct summands of a multigraded free module + -- ~0.05s R = QQ[x,y,z] ** QQ[a,b,c] M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} assert same(M, directSum summands M) - --assert first isIsomorphic(directSum elapsedTime summands M, M) + assert first isIsomorphic(directSum summands M, M) /// TEST /// -- direct summands of a ring + -- ~0.06s S = ZZ/3[x,y,z] R = ZZ/3[x,y,z,w]/(x^3+y^3+z^3+w^3) f = map(R, S) @@ -54,14 +58,16 @@ TEST /// -- direct summands over field extensions /// TEST /// + -- ~2.2s R = ZZ/101[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) C = res(coker vars R, LengthLimit => 3) D = res(coker transpose C.dd_3, LengthLimit => 3) M = coker D.dd_3 - assert(8 == #summands M) + elapsedTime assert(8 == #summands M) /// TEST /// + -- ~1.7s debug needsPackage "DirectSummands" n = 4 S = ZZ/11[x_0..x_(n-1)] @@ -69,17 +75,12 @@ TEST /// R = quotient I C = res coker vars R M = image C.dd_3 - needsPackage "RationalPoints2" - h = generalEndomorphism M - rationalPoints ideal char h - -- TODO: does the kernel and cokernel of this give summands? - h' = h - 1 * id_(target h) - prune ker h' - prune coker h' - summands M + assert(6 == #summands M) /// TEST /// + -- FIXME: why is this test so slow? + end n = 3 S = (ZZ/2)[x_0..x_(n-1)] R = quotient (ideal vars S)^3 @@ -98,12 +99,8 @@ TEST /// -- testing in char 0 S = QQ[x,y]; assert(2 == #summands coker matrix "x,y; y,x") assert(1 == #summands coker matrix "x,y;-y,x") - -- restart debug needsPackage "DirectSummands" S = QQ[a,b,c,d]; - M = coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a" - findIdempotent M - -- TODO: can we make sure the last block remains symmetric? assert(3 == #summands coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a") K = toField(QQ[i]/(i^2+1)); S = K[x,y]; @@ -112,7 +109,7 @@ TEST /// -- testing in char 0 S = K[a,b,c,d]; assert(4 == #summands coker matrix "a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a") S = CC[x,y]; - scan(20, i -> assert(set summands coker matrix {{x,y},{-y,x}} == set {cokernel matrix {{x-ii*y}}, cokernel matrix {{x+ii*y}}})) + -- FIXME scan(20, i -> assert(set summands coker matrix {{x,y},{-y,x}} == set {cokernel matrix {{x-ii*y}}, cokernel matrix {{x+ii*y}}})) /// -- FIXME: takes 78s @@ -163,11 +160,10 @@ needsPackage "DirectSummands" set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} /// - /// debug needsPackage "DirectSummands" kk = ZZ/11 - S = kk[x,y,z,Degrees=>{5,1,5}] + S = kk[x,y,z, Degrees => {5,1,5}] R = S/(x*z-y^10) L = summands frobeniusPushforward(1, R); elapsedTime isomorphismTally L; @@ -200,3 +196,8 @@ TEST /// /// load "./large-tests.m2" + +end-- + +restart +elapsedTime check "DirectSummands" From f690a410a6e729da8d0adc8d690566f83db6786d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 17:31:30 -0400 Subject: [PATCH 47/88] fixed bug in isisomfree --- M2/Macaulay2/packages/DirectSummands.m2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 0d999c52ff5..0b5277ac9a2 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -149,7 +149,7 @@ isisofree = o -> (M, N0) -> ( (d1, d2) := (degrees M, degrees N0); if #d1 =!= #d2 then return (false, null); if o.Strict then N := N0 else d2 = degrees( - N = N0 ** (ring N0)^(min d2 - min d1)); + N = N0 ** (ring N0)^{min d2 - min d1}); if sort d1 != sort d2 then return (false, null); p1 := first \ (sortBy last) toList pairs d1; p2 := first \ (sortBy last) toList pairs d2; From 2985ea7465c230e8af938597f79285911f923dcd Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 18:13:12 -0400 Subject: [PATCH 48/88] added projection and injection maps for summandsFromProjectors --- .../packages/DirectSummands/homogeneous.m2 | 73 ++++++++----------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 638fc4bf90e..f82bbe58ff6 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -17,26 +17,14 @@ findProjectors Module := opts -> M -> ( -- (i.e. without passing to frac R), hence we can -- compute the eigenvalues by going to the field f0 := sub(K ** f, F); - -- to be used as a suggestion in the error - -- TODO: expand for characteristic zero - L = extField { char f0 }; -- about 25% of computation -- finding eigenvalues would be faster if the matrix -- was put in Jordan form first, but this is easier... eigen := eigenvalues' f0; -- about 25% of computation - if #eigen <= 1 then continue; - -- TODO: n is sufficient but usually too large - -- we need the maximum sum of multiplicities of an eigenvalue - -- TODO: worth using largePower? - -* - netList(projs = for y in eigen list (f - y * id_M)^n) - (f - eigen#0 * id_M)^n - (f - eigen#1 * id_M)^n - netList(injs = apply(projs, pr -> inducedMap(M, ker pr))) - projs#0 - projs#1 - image injs#0 - image injs#1 - *- + if #eigen <= 1 then ( + -- to be used as a suggestion in the error + -- TODO: expand for characteristic zero + L = extField { char f0 }; + continue); return for y in eigen list (f - y * id_M)^n ); -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K @@ -47,29 +35,28 @@ findProjectors Module := opts -> M -> ( summandsFromProjectors = method(Options => { Tries => 50 }) summandsFromProjectors Module := opts -> M -> ( if degree M <= 1 then return {M}; + -- maps M -> M whose (co)kernel is a (usually indecomposable) summand projs := try findProjectors(M, Tries => opts.Tries) else return {M}; + -- assert(0 == intersect apply(projs, ker)); + -- maps M_i -> M from the kernel summands injs := apply(projs, pr -> inducedMap(M, ker pr)); - iota := matrix { injs }; -- assert(0 == intersect apply(injs, image)); - -- assert same { numcols mingens M - numcols mingens coker iota, - -- sum(projs, f -> numcols mingens kernel f), - -- sum(injs, f -> numcols mingens image f) }; - L1 := flatten for pr in projs list ( - -- inducedMap(M, ker pr) - N := prune ker pr; - Ncomps := summandsFromProjectors(N, opts); - -- TODO: Projection maps to the summands - --c := -1; - --p := inverse N.cache.pruningMap * inducedMap(ker pr, N, ??); - --if #Ncomps > 1 then apply(#Ncomps, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) else M.cache#(symbol ^, [c += 1]) = p; - -- Inclusion maps from the summands - -- TODO: will this always work? - --scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); - Ncomps); - N := prune coker iota; - L2 := if N != 0 then summandsFromProjectors(N, opts) else {}; - join(L1, L2) -) + -- the map \bigoplus M_i -> M, whose cokernel is the complement of M_i + iota := matrix { injs }; + -- assert first isIsomorphic(M, coker iota ++ directSum(coker \ projs)); + c := -1; + comps := flatten for pr in append(projs, iota) list ( + N := prune coker pr; + L := nonzero summandsFromProjectors(N, opts); + -- Projection maps to the summands + p := inverse N.cache.pruningMap * inducedMap(coker pr, M); + if #L > 1 then apply(#L, i -> + M.cache#(symbol ^, [c += 1]) = N^[i] * p) + else M.cache#(symbol ^, [c += 1]) = p; + L); + -- Inclusion maps from the summands + scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); + comps) end-- @@ -80,8 +67,10 @@ debug needsPackage "DirectSummands" C = res(coker vars R, LengthLimit => 3) D = res(coker transpose C.dd_3, LengthLimit => 3) M = coker D.dd_3 - summands M - elapsedTime profile summands M; - profileSummary "DirectSum" - elapsedTime assert(8 == #summands M) - isIsomorphic(M, directSum summands M) + elapsedTime L = summands M + assert first isIsomorphic(M, directSum M) + assert(8 == #L) + assert all(8, i -> same { M, target M_[i], source M^[i] } + and same { L#i, target M^[i], source M_[i] }) + --elapsedTime profile summands M; + --profileSummary "DirectSum" From 32fc75c57b286d56f3d2d4c45684081ccdbf357f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 21:48:21 -0400 Subject: [PATCH 49/88] lowered the number of Tries --- M2/Macaulay2/packages/DirectSummands/homogeneous.m2 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index f82bbe58ff6..e39f10d8a1e 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -1,6 +1,6 @@ needsPackage "RationalPoints2" -findProjectors = method(Options => { Tries => 50 }) +findProjectors = method(Options => { Tries => null }) findProjectors Module := opts -> M -> ( R := ring M; p := char R; @@ -10,7 +10,8 @@ findProjectors Module := opts -> M -> ( L := null; -- TODO: sort the degrees to make finding eigenvalues faster? -- degs := unique sort degrees M; - for c to opts.Tries - 1 do ( + tries := opts.Tries ?? ceiling(0.1 + 50 / log p); + for c to tries - 1 do ( f := generalEndomorphism M; -- about 20% of computation -- eigenvalues of f must be over the field, -- and we can prove that f can be diagonalized over R @@ -23,6 +24,9 @@ findProjectors Module := opts -> M -> ( if #eigen <= 1 then ( -- to be used as a suggestion in the error -- TODO: expand for characteristic zero + -- TODO: is there any way to tell if the module is indecomposable here? + -- e.g. based on the characteristic polynomial factoring completely + -- but having a single root only? L = extField { char f0 }; continue); return for y in eigen list (f - y * id_M)^n @@ -32,7 +36,7 @@ findProjectors Module := opts -> M -> ( ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) -- TODO: this should take the same options as summands -summandsFromProjectors = method(Options => { Tries => 50 }) +summandsFromProjectors = method(Options => { Tries => null }) summandsFromProjectors Module := opts -> M -> ( if degree M <= 1 then return {M}; -- maps M -> M whose (co)kernel is a (usually indecomposable) summand From f47a51cd8cbcd0769591490bb487befc342dd7d1 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 21:49:20 -0400 Subject: [PATCH 50/88] bumped the version of DirectSummands --- M2/Macaulay2/packages/DirectSummands.m2 | 34 +++++++++++-------- .../packages/DirectSummands/idempotents.m2 | 1 + M2/Macaulay2/packages/DirectSummands/tests.m2 | 18 ++++++---- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 0b5277ac9a2..99b4fc496ba 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -15,8 +15,8 @@ --------------------------------------------------------------------------- newPackage( "DirectSummands", - Version => "0.1", - Date => "25 Oct 2023", + Version => "0.2", + Date => "April 1st 2025", Headline => "decompositions of modules and coherent sheaves", Authors => { { Name => "Devlin Mallory", Email => "malloryd@math.utah.edu", HomePage => "https://math.utah.edu/~malloryd/"}, @@ -274,12 +274,17 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- TODO: add option to provide a general endomorphism or idempotent -- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps --- Strategies: --- 1 => use degrees of generators as heuristic to peel off line bundles first --- 2 => use Hom option DegreeLimit => 0 --- 4 => use Hom option MinimalGenerators => false --- 8 => precompute Homs before looking for idempotents -directSummands = method(Options => { Recursive => true, Tries => 10, Verbose => true, Strategy => 7, ExtendGroundField => null }) +directSummands = method(Options => { + ExtendGroundField => null, -- a field extension or integer e for GF(p, e) + Recursive => true, -- used in directSummands(Module, Module) + Strategy => 7, -- Strategy is a bitwise sum of the following: + -- 1 => use degrees of generators as heuristic to peel off line bundles first + -- 2 => use Hom option DegreeLimit => 0 + -- 4 => use Hom option MinimalGenerators => false + -- 8 => precompute Homs before looking for idempotents + Tries => 10, -- used in directSummands(Module, Module) + Verbose => true, -- whether to print extra debugging info + }) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> ( checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain @@ -374,13 +379,14 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if rank L >= rank M then return {M}; if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); zdeg := degree 0_M; - if isFreeModule L then( - B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); - --h := for i from 0 to numcols B - 1 do( if isSurjective(b := homomorphism B_{i}) then break matrix{L_0}//b); - h := for i to opts.Tries - 1 do ( + if isFreeModule L then ( + B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); + -- Previous alternative: + -- h := for i from 0 to numcols B - 1 do ( isSurjective(b := homomorphism B_{i}) ...) + h := for i to opts.Tries - 1 do ( b := homomorphism(B * random source B); - if isSurjective b then break matrix{L_0}//b);) - else( + if isSurjective b then break matrix {L_0} // b)) + else ( -- we look for a composition L -> M -> L which is the identity B = smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); if numcols B == 0 then return {M}; diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index e653b3a5da9..8c08a165078 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -21,6 +21,7 @@ generalEndomorphism Module := Matrix => o -> M -> ( DegreeLimit => zdeg, MinimalGenerators => false); B := if isHomogeneous M then smartBasis(zdeg, A) else inducedMap(A, , gens A); + -- TODO: we need to pass an option to random to limit the coefficient field homomorphism(B * random(source B, o))) -- the sheaf needs to be pruned to prevent missing endomorphisms generalEndomorphism CoherentSheaf := SheafMap => o -> F -> sheaf generalEndomorphism(module prune F, o) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 586680af805..d84bcbace43 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -39,8 +39,8 @@ TEST /// -- direct summands of a ring assert(M == S^{0,-1,-2}) /// --- FIXME: takes 13s, which is more than it used to TEST /// -- direct summands over field extensions + -- ~9s debug needsPackage "DirectSummands" R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); X = Proj R; @@ -51,6 +51,7 @@ TEST /// -- direct summands over field extensions elapsedTime B = smartBasis({0}, A); -- ~0.03s *- -- if this test fails, check if "try findIdempotent M" if hiding any unexpected errors + -- FIXME: this is slow because random homomorphisms shouldn't be over the extended field elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -- 2.87s -> 2.05 elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 @@ -112,16 +113,19 @@ TEST /// -- testing in char 0 -- FIXME scan(20, i -> assert(set summands coker matrix {{x,y},{-y,x}} == set {cokernel matrix {{x-ii*y}}, cokernel matrix {{x+ii*y}}})) /// --- FIXME: takes 78s TEST /// - --tests largepowers K = ZZ/7 R = K[x,y,z]/(x^3+y^3+z^3) X = Proj R - M1 = summands(frobeniusPushforward(1, OO_X), ExtendGroundField => 2) - M2 = frobeniusPushforward(1, M1#1) - L = potentialExtension M2 - findIdem changeBaseField(L, M2) + F1 = frobeniusPushforward(1, OO_X) + L1 = summands(F1, ExtendGroundField => 2) + assert(7 == #L1) + F2 = frobeniusPushforward(1, L1#1) + L = potentialExtension F2 + -- tests largepowers, but is very slow + -- findIdem changeBaseField(L, F2) + -- TODO: is 7 correct here? + assert(7 == #summands changeBaseField(L, F2)) /// /// From a1ef191f2bb4c45d21a9815b4e7e514ebbf125cc Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 2 Apr 2025 23:47:52 -0400 Subject: [PATCH 51/88] updated summands(L, M) to be non-recursive --- M2/Macaulay2/packages/DirectSummands.m2 | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 99b4fc496ba..7b495c9d08e 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -44,7 +44,6 @@ export { -- symbols "ExtendGroundField", "Indecomposable", - "Recursive", "Tries", -- frobenius methods "frobeniusMap", @@ -276,7 +275,7 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- TODO: cache the inclusion maps directSummands = method(Options => { ExtendGroundField => null, -- a field extension or integer e for GF(p, e) - Recursive => true, -- used in directSummands(Module, Module) + Limit => null, -- used in directSummands(Module, Module) Strategy => 7, -- Strategy is a bitwise sum of the following: -- 1 => use degrees of generators as heuristic to peel off line bundles first -- 2 => use Hom option DegreeLimit => 0 @@ -370,14 +369,25 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf'(variety F, N)) -- tests whether L (perhaps a line bundle) is a summand of M --- Recursive => true (default) attempts to peel as many copies of L as possible +-- Limit => N _recommends_ stopping after peeling N summands of L -- FIXME: it's not guaranteed to work, e.g. on X_4 over ZZ/2 +-- TODO: cache projection/inclusion maps -- TODO: cache this directSummands(Module, Module) := List => opts -> (L, M) -> ( checkRecursionDepth(); if ring L =!= ring M then error "expected objects over the same ring"; if rank L >= rank M then return {M}; + n := opts.Limit ?? numgens M; if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); + if 1 < n then ( + -- TODO: can we detect multiple summands of L at once? + comps := new MutableList from {M}; + for i to n - 1 do ( + LL := directSummands(L, M, opts, Limit => 1); + if #LL == 1 then return sort toList comps; + comps#(#comps-1) = L; + comps##comps = M = LL#1) + ); zdeg := degree 0_M; if isFreeModule L then ( B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); @@ -415,9 +425,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if ind =!= null then homomorphism B_{last ind}));); if h === null then return {M}; if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; - -- TODO: can we detect multiple summands of L at once? - sort flatten join({L}, if not opts.Recursive then {prune coker h} - else directSummands(L, prune coker h, opts))) + {L, prune coker h}) directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( directSummands(module L, module G, opts), N -> sheaf(variety L, N)) From e2d87d326efd60d1c44e59003a1a3b4dc66425ea Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 3 Apr 2025 00:40:08 -0400 Subject: [PATCH 52/88] added debug printing --- M2/Macaulay2/packages/DirectSummands.m2 | 5 +++-- .../packages/DirectSummands/homogeneous.m2 | 19 ++----------------- .../packages/DirectSummands/idempotents.m2 | 1 + 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 7b495c9d08e..ed190ece9f3 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -315,10 +315,11 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex K := coker vars R; zdeg := degree 0_M; -- TODO: make "elapsedTime" contingent on verbosity - elapsedTime A := Hom(M, M, -- most time consuming step + if debugLevel > 1 then printerr "computing Hom module"; + A := Hom(M, M, -- most time consuming step DegreeLimit => if opts.Strategy & 2 == 2 then zdeg, MinimalGenerators => if opts.Strategy & 4 == 4 then false); - elapsedTime B := smartBasis(zdeg, A); + B := smartBasis(zdeg, A); -- FIXME: this currently does not find _all_ idempotents flag := true; -- whether all non-identity homomorphisms are zero mod m -- TODO: 10k columns for F_*(OO_X) on Gr(2,4) over ZZ/3 take a long time diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index e39f10d8a1e..152f298f4f5 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -48,6 +48,8 @@ summandsFromProjectors Module := opts -> M -> ( -- the map \bigoplus M_i -> M, whose cokernel is the complement of M_i iota := matrix { injs }; -- assert first isIsomorphic(M, coker iota ++ directSum(coker \ projs)); + if 0 < debugLevel then printerr("splitting ", toString(#projs+1), + " summands of ranks ", toString apply(injs, i -> degree source i)); c := -1; comps := flatten for pr in append(projs, iota) list ( N := prune coker pr; @@ -61,20 +63,3 @@ summandsFromProjectors Module := opts -> M -> ( -- Inclusion maps from the summands scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); comps) - -end-- - -restart -debug needsPackage "DirectSummands" - -- ~2.2s - R = ZZ/101[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) - C = res(coker vars R, LengthLimit => 3) - D = res(coker transpose C.dd_3, LengthLimit => 3) - M = coker D.dd_3 - elapsedTime L = summands M - assert first isIsomorphic(M, directSum M) - assert(8 == #L) - assert all(8, i -> same { M, target M_[i], source M^[i] } - and same { L#i, target M^[i], source M_[i] }) - --elapsedTime profile summands M; - --profileSummary "DirectSum" diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 8c08a165078..ec848d0af99 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -17,6 +17,7 @@ random Module := Vector => o -> M -> ( generalEndomorphism = method(Options => options random) generalEndomorphism Module := Matrix => o -> M -> ( zdeg := if isHomogeneous M then degree 0_M; + if debugLevel > 1 then printerr "computing Hom module"; A := Hom(M, M, DegreeLimit => zdeg, MinimalGenerators => false); From 00c97c9d778e92ca655f7df54e937ab59466035d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 3 Apr 2025 02:10:14 -0400 Subject: [PATCH 53/88] moved part of summands code to summandsFromIdempotents --- M2/Macaulay2/packages/DirectSummands.m2 | 70 +++++++------------ .../packages/DirectSummands/homogeneous.m2 | 6 +- .../packages/DirectSummands/idempotents.m2 | 41 ++++++++++- 3 files changed, 68 insertions(+), 49 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index ed190ece9f3..12ee6c28dd8 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -66,6 +66,20 @@ importFrom_Core { -* Code section *- ----------------------------------------------------------------------------- +-- defined here and used in idempotents.m2 and homogeneous.m2 +DirectSummandsOptions = new OptionTable from { + ExtendGroundField => null, -- a field extension or integer e for GF(p, e) + Limit => null, -- used in directSummands(Module, Module) + Strategy => 7, -- Strategy is a bitwise sum of the following: + -- 1 => use degrees of generators as heuristic to peel off line bundles first + -- 2 => use Hom option DegreeLimit => 0 + -- 4 => use Hom option MinimalGenerators => false + -- 8 => precompute Homs before looking for idempotents + -- 16 => use summandsFromIdempotents even in graded case + Tries => 10, -- used in randomized algorithms + Verbose => true, -- whether to print extra debugging info +} + -- helpers for computing Frobenius pushforwards of modules and sheaves -- TODO: move to PushForward package? load "./DirectSummands/frobenius.m2" @@ -273,17 +287,7 @@ cachedSummands = { ExtendGroundField => null } >> o -> M -> ( -- TODO: add option to provide a general endomorphism or idempotent -- TODO: when splitting over a field extension, use cached splitting over summands -- TODO: cache the inclusion maps -directSummands = method(Options => { - ExtendGroundField => null, -- a field extension or integer e for GF(p, e) - Limit => null, -- used in directSummands(Module, Module) - Strategy => 7, -- Strategy is a bitwise sum of the following: - -- 1 => use degrees of generators as heuristic to peel off line bundles first - -- 2 => use Hom option DegreeLimit => 0 - -- 4 => use Hom option MinimalGenerators => false - -- 8 => precompute Homs before looking for idempotents - Tries => 10, -- used in directSummands(Module, Module) - Verbose => true, -- whether to print extra debugging info - }) +directSummands = method(Options => DirectSummandsOptions) directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> ( checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain @@ -309,9 +313,6 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; if 1 < #L then return directSummands(directSum L, opts)); -- - -- TODO: should this happen now, or after the indecomposability check? - if isHomogeneous M then return summandsFromProjectors M; - -- K := coker vars R; zdeg := degree 0_M; -- TODO: make "elapsedTime" contingent on verbosity @@ -320,6 +321,9 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex DegreeLimit => if opts.Strategy & 2 == 2 then zdeg, MinimalGenerators => if opts.Strategy & 4 == 4 then false); B := smartBasis(zdeg, A); + -- TODO: where should indecomposability check happen? + -- for now it's here, but once we figure out random endomorphisms + -- without computing Hom, this would need to move. -- FIXME: this currently does not find _all_ idempotents flag := true; -- whether all non-identity homomorphisms are zero mod m -- TODO: 10k columns for F_*(OO_X) on Gr(2,4) over ZZ/3 take a long time @@ -329,41 +333,17 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if h == id_M or h == 0 then false else ( if flag and K ** h != 0 then flag = false; - --TODO: is it worth asking if K**h is idempotent instead? (in graded/local case) - isIdempotent h)) - ); + -- TODO: is it worth asking if K**h is idempotent instead? (in graded/local case) + isIdempotent h))); + if idem =!= null then B = B_{idem}; -- check if M is certifiably indecomposable if flag then ( if 0 < debugLevel then printerr("\t... certified indecomposable!"); M.cache.Indecomposable = true; return {M} ); - -- TODO: parallelize - h := if idem =!= null then homomorphism B_{idem} else try findIdempotent M; - -- TODO: add this when the maps M_[w] and M^[w] also work with subsets - -- M.cache.components = - if h === null then {M} else ( - -- TODO: restrict End M to each summand and pass it on - -- TODO: could use 'compose' perhaps - -- TODO: can we check if M has multiple copies of M0 or M1 quickly? - M0 := prune image h; - M1 := prune coker h; - -- TODO: can we keep homomorphisms? - --B0.cache.homomorphism = f -> map(M0, M0, adjoint'(p1 * f * inverse p0, M0, M0), Degree => first degrees source f + degree f); - M0comps := directSummands(M0, opts); - M1comps := directSummands(M1, opts); - -- Projection maps to the summands - c := -1; - p0 := inverse M0.cache.pruningMap * inducedMap(image h, M, h); - p1 := inverse M1.cache.pruningMap * inducedMap(coker h, M); - if #M0comps > 1 then apply(#M0comps, i -> M.cache#(symbol ^, [c += 1]) = M0^[i] * p0) else M.cache#(symbol ^, [c += 1]) = p0; - if #M1comps > 1 then apply(#M1comps, i -> M.cache#(symbol ^, [c += 1]) = M1^[i] * p1) else M.cache#(symbol ^, [c += 1]) = p1; - -- Inclusion maps from the summands - -- TODO: will this always work? - scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); - -- return the lists - -- TODO: why is this nonzero needed? - -- TODO: sort these, along with the projections - nonzero flatten join(M0comps, M1comps)) - )) + -- + if isHomogeneous M + then summandsFromProjectors(M, opts) + else summandsFromIdempotents(M, B, opts))) -- TODO: if ExtendGroundField is given, change variety -- TODO: when ExtendGroundField is given, the variety will change! diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 152f298f4f5..370ef96debf 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -1,6 +1,6 @@ needsPackage "RationalPoints2" -findProjectors = method(Options => { Tries => null }) +findProjectors = method(Options => DirectSummandsOptions) findProjectors Module := opts -> M -> ( R := ring M; p := char R; @@ -36,11 +36,11 @@ findProjectors Module := opts -> M -> ( ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) -- TODO: this should take the same options as summands -summandsFromProjectors = method(Options => { Tries => null }) +summandsFromProjectors = method(Options => DirectSummandsOptions) summandsFromProjectors Module := opts -> M -> ( if degree M <= 1 then return {M}; -- maps M -> M whose (co)kernel is a (usually indecomposable) summand - projs := try findProjectors(M, Tries => opts.Tries) else return {M}; + projs := try findProjectors(M, opts) else return {M}; -- assert(0 == intersect apply(projs, ker)); -- maps M_i -> M from the kernel summands injs := apply(projs, pr -> inducedMap(M, ker pr)); diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index ec848d0af99..75022fbe8ad 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -117,7 +117,7 @@ lift(CC, CC_*) := opts -> (r, C) -> numeric(precision C, r) findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) --TODO: findIdem right now will fail if K is not L[a]/f(a); in general, will need to find a primitive element first -findIdempotent = method(Options => { Tries => 50 }) +findIdempotent = method(Options => DirectSummandsOptions) findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent ring M,opts) findIdempotent(Module, ZZ) := opts -> (M, e) -> ( R := ring M; @@ -168,3 +168,42 @@ findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M,opts) potentialExtension = method() potentialExtension Module := M -> extField {char generalEndomorphism M} potentialExtension CoherentSheaf := M -> potentialExtension module M + +summandsFromIdempotents = method(Options => DirectSummandsOptions) +summandsFromIdempotents Module := opts -> M -> ( + zdeg := degree 0_M; + -- TODO: make "elapsedTime" contingent on verbosity + if debugLevel > 1 then printerr "computing Hom module"; + A := Hom(M, M, -- most time consuming step + DegreeLimit => if opts.Strategy & 2 == 2 then zdeg, + MinimalGenerators => if opts.Strategy & 4 == 4 then false); + B := smartBasis(zdeg, A); + summandsFromIdempotents(M, B, opts)) + +summandsFromIdempotents(Module, Matrix) := opts -> (M, B) -> ( + h := if numcols B == 1 then homomorphism B_{0} + else try findIdempotent(M, opts) else return {M}; + -- TODO: add this when the maps M_[w] and M^[w] also work with subsets + -- M.cache.components = + -- TODO: restrict End M to each summand and pass it on + -- TODO: could use 'compose' perhaps + -- TODO: can we check if M has multiple copies of M0 or M1 quickly? + M0 := prune image h; + M1 := prune coker h; + -- TODO: can we keep homomorphisms? + --B0.cache.homomorphism = f -> map(M0, M0, adjoint'(p1 * f * inverse p0, M0, M0), Degree => first degrees source f + degree f); + M0comps := directSummands(M0, opts); + M1comps := directSummands(M1, opts); + -- Projection maps to the summands + c := -1; + p0 := inverse M0.cache.pruningMap * inducedMap(image h, M, h); + p1 := inverse M1.cache.pruningMap * inducedMap(coker h, M); + if #M0comps > 1 then apply(#M0comps, i -> M.cache#(symbol ^, [c += 1]) = M0^[i] * p0) else M.cache#(symbol ^, [c += 1]) = p0; + if #M1comps > 1 then apply(#M1comps, i -> M.cache#(symbol ^, [c += 1]) = M1^[i] * p1) else M.cache#(symbol ^, [c += 1]) = p1; + -- Inclusion maps from the summands + -- TODO: will this always work? + scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); + -- return the lists + -- TODO: why is this nonzero needed? + -- TODO: sort these, along with the projections + nonzero flatten join(M0comps, M1comps)) From 70cd68da4021bb735afc41d4673391dc89c2dde5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 6 Apr 2025 10:58:45 +0200 Subject: [PATCH 54/88] added gensEnd0 and moved generalEndomorphism --- M2/Macaulay2/packages/DirectSummands.m2 | 32 +++++++++++++++++++ .../packages/DirectSummands/idempotents.m2 | 27 ---------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 12ee6c28dd8..0b5f3d22bdc 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -243,6 +243,38 @@ smartBasis = (deg, M) -> ( M'.cache.homomorphism = M.cache.homomorphism; basis(deg, M')) -- caching this globally causes issues! +-- matrix of degree zero generators of End M +gensEnd0 = M -> M.cache#"End0" ??= ( + -- TODO: need to pass options from Hom + choose the coefficient field + zdeg := if isHomogeneous M then degree 0_M; + A := Hom(M, M, + DegreeLimit => zdeg, + MinimalGenerators => false); + if isHomogeneous M + then smartBasis(zdeg, A) + else inducedMap(A, , gens A)) + +-- give a random vector in a module over a local ring +localRandom = (M, opts) -> ( + R := ring M; + -- TODO: which coefficient ring do we want? + K := try coefficientRing R else R; + v := random(cover M ** K, module K, opts); + -- TODO: sub should be unnecessary, but see https://github.com/Macaulay2/M2/issues/3638 + vector inducedMap(M, , generators M * sub(v, R))) + +random(ZZ, Module) := +random(List, Module) := Vector => o -> (d, M) -> vector map(M, , random(cover M, (ring M)^{-d}, o)) +random Module := Vector => o -> M -> ( + if isHomogeneous M then random(degree 1_(ring M), M, o) else localRandom(M, o)) + +generalEndomorphism = method(Options => options random) +generalEndomorphism Module := Matrix => o -> M -> ( + homomorphism((B := gensEnd0 M) * random(source B, o))) +-- the sheaf needs to be pruned to prevent missing endomorphisms +generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( + sheaf generalEndomorphism(module prune F, o)) + ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 75022fbe8ad..609289c1e17 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -1,32 +1,5 @@ --needsPackage "RationalPoints2" --- give a random vector in a module over a local ring -localRandom = (M, opts) -> ( - R := ring M; - -- TODO: which coefficient ring do we want? - K := try coefficientRing R else R; - v := random(cover M ** K, module K, opts); - -- TODO: sub should be unnecessary, but see https://github.com/Macaulay2/M2/issues/3638 - vector inducedMap(M, , generators M * sub(v, R))) - -random(ZZ, Module) := -random(List, Module) := Vector => o -> (d, M) -> vector map(M, , random(cover M, (ring M)^{-d}, o)) -random Module := Vector => o -> M -> ( - if isHomogeneous M then random(degree 1_(ring M), M, o) else localRandom(M, o)) - -generalEndomorphism = method(Options => options random) -generalEndomorphism Module := Matrix => o -> M -> ( - zdeg := if isHomogeneous M then degree 0_M; - if debugLevel > 1 then printerr "computing Hom module"; - A := Hom(M, M, - DegreeLimit => zdeg, - MinimalGenerators => false); - B := if isHomogeneous M then smartBasis(zdeg, A) else inducedMap(A, , gens A); - -- TODO: we need to pass an option to random to limit the coefficient field - homomorphism(B * random(source B, o))) --- the sheaf needs to be pruned to prevent missing endomorphisms -generalEndomorphism CoherentSheaf := SheafMap => o -> F -> sheaf generalEndomorphism(module prune F, o) - -----NEW STUFF FOR INHOMOGENEOUS CASE----- findSplitInclusion = method(Options => { Tries => 50 }) From d8a9d657939478bf95523d39646fdca8a1b81b5c Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 16 Apr 2025 18:19:27 -0400 Subject: [PATCH 55/88] added findBasicIdempotent, updated isIndecomposable --- M2/Macaulay2/packages/DirectSummands.m2 | 126 ++++++--------- M2/Macaulay2/packages/DirectSummands/docs.m2 | 11 +- .../packages/DirectSummands/homogeneous.m2 | 19 ++- .../packages/DirectSummands/idempotents.m2 | 150 +++++++++++------- 4 files changed, 172 insertions(+), 134 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 0b5f3d22bdc..124ff7417f4 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -5,13 +5,9 @@ -- -- TODO : -- 1. implement over Dmodules and other non-commutative rings --- 2. try to find all irreducible idempotents from End M using representation theory --- 3. cache maps to the summands --- 4. rewrite (isDirectSum, Module) --- 5. implement diagonalize for matrices (and later, complexes) --- 6. restrict and pass End to the summands --- 7. check for isomorphic summands --- 8. make summands work over ZZ (currently rank fails) +-- 2. implement diagonalize for matrices (and later, complexes) +-- 3. find a way to restrict and pass End to the summands +-- 4. make summands work over ZZ (currently rank fails) --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -41,9 +37,9 @@ export { "findIdempotent", "findIdem" => "findIdempotent", "findSplitInclusion", "isomorphismTally", + "isIndecomposable", -- symbols "ExtendGroundField", - "Indecomposable", "Tries", -- frobenius methods "frobeniusMap", @@ -59,6 +55,7 @@ importFrom_Core { "raw", "rawReshape", "rawNumberOfColumns", "rawNumberOfRows", + "tryHooks", "sortBy", } @@ -72,8 +69,8 @@ DirectSummandsOptions = new OptionTable from { Limit => null, -- used in directSummands(Module, Module) Strategy => 7, -- Strategy is a bitwise sum of the following: -- 1 => use degrees of generators as heuristic to peel off line bundles first - -- 2 => use Hom option DegreeLimit => 0 - -- 4 => use Hom option MinimalGenerators => false + -- 2 => check generators of End_0 as heuristic for finding idempotents + -- 4 => unused -- 8 => precompute Homs before looking for idempotents -- 16 => use summandsFromIdempotents even in graded case Tries => 10, -- used in randomized algorithms @@ -153,6 +150,10 @@ nonnull = x -> select(x, i -> i =!= null) checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then printerr( "Warning: the recursion depth limit may need to be extended; use `recursionLimit = N`") +----------------------------------------------------------------------------- +-- things to move to Isomorphism package +----------------------------------------------------------------------------- + module Module := identity -- give an isomorphism between two free modules with same degrees @@ -213,6 +214,7 @@ isomorphismTally List := L -> ( (L#(j-1), c))) ----------------------------------------------------------------------------- +-- methods for finding general endomorphisms of degree zero ----------------------------------------------------------------------------- importFrom_Truncations { "effGenerators" } @@ -243,7 +245,8 @@ smartBasis = (deg, M) -> ( M'.cache.homomorphism = M.cache.homomorphism; basis(deg, M')) -- caching this globally causes issues! --- matrix of degree zero generators of End M +-- matrix of (degree zero) generators of End M +-- TODO: rename this gensEnd0 = M -> M.cache#"End0" ??= ( -- TODO: need to pass options from Hom + choose the coefficient field zdeg := if isHomogeneous M then degree 0_M; @@ -276,39 +279,9 @@ generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( sheaf generalEndomorphism(module prune F, o)) ----------------------------------------------------------------------------- +-- directSummands ----------------------------------------------------------------------------- --- same as flatten(Matrix), but doesn't bother homogenizing the result -flatten' = m -> map(R := ring m, rawReshape(m = raw m, raw R^1, raw R^(rawNumberOfColumns m * rawNumberOfRows m))) - --- not strictly speaking the "lead" coefficient -leadCoefficient Matrix := RingElement => m -> for c to numcols m - 1 do for r to numrows m - 1 do ( - if not zero m_(r,c) then return leadCoefficient m_(r,c)) - --- hacky things for CC --- TODO: move to Core, also add conjugate Matrix, realPart, imaginaryPart, etc. -conjugate RingElement := x -> sum(listForm x, (e, c) -> conjugate c * (ring x)_e) -magnitude = x -> x * conjugate x -isZero = x -> if not instance(F := ultimate(coefficientRing, ring x), InexactField) then x == 0 else ( - leadCoefficient magnitude x < 2^(-precision F)) - --- borrowed from Varieties as hack to get around --- https://github.com/Macaulay2/M2/issues/3407 -flattenMorphism = f -> ( - g := presentation ring f; - S := ring g; - -- TODO: sometimes lifting to ring g is enough, how can we detect this? - -- TODO: why doesn't lift(f, ring g) do this automatically? - map(target f ** S, source f ** S, lift(cover f, S)) ** cokernel g) - -leadCoefficient Number := identity - --- this is a kludge to handle the case when h^2 = ah -reduceScalar = m -> if m == 0 then m else map(target m, source m, cover m // leadCoefficient m) -isIdempotent = h -> reduceScalar(h^2) == reduceScalar h -isWeakIdempotent = h -> all(flatten entries flattenMorphism(reduceScalar(h^2) - reduceScalar h), isZero) ---isWeakIdempotent = h -> isZero det cover flattenMorphism(reduceScalar(h^2) - reduceScalar h) - -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) cachedSummands = { ExtendGroundField => null } >> o -> M -> ( @@ -345,37 +318,20 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; if 1 < #L then return directSummands(directSum L, opts)); -- - K := coker vars R; - zdeg := degree 0_M; - -- TODO: make "elapsedTime" contingent on verbosity - if debugLevel > 1 then printerr "computing Hom module"; - A := Hom(M, M, -- most time consuming step - DegreeLimit => if opts.Strategy & 2 == 2 then zdeg, - MinimalGenerators => if opts.Strategy & 4 == 4 then false); - B := smartBasis(zdeg, A); -- TODO: where should indecomposability check happen? -- for now it's here, but once we figure out random endomorphisms -- without computing Hom, this would need to move. - -- FIXME: this currently does not find _all_ idempotents - flag := true; -- whether all non-identity homomorphisms are zero mod m - -- TODO: 10k columns for F_*(OO_X) on Gr(2,4) over ZZ/3 take a long time -- TODO: add option to skip this step - idem := position(numcols B, c -> ( - h := homomorphism B_{c}; - if h == id_M or h == 0 then false else ( - if flag and K ** h != 0 - then flag = false; - -- TODO: is it worth asking if K**h is idempotent instead? (in graded/local case) - isIdempotent h))); - if idem =!= null then B = B_{idem}; - -- check if M is certifiably indecomposable - if flag then ( - if 0 < debugLevel then printerr("\t... certified indecomposable!"); - M.cache.Indecomposable = true; return {M} ); + -- Note: if an idempotent is found among columns of B, + -- it is cached under M.cache.idempotents, but idempotents + -- that are linear combinations cannot be found this way. + -- Note: this may return null if it is inconclusive + if 2 == 2 & opts.Strategy then + if isIndecomposable M === true then return {M}; -- - if isHomogeneous M + if isHomogeneous M and 16 != 16 & opts.Strategy then summandsFromProjectors(M, opts) - else summandsFromIdempotents(M, B, opts))) + else summandsFromIdempotents(M, opts))) -- TODO: if ExtendGroundField is given, change variety -- TODO: when ExtendGroundField is given, the variety will change! @@ -449,11 +405,35 @@ directSummands(List, Module) := List => opts -> (Ls, M) -> sort ( if 1 < #cachedSummands M then flatten apply(cachedSummands M, N -> directSummands(Ls, N, opts)) else fold(Ls, {M}, (L, LL) -> join(drop(LL, -1), directSummands(L, last LL, opts)))) --- -isDefinitelyIndecomposable = method() -isDefinitelyIndecomposable Module := M -> M.cache.?Indecomposable +----------------------------------------------------------------------------- +-- isIndecomposable +----------------------------------------------------------------------------- + +-- returns false if an easy decomposition can be found +-- (but does _not_ run the full directSummands algorithm) +-- returns true if the module is certifiably indecomposable +-- returns null for non-conclusive results +isIndecomposable = method(Options => { Strategy => null }) -- TODO: check that the sheaf is pruned also -isDefinitelyIndecomposable CoherentSheaf := M -> isDefinitelyIndecomposable module M +isIndecomposable CoherentSheaf := o -> F -> isIndecomposable(module F, o) +isIndecomposable Module := o -> M -> M.cache.isIndecomposable ??= tryHooks( + (isIndecomposable, Module), (o, M), (o, M) -> ( + if 1 < debugLevel then printerr("isIndecomposable was inconclusive. ", + "Try extending the field or pruning the sheaf."))) + +-- this strategy checks if: +-- * M has only one degree zero endomorphism, namely identity, or +-- * all degree zero endomorphisms of M are zero mod maximal ideal +-- if a non-identity idempotent is found, it is cached in M +addHook((isIndecomposable, Module), Strategy => "IdempotentSearch", (opts, M) -> ( + (idemp, certified) := findBasicIdempotent M; + if idemp =!= null then ( if 1 < debugLevel then printerr "module is decomposable!"; false ) + else if certified then ( if 1 < debugLevel then printerr "module is indecomposable!"; true ) + )) + +----------------------------------------------------------------------------- +-* Development section *- +----------------------------------------------------------------------------- --directSummands Matrix := List => opts -> f -> apply(directSummands(coker f, opts), presentation) -* TODO: not done yet @@ -492,10 +472,6 @@ load "./DirectSummands/docs.m2" end-- ------------------------------------------------------------------------------ --* Development section *- ------------------------------------------------------------------------------ - restart check needsPackage "DirectSummands" diff --git a/M2/Macaulay2/packages/DirectSummands/docs.m2 b/M2/Macaulay2/packages/DirectSummands/docs.m2 index 2da6ed7b2f5..65996787cfc 100644 --- a/M2/Macaulay2/packages/DirectSummands/docs.m2 +++ b/M2/Macaulay2/packages/DirectSummands/docs.m2 @@ -1,3 +1,12 @@ +-- TODO: +-- [x] example that is indecomposable +-- [ ] example that decomposes after field extension +-- [ ] example over ZZ, ZZ[i]/(i^2+1), QQ, QQ[i]/(i^2+1), GF, RR, CC? +-- [ ] graded example +-- [ ] local example +-- [ ] example of passing hints of summands +-- [ ] example of isIndecomposable + doc /// Node Key @@ -21,7 +30,7 @@ Node assert(2 == rank FHM) -- initially ~30s for End(FHM), ~110s for basis; ~35s in ZZ/2; now down to ~1s total! assert({FHM} == summands FHM) - assert FHM.cache.Indecomposable + assert FHM.cache.isIndecomposable Acknowledgement The authors thank the organizers of the @HREF{"https://aimath.org/pastworkshops/macaulay2efie.html", "Macaulay2 workshop at AIM"}@, where significant progress on this package was made. diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 370ef96debf..a874d42218e 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -35,12 +35,29 @@ findProjectors Module := opts -> M -> ( error("no projector found after ", toString opts.Tries, " attempts. Try passing ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) --- TODO: this should take the same options as summands +-- TODO: can this be useful? +findBasicProjectors = M -> ( + R := ring M; + F := groundField R; + K := quotient ideal gens R; + n := numgens M; + B := gensEnd0 M; + for c to numcols B - 1 do ( + f := homomorphism B_{c}; + if f == id_M then return; + f0 := sub(K ** f, F); + eigen := eigenvalues' f0; + if #eigen > 1 then return for y in eigen list (f - y * id_M)^n); + {}) + summandsFromProjectors = method(Options => DirectSummandsOptions) summandsFromProjectors Module := opts -> M -> ( if degree M <= 1 then return {M}; -- maps M -> M whose (co)kernel is a (usually indecomposable) summand projs := try findProjectors(M, opts) else return {M}; + summandsFromProjectors(M, projs, opts)) + +summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( -- assert(0 == intersect apply(projs, ker)); -- maps M_i -> M from the kernel summands injs := apply(projs, pr -> inducedMap(M, ker pr)); diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 609289c1e17..6f8006bd661 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -1,51 +1,64 @@ --needsPackage "RationalPoints2" ------NEW STUFF FOR INHOMOGENEOUS CASE----- - -findSplitInclusion = method(Options => { Tries => 50 }) ---tests if M is a split summand of N -findSplitInclusion(Module, Module) := opts -> (M, N) -> ( - h := for i to opts.Tries - 1 do ( - b := homomorphism random Hom(M, N, MinimalGenerators => false); - c := homomorphism random Hom(N, M, MinimalGenerators => false); - if isIsomorphism(c * b) then break b); - if h === null then return "not known" else return h) - -findIdem' = method(Options => { Tries=>500 }) -findIdem' Module := opts -> M -> findIdem'(M, fieldExponent ring M,opts) -findIdem'(Module,ZZ) := opts -> (M,e) -> ( - R := ring M; - p := char R; - K := quotient ideal gens R; - l := if p == 0 then e else max(e, ceiling log_p numgens M); - L := infinity; - for c to opts.Tries - 1 do ( - f := generalEndomorphism M; - eigen := eigenvalues' f; - if #eigen <= 1 then continue; - opers := flatten for y in eigen list ( - if p == 0 then (f - y*id_M) else ( - for j from 0 to e list largePower'(p, j+1, largePower(p, l, f - y*id_M)))); - idem := position(opers, g -> findSplitInclusion(image g, source g) =!= null and g != id_M and K ** g != 0 and prune ker g != 0); - if idem =!= null then ( - if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); - return opers_idem)); -) - ---ONLY IF WE NEED THE FULL BASE FIELD: ---randomFieldElement = method() ---randomFieldElement(Ring) := K -> ( --- gensK := numgens first flattenRing K; --- (random(K^1,K^1))_(0,0) + sum for i from 0 to gensK - 1 list (random(K^1,K^1))_(0,0) * K_i ---) - ---------------------- +----------------------------------------------------------------------------- +-- helpers that should probably move to Core +----------------------------------------------------------------------------- + +-- same as flatten(Matrix), but doesn't bother homogenizing the result +--flatten' = m -> map(R := ring m, rawReshape(m = raw m, raw R^1, raw R^(rawNumberOfColumns m * rawNumberOfRows m))) + +leadCoefficient Number := x -> x +leadMonomial Number := x -> 0 + +-- not strictly speaking the "lead" coefficient, but the first nonzero coefficient +leadCoefficient Matrix := RingElement => m -> if zero m then 0 else ( + for c to numcols m - 1 do for r to numrows m - 1 do ( + if not zero m_(r,c) then return leadCoefficient m_(r,c))) + +-- not strictly speaking the "lead" monomial, but the first nonzero monomial +leadMonomial Matrix := RingElement => m -> if zero m then 0 else ( + for c to numcols m - 1 do for r to numrows m - 1 do ( + if not zero m_(r,c) then return leadMonomial m_(r,c))) + +-- used to be called reduceScalar +reduceCoefficient = m -> if zero m then m else ( + map(target m, source m, cover m // leadCoefficient m)) + +reduceMonomial = m -> if zero m then m else ( + map(target m, source m, cover m // leadMonomial m)) + +-- hacky things for CC +-- TODO: move to Core, also add conjugate Matrix, realPart, imaginaryPart, etc. +conjugate RingElement := x -> sum(listForm x, (e, c) -> conjugate c * (ring x)_e) +magnitude = x -> x * conjugate x +isZero = x -> if not instance(F := ultimate(coefficientRing, ring x), InexactField) then x == 0 else ( + leadCoefficient magnitude x < 2^(-precision F)) + +-- borrowed from Varieties as hack to get around +-- https://github.com/Macaulay2/M2/issues/3407 +flattenMorphism = f -> ( + g := presentation ring f; + S := ring g; + -- TODO: sometimes lifting to ring g is enough, how can we detect this? + -- TODO: why doesn't lift(f, ring g) do this automatically? + map(target f ** S, source f ** S, lift(cover f, S)) ** cokernel g) + +-- reduceCoefficient is a kludge to handle the case when h^2 = ah +isIdempotent = h -> reduceCoefficient(h^2) == reduceCoefficient h +isWeakIdempotent = h -> all(flatten entries flattenMorphism(reduceCoefficient(h^2) - reduceCoefficient h), isZero) +--isWeakIdempotent = h -> isZero det cover flattenMorphism(reduceCoefficient(h^2) - reduceCoefficient h) + +----------------------------------------------------------------------------- -- e.g. given a tower such as K[x][y]/I, returns K -- TODO: use in localRandom? groundField = method() groundField Ring := R -> ultimate(K -> if isField K then K else coefficientRing K, R) +potentialExtension = method() +potentialExtension Module := M -> extField {char generalEndomorphism M} +potentialExtension CoherentSheaf := M -> potentialExtension module M + -- e.g. given a field isomorphic to GF(p,e), returns e fieldExponent = R -> ( L := groundField R; @@ -89,13 +102,18 @@ lift(CC, CC_*) := opts -> (r, C) -> numeric(precision C, r) -- adjust as needed LOL findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) +----------------------------------------------------------------------------- +-- findIdempotents +----------------------------------------------------------------------------- + --TODO: findIdem right now will fail if K is not L[a]/f(a); in general, will need to find a primitive element first findIdempotent = method(Options => DirectSummandsOptions) -findIdempotent Module := opts -> M -> findIdempotent(M, fieldExponent ring M,opts) -findIdempotent(Module, ZZ) := opts -> (M, e) -> ( +findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M, opts) +findIdempotent Module := opts -> M -> ( R := ring M; p := char R; F := groundField R; + e := fieldExponent R; K := quotient ideal gens R; V := K ** M; exactFlag := not instance(F, InexactField); @@ -136,26 +154,44 @@ findIdempotent(Module, ZZ) := opts -> (M, e) -> ( error("no idempotent found after ", toString opts.Tries, " attempts. Try passing ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) -findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M,opts) +protect Idempotents -potentialExtension = method() -potentialExtension Module := M -> extField {char generalEndomorphism M} -potentialExtension CoherentSheaf := M -> potentialExtension module M +-- only tries to find an idempotent among the generators of End_0(M) +-- which is in general unlikely to be successful, but it often works! +-- returns a pair: (idempotent or null, whether M is certified indecomposable) +findBasicIdempotent = M -> ( + M.cache.Idempotents ??= {}; + if 0 < #M.cache.Idempotents + then return (first M.cache.Idempotents, false); + R := ring M; + B := gensEnd0 M; + K := coker vars R; + -- whether all non-identity endomorphisms are zero mod m + -- if this remains true till the end, the module is + -- certifiably indecomposable. + certified := true; + -- TODO: searching over 10k generators for F_*(OO_X) + -- on Gr(2,4) even over ZZ/3 takes a very long time + -- TODO: parallelized this and break on first success + idemp := scan(numcols B, c -> ( + h := homomorphism B_{c}; + if zero h or h == id_M + or zero(hm := h ** K) then return; + certified = false; + if isWeakIdempotent hm then break h)); + if idemp =!= null then M.cache.Idempotents ??= { idemp }; + (idemp, certified)) summandsFromIdempotents = method(Options => DirectSummandsOptions) summandsFromIdempotents Module := opts -> M -> ( - zdeg := degree 0_M; - -- TODO: make "elapsedTime" contingent on verbosity - if debugLevel > 1 then printerr "computing Hom module"; - A := Hom(M, M, -- most time consuming step - DegreeLimit => if opts.Strategy & 2 == 2 then zdeg, - MinimalGenerators => if opts.Strategy & 4 == 4 then false); - B := smartBasis(zdeg, A); - summandsFromIdempotents(M, B, opts)) - -summandsFromIdempotents(Module, Matrix) := opts -> (M, B) -> ( - h := if numcols B == 1 then homomorphism B_{0} + M.cache.Idempotents ??= {}; + h := if 0 < #M.cache.Idempotents then M.cache.Idempotents else try findIdempotent(M, opts) else return {M}; + summandsFromIdempotents(M, h, opts)) + +-- FIXME: handle the case when multiple idempotents are given +summandsFromIdempotents(Module, List) := opts -> (M, idems) -> summandsFromIdempotents(M, idems#0, opts) +summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> ( -- TODO: add this when the maps M_[w] and M^[w] also work with subsets -- M.cache.components = -- TODO: restrict End M to each summand and pass it on From 10a214b7383a1cd341ee2b5e216dab630cd2bed5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 16 Apr 2025 21:09:02 -0400 Subject: [PATCH 56/88] implemented method to avoid recomputing Hom --- M2/Macaulay2/packages/DirectSummands.m2 | 11 ++++++++++- .../packages/DirectSummands/homogeneous.m2 | 19 +++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 124ff7417f4..2a86bc379dc 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -250,9 +250,11 @@ smartBasis = (deg, M) -> ( gensEnd0 = M -> M.cache#"End0" ??= ( -- TODO: need to pass options from Hom + choose the coefficient field zdeg := if isHomogeneous M then degree 0_M; - A := Hom(M, M, + if 0 < debugLevel then stderr << " -- computing Hom module ... " << flush; + elapsedTime A := Hom(M, M, DegreeLimit => zdeg, MinimalGenerators => false); + if 0 < debugLevel then stderr << "done!" << endl; if isHomogeneous M then smartBasis(zdeg, A) else inducedMap(A, , gens A)) @@ -278,6 +280,13 @@ generalEndomorphism Module := Matrix => o -> M -> ( generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( sheaf generalEndomorphism(module prune F, o)) +-- given N and a split surjection pr:M -> N, +-- we use precomputed endomorphisms of M +-- to produce a general endomorphism of N +generalEndomorphism(Module, Matrix) := Matrix => o -> (M, pr) -> ( + pr * generalEndomorphism source pr * inverse pr) +-- Note: we may not be able to invert a split inclusion. + ----------------------------------------------------------------------------- -- directSummands ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index a874d42218e..a3ce1a0af32 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -1,6 +1,6 @@ needsPackage "RationalPoints2" -findProjectors = method(Options => DirectSummandsOptions) +findProjectors = method(Options => DirectSummandsOptions ++ { "SplitSurjection" => null }) findProjectors Module := opts -> M -> ( R := ring M; p := char R; @@ -8,11 +8,14 @@ findProjectors Module := opts -> M -> ( K := quotient ideal gens R; n := numgens M; L := null; + -- this is used in generalEndomorphism + -- to avoid recomputing the Hom module + surj := opts#"SplitSurjection" ?? id_M; -- TODO: sort the degrees to make finding eigenvalues faster? -- degs := unique sort degrees M; tries := opts.Tries ?? ceiling(0.1 + 50 / log p); for c to tries - 1 do ( - f := generalEndomorphism M; -- about 20% of computation + f := generalEndomorphism(M, surj); -- about 20% of computation -- eigenvalues of f must be over the field, -- and we can prove that f can be diagonalized over R -- (i.e. without passing to frac R), hence we can @@ -50,7 +53,7 @@ findBasicProjectors = M -> ( if #eigen > 1 then return for y in eigen list (f - y * id_M)^n); {}) -summandsFromProjectors = method(Options => DirectSummandsOptions) +summandsFromProjectors = method(Options => options findProjectors) summandsFromProjectors Module := opts -> M -> ( if degree M <= 1 then return {M}; -- maps M -> M whose (co)kernel is a (usually indecomposable) summand @@ -65,14 +68,18 @@ summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( -- the map \bigoplus M_i -> M, whose cokernel is the complement of M_i iota := matrix { injs }; -- assert first isIsomorphic(M, coker iota ++ directSum(coker \ projs)); + -- this is a split surjection from a module whose + -- degree zero endomorphisms have already been computed + surj := opts#"SplitSurjection" ?? id_M; if 0 < debugLevel then printerr("splitting ", toString(#projs+1), - " summands of ranks ", toString apply(injs, i -> degree source i)); + " summands of ranks ", toString apply(injs, i -> rank source i)); c := -1; comps := flatten for pr in append(projs, iota) list ( N := prune coker pr; - L := nonzero summandsFromProjectors(N, opts); - -- Projection maps to the summands p := inverse N.cache.pruningMap * inducedMap(coker pr, M); + if not (source(p * surj)).cache#?"End0" then error "something went wrong"; + L := nonzero summandsFromProjectors(N, opts, "SplitSurjection" => p * surj); + -- Projection maps to the summands if #L > 1 then apply(#L, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) else M.cache#(symbol ^, [c += 1]) = p; From 3fafd529bd79804dfd8c9d8649146dee682d0121 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 19 Apr 2025 15:54:01 -0400 Subject: [PATCH 57/88] made various improvements; all tests pass --- M2/Macaulay2/packages/DirectSummands.m2 | 67 +++++++++++++++---- .../packages/DirectSummands/homogeneous.m2 | 12 ++-- .../packages/DirectSummands/idempotents.m2 | 39 ++++++----- .../packages/DirectSummands/large-tests.m2 | 40 +++++++++-- M2/Macaulay2/packages/DirectSummands/tests.m2 | 53 +++++++-------- 5 files changed, 142 insertions(+), 69 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 2a86bc379dc..3a6d4ac6b9a 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -73,10 +73,15 @@ DirectSummandsOptions = new OptionTable from { -- 4 => unused -- 8 => precompute Homs before looking for idempotents -- 16 => use summandsFromIdempotents even in graded case - Tries => 10, -- used in randomized algorithms + Tries => null, -- see defaultNumTries below Verbose => true, -- whether to print extra debugging info } +-- the default number of attempts for randomized algorithms +-- e.g. 145 tries in char 2, 10 in char 32003, and 1 in char 0 +defaultNumTries = p -> ceiling(0.1 + 100 / log p) +--apply({2, 32003, 0}, defaultNumTries) + -- helpers for computing Frobenius pushforwards of modules and sheaves -- TODO: move to PushForward package? load "./DirectSummands/frobenius.m2" @@ -250,8 +255,8 @@ smartBasis = (deg, M) -> ( gensEnd0 = M -> M.cache#"End0" ??= ( -- TODO: need to pass options from Hom + choose the coefficient field zdeg := if isHomogeneous M then degree 0_M; - if 0 < debugLevel then stderr << " -- computing Hom module ... " << flush; - elapsedTime A := Hom(M, M, + if 0 < debugLevel then stderr << " -- computing End module ... " << flush; + A := Hom(M, M, DegreeLimit => zdeg, MinimalGenerators => false); if 0 < debugLevel then stderr << "done!" << endl; @@ -280,11 +285,36 @@ generalEndomorphism Module := Matrix => o -> M -> ( generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( sheaf generalEndomorphism(module prune F, o)) +-- overwrite the existing hook, to be updated in Core +addHook((quotient', Matrix, Matrix), Strategy => Default, + -- Note: this strategy only works if the remainder is zero, i.e.: + -- homomorphism' f % image Hom(g, target f) == 0 + (opts, f, g) -> ( + opts = new OptionTable from { + DegreeLimit => opts.DegreeLimit, + MinimalGenerators => opts.MinimalGenerators }; + map(target f, target g, homomorphism(homomorphism'(f, opts) // Hom(g, target f, opts))))) + +-- FIXME: inverse may fail for a general split surjection: +-- c.f. https://github.com/Macaulay2/M2/issues/3738 +inverse' = method(Options => options Hom) +inverse' Matrix := opts -> g -> g.cache.inverse' ??= ( + -- two options: id_(target g) // g or g \\ id_(source g) + -- we use the latter, because gensEnd0 source g is cached. + quotient'(id_(source g), g, opts)) + -- given N and a split surjection pr:M -> N, -- we use precomputed endomorphisms of M -- to produce a general endomorphism of N -generalEndomorphism(Module, Matrix) := Matrix => o -> (M, pr) -> ( - pr * generalEndomorphism source pr * inverse pr) +generalEndomorphism(Module, Matrix) := Matrix => o -> (N, pr) -> ( + -- assert(N === target pr); + --return generalEndomorphism(N, o); + inc := inverse'(pr, + DegreeLimit => degree 0_N, + -- FIXME: setting this to false sometimes + -- produces non-well-defined inverses + MinimalGenerators => true); + pr * generalEndomorphism(source pr, o) * inc) -- Note: we may not be able to invert a split inclusion. ----------------------------------------------------------------------------- @@ -294,8 +324,7 @@ generalEndomorphism(Module, Matrix) := Matrix => o -> (M, pr) -> ( -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) cachedSummands = { ExtendGroundField => null } >> o -> M -> ( - if M.cache#?(symbol summands => o.ExtendGroundField) - then M.cache#(symbol summands => o.ExtendGroundField) else components M) + M.cache#(symbol summands => o.ExtendGroundField) ?? components M) -- Note: M may need to be extended to a field extensions -- TODO: add option to provide a general endomorphism or idempotent @@ -344,7 +373,7 @@ directSummands Module := List => opts -> (cacheValue (symbol summands => opts.Ex -- TODO: if ExtendGroundField is given, change variety -- TODO: when ExtendGroundField is given, the variety will change! -directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module F, opts), N -> sheaf'(variety F, N)) +directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module prune F, opts), N -> sheaf'(variety F, N)) -- tests whether L (perhaps a line bundle) is a summand of M -- Limit => N _recommends_ stopping after peeling N summands of L @@ -356,6 +385,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if ring L =!= ring M then error "expected objects over the same ring"; if rank L >= rank M then return {M}; n := opts.Limit ?? numgens M; + tries := opts.Tries ?? defaultNumTries char ring M; if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); if 1 < n then ( -- TODO: can we detect multiple summands of L at once? @@ -371,7 +401,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); -- Previous alternative: -- h := for i from 0 to numcols B - 1 do ( isSurjective(b := homomorphism B_{i}) ...) - h := for i to opts.Tries - 1 do ( + h := for i to tries - 1 do ( b := homomorphism(B * random source B); if isSurjective b then break matrix {L_0} // b)) else ( @@ -381,7 +411,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( C := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); if numcols C == 0 then return {M}; -- attempt to find a random isomorphism - h = for i to opts.Tries - 1 do ( + h = for i to tries - 1 do ( b := homomorphism(B * random source B); c := homomorphism(C * random source C); --TODO: change isIsomorphism to isSurjective? @@ -406,7 +436,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( {L, prune coker h}) directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( - directSummands(module L, module G, opts), N -> sheaf(variety L, N)) + directSummands(module prune L, module prune G, opts), N -> sheaf(variety L, N)) -- attempt to peel off summands from a given list of modules directSummands(List, CoherentSheaf) := @@ -423,8 +453,7 @@ directSummands(List, Module) := List => opts -> (Ls, M) -> sort ( -- returns true if the module is certifiably indecomposable -- returns null for non-conclusive results isIndecomposable = method(Options => { Strategy => null }) --- TODO: check that the sheaf is pruned also -isIndecomposable CoherentSheaf := o -> F -> isIndecomposable(module F, o) +isIndecomposable CoherentSheaf := o -> F -> isIndecomposable(module prune F, o) isIndecomposable Module := o -> M -> M.cache.isIndecomposable ??= tryHooks( (isIndecomposable, Module), (o, M), (o, M) -> ( if 1 < debugLevel then printerr("isIndecomposable was inconclusive. ", @@ -440,6 +469,18 @@ addHook((isIndecomposable, Module), Strategy => "IdempotentSearch", (opts, M) -> else if certified then ( if 1 < debugLevel then printerr "module is indecomposable!"; true ) )) +----------------------------------------------------------------------------- +-- Split inclusions +----------------------------------------------------------------------------- + +-- these are computed on-demand from the cached split surjection +oldinclusions = lookup(symbol_, Module, Array) +Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( + if not M.cache#?(symbol ^, w) then oldinclusions(M, w) + else inverse'(M.cache#(symbol ^, w), + DegreeLimit => degree 0_M, + MinimalGenerators => true)) + ----------------------------------------------------------------------------- -* Development section *- ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index a3ce1a0af32..5aa2d2df327 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -13,9 +13,10 @@ findProjectors Module := opts -> M -> ( surj := opts#"SplitSurjection" ?? id_M; -- TODO: sort the degrees to make finding eigenvalues faster? -- degs := unique sort degrees M; - tries := opts.Tries ?? ceiling(0.1 + 50 / log p); + tries := opts.Tries ?? defaultNumTries p; for c to tries - 1 do ( f := generalEndomorphism(M, surj); -- about 20% of computation + if f == 0 then continue; -- eigenvalues of f must be over the field, -- and we can prove that f can be diagonalized over R -- (i.e. without passing to frac R), hence we can @@ -35,7 +36,7 @@ findProjectors Module := opts -> M -> ( return for y in eigen list (f - y * id_M)^n ); -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K - error("no projector found after ", toString opts.Tries, " attempts. Try passing + error("no projector found after ", tries, " attempts. Try passing ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) -- TODO: can this be useful? @@ -77,13 +78,12 @@ summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( comps := flatten for pr in append(projs, iota) list ( N := prune coker pr; p := inverse N.cache.pruningMap * inducedMap(coker pr, M); - if not (source(p * surj)).cache#?"End0" then error "something went wrong"; - L := nonzero summandsFromProjectors(N, opts, "SplitSurjection" => p * surj); + L := nonzero summandsFromProjectors(N, opts, + "SplitSurjection" => p * surj); -- Projection maps to the summands if #L > 1 then apply(#L, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) else M.cache#(symbol ^, [c += 1]) = p; + -- Inclusion maps are computed on-demand L); - -- Inclusion maps from the summands - scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); comps) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 6f8006bd661..f5b352d074a 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -107,7 +107,7 @@ findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) ----------------------------------------------------------------------------- --TODO: findIdem right now will fail if K is not L[a]/f(a); in general, will need to find a primitive element first -findIdempotent = method(Options => DirectSummandsOptions) +findIdempotent = method(Options => DirectSummandsOptions ++ { "SplitSurjection" => null }) findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M, opts) findIdempotent Module := opts -> M -> ( R := ring M; @@ -119,8 +119,13 @@ findIdempotent Module := opts -> M -> ( exactFlag := not instance(F, InexactField); l := if p == 0 then e else max(e, ceiling log_p numgens M); L := infinity; - for c to opts.Tries - 1 do ( - f := generalEndomorphism M; + -- this is used in generalEndomorphism + -- to avoid recomputing the Hom module + surj := opts#"SplitSurjection" ?? id_M; + tries := opts.Tries ?? defaultNumTries p; + for c to tries - 1 do ( + f := generalEndomorphism(M, surj); + if f == 0 then continue; fm := K ** f; Chi := char f; K' := if not exactFlag then F else try extField {Chi}; @@ -151,7 +156,7 @@ findIdempotent Module := opts -> M -> ( if instance(F, InexactField) then idem = idem ^ (findErrorMargin idem); return idem)); -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K - error("no idempotent found after ", toString opts.Tries, " attempts. Try passing + error("no idempotent found after ", tries, " attempts. Try passing ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) protect Idempotents @@ -182,7 +187,7 @@ findBasicIdempotent = M -> ( if idemp =!= null then M.cache.Idempotents ??= { idemp }; (idemp, certified)) -summandsFromIdempotents = method(Options => DirectSummandsOptions) +summandsFromIdempotents = method(Options => options findIdempotent) summandsFromIdempotents Module := opts -> M -> ( M.cache.Idempotents ??= {}; h := if 0 < #M.cache.Idempotents then M.cache.Idempotents @@ -194,25 +199,25 @@ summandsFromIdempotents(Module, List) := opts -> (M, idems) -> summandsFromIde summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> ( -- TODO: add this when the maps M_[w] and M^[w] also work with subsets -- M.cache.components = - -- TODO: restrict End M to each summand and pass it on - -- TODO: could use 'compose' perhaps + -- this is a split surjection from a module whose + -- degree zero endomorphisms have already been computed + surj := opts#"SplitSurjection" ?? id_M; -- TODO: can we check if M has multiple copies of M0 or M1 quickly? M0 := prune image h; M1 := prune coker h; - -- TODO: can we keep homomorphisms? - --B0.cache.homomorphism = f -> map(M0, M0, adjoint'(p1 * f * inverse p0, M0, M0), Degree => first degrees source f + degree f); - M0comps := directSummands(M0, opts); - M1comps := directSummands(M1, opts); - -- Projection maps to the summands - c := -1; + if M0 == 0 or M1 == 0 then return {M}; + if 0 < debugLevel then printerr("splitting 2 summands of ranks ", + toString {rank M0, rank M1}); p0 := inverse M0.cache.pruningMap * inducedMap(image h, M, h); p1 := inverse M1.cache.pruningMap * inducedMap(coker h, M); + M0comps := summandsFromIdempotents(M0, opts, "SplitSurjection" => p0 * surj); + M1comps := summandsFromIdempotents(M1, opts, "SplitSurjection" => p1 * surj); + -- Projection maps to the summands + c := -1; if #M0comps > 1 then apply(#M0comps, i -> M.cache#(symbol ^, [c += 1]) = M0^[i] * p0) else M.cache#(symbol ^, [c += 1]) = p0; if #M1comps > 1 then apply(#M1comps, i -> M.cache#(symbol ^, [c += 1]) = M1^[i] * p1) else M.cache#(symbol ^, [c += 1]) = p1; - -- Inclusion maps from the summands - -- TODO: will this always work? - scan(c + 1, i -> M.cache#(symbol _, [i]) = inverse M.cache#(symbol ^, [i])); + -- Inclusion maps are computed on-demand -- return the lists -- TODO: why is this nonzero needed? -- TODO: sort these, along with the projections - nonzero flatten join(M0comps, M1comps)) + flatten join(M0comps, M1comps)) diff --git a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 index ed8007fc10f..3183f0945c6 100644 --- a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 @@ -6,10 +6,42 @@ TEST /// -- R = (ZZ/3)[x,y,z,w]/(x^3*y+y^3*z+x*y*z*w+z^3*w+x*w^3) m = map(subquotient(map(R^36,R^{{0}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, 0, 0, 0, 0, 0, 0, 0, y*z, 0, w^2, x*w, y^2, z*w, x*w, x*y+z^2, z^2, y*z, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, x*w, 0, 0, 0, 0, 0, 0, y*w}, {0, w, 0, z, 0, 0, y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, y^2, 0, x*w, 0, y*z, 0, 0}, {0, y, x, 0, 0, z, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, x*w, 0, y^2, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, 0, y^2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, y^2, z*w, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, z^2, x*z, x*w, x*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, w^2, 0, 0, x*z, 0, z^2, 0, 0, y^2, 0, 0}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, 0, 0, 0, 0, 0, 0, y*z, x*w, 0, z^2, 0, 0, 0, x*z, 0, x^2, 0, 0}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, x*z, y*z, 0, z^2, 0, 0, 0, 0, x^2, x*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, z*w, y*w, x^2, y*z, y*z, y^2+x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, 0, 0, 0, 0, 0, y*w, 0, z*w, 0, x^2, 0, 0, 0, 0, 0, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, x^2, 0, x*w, 0, w^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, y*w}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0}, {0, y, x, 0, 0, z, 0, y*z, x^2, 0, z*w, y*w, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, z*w}, {0, 0, z, x, w, 0, 0, 0, 0, 0, x*z, 0, x*y, y*z+w^2, z^2, z*w, z*w, y*w, x*w, x*w, 0, 0, z*w, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, y*z, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, y*z+w^2, 0, x*y, 0, z^2, 0, y*w, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, y*z+w^2, x*w, 0, 0, 0, 0, 0, 0, y*w, y*w, 0, 0, 0, 0, x*y, y^2, 0, y*z}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, x*w, 0, 0, 0, 0, 0, y*z, 0, z^2, 0, 0, 0, 0, 0, 0, 0, x*y, 0}, {0, w, 0, z, 0, 0, y, z*w, z^2, 0, z^2, y*z+w^2, 0, 0, 0, 0, 0, 0, z*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, x*z, y*w, x*y, 0, y^2, y^2, z*w, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, x*y, 0, y*w, y*z, 0, 0, z*w, 0, 0}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, y*z, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, x*y}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, 0, y^2, y*w, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, y*w, z^2, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, x*w, y*z, x*z, x*z, x*y, 0, 0, 0, 0, 0, 0, 0, w^2, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, w^2, y*z, 0, 0, 0, 0, y*w, 0, 0}, {0, 0, z, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, y*z, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}),map(R^36,R^{{0}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2}})),subquotient(map(R^36,R^{{0}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, 0, 0, 0, 0, 0, 0, 0, y*z, 0, w^2, x*w, y^2, z*w, x*w, x*y+z^2, z^2, y*z, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, x*w, 0, 0, 0, 0, 0, 0, y*w}, {0, w, 0, z, 0, 0, y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, y^2, 0, x*w, 0, y*z, 0, 0}, {0, y, x, 0, 0, z, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, x*w, 0, y^2, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, 0, y^2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, x*w, 0, y^2, z*w, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, z^2, x*z, x*w, x*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, w^2, 0, 0, x*z, 0, z^2, 0, 0, y^2, 0, 0}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, 0, 0, 0, 0, 0, 0, y*z, x*w, 0, z^2, 0, 0, 0, x*z, 0, x^2, 0, 0}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, x, 0, 0, z, x*w, x*z, y*z, 0, z^2, 0, 0, 0, 0, x^2, x*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, z*w, y*w, x^2, y*z, y*z, y^2+x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, 0, 0, 0, 0, 0, y*w, 0, z*w, 0, x^2, 0, 0, 0, 0, 0, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, w^2, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, x^2, 0, x*w, 0, w^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, y*w}, {0, 0, 0, 0, x, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0}, {0, y, x, 0, 0, z, 0, y*z, x^2, 0, z*w, y*w, 0, 0, 0, 0, x*w, 0, 0, 0, 0, 0, x*w, 0, 0, 0, 0, z*w}, {0, 0, z, x, w, 0, 0, 0, 0, 0, x*z, 0, x*y, y*z+w^2, z^2, z*w, z*w, y*w, x*w, x*w, 0, 0, z*w, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, y*z, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, y*z+w^2, 0, x*y, 0, z^2, 0, y*w, 0, 0}, {1, x, 0, y, z, w, 0, x*z, y*z, z*w, y*z+w^2, x*w, 0, 0, 0, 0, 0, 0, y*w, y*w, 0, 0, 0, 0, x*y, y^2, 0, y*z}, {0, z, 0, 0, 0, 0, w, 0, 0, x*z, x*w, 0, 0, 0, 0, 0, y*z, 0, z^2, 0, 0, 0, 0, 0, 0, 0, x*y, 0}, {0, w, 0, z, 0, 0, y, z*w, z^2, 0, z^2, y*z+w^2, 0, 0, 0, 0, 0, 0, z*w, z*w, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, x*z, y*w, x*y, 0, y^2, y^2, z*w, 0, w^2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y^2, 0, 0, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, w, 0, y, 0, 0, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, x*y, 0, y*w, y*z, 0, 0, z*w, 0, 0}, {0, w, y, 0, 0, 0, 0, z*w, 0, x*w, 0, y*z, 0, 0, 0, 0, y*w, 0, 0, 0, 0, 0, 0, 0, y*w, 0, 0, x*y}, {1, x, 0, 0, z, 0, 0, y*w, y*z, z*w, w^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, 0, 0, y^2, y*w, 0}, {0, 0, z, 0, 0, y, 0, 0, 0, w^2, y*w, z^2, 0, 0, 0, 0, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, x*w, y*z, x*z, x*z, x*y, 0, 0, 0, 0, 0, 0, 0, w^2, 0, 0}, {0, 0, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, w^2, y*z, 0, 0, 0, 0, y*w, 0, 0}, {0, 0, z, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, y*z, 0, 0, 0, 0}, {0, 0, 0, 0, y, x, w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w, 0, 0}, {0, 0, 0, 0, 0, z, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w^2, z*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}),map(R^36,R^{{0}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}, {-2}},{{1, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*w, y*z, x*y, x^2+z*w, w^2, x*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x*y, 0, z*w, w^2, y*w, y^2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z+w^2, z^2, y*w, x*w, x*y, z*w}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*w, x^2, y^2+x*w, x*y, z*w, y*z}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z^2, x*z, x^2, y*z, 0, x*w}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z*w, x*w, y*z, x*z, y^2, x*y+z^2}})),{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z, 0, 0, 0, w, 0, 0, 0, 0, y, 0, z, z, 0, 0, 0, x, y, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, x, 0, 0, 0, 0, y, z, 0, w, 0, 0, 0, 0, 0, w, y, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, w, z, 0, 0, 0, y, 0, 0, 0, x, z, w, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w, 0, 0, 0, y, x, x, 0, w, z, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, w, x, 0, z, x, 0, 0, 0, y, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x, z, z, 0, 0, 0, 0, x, 0, 0, 0, y, 0, 0, w, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}); - assert try m == quotient(m, map(target m, cover target m, 1), Strategy => "Reflexive") else false - quotient(m, map(target m, cover target m, 1), DegreeLimit => 0) - elapsedTime(m // 1); assert not elapsedTime isIdempotent m -- 0.002 assert elapsedTime isIdempotent(2 * id_(target m)) - leadCoefficient (2 * id_(target m)) + -- FIXME: what were these tests for? + --assert try m == quotient(m, map(target m, cover target m, 1), Strategy => "Reflexive") else false + --quotient(m, map(target m, cover target m, 1), DegreeLimit => {0}) + --elapsedTime(m // 1); +/// + +/// + -- from David's email: reaches recursion limit overnight + needsPackage "DirectSummands" + kk = ZZ/101 + S = kk[x,y,z] + I = monomialIdeal(x^4,x*y,y^4,x*z,y^2*z,z^4) + R = S/I + F = res(coker vars R, LengthLimit => 5) + M = coker F.dd_5; + debugLevel = 1 + elapsedTime L5 = summands M; -- takes ~30min on Fields' server + assert(#L5 == 75); -- could be wrong + assert first isIsomorphic(M, directSum L5) +/// + +/// + -- c.f. https://github.com/Macaulay2/M2/issues/3738#issuecomment-2816840279 + restart + debug needsPackage "DirectSummands" + R = ZZ/32003[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) + M = cokernel(map(R^{5:{1}, 6:{0}, 2:{-1}}, R^{7:{0}, 20:{-1}, 9:{-2}}, {{0, z, 0, y, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, z, 0, y, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {-x, 0, 0, 0, 0, 0, 0, x*y, 0, 0, 0, x^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y^2*z, 0, 0, 0, 0, y^3, 0, 0, 0}, {z, 0, 0, 0, 0, 0, 0, -y*z, y^2, 0, 0, -x*z, x*y, 0, 0, x^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z, -y, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z, 0, 0, 0, 0, y^2, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y, z, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y, 0, z, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, y, 0, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, y, 0, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, y, 0, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -z, 0, y, 0, x, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, 0, y, x}})) + N = cokernel(map(R^{2:{1}}, R^6, {{z, y, x, 0, 0, 0}, {0, 0, 0, z, y, x}})) + g = map(N, M, {{10821, -12119, 15659, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {-7155, -15464, 7853, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}}) + g * (id_N // g) + g * (g \\ id_M) + assert isWellDefined (g \\ id_M) + f = id_M + code(quotient', Matrix, Matrix) + homomorphism' f % image Hom(g, target f) == 0 + homomorphism(homomorphism' f // Hom(g, target f)) /// diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index d84bcbace43..af1fabdfc9e 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -41,7 +41,6 @@ TEST /// -- direct summands of a ring TEST /// -- direct summands over field extensions -- ~9s - debug needsPackage "DirectSummands" R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); X = Proj R; M = module frobeniusPushforward(1, OO_X); @@ -59,24 +58,35 @@ TEST /// -- direct summands over field extensions /// TEST /// - -- ~2.2s - R = ZZ/101[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) +-- ~1.1s +restart +debug needsPackage "DirectSummands" + R = ZZ/32003[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) C = res(coker vars R, LengthLimit => 3) D = res(coker transpose C.dd_3, LengthLimit => 3) M = coker D.dd_3 - elapsedTime assert(8 == #summands M) + elapsedTime L = summands M + assert(8 == #L) + assert all(L, isHomogeneous) + assert first isIsomorphic(M, directSum L) + assert all(8, i -> same { M, target M_[i], source M^[i] } + and same { L#i, target M^[i], source M_[i] }) + --elapsedTime profile summands M; + --profileSummary "DirectSum" /// TEST /// -- ~1.7s - debug needsPackage "DirectSummands" n = 4 - S = ZZ/11[x_0..x_(n-1)] + S = ZZ/32003[x_0..x_(n-1)] I = trim minors_2 matrix { S_*_{0..n-2}, S_*_{1..n-1}} R = quotient I C = res coker vars R - M = image C.dd_3 - assert(6 == #summands M) + M = prune image C.dd_3 + elapsedTime L = summands M + assert(6 == #L) + all(6, i -> isWellDefined M^[i] and isWellDefined M_[i] + and M^[i] * M_[i] == id_(L#i)) /// TEST /// @@ -128,28 +138,16 @@ TEST /// assert(7 == #summands changeBaseField(L, F2)) /// -/// - -- from David's email: reaches recursion limit overnight - needsPackage "DirectSummands" - kk = ZZ/101 - S = kk[x,y,z] - I = monomialIdeal(x^4,x*y,y^4,x*z,y^2*z,z^4) - R = S/I - F = res(coker vars R, LengthLimit => 5) - M = coker F.dd_5; - debugLevel = 1 - elapsedTime L5 = summands M; -/// - -/// -needsPackage "DirectSummands" +TEST /// kk = ZZ/101 S = kk[x,y,z] P = Proj S - TP = tangentSheaf P + T = tangentSheaf P R = S/(x*y-z^2) - assert(length summands prune sheaf(module TP ** R) == rank TP) - assert(length summands sheaf(module TP ** R) == length summands prune sheaf(module TP ** R)) + M = module T ** R + -- the module doesn't split, but the sheaf does + assert(1 == length summands M) + assert(2 == length summands sheaf M) /// /// @@ -162,10 +160,7 @@ needsPackage "DirectSummands" elapsedTime isomorphismTally L elapsedTime tallySummands L set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} -/// -/// - debug needsPackage "DirectSummands" kk = ZZ/11 S = kk[x,y,z, Degrees => {5,1,5}] R = S/(x*z-y^10) From 1eabc57093bc0e817c036181625aeca7bf2c23b4 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 19 Apr 2025 19:31:20 -0400 Subject: [PATCH 58/88] removed ExtendGroundField option --- M2/Macaulay2/packages/DirectSummands.m2 | 87 +++++++++---------- .../packages/DirectSummands/homogeneous.m2 | 8 +- .../packages/DirectSummands/idempotents.m2 | 6 +- .../packages/DirectSummands/large-tests.m2 | 13 +++ M2/Macaulay2/packages/DirectSummands/tests.m2 | 66 ++++++-------- 5 files changed, 89 insertions(+), 91 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 3a6d4ac6b9a..644f252f712 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -39,7 +39,6 @@ export { "isomorphismTally", "isIndecomposable", -- symbols - "ExtendGroundField", "Tries", -- frobenius methods "frobeniusMap", @@ -65,16 +64,15 @@ importFrom_Core { -- defined here and used in idempotents.m2 and homogeneous.m2 DirectSummandsOptions = new OptionTable from { - ExtendGroundField => null, -- a field extension or integer e for GF(p, e) Limit => null, -- used in directSummands(Module, Module) Strategy => 7, -- Strategy is a bitwise sum of the following: -- 1 => use degrees of generators as heuristic to peel off line bundles first -- 2 => check generators of End_0 as heuristic for finding idempotents - -- 4 => unused + -- 4 => whether we are splitting coherent sheaves, so skip line bundles -- 8 => precompute Homs before looking for idempotents -- 16 => use summandsFromIdempotents even in graded case Tries => null, -- see defaultNumTries below - Verbose => true, -- whether to print extra debugging info + Verbose => false -- whether to print extra debugging info } -- the default number of attempts for randomized algorithms @@ -105,13 +103,19 @@ submatrixByDegrees(Matrix, Sequence) := (m, degs) -> ( CoherentSheaf ? CoherentSheaf := Module ? Module := (M, N) -> if rank M != rank N then rank M ? rank N else degrees M ? degrees N --- TODO: move to Core position(ZZ, Function) := o -> (n, f) -> position(0..n-1, f) -- TODO: this is different from position(List,List,Function) position' = method() position'(VisibleList, VisibleList, Function) := (B, C, f) -> for b in B do for c in C do if f(b, c) then return (b, c) position'(ZZ, ZZ, Function) := (B, C, f) -> position'(0..B-1, 0..C-1, f) +char SheafOfRings := O -> char variety O + +GF'ZZ'ZZ = memoize (lookup(GF, ZZ, ZZ)) (options GF) +GF'Ring = memoize (lookup(GF, Ring)) (options GF) +GF(ZZ, ZZ) := GaloisField => opts -> GF'ZZ'ZZ +GF(Ring) := GaloisField => opts -> GF'Ring + -- TODO: what generality should this be in? -- WANT: -- R ** ZZ/101 to change characteristic @@ -124,13 +128,17 @@ PolynomialRing ** GaloisField := (R, L) -> ( quotient sub(ideal A, L monoid A)) changeBaseField = method() -changeBaseField(GaloisField, Module) := (L, M) -> ( +-- TODO: add more automated methods, e.g. where minors of the presentation factor +changeBaseField(ZZ, Module) := +changeBaseField(ZZ, CoherentSheaf) := (e, M) -> changeBaseField(GF(char ring M, e), M) +changeBaseField(GaloisField, Module) := (L, M) -> M.cache#(symbol changeBaseField, L) ??= ( S := first flattenRing(ring M, CoefficientRing => null); K := coefficientRing S; - if class K =!= GaloisField then ( + if class K =!= GaloisField then return ( R0 := quotient sub(ideal S, L monoid S); - return directSum apply(components M, - N -> coker sub(presentation N, R0))); + M' := directSum apply(cachedSummands M, + N -> coker sub(presentation N, R0)); + M.cache#(symbol changeBaseField, L) = M'); -- don't needlessly create new rings: if K.order === L.order then return M; i0 := map(L, K); @@ -138,7 +146,7 @@ changeBaseField(GaloisField, Module) := (L, M) -> ( i1 := map(LS, ring ideal S, gens LS | { i0 K_0 }); R := quotient i1 ideal S; i := map(R, S, i1); - directSum apply(components M, N -> i ** N)) + directSum apply(cachedSummands M, N -> i ** N)) -- TODO: come up with a better way to extend ground field of a variety -- TODO: does this also need to be used for frobenius pushforward? @@ -174,7 +182,6 @@ isisofree = o -> (M, N0) -> ( p2 := first \ (sortBy last) toList pairs d2; (true, map(M, N0, id_N^p2 // id_M^p1))) --- TODO: move to isIsomorphism isiso = lookup(isIsomorphic, Module, Module) isIsomorphic(Module, Module) := Sequence => o -> (M, N) -> ( if isFreeModule M and isFreeModule N @@ -301,7 +308,10 @@ inverse' = method(Options => options Hom) inverse' Matrix := opts -> g -> g.cache.inverse' ??= ( -- two options: id_(target g) // g or g \\ id_(source g) -- we use the latter, because gensEnd0 source g is cached. - quotient'(id_(source g), g, opts)) + -- quotient'(id_(source g), g, opts)) + -- FIXME: we have to use the slower options because the above fails, + -- e.g. https://github.com/Macaulay2/M2/issues/3738#issuecomment-2816840279 + quotient(id_(target g), g, opts)) -- given N and a split surjection pr:M -> N, -- we use precomputed endomorphisms of M @@ -323,57 +333,42 @@ generalEndomorphism(Module, Matrix) := Matrix => o -> (N, pr) -> ( -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) -cachedSummands = { ExtendGroundField => null } >> o -> M -> ( - M.cache#(symbol summands => o.ExtendGroundField) ?? components M) +cachedSummands = M -> M.cache.summands ?? components M -- Note: M may need to be extended to a field extensions --- TODO: add option to provide a general endomorphism or idempotent -- TODO: when splitting over a field extension, use cached splitting over summands --- TODO: cache the inclusion maps directSummands = method(Options => DirectSummandsOptions) -directSummands Module := List => opts -> (cacheValue (symbol summands => opts.ExtendGroundField)) (M -> ( +directSummands Module := List => opts -> M -> M.cache.summands ??= ( checkRecursionDepth(); -- Note: rank does weird stuff if R is not a domain -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis R := ring M; if not isCommutative R and not isWeylAlgebra R then error "expected a commutative base ring"; - if 0 < debugLevel then printerr("splitting module of rank: ", toString rank M); - --TODO: is there an easy way to check if rank = 1 and M torsionfree? - if 1 < #components M then return flatten apply(components M, directSummands_opts); -- TODO: parallelize - if isFreeModule M then return apply(toList pairs(-degrees M), (i, d) -> ( - M.cache#(symbol ^, [i]) = transpose (M.cache#(symbol _, [i]) = matrix M_i); R^{d})); - if opts.ExtendGroundField =!= null then ( - L := opts.ExtendGroundField; - L = if instance(L, ZZ) then GF(char R, L) - else if instance(L, Ring) then L else error "expected an integer or a ground field"; - M = changeBaseField(L, directSum cachedSummands M); - R = ring M); + if opts.Verbose then printerr("splitting module of rank: ", toString rank M); + -- TODO: is there an easy way to check if rank = 1 and M torsionfree? + -- TODO: extend the cached inclusion and projection maps to the summands + if isDirectSum M then return M.cache.summands = flatten apply(components M, directSummands_opts); + if isFreeModule M then return M.cache.summands = apply(numgens M, i -> target(M.cache#(symbol ^, [i]) = M^{i})); -- Attempt to peel off line bundles by observing the degrees of generators if opts.Strategy & 1 == 1 then ( - opts = opts ++ { Strategy => opts.Strategy ^^ 1 }; -- so we only try this once - if 0 < debugLevel then stderr << " -- peeling off rank 1 summands: " << flush; - L = directSummands(apply(-unique degrees M, d -> R^{d}), M, opts); - if 0 < debugLevel then stderr << endl << " -- split off " << #L - 1 << " summands!" << endl; - if 1 < #L then return directSummands(directSum L, opts)); - -- + if opts.Verbose then stderr << " -- peeling off rank 1 summands: " << flush; + L := directSummands(apply(-unique degrees M, d -> R^{d}), M, opts); + if 1 < #L then return M.cache.summands = + directSummands(directSum L, opts, Strategy => opts.Strategy ^^ 1)); -- TODO: where should indecomposability check happen? -- for now it's here, but once we figure out random endomorphisms -- without computing Hom, this would need to move. - -- TODO: add option to skip this step -- Note: if an idempotent is found among columns of B, -- it is cached under M.cache.idempotents, but idempotents -- that are linear combinations cannot be found this way. -- Note: this may return null if it is inconclusive - if 2 == 2 & opts.Strategy then - if isIndecomposable M === true then return {M}; - -- - if isHomogeneous M and 16 != 16 & opts.Strategy - then summandsFromProjectors(M, opts) - else summandsFromIdempotents(M, opts))) + if opts.Strategy & 2 == 2 and isIndecomposable M === true then { M } else + if opts.Strategy & 16 != 16 and isHomogeneous M + then summandsFromProjectors(M, opts) -- see DirectSummands/homogeneous.m2 + else summandsFromIdempotents(M, opts)) -- see DirectSummands/idempotents.m2 --- TODO: if ExtendGroundField is given, change variety --- TODO: when ExtendGroundField is given, the variety will change! -directSummands CoherentSheaf := List => opts -> F -> apply(directSummands(module prune F, opts), N -> sheaf'(variety F, N)) +directSummands CoherentSheaf := List => opts -> F -> apply( + directSummands(module prune F, opts), N -> sheaf(variety F, N)) -- tests whether L (perhaps a line bundle) is a summand of M -- Limit => N _recommends_ stopping after peeling N summands of L @@ -386,7 +381,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( if rank L >= rank M then return {M}; n := opts.Limit ?? numgens M; tries := opts.Tries ?? defaultNumTries char ring M; - if 1 < #cachedSummands M then return sort flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); + if 1 < #cachedSummands M then return flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); if 1 < n then ( -- TODO: can we detect multiple summands of L at once? comps := new MutableList from {M}; @@ -432,7 +427,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( (c, b) -> isSurjective homomorphism C_{c} and isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); if ind =!= null then homomorphism B_{last ind}));); if h === null then return {M}; - if 0 < debugLevel then stderr << concatenate(rank L : ".") << flush; + if opts.Verbose then stderr << concatenate(rank L : ".") << flush; {L, prune coker h}) directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 5aa2d2df327..4f41999e8d2 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -31,13 +31,13 @@ findProjectors Module := opts -> M -> ( -- TODO: is there any way to tell if the module is indecomposable here? -- e.g. based on the characteristic polynomial factoring completely -- but having a single root only? - L = extField { char f0 }; + if L === null then L = extField { char f0 }; continue); return for y in eigen list (f - y * id_M)^n ); - -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K - error("no projector found after ", tries, " attempts. Try passing - ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) + -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K + error("no idempotent found after ", tries, " attempts. ", + "Try using changeBaseField with ", if p != 0 then ("GF " | toString L) else toString L)) -- TODO: can this be useful? findBasicProjectors = M -> ( diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index f5b352d074a..2d88f341385 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -155,9 +155,9 @@ findIdempotent Module := opts -> M -> ( -- for inexact fields, we compose the idempotent until the determinant is zero if instance(F, InexactField) then idem = idem ^ (findErrorMargin idem); return idem)); - -- TODO: skip the "Try passing" line if the field is large enough, e.g. L === K - error("no idempotent found after ", tries, " attempts. Try passing - ExtendGroundField => ", if p != 0 then ("GF " | toString L) else toString L)) + -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K + error("no idempotent found after ", tries, " attempts. ", + "Try using changeBaseField with ", if p != 0 then ("GF " | toString L) else toString L)) protect Idempotents diff --git a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 index 3183f0945c6..a708be481c3 100644 --- a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 @@ -29,6 +29,19 @@ TEST /// assert first isIsomorphic(M, directSum L5) /// +/// + -- FIXME: why is this test so slow? + n = 3 + S = (ZZ/2)[x_0..x_(n-1)] + R = quotient (ideal vars S)^3 + F = res coker vars R + M = image F.dd_3 + summands M + summands(image F.dd_1, M) + -- TODO: have a flag to test for twists of input summands as well + summands({image F.dd_1, coker vars R}, M) +/// + /// -- c.f. https://github.com/Macaulay2/M2/issues/3738#issuecomment-2816840279 restart diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index af1fabdfc9e..03e056f09da 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -14,12 +14,12 @@ TEST /// -- direct summands of a free module R = ZZ/2[x_0..x_5] M = R^{100:-2,100:0,100:2} A = summands M; - B = summands(M, ExtendGroundField => 2); - C = summands(M, ExtendGroundField => 4); - D = summands(M, ExtendGroundField => ZZ/101); - E = summands(M, ExtendGroundField => GF(2,2)); + B = summands changeBaseField(2, M); + C = summands changeBaseField(4, M); + D = summands changeBaseField(GF 101, M); + E = summands changeBaseField(GF(2,2), M); assert same(M, directSum A) - assert same(A, B, C, D, E) + assert same apply({A, B, C, D, E}, length) /// TEST /// -- direct summands of a multigraded free module @@ -52,15 +52,13 @@ TEST /// -- direct summands over field extensions -- if this test fails, check if "try findIdempotent M" if hiding any unexpected errors -- FIXME: this is slow because random homomorphisms shouldn't be over the extended field elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s - elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF 7)) -- 2.87s -> 2.05 - elapsedTime assert({1, 2, 2, 2} == rank \ summands(M, ExtendGroundField => GF(7, 3))) -- 3.77s -> 2.6 - elapsedTime assert(toList(7:1) == rank \ summands(M, ExtendGroundField => GF(7, 2))) -- 2.18s -> 0.47 + elapsedTime assert({1, 2, 2, 2} == rank \ summands changeBaseField(GF 7, M)) -- 2.87s -> 2.05 + elapsedTime assert({1, 2, 2, 2} == rank \ summands changeBaseField(GF(7, 3), M)) -- 3.77s -> 2.6 + elapsedTime assert(toList(7:1) == rank \ summands changeBaseField(GF(7, 2), M)) -- 2.18s -> 0.47 /// TEST /// --- ~1.1s -restart -debug needsPackage "DirectSummands" + -- ~1.1s R = ZZ/32003[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) C = res(coker vars R, LengthLimit => 3) D = res(coker transpose C.dd_3, LengthLimit => 3) @@ -89,20 +87,6 @@ TEST /// and M^[i] * M_[i] == id_(L#i)) /// -TEST /// - -- FIXME: why is this test so slow? - end - n = 3 - S = (ZZ/2)[x_0..x_(n-1)] - R = quotient (ideal vars S)^3 - F = res coker vars R - M = image F.dd_3 - summands M - summands(image F.dd_1, M) - -- TODO: have a flag to test for twists of input summands as well - summands({image F.dd_1, coker vars R}, M) -/// - TEST /// -- testing in char 0 -- FIXME: --S = ZZ[x,y]; @@ -124,18 +108,23 @@ TEST /// -- testing in char 0 /// TEST /// + debug needsPackage "DirectSummands" K = ZZ/7 R = K[x,y,z]/(x^3+y^3+z^3) X = Proj R - F1 = frobeniusPushforward(1, OO_X) - L1 = summands(F1, ExtendGroundField => 2) - assert(7 == #L1) - F2 = frobeniusPushforward(1, L1#1) + -- + F1 = frobeniusPushforward(1, OO_X); + elapsedTime assert({1, 2, 2, 2} == rank \ summands F1) -- 2s + elapsedTime L1 = summands changeBaseField(2, F1); -- 5s + assert(toList(7:1) == rank \ L1) + -- + F2 = frobeniusPushforward(1, L1#1); + elapsedTime assert({7} == rank \ summands F2) -- 2s L = potentialExtension F2 - -- tests largepowers, but is very slow - -- findIdem changeBaseField(L, F2) - -- TODO: is 7 correct here? - assert(7 == #summands changeBaseField(L, F2)) + elapsedTime L2 = summands changeBaseField(L, F2); -- 14s + assert(toList(7:1) == rank \ L2) + -- tests largepowers, but is very slow: + -- findIdempotent changeBaseField(L, F2) /// TEST /// @@ -151,6 +140,7 @@ TEST /// /// /// + restart debug needsPackage "DirectSummands" kk = ZZ/13 S = kk[x,y,z] @@ -170,7 +160,7 @@ TEST /// set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} /// -TEST /// +/// restart errorDepth=2 debug needsPackage "DirectSummands" @@ -179,10 +169,10 @@ TEST /// assert(2 == #summands coker matrix {{a, b^3}, {-b^3, a}}) R = ZZ/32003[a,b, Degrees => {6,2}]/(a^2+b^6) assert(1 == #summands coker matrix {{a, b^3}, {-b^3, a}}) - assert(2 == #summands(coker matrix {{a, b^3}, {-b^3, a}}, ExtendGroundField => 2)) + assert(2 == #summands changeBaseField(2, coker matrix {{a, b^3}, {-b^3, a}})) R = ZZ/32003[a,b]/(a^2+b^6) assert(1 == #summands coker matrix {{a, b^3}, {-b^3, a}}) - assert(2 == #summands(coker matrix {{a, b^3}, {-b^3, a}}, ExtendGroundField => 2)) + assert(2 == #summands changeBaseField(2, coker matrix {{a, b^3}, {-b^3, a}})) R = GF(32003, 2)[a,b, Degrees => {6,2}]/(a^2+b^6) assert(2 == #summands coker matrix {{a, b^3}, {-b^3, a}}) @@ -191,7 +181,7 @@ TEST /// M = coker matrix {{a, b^3}, {-b^3, a}} findIdempotent M - summands(M, ExtendGroundField => 2) + summands changeBaseField(2, M) /// load "./large-tests.m2" @@ -199,4 +189,4 @@ load "./large-tests.m2" end-- restart -elapsedTime check "DirectSummands" +elapsedTime check "DirectSummands" -- ~48s From 635d2a83913ca7610adcf8fceba9ae492b43a419 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 19 Apr 2025 20:19:52 -0400 Subject: [PATCH 59/88] made summandsFromIdempotents almost identical to summandsFromProjectors --- .../packages/DirectSummands/homogeneous.m2 | 6 ++- .../packages/DirectSummands/idempotents.m2 | 51 ++++++++++--------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 4f41999e8d2..ae32203392c 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -61,6 +61,7 @@ summandsFromProjectors Module := opts -> M -> ( projs := try findProjectors(M, opts) else return {M}; summandsFromProjectors(M, projs, opts)) +summandsFromProjectors(Module, Matrix) := opts -> (M, pr) -> summandsFromProjectors(M, {pr}, opts) summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( -- assert(0 == intersect apply(projs, ker)); -- maps M_i -> M from the kernel summands @@ -72,9 +73,9 @@ summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( -- this is a split surjection from a module whose -- degree zero endomorphisms have already been computed surj := opts#"SplitSurjection" ?? id_M; - if 0 < debugLevel then printerr("splitting ", toString(#projs+1), - " summands of ranks ", toString apply(injs, i -> rank source i)); c := -1; + if opts.Verbose then printerr("splitting summands of ranks ", + toString prepend_(rank coker iota) apply(injs, i -> rank source i)); comps := flatten for pr in append(projs, iota) list ( N := prune coker pr; p := inverse N.cache.pruningMap * inducedMap(coker pr, M); @@ -86,4 +87,5 @@ summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( else M.cache#(symbol ^, [c += 1]) = p; -- Inclusion maps are computed on-demand L); + -- TODO: sort these, along with the projections comps) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 2d88f341385..e61911a3676 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -190,34 +190,35 @@ findBasicIdempotent = M -> ( summandsFromIdempotents = method(Options => options findIdempotent) summandsFromIdempotents Module := opts -> M -> ( M.cache.Idempotents ??= {}; - h := if 0 < #M.cache.Idempotents then M.cache.Idempotents + idems := if 0 < #M.cache.Idempotents then M.cache.Idempotents else try findIdempotent(M, opts) else return {M}; - summandsFromIdempotents(M, h, opts)) - --- FIXME: handle the case when multiple idempotents are given -summandsFromIdempotents(Module, List) := opts -> (M, idems) -> summandsFromIdempotents(M, idems#0, opts) -summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> ( - -- TODO: add this when the maps M_[w] and M^[w] also work with subsets - -- M.cache.components = + summandsFromIdempotents(M, idems, opts)) + +summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> summandsFromIdempotents(M, {h}, opts) +summandsFromIdempotents(Module, List) := opts -> (M, idems) -> ( + -- maps M_i -> M from the kernel summands + injs := apply(idems, h -> inducedMap(M, ker h)); + -- assert(0 == intersect apply(injs, image)); + -- the map \bigoplus M_i -> M, whose cokernel is the complement of M_i + iota := matrix { injs }; + -- assert first isIsomorphic(M, coker iota ++ directSum(coker \ idems)); -- this is a split surjection from a module whose -- degree zero endomorphisms have already been computed surj := opts#"SplitSurjection" ?? id_M; - -- TODO: can we check if M has multiple copies of M0 or M1 quickly? - M0 := prune image h; - M1 := prune coker h; - if M0 == 0 or M1 == 0 then return {M}; - if 0 < debugLevel then printerr("splitting 2 summands of ranks ", - toString {rank M0, rank M1}); - p0 := inverse M0.cache.pruningMap * inducedMap(image h, M, h); - p1 := inverse M1.cache.pruningMap * inducedMap(coker h, M); - M0comps := summandsFromIdempotents(M0, opts, "SplitSurjection" => p0 * surj); - M1comps := summandsFromIdempotents(M1, opts, "SplitSurjection" => p1 * surj); - -- Projection maps to the summands c := -1; - if #M0comps > 1 then apply(#M0comps, i -> M.cache#(symbol ^, [c += 1]) = M0^[i] * p0) else M.cache#(symbol ^, [c += 1]) = p0; - if #M1comps > 1 then apply(#M1comps, i -> M.cache#(symbol ^, [c += 1]) = M1^[i] * p1) else M.cache#(symbol ^, [c += 1]) = p1; - -- Inclusion maps are computed on-demand - -- return the lists - -- TODO: why is this nonzero needed? + if opts.Verbose then printerr("splitting summands of ranks ", + toString prepend_(rank coker iota) apply(injs, i -> rank source i)); + comps := flatten for pr in append(idems, iota) list ( + -- TODO: can we check if M has multiple copies of N quickly? + N := prune coker pr; + p := inverse N.cache.pruningMap * inducedMap(coker pr, M); + L := nonzero summandsFromIdempotents(N, opts, + "SplitSurjection" => p * surj); + -- Projection maps to the summands + if #L > 1 then apply(#L, i -> + M.cache#(symbol ^, [c += 1]) = N^[i] * p) + else M.cache#(symbol ^, [c += 1]) = p; + -- Inclusion maps are computed on-demand + L); -- TODO: sort these, along with the projections - flatten join(M0comps, M1comps)) + comps) From e63c71f430a89212dc0845d63f6bc5875e2e760b Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 20 Apr 2025 06:34:08 -0400 Subject: [PATCH 60/88] fixed bug in generalEndomorphism for inhomogeneous case also added trivial indecomposability check based on rank cover M <= 1 --- M2/Macaulay2/packages/DirectSummands.m2 | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 644f252f712..5760ddbf4d7 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -287,7 +287,11 @@ random Module := Vector => o -> M -> ( generalEndomorphism = method(Options => options random) generalEndomorphism Module := Matrix => o -> M -> ( - homomorphism((B := gensEnd0 M) * random(source B, o))) + B := gensEnd0 M; + r := if isHomogeneous M + then random(source B, o) + else localRandom(source B, o); + homomorphism(B * r)) -- the sheaf needs to be pruned to prevent missing endomorphisms generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( sheaf generalEndomorphism(module prune F, o)) @@ -347,6 +351,7 @@ directSummands Module := List => opts -> M -> M.cache.summands ??= ( if opts.Verbose then printerr("splitting module of rank: ", toString rank M); -- TODO: is there an easy way to check if rank = 1 and M torsionfree? -- TODO: extend the cached inclusion and projection maps to the summands + if rank cover M <= 1 then return M.cache.summands = { M.cache.isIndecomposable = true; M }; if isDirectSum M then return M.cache.summands = flatten apply(components M, directSummands_opts); if isFreeModule M then return M.cache.summands = apply(numgens M, i -> target(M.cache#(symbol ^, [i]) = M^{i})); -- Attempt to peel off line bundles by observing the degrees of generators @@ -464,10 +469,24 @@ addHook((isIndecomposable, Module), Strategy => "IdempotentSearch", (opts, M) -> else if certified then ( if 1 < debugLevel then printerr "module is indecomposable!"; true ) )) +-- TODO +--addHook((isIndecomposable, Module), Strategy => TorsionFree, (opts, M) -> ( +-- if isTorsionFree M and isDomain M +-- and degree M // degree R <= 1 then return true)) + ----------------------------------------------------------------------------- -- Split inclusions ----------------------------------------------------------------------------- +findSplitInclusion = method(Options => { Tries => 50 }) +--tests if M is a split summand of N +findSplitInclusion(Module, Module) := opts -> (M, N) -> ( + h := for i to opts.Tries - 1 do ( + b := homomorphism random Hom(M, N, MinimalGenerators => false); + c := homomorphism random Hom(N, M, MinimalGenerators => false); + if isIsomorphism(c * b) then break b); + if h === null then return "not known" else return h) + -- these are computed on-demand from the cached split surjection oldinclusions = lookup(symbol_, Module, Array) Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( From 0c4e34e09680a9d5950d2de16621e28fabb2005d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 20 Apr 2025 07:41:27 -0400 Subject: [PATCH 61/88] simplified findIdempotent --- .../packages/DirectSummands/homogeneous.m2 | 24 ++++--- .../packages/DirectSummands/idempotents.m2 | 63 ++++++++++--------- M2/Macaulay2/packages/DirectSummands/tests.m2 | 36 +++++++++++ 3 files changed, 86 insertions(+), 37 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index ae32203392c..c3c6d10d1fd 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -17,27 +17,28 @@ findProjectors Module := opts -> M -> ( for c to tries - 1 do ( f := generalEndomorphism(M, surj); -- about 20% of computation if f == 0 then continue; - -- eigenvalues of f must be over the field, + -- eigenvalues of f are necessarily over the field, -- and we can prove that f can be diagonalized over R -- (i.e. without passing to frac R), hence we can -- compute the eigenvalues by going to the field - f0 := sub(K ** f, F); + f0 := sub(K ** cover f, F); -- finding eigenvalues would be faster if the matrix -- was put in Jordan form first, but this is easier... eigen := eigenvalues' f0; -- about 25% of computation if #eigen <= 1 then ( -- to be used as a suggestion in the error - -- TODO: expand for characteristic zero -- TODO: is there any way to tell if the module is indecomposable here? -- e.g. based on the characteristic polynomial factoring completely - -- but having a single root only? - if L === null then L = extField { char f0 }; + -- but having a single root only? (= End_0(M) has only one generator?) + -- TODO: expand for inexact fields + if L === null and not instance(F, InexactField) then L = extField { char f0 }; continue); return for y in eigen list (f - y * id_M)^n ); -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K + -- TODO: if L is still null, chane the error error("no idempotent found after ", tries, " attempts. ", - "Try using changeBaseField with ", if p != 0 then ("GF " | toString L) else toString L)) + "Try using changeBaseField with ", toString L)) -- TODO: can this be useful? findBasicProjectors = M -> ( @@ -49,18 +50,24 @@ findBasicProjectors = M -> ( for c to numcols B - 1 do ( f := homomorphism B_{c}; if f == id_M then return; - f0 := sub(K ** f, F); + f0 := sub(K ** cover f, F); eigen := eigenvalues' f0; if #eigen > 1 then return for y in eigen list (f - y * id_M)^n); {}) +-- this algorithm does not depend on finding idempotents, +-- hence it is distict from the Meat-Axe algorithm. summandsFromProjectors = method(Options => options findProjectors) summandsFromProjectors Module := opts -> M -> ( - if degree M <= 1 then return {M}; + if rank cover M <= 1 then return {M}; + -- TODO: if M.cache.Idempotents is nonempty, should we use it here? -- maps M -> M whose (co)kernel is a (usually indecomposable) summand projs := try findProjectors(M, opts) else return {M}; summandsFromProjectors(M, projs, opts)) +-- keep close to summandsFromIdempotents +-- this algorithm is more efficient as it has a significant +-- chance of splitting the module in a single iteration. summandsFromProjectors(Module, Matrix) := opts -> (M, pr) -> summandsFromProjectors(M, {pr}, opts) summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( -- assert(0 == intersect apply(projs, ker)); @@ -87,5 +94,6 @@ summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( else M.cache#(symbol ^, [c += 1]) = p; -- Inclusion maps are computed on-demand L); + --M.cache.Idempotents = apply(c, i -> M.cache#(symbol ^, [i])); -- TODO: sort these, along with the projections comps) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index e61911a3676..049f5bee393 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -106,7 +106,8 @@ findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) -- findIdempotents ----------------------------------------------------------------------------- ---TODO: findIdem right now will fail if K is not L[a]/f(a); in general, will need to find a primitive element first +-- TODO: findIdem right now will fail if K is not L[a]/f(a); +-- in general, will need to find a primitive element first findIdempotent = method(Options => DirectSummandsOptions ++ { "SplitSurjection" => null }) findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M, opts) findIdempotent Module := opts -> M -> ( @@ -116,48 +117,45 @@ findIdempotent Module := opts -> M -> ( e := fieldExponent R; K := quotient ideal gens R; V := K ** M; - exactFlag := not instance(F, InexactField); + inexactFlag := instance(F, InexactField); l := if p == 0 then e else max(e, ceiling log_p numgens M); - L := infinity; + L := null; -- this is used in generalEndomorphism -- to avoid recomputing the Hom module surj := opts#"SplitSurjection" ?? id_M; tries := opts.Tries ?? defaultNumTries p; for c to tries - 1 do ( - f := generalEndomorphism(M, surj); - if f == 0 then continue; - fm := K ** f; - Chi := char f; - K' := if not exactFlag then F else try extField {Chi}; - --TODO: remember different L to extend to; right now the L you return at the end may not be large enough - if p != 0 then L = min(L, K'.order) else L = K'; - -- TODO: this seems too messy, what's the precise requirement? - -- maybe we should separate this in a different method - --exactFlag := not( instance(F, InexactField) or isMember(coefficientRing ring Chi, {ZZ, QQ})); - --needsPackage "RationalPoints2" - -- TODO: replace with eigenvalues'? - eigen := if not exactFlag then roots Chi else flatten rationalPoints ideal Chi; - -- if at most one eigenvalue is found then the module is probably indecomposable - if not exactFlag and #eigen <= 1 then continue; - if exactFlag and p!= 0 and #eigen <= 1 and L == F.order then continue; - --TODO: add check for when the field is QQ - -- we only want eigen values in F + f := generalEndomorphism(M, surj); + fm := sub(K ** cover f, F); + if fm == 0 then continue; + -- if at most one eigenvalue is found the module is probably indecomposable, + -- unless the characteristic polynomial has odd degree, then one is enough. + eigen := eigenvalues' fm; + -- we only want eigenvalues in F eigen = nonnull apply(eigen, y -> try lift(y, F)); - if #eigen == 0 then continue; + if #eigen <= 1 then ( + -- to be used as a suggestion in the error + -- TODO: expand for inexact fields + if L === null and not inexactFlag then L = try extField { char fm }; + -- if char fm doesn't factor over F, or if it fully factors + -- but has only one eigenvalue, we can't find an idempotent + if #eigen == 1 and F === L + or #eigen == 0 then continue); + -- try to find idempotens from eigenvalues opers := flatten for y in eigen list ( if p == 0 then (1, f - y*id_M, fm - y*id_V) else ( for j from 0 to e list (j, f - y*id_M, largePower'(p, j+1, largePower(p, l, fm - y*id_V))))); - idem := position(opers, (j, g, g') -> isWeakIdempotent g' and not isSurjective g' and g' != 0); + idem := position(opers, (j, g, gm) -> isWeakIdempotent gm and not isSurjective gm and gm != 0); if idem =!= null then ( - (j, g, g') := opers_idem; - if 1 < debugLevel then printerr("found idempotent after ", toString c, " attempts."); - idem = (if p != 0 then largePower'(p, j+1, largePower(p, l, g)) else g); + (j, g, gm) := opers_idem; + idem = if p == 0 then g else largePower'(p, j+1, largePower(p, l, g)); -- for inexact fields, we compose the idempotent until the determinant is zero - if instance(F, InexactField) then idem = idem ^ (findErrorMargin idem); + if inexactFlag then idem = idem ^ (findErrorMargin idem); return idem)); -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K + -- TODO: if L is still null, chane the error error("no idempotent found after ", tries, " attempts. ", - "Try using changeBaseField with ", if p != 0 then ("GF " | toString L) else toString L)) + "Try using changeBaseField with ", toString L)) protect Idempotents @@ -181,21 +179,27 @@ findBasicIdempotent = M -> ( idemp := scan(numcols B, c -> ( h := homomorphism B_{c}; if zero h or h == id_M - or zero(hm := h ** K) then return; + or zero(hm := K ** cover h) then return; certified = false; if isWeakIdempotent hm then break h)); if idemp =!= null then M.cache.Idempotents ??= { idemp }; (idemp, certified)) +-- this is essentially the Meat-Axe algorithm, +-- but the process for finding an idempotent for +-- a module over a polynomial ring makes it distict. summandsFromIdempotents = method(Options => options findIdempotent) summandsFromIdempotents Module := opts -> M -> ( + if rank cover M <= 1 then return {M}; M.cache.Idempotents ??= {}; idems := if 0 < #M.cache.Idempotents then M.cache.Idempotents else try findIdempotent(M, opts) else return {M}; summandsFromIdempotents(M, idems, opts)) +-- keep close to summandsFromProjectors summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> summandsFromIdempotents(M, {h}, opts) summandsFromIdempotents(Module, List) := opts -> (M, idems) -> ( + checkRecursionDepth(); -- maps M_i -> M from the kernel summands injs := apply(idems, h -> inducedMap(M, ker h)); -- assert(0 == intersect apply(injs, image)); @@ -220,5 +224,6 @@ summandsFromIdempotents(Module, List) := opts -> (M, idems) -> ( else M.cache#(symbol ^, [c += 1]) = p; -- Inclusion maps are computed on-demand L); + --M.cache.Idempotents = apply(c, i -> M.cache#(symbol ^, [i])); -- TODO: sort these, along with the projections comps) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 03e056f09da..d1c915ff00e 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -39,6 +39,20 @@ TEST /// -- direct summands of a ring assert(M == S^{0,-1,-2}) /// +TEST /// -- direct summands of a finite dimensional algebra + R = ZZ/101[x]/x^3 + T = R/x + f = map(R, T) + -- FIXME: pushforward is wrong in this case + --assert(3 == #summands pushForward_f R^1) + --needsPackage "PushForward" + --assert(3 == #summands pushFwd_f R^1) + f = map(R, prune T) + assert(3 == #summands pushForward_f R^1) + f = map(R, ZZ/101) + assert(3 == #summands pushForward_f R^1) +/// + TEST /// -- direct summands over field extensions -- ~9s R = (ZZ/7)[x,y,z]/(x^3+y^3+z^3); @@ -107,6 +121,28 @@ TEST /// -- testing in char 0 -- FIXME scan(20, i -> assert(set summands coker matrix {{x,y},{-y,x}} == set {cokernel matrix {{x-ii*y}}, cokernel matrix {{x+ii*y}}})) /// +TEST /// -- testing inhomogeneous + debug needsPackage "DirectSummands" + S = GF(2,2)[x,y,z]; + -- homogeneous baseline, used as control + M = coker matrix matrix"x,y,z;y,z,x;z,x,y" + assert(3 == #summands M) + assert(3 == #summandsFromIdempotents M) + assert(3 == #summandsFromProjectors M) + -- + M = coker matrix matrix"1,y,z;y,1,x;z,x,1" + assert(2 == #summands M) + assert(2 == #summandsFromIdempotents M) + -- assert(3 == #summandsFromProjectors M) -- TODO: why does this return 3? + -- + -- FIXME: + S = QQ[x,y,z]; + M = coker matrix matrix"x,y,z;y,z,x;z,x,y" + M = coker matrix matrix"1,y,z;y,1,x;z,x,1" + -- findBasicIdempotent M + -- findIdempotent M +/// + TEST /// debug needsPackage "DirectSummands" K = ZZ/7 From 823d33f5c5bc1fe29fe19ed707a69a44204ef27b Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 20 Apr 2025 16:40:04 -0400 Subject: [PATCH 62/88] added leftInverse and rightInverse --- M2/Macaulay2/packages/DirectSummands.m2 | 57 +++++++++++++++++++------ 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 5760ddbf4d7..e8ce768340d 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -296,7 +296,16 @@ generalEndomorphism Module := Matrix => o -> M -> ( generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( sheaf generalEndomorphism(module prune F, o)) --- overwrite the existing hook, to be updated in Core +-- overwrite two existing hooks, to be updated in Core +addHook((quotient, Matrix, Matrix), Strategy => Default, + -- Note: this strategy only works if the remainder is zero, i.e.: + -- homomorphism' f % image Hom(source f, g) == 0 + (opts, f, g) -> ( + opts = new OptionTable from { + DegreeLimit => opts.DegreeLimit, + MinimalGenerators => opts.MinimalGenerators }; + map(source g, source f, homomorphism(homomorphism'(f, opts) // Hom(source f, g, opts))))) + addHook((quotient', Matrix, Matrix), Strategy => Default, -- Note: this strategy only works if the remainder is zero, i.e.: -- homomorphism' f % image Hom(g, target f) == 0 @@ -306,30 +315,52 @@ addHook((quotient', Matrix, Matrix), Strategy => Default, MinimalGenerators => opts.MinimalGenerators }; map(target f, target g, homomorphism(homomorphism'(f, opts) // Hom(g, target f, opts))))) +importFrom_Core {"Hooks", "HookPriority"} +Matrix.Hooks#(quotient, Matrix, Matrix).HookPriority = drop(Matrix.Hooks#(quotient, Matrix, Matrix).HookPriority, -1) +Matrix.Hooks#(quotient', Matrix, Matrix).HookPriority = drop(Matrix.Hooks#(quotient', Matrix, Matrix).HookPriority, -1) + +-- left inverse of a split injection +-- TODO: figure out if we can ever do this without computing End source g +leftInverse = inverse' = method(Options => options Hom) +leftInverse Matrix := opts -> g -> g.cache.leftInverse ??= quotient'(id_(source g), g, opts) + +-- right inverse of a split surjection -- FIXME: inverse may fail for a general split surjection: -- c.f. https://github.com/Macaulay2/M2/issues/3738 -inverse' = method(Options => options Hom) -inverse' Matrix := opts -> g -> g.cache.inverse' ??= ( - -- two options: id_(target g) // g or g \\ id_(source g) - -- we use the latter, because gensEnd0 source g is cached. - -- quotient'(id_(source g), g, opts)) - -- FIXME: we have to use the slower options because the above fails, - -- e.g. https://github.com/Macaulay2/M2/issues/3738#issuecomment-2816840279 - quotient(id_(target g), g, opts)) +-- TODO: figure out if we can ever do this without computing End target g +rightInverse = method(Options => options Hom) +rightInverse Matrix := opts -> g -> g.cache.rightInverse ??= quotient(id_(target g), g, opts) + +-- given N and a split injection inc:N -> M, +-- we use precomputed endomorphisms of M +-- to produce a general endomorphism of N +generalEndomorphism(Matrix, Nothing, Matrix) := Matrix => o -> (N, null, inc) -> ( + -- assert(N === source inc); + inv := leftInverse(inc, + DegreeLimit => degree 0_N, + -- FIXME: setting this to false sometimes + -- produces non-well-defined inverses + MinimalGenerators => true); + inc * generalEndomorphism(target inc, o) * inv) -- given N and a split surjection pr:M -> N, -- we use precomputed endomorphisms of M -- to produce a general endomorphism of N generalEndomorphism(Module, Matrix) := Matrix => o -> (N, pr) -> ( -- assert(N === target pr); - --return generalEndomorphism(N, o); - inc := inverse'(pr, + -- TODO: currently this still computed End_0(N) + -- figure out a way to compute the inverse without doing so + inv := rightInverse(pr, DegreeLimit => degree 0_N, -- FIXME: setting this to false sometimes -- produces non-well-defined inverses MinimalGenerators => true); + pr * generalEndomorphism(source pr, o) * inv) + +generalEndomorphism(Module, Matrix, Nothing) := Matrix => o -> (N, pr, null) -> generalEndomorphism(N, pr, o) +generalEndomorphism(Module, Matrix, Matrix) := Matrix => o -> (N, pr, inc) -> ( + -- assert(N === target pr and source pr === target inc and N === source inc); pr * generalEndomorphism(source pr, o) * inc) --- Note: we may not be able to invert a split inclusion. ----------------------------------------------------------------------------- -- directSummands @@ -491,7 +522,7 @@ findSplitInclusion(Module, Module) := opts -> (M, N) -> ( oldinclusions = lookup(symbol_, Module, Array) Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( if not M.cache#?(symbol ^, w) then oldinclusions(M, w) - else inverse'(M.cache#(symbol ^, w), + else rightInverse(M.cache#(symbol ^, w), DegreeLimit => degree 0_M, MinimalGenerators => true)) From 6c52cb2f4091bf4bc93a3c34b1cbc57e82cb95a8 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 20 Apr 2025 18:33:19 -0400 Subject: [PATCH 63/88] skip zero summands --- M2/Macaulay2/packages/DirectSummands/homogeneous.m2 | 1 + M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 1 + 2 files changed, 2 insertions(+) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index c3c6d10d1fd..2528380ebc7 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -85,6 +85,7 @@ summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( toString prepend_(rank coker iota) apply(injs, i -> rank source i)); comps := flatten for pr in append(projs, iota) list ( N := prune coker pr; + if N == 0 then continue; p := inverse N.cache.pruningMap * inducedMap(coker pr, M); L := nonzero summandsFromProjectors(N, opts, "SplitSurjection" => p * surj); diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 049f5bee393..88e6e5bd315 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -215,6 +215,7 @@ summandsFromIdempotents(Module, List) := opts -> (M, idems) -> ( comps := flatten for pr in append(idems, iota) list ( -- TODO: can we check if M has multiple copies of N quickly? N := prune coker pr; + if N == 0 then continue; p := inverse N.cache.pruningMap * inducedMap(coker pr, M); L := nonzero summandsFromIdempotents(N, opts, "SplitSurjection" => p * surj); From 60767e7058d30f1bb8aaf88d80ee5f8c5a98c9a3 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 20 Apr 2025 20:30:18 -0400 Subject: [PATCH 64/88] implemented a version with a single Hom computation --- .../packages/DirectSummands/elimination.m2 | 158 ++++++++++++++++++ .../packages/DirectSummands/homogeneous.m2 | 48 +++--- .../packages/DirectSummands/idempotents.m2 | 45 ++--- .../packages/DirectSummands/large-tests.m2 | 17 -- M2/Macaulay2/packages/DirectSummands/tests.m2 | 9 +- 5 files changed, 218 insertions(+), 59 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/elimination.m2 diff --git a/M2/Macaulay2/packages/DirectSummands/elimination.m2 b/M2/Macaulay2/packages/DirectSummands/elimination.m2 new file mode 100644 index 00000000000..e5544693682 --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/elimination.m2 @@ -0,0 +1,158 @@ +needsPackage "RationalPoints2" +debug needsPackage "DirectSummands" + +List * Set := List => (x,y) -> select(x, i -> y#?i) + +idempotentsFromElimination = M -> ( + R := ring M; + F := groundField R; + B := gensEnd0 M; + -- TODO: can we reduce all to the residue field and solve there? + H := apply(numcols B, i -> homomorphism B_{i}) - set { id_M, 0 * id_M }; + T := R(monoid[Variables => #H]); + f := map(T, R); + A := sum(gens T, H, (g, h) -> g * (f ** h)); + I := trim ideal last coefficients(cover(A^2 - A), + Variables => first entries f(vars R)); + -- decompose I + T' := F(monoid T); + f' := map(T', T); + pts := rationalPoints f'(I); + -- if #pts == 2 then module is indecomposable + idems := apply(pts, pt -> sum(H, pt, times)) - set { id_M, 0 * id_M }; + G := mingens sum(idems, image @@ homomorphism'); + idems * set apply(numcols G, i -> homomorphism inducedMap(target B, , G_{i})) +) + +summandsFromElimination = M -> prune \ image \ idempotentsFromElimination M + +end-- +restart +needs "DirectSummands/elimination.m2" + +S = (ZZ/2)[x] +M = S^1 ++ S^1/(x^2) +elapsedTime summandsFromElimination M +elapsedTime summands M +idempotentsFromElimination M + +S = (ZZ/2)[x,y,z] +I = ideal(x^3+y^3+z^3) +R = S/I +X = Proj R +M = module frobeniusPushforward(1, R) +elapsedTime summandsFromElimination M -- faster +elapsedTime summands M + +S = (ZZ/5)[x,y,z,w] +I = ideal(x^3+y^3+z^3+w^3) +R = S/I +X = Proj R +M = module frobeniusPushforward(1, OO_X) +elapsedTime summandsFromElimination M -- doesn't finish +elapsedTime summands M + +K = QQ +R = K[a,b,c,d]; +M = coker matrix"a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a" +elapsedTime summandsFromElimination M -- doesn't work +elapsedTime summands M + +-- FIXME: rationalPoints doesn't work over this field +K = toField(QQ[i]/(i^2+1)); +S = K[x,y,z]; +M = coker matrix matrix"1,y,z;y,1,x;z,x,1" +elapsedTime summandsFromElimination M +elapsedTime summands M + + +--- initial attempt +-- TODO: delete + +T := R(monoid[]); +use source psi +A=sub(cover sum for i to numcols B - 1 list a_i * homomorphism B_(i-1), Ra) +J=trim ideal cover(A^2-A) +degree eliminate(apply(gens R,i->sub(i,ambient Ra)), sub(J,ambient Ra)+ideal(Ra)) +decompose J + +--- +ungraded case: +d = numgens End M0 +(Ra,psi) = flattenRing( R[a_1..a_d]) +M=M0**source psi +E=End M +use source psi +A=sub(cover sum for i from 1 to d list a_i * homomorphism E_(i-1),Ra) +J=trim ideal cover(A^2-A) +eliminate(apply(gens R,i->sub(i,ambient Ra)), sub(J,ambient Ra)+ideal(Ra)) +decompose (J+ideal(x,y,z)) + +---- +S=(ZZ/2)[x,y,z] +I=ideal(x^3+y^3+z^3) +R=S/I +X=Proj R +M0 = module frobeniusPushforward(1,R) +R=ring M0 +d = rank End sheaf M0 +(Ra,psi) = flattenRing( R[a_1..a_d]) +M=M0**source psi +B=smartBasis({0,0},End( M, DegreeLimit=>0)) +use source psi +A=sub(cover sum for i from 1 to rank source B list a_i * homomorphism B_(i-1),Ra) +J=trim ideal cover(A^2-A) +degree eliminate(apply(gens R,i->sub(i,ambient Ra)), sub(J,ambient Ra)+ideal(Ra)) +netList decompose J + +restart +debug needsPackage "DirectSummands" +needsPackage "RationalPoints2" + +S = (ZZ/2)[x] +M0 = S^1 ++ S^1/(x^2) +d = numcols basis(0, End M0) +(S', psi) = flattenRing( S[a_0..a_(d-1)]) +M = M0 ** source psi +B = smartBasis({0}, End( M0, DegreeLimit => 0)) + +A = sum(numcols B, i -> a_i * map(M ** S', M ** S', psi cover homomorphism B_i)) +J = trim ideal cover(A^2 - A) +netList decompose J +I = first decompose J +syz gens I + + + +S=(ZZ/5)[x,y,z,w] +I=ideal(x^3+y^3+z^3+w^3) +R=S/I +X=Proj R +M0 = module frobeniusPushforward(1,OO_X) +d = rank End sheaf M0 +(Ra,psi) = flattenRing( R[a_1..a_d]) +M=M0**source psi +B=smartBasis({0,0}, End(M, DegreeLimit=>0)) +use source psi +A=sub(cover sum for i from 1 to rank source B list a_i * homomorphism B_(i-1),Ra) +J=trim ideal cover(A^2-A) +use ambient Ra +degree eliminate(apply(gens R,i->sub(i,ambient Ra)), sub(J,ambient Ra)+ideal(Ra)) + +summands M0 + +K = QQ +R = K[a,b,c,d]; +M0=coker matrix"a,b,c,d;d,a,b,c;c,d,a,b;b,c,d,a" +r = rank source basis(0, End M0) +(Ra,psi) = flattenRing( R[aa_1..aa_r]) +M=M0**source psi +B=smartBasis({0,0},End( M, DegreeLimit=>0)) +use source psi +A=sub(cover sum for i from 1 to rank source B list aa_i * homomorphism B_(i-1),Ra) +J=trim ideal cover(A^2-A) +use ambient Ra +rationalPoints(sub(J,ambient Ra)+ideal Ra+ideal( apply(gens R,i->sub(i,ambient Ra)))) +decompose(J+ideal(x,y,z,w)) + +summands M0 diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 2528380ebc7..650afb65fd0 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -1,6 +1,6 @@ needsPackage "RationalPoints2" -findProjectors = method(Options => DirectSummandsOptions ++ { "SplitSurjection" => null }) +findProjectors = method(Options => DirectSummandsOptions ++ { "Splitting" => null }) findProjectors Module := opts -> M -> ( R := ring M; p := char R; @@ -10,12 +10,12 @@ findProjectors Module := opts -> M -> ( L := null; -- this is used in generalEndomorphism -- to avoid recomputing the Hom module - surj := opts#"SplitSurjection" ?? id_M; + (pr, inc) := opts#"Splitting" ?? (id_M, id_M); -- TODO: sort the degrees to make finding eigenvalues faster? -- degs := unique sort degrees M; tries := opts.Tries ?? defaultNumTries p; for c to tries - 1 do ( - f := generalEndomorphism(M, surj); -- about 20% of computation + f := generalEndomorphism(M, pr, inc); -- about 20% of computation if f == 0 then continue; -- eigenvalues of f are necessarily over the field, -- and we can prove that f can be diagonalized over R @@ -65,30 +65,38 @@ summandsFromProjectors Module := opts -> M -> ( projs := try findProjectors(M, opts) else return {M}; summandsFromProjectors(M, projs, opts)) +isomorphism = (M, N) -> try last isIsomorphic(M, N) + -- keep close to summandsFromIdempotents -- this algorithm is more efficient as it has a significant -- chance of splitting the module in a single iteration. summandsFromProjectors(Module, Matrix) := opts -> (M, pr) -> summandsFromProjectors(M, {pr}, opts) -summandsFromProjectors(Module, List) := opts -> (M, projs) -> ( - -- assert(0 == intersect apply(projs, ker)); - -- maps M_i -> M from the kernel summands - injs := apply(projs, pr -> inducedMap(M, ker pr)); +summandsFromProjectors(Module, List) := opts -> (M, ends) -> ( + -- maps from kernel summands and to cokernel summands + injs := apply(ends, h -> inducedMap(M, ker h)); + projs := apply(ends, h -> inducedMap(coker h, M)); + -- composition of all endomorphisms is the complement + comp := product ends; + injs = append(injs, inducedMap(M, image comp)); + projs = append(projs, inducedMap(image comp, M, comp)); + -- assert(0 == intersect apply(ends, ker)); -- assert(0 == intersect apply(injs, image)); - -- the map \bigoplus M_i -> M, whose cokernel is the complement of M_i - iota := matrix { injs }; - -- assert first isIsomorphic(M, coker iota ++ directSum(coker \ projs)); - -- this is a split surjection from a module whose - -- degree zero endomorphisms have already been computed - surj := opts#"SplitSurjection" ?? id_M; - c := -1; + -- assert first isIsomorphic(M, directSum apply(projs, target)); + -- this is the splitting (surjection, inclusion) of M to a module + -- whose degree zero endomorphisms have already been computed. + (pr0, inc0) := opts#"Splitting" ?? (id_M, id_M); if opts.Verbose then printerr("splitting summands of ranks ", - toString prepend_(rank coker iota) apply(injs, i -> rank source i)); - comps := flatten for pr in append(projs, iota) list ( - N := prune coker pr; - if N == 0 then continue; - p := inverse N.cache.pruningMap * inducedMap(coker pr, M); + toString apply(injs, i -> rank source i)); + c := -1; + comps := flatten for n to #ends list ( + (pr, inc) := (projs#n, injs#n); + (N0, K0) := (target pr, source inc); + if (N := prune N0) == 0 then continue; + iso := try isomorphism(K0, N0); + p := inverse N.cache.pruningMap * pr; + i := try inc * iso * N.cache.pruningMap; L := nonzero summandsFromProjectors(N, opts, - "SplitSurjection" => p * surj); + "Splitting" => (p * pr0, try inc0 * i)); -- Projection maps to the summands if #L > 1 then apply(#L, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 88e6e5bd315..a3d66f4d903 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -108,7 +108,7 @@ findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) -- TODO: findIdem right now will fail if K is not L[a]/f(a); -- in general, will need to find a primitive element first -findIdempotent = method(Options => DirectSummandsOptions ++ { "SplitSurjection" => null }) +findIdempotent = method(Options => DirectSummandsOptions ++ { "Splitting" => null }) findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M, opts) findIdempotent Module := opts -> M -> ( R := ring M; @@ -122,10 +122,10 @@ findIdempotent Module := opts -> M -> ( L := null; -- this is used in generalEndomorphism -- to avoid recomputing the Hom module - surj := opts#"SplitSurjection" ?? id_M; + (pr, inc) := opts#"Splitting" ?? (id_M, id_M); tries := opts.Tries ?? defaultNumTries p; for c to tries - 1 do ( - f := generalEndomorphism(M, surj); + f := generalEndomorphism(M, pr, inc); fm := sub(K ** cover f, F); if fm == 0 then continue; -- if at most one eigenvalue is found the module is probably indecomposable, @@ -198,27 +198,34 @@ summandsFromIdempotents Module := opts -> M -> ( -- keep close to summandsFromProjectors summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> summandsFromIdempotents(M, {h}, opts) -summandsFromIdempotents(Module, List) := opts -> (M, idems) -> ( +summandsFromIdempotents(Module, List) := opts -> (M, ends) -> ( checkRecursionDepth(); - -- maps M_i -> M from the kernel summands - injs := apply(idems, h -> inducedMap(M, ker h)); + -- maps from kernel summands and to cokernel summands + injs := apply(ends, h -> inducedMap(M, ker h)); + projs := apply(ends, h -> inducedMap(coker h, M)); + -- composition of all endomorphisms is the complement + comp := product ends; + injs = append(injs, inducedMap(M, image comp)); + projs = append(projs, inducedMap(image comp, M, comp)); + -- assert(0 == intersect apply(ends, ker)); -- assert(0 == intersect apply(injs, image)); - -- the map \bigoplus M_i -> M, whose cokernel is the complement of M_i - iota := matrix { injs }; - -- assert first isIsomorphic(M, coker iota ++ directSum(coker \ idems)); - -- this is a split surjection from a module whose - -- degree zero endomorphisms have already been computed - surj := opts#"SplitSurjection" ?? id_M; - c := -1; + -- assert first isIsomorphic(M, directSum apply(projs, target)); + -- this is the splitting (surjection, inclusion) of M to a module + -- whose degree zero endomorphisms have already been computed. + (pr0, inc0) := opts#"Splitting" ?? (id_M, id_M); if opts.Verbose then printerr("splitting summands of ranks ", - toString prepend_(rank coker iota) apply(injs, i -> rank source i)); - comps := flatten for pr in append(idems, iota) list ( + toString apply(injs, i -> rank source i)); + c := -1; + comps := flatten for n to #ends list ( + (pr, inc) := (projs#n, injs#n); + (N0, K0) := (target pr, source inc); + if (N := prune N0) == 0 then continue; -- TODO: can we check if M has multiple copies of N quickly? - N := prune coker pr; - if N == 0 then continue; - p := inverse N.cache.pruningMap * inducedMap(coker pr, M); + iso := isomorphism(K0, N0); + p := inverse N.cache.pruningMap * pr; + i := try inc * iso * N.cache.pruningMap; L := nonzero summandsFromIdempotents(N, opts, - "SplitSurjection" => p * surj); + "Splitting" => (p * pr0, try inc0 * i)); -- Projection maps to the summands if #L > 1 then apply(#L, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) diff --git a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 index a708be481c3..934bf362421 100644 --- a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 @@ -41,20 +41,3 @@ TEST /// -- TODO: have a flag to test for twists of input summands as well summands({image F.dd_1, coker vars R}, M) /// - -/// - -- c.f. https://github.com/Macaulay2/M2/issues/3738#issuecomment-2816840279 - restart - debug needsPackage "DirectSummands" - R = ZZ/32003[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) - M = cokernel(map(R^{5:{1}, 6:{0}, 2:{-1}}, R^{7:{0}, 20:{-1}, 9:{-2}}, {{0, z, 0, y, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, z, 0, y, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {-x, 0, 0, 0, 0, 0, 0, x*y, 0, 0, 0, x^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y^2*z, 0, 0, 0, 0, y^3, 0, 0, 0}, {z, 0, 0, 0, 0, 0, 0, -y*z, y^2, 0, 0, -x*z, x*y, 0, 0, x^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x^2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, z, -y, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y*z, 0, 0, 0, 0, y^2, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y, z, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, y, 0, z, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, y, 0, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, y, 0, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, y, 0, 0, x, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -z, 0, y, 0, x, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, z, 0, 0, 0, y, x}})) - N = cokernel(map(R^{2:{1}}, R^6, {{z, y, x, 0, 0, 0}, {0, 0, 0, z, y, x}})) - g = map(N, M, {{10821, -12119, 15659, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {-7155, -15464, 7853, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}}) - g * (id_N // g) - g * (g \\ id_M) - assert isWellDefined (g \\ id_M) - f = id_M - code(quotient', Matrix, Matrix) - homomorphism' f % image Hom(g, target f) == 0 - homomorphism(homomorphism' f // Hom(g, target f)) -/// diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index d1c915ff00e..b399644e645 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -23,11 +23,13 @@ TEST /// -- direct summands of a free module /// TEST /// -- direct summands of a multigraded free module + debug needsPackage "DirectSummands" -- ~0.05s R = QQ[x,y,z] ** QQ[a,b,c] - M = R^{{0,0},{0,1},{1,0},{-1,0},{0,-1},{1,-1},{-1,1}} + M = R^{{1, 0}, {1, -1}, {0, 0}, {-1, 0}} assert same(M, directSum summands M) - assert first isIsomorphic(directSum summands M, M) + assert same(M, directSum sort summandsFromProjectors M) + assert same(M, directSum sort summandsFromIdempotents M) /// TEST /// -- direct summands of a ring @@ -35,8 +37,9 @@ TEST /// -- direct summands of a ring S = ZZ/3[x,y,z] R = ZZ/3[x,y,z,w]/(x^3+y^3+z^3+w^3) f = map(R, S) + -- TODO: find a non-F-split example M = pushForward(f, module R) - assert(M == S^{0,-1,-2}) + assert(summands M == {S^{0}, S^{-1}, S^{-2}}) /// TEST /// -- direct summands of a finite dimensional algebra From 54d5a627465b9a1045f0ed78324afe0ca9552c91 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 20 Apr 2025 23:45:56 -0400 Subject: [PATCH 65/88] added splitComponents, improved isomorphism --- M2/Macaulay2/packages/DirectSummands.m2 | 112 +++++++++++------- .../packages/DirectSummands/homogeneous.m2 | 22 ++-- .../packages/DirectSummands/idempotents.m2 | 22 ++-- 3 files changed, 88 insertions(+), 68 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index e8ce768340d..8c390d1bd67 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -188,11 +188,23 @@ isIsomorphic(Module, Module) := Sequence => o -> (M, N) -> ( then (isisofree o)(M, N) else (isiso o)(M, N)) -isIsomorphic' = method(Options => options isIsomorphic ++ { Tries => 10 }) +protect Isomorphisms +isIsomorphic' = method(Options => options isIsomorphic ++ { Tries => null }) isIsomorphic'(Module, Module) := opts -> (M, N) -> ( + M.cache.Isomorphisms ??= new MutableHashTable; + if M.cache.Isomorphisms#?N then return true; + tries := opts.Tries ?? defaultNumTries char ring M; opts' := selectKeys(opts, k -> (options isIsomorphic)#?k); -- TODO: parallelize - any(opts.Tries, i -> first isIsomorphic(M, N, opts'))) + for c to tries - 1 do ( + (bool, isom) := isIsomorphic(M, N, opts'); + if bool then ( M.cache.Isomorphisms#N = isom; return true )); + false) + +isomorphism = method(Options => options isIsomorphic ++ { Tries => null }) +isomorphism(Module, Module) := Matrix => o -> (M, N) -> ( + if not isIsomorphic'(M, N, o) then error "modules not isomorphic"; + M.cache.Isomorphisms#N) -- TODO: speed this up -- TODO: implement isIsomorphic for sheaves @@ -363,49 +375,88 @@ generalEndomorphism(Module, Matrix, Matrix) := Matrix => o -> (N, pr, inc) -> ( pr * generalEndomorphism(source pr, o) * inc) ----------------------------------------------------------------------------- --- directSummands +-- helpers for splitting and caching projection maps ----------------------------------------------------------------------------- +findSplitInclusion = method(Options => { Tries => 50 }) +--tests if M is a split summand of N +findSplitInclusion(Module, Module) := opts -> (M, N) -> ( + h := for i to opts.Tries - 1 do ( + b := homomorphism random Hom(M, N, MinimalGenerators => false); + c := homomorphism random Hom(N, M, MinimalGenerators => false); + if isIsomorphism(c * b) then break b); + if h === null then return "not known" else return h) + +-- helper for splitting a free module and setting the split surjections +splitFreeModule = (M, opts) -> apply(numgens M, i -> target(M.cache#(symbol ^, [i]) = M^{i})) + +-- helper for splitting free summands by observing the degrees of generators +splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" = ( + if opts.Verbose then stderr << " -- splitting free summands: " << flush; + directSummands(R := ring M; apply(-unique degrees M, d -> R^{d}), M, opts)) + +-- helper for splitting a module with known components +-- (in particular, the components may also have summands) +-- TODO: can we sort the summands here? +splitComponents = (M, comps, splitfunc) -> ( + n := #comps; + c := -1; -- summand counter + projs := apply(n, i -> M^[i]); + flatten apply(comps, projs, (N, p) -> ( + L := splitfunc N; + -- Projection maps to the summands + if #L > 1 then apply(#L, i -> + M.cache#(symbol ^, [c += 1]) = N^[i] * p) + else M.cache#(symbol ^, [c += 1]) = p; + -- Inclusion maps are computed on-demand + L))) + +-- these are computed on-demand from the cached split surjection +oldinclusions = lookup(symbol_, Module, Array) +Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( + if not M.cache#?(symbol ^, w) then oldinclusions(M, w) + else rightInverse(M.cache#(symbol ^, w), + DegreeLimit => degree 0_M, + MinimalGenerators => true)) + -- TODO: can we return cached summands from the closest field extension? -- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) cachedSummands = M -> M.cache.summands ?? components M +----------------------------------------------------------------------------- +-- directSummands +----------------------------------------------------------------------------- + -- Note: M may need to be extended to a field extensions -- TODO: when splitting over a field extension, use cached splitting over summands directSummands = method(Options => DirectSummandsOptions) directSummands Module := List => opts -> M -> M.cache.summands ??= ( checkRecursionDepth(); - -- Note: rank does weird stuff if R is not a domain - -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis + strategy := opts.Strategy; R := ring M; + -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis if not isCommutative R and not isWeylAlgebra R then error "expected a commutative base ring"; + -- Note: rank does weird stuff if R is not a domain if opts.Verbose then printerr("splitting module of rank: ", toString rank M); - -- TODO: is there an easy way to check if rank = 1 and M torsionfree? - -- TODO: extend the cached inclusion and projection maps to the summands if rank cover M <= 1 then return M.cache.summands = { M.cache.isIndecomposable = true; M }; - if isDirectSum M then return M.cache.summands = flatten apply(components M, directSummands_opts); - if isFreeModule M then return M.cache.summands = apply(numgens M, i -> target(M.cache#(symbol ^, [i]) = M^{i})); - -- Attempt to peel off line bundles by observing the degrees of generators - if opts.Strategy & 1 == 1 then ( - if opts.Verbose then stderr << " -- peeling off rank 1 summands: " << flush; - L := directSummands(apply(-unique degrees M, d -> R^{d}), M, opts); - if 1 < #L then return M.cache.summands = - directSummands(directSum L, opts, Strategy => opts.Strategy ^^ 1)); + if isDirectSum M then return M.cache.summands = splitComponents(M, components M, directSummands_opts); + if isFreeModule M then return M.cache.summands = splitFreeModule(M, opts); + if strategy & 1 == 1 then return M.cache.summands = ( + directSummands(directSum splitFreeSummands(M, opts), opts, Strategy => strategy - 1)); -- TODO: where should indecomposability check happen? -- for now it's here, but once we figure out random endomorphisms -- without computing Hom, this would need to move. - -- Note: if an idempotent is found among columns of B, - -- it is cached under M.cache.idempotents, but idempotents - -- that are linear combinations cannot be found this way. -- Note: this may return null if it is inconclusive - if opts.Strategy & 2 == 2 and isIndecomposable M === true then { M } else - if opts.Strategy & 16 != 16 and isHomogeneous M + if strategy & 2 == 2 and isIndecomposable M === true then { M } else + if strategy & 16 != 16 and isHomogeneous M then summandsFromProjectors(M, opts) -- see DirectSummands/homogeneous.m2 else summandsFromIdempotents(M, opts)) -- see DirectSummands/idempotents.m2 directSummands CoherentSheaf := List => opts -> F -> apply( directSummands(module prune F, opts), N -> sheaf(variety F, N)) +----------------------------------------------------------------------------- + -- tests whether L (perhaps a line bundle) is a summand of M -- Limit => N _recommends_ stopping after peeling N summands of L -- FIXME: it's not guaranteed to work, e.g. on X_4 over ZZ/2 @@ -505,27 +556,6 @@ addHook((isIndecomposable, Module), Strategy => "IdempotentSearch", (opts, M) -> -- if isTorsionFree M and isDomain M -- and degree M // degree R <= 1 then return true)) ------------------------------------------------------------------------------ --- Split inclusions ------------------------------------------------------------------------------ - -findSplitInclusion = method(Options => { Tries => 50 }) ---tests if M is a split summand of N -findSplitInclusion(Module, Module) := opts -> (M, N) -> ( - h := for i to opts.Tries - 1 do ( - b := homomorphism random Hom(M, N, MinimalGenerators => false); - c := homomorphism random Hom(N, M, MinimalGenerators => false); - if isIsomorphism(c * b) then break b); - if h === null then return "not known" else return h) - --- these are computed on-demand from the cached split surjection -oldinclusions = lookup(symbol_, Module, Array) -Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( - if not M.cache#?(symbol ^, w) then oldinclusions(M, w) - else rightInverse(M.cache#(symbol ^, w), - DegreeLimit => degree 0_M, - MinimalGenerators => true)) - ----------------------------------------------------------------------------- -* Development section *- ----------------------------------------------------------------------------- diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 650afb65fd0..499e830e064 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -65,8 +65,6 @@ summandsFromProjectors Module := opts -> M -> ( projs := try findProjectors(M, opts) else return {M}; summandsFromProjectors(M, projs, opts)) -isomorphism = (M, N) -> try last isIsomorphic(M, N) - -- keep close to summandsFromIdempotents -- this algorithm is more efficient as it has a significant -- chance of splitting the module in a single iteration. @@ -87,22 +85,18 @@ summandsFromProjectors(Module, List) := opts -> (M, ends) -> ( (pr0, inc0) := opts#"Splitting" ?? (id_M, id_M); if opts.Verbose then printerr("splitting summands of ranks ", toString apply(injs, i -> rank source i)); - c := -1; - comps := flatten for n to #ends list ( + c := -1; -- component counter + comps := for n to #ends list ( (pr, inc) := (projs#n, injs#n); (N0, K0) := (target pr, source inc); if (N := prune N0) == 0 then continue; iso := try isomorphism(K0, N0); p := inverse N.cache.pruningMap * pr; i := try inc * iso * N.cache.pruningMap; - L := nonzero summandsFromProjectors(N, opts, - "Splitting" => (p * pr0, try inc0 * i)); - -- Projection maps to the summands - if #L > 1 then apply(#L, i -> - M.cache#(symbol ^, [c += 1]) = N^[i] * p) - else M.cache#(symbol ^, [c += 1]) = p; - -- Inclusion maps are computed on-demand - L); + M.cache#(symbol ^, [c += 1]) = p; -- temporary + N.cache.components = summandsFromProjectors(N, + opts, "Splitting" => (p * pr0, try inc0 * i)); + N); --M.cache.Idempotents = apply(c, i -> M.cache#(symbol ^, [i])); - -- TODO: sort these, along with the projections - comps) + -- Finally, call a helper to add splitting maps + splitComponents(M, comps, components)) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index a3d66f4d903..53baa934c89 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -215,23 +215,19 @@ summandsFromIdempotents(Module, List) := opts -> (M, ends) -> ( (pr0, inc0) := opts#"Splitting" ?? (id_M, id_M); if opts.Verbose then printerr("splitting summands of ranks ", toString apply(injs, i -> rank source i)); - c := -1; - comps := flatten for n to #ends list ( + c := -1; -- component counter + comps := for n to #ends list ( (pr, inc) := (projs#n, injs#n); (N0, K0) := (target pr, source inc); if (N := prune N0) == 0 then continue; -- TODO: can we check if M has multiple copies of N quickly? - iso := isomorphism(K0, N0); + iso := try isomorphism(K0, N0); p := inverse N.cache.pruningMap * pr; i := try inc * iso * N.cache.pruningMap; - L := nonzero summandsFromIdempotents(N, opts, - "Splitting" => (p * pr0, try inc0 * i)); - -- Projection maps to the summands - if #L > 1 then apply(#L, i -> - M.cache#(symbol ^, [c += 1]) = N^[i] * p) - else M.cache#(symbol ^, [c += 1]) = p; - -- Inclusion maps are computed on-demand - L); + M.cache#(symbol ^, [c += 1]) = p; -- temporary + N.cache.components = summandsFromIdempotents(N, + opts, "Splitting" => (p * pr0, try inc0 * i)); + N); --M.cache.Idempotents = apply(c, i -> M.cache#(symbol ^, [i])); - -- TODO: sort these, along with the projections - comps) + -- Finally, call a helper to add splitting maps + splitComponents(M, comps, components)) From 2cc7f74f0aa4733e72eebe5c5a8af98ccf1030a4 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 21 Apr 2025 15:01:37 -0400 Subject: [PATCH 66/88] fixed a missing ??= --- M2/Macaulay2/packages/DirectSummands.m2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 8c390d1bd67..8a68e121543 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -11,8 +11,8 @@ --------------------------------------------------------------------------- newPackage( "DirectSummands", - Version => "0.2", - Date => "April 1st 2025", + Version => "0.3", + Date => "April 20th 2025", Headline => "decompositions of modules and coherent sheaves", Authors => { { Name => "Devlin Mallory", Email => "malloryd@math.utah.edu", HomePage => "https://math.utah.edu/~malloryd/"}, @@ -28,7 +28,7 @@ newPackage( "Varieties", }, AuxiliaryFiles => true, - DebuggingMode => true + DebuggingMode => false ) export { @@ -391,7 +391,7 @@ findSplitInclusion(Module, Module) := opts -> (M, N) -> ( splitFreeModule = (M, opts) -> apply(numgens M, i -> target(M.cache#(symbol ^, [i]) = M^{i})) -- helper for splitting free summands by observing the degrees of generators -splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" = ( +splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" ??= ( if opts.Verbose then stderr << " -- splitting free summands: " << flush; directSummands(R := ring M; apply(-unique degrees M, d -> R^{d}), M, opts)) From 8f100fa30e72781901372e08abca3f68cd6b314e Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 21 Apr 2025 15:03:34 -0400 Subject: [PATCH 67/88] added dumb eigenvalue hack over ZZ/p --- .../packages/DirectSummands/homogeneous.m2 | 9 +++++--- .../packages/DirectSummands/idempotents.m2 | 22 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index 499e830e064..b3fdad84cb5 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -24,7 +24,9 @@ findProjectors Module := opts -> M -> ( f0 := sub(K ** cover f, F); -- finding eigenvalues would be faster if the matrix -- was put in Jordan form first, but this is easier... - eigen := eigenvalues' f0; -- about 25% of computation + -- TODO: computing eigenvalues over coefficient field + -- would significantly speed up this step + eigen := eigenvalues'' f0; -- about 25% of computation if #eigen <= 1 then ( -- to be used as a suggestion in the error -- TODO: is there any way to tell if the module is indecomposable here? @@ -36,7 +38,7 @@ findProjectors Module := opts -> M -> ( return for y in eigen list (f - y * id_M)^n ); -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K - -- TODO: if L is still null, chane the error + -- TODO: if L is still null, change the error error("no idempotent found after ", tries, " attempts. ", "Try using changeBaseField with ", toString L)) @@ -56,7 +58,7 @@ findBasicProjectors = M -> ( {}) -- this algorithm does not depend on finding idempotents, --- hence it is distict from the Meat-Axe algorithm. +-- hence it is distinct from the Meat-Axe algorithm. summandsFromProjectors = method(Options => options findProjectors) summandsFromProjectors Module := opts -> M -> ( if rank cover M <= 1 then return {M}; @@ -90,6 +92,7 @@ summandsFromProjectors(Module, List) := opts -> (M, ends) -> ( (pr, inc) := (projs#n, injs#n); (N0, K0) := (target pr, source inc); if (N := prune N0) == 0 then continue; + -- TODO: can we check if M has multiple copies of N quickly? iso := try isomorphism(K0, N0); p := inverse N.cache.pruningMap * pr; i := try inc * iso * N.cache.pruningMap; diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 53baa934c89..0069e634c50 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -75,7 +75,8 @@ char Matrix := A -> A.cache.char ??= ( T := (groundField ring A)(monoid[b]); B := sub(cover A, T); I := id_(source B); - det(B - T_0 * I)) + -- TODO: this is a major step in large examples + det(B - T_0 * I, Strategy => Bareiss)) eigenvalues' = A -> ( Chi := char A; @@ -83,6 +84,19 @@ eigenvalues' = A -> ( if instance(F, InexactField) then roots Chi else flatten rationalPoints ideal Chi) +fieldElements = method() +fieldElements QuotientRing := ZZp -> toList(0..char ZZp - 1) +fieldElements GaloisField := GFq -> prepend_(0_GFq) apply(GFq.order - 1, i -> GFq_0^i) +fieldElements' = memoize fieldElements -- FIXME: don't cache globally + +-- dumb search over finite fields ... +eigenvalues'' = A -> ( + R := ring A; + p := char R; + I := id_(target A); + if p == 0 or p > 1000 then return eigenvalues' A; + select(fieldElements' groundField R, e -> zero det(A - e * I))) + largePower = (p,l,M) -> ( if p^l < 2^30 then return M^(p^l); --should have this line check for monomial size of ambient ring also @@ -130,7 +144,7 @@ findIdempotent Module := opts -> M -> ( if fm == 0 then continue; -- if at most one eigenvalue is found the module is probably indecomposable, -- unless the characteristic polynomial has odd degree, then one is enough. - eigen := eigenvalues' fm; + eigen := eigenvalues'' fm; -- we only want eigenvalues in F eigen = nonnull apply(eigen, y -> try lift(y, F)); if #eigen <= 1 then ( @@ -153,7 +167,7 @@ findIdempotent Module := opts -> M -> ( if inexactFlag then idem = idem ^ (findErrorMargin idem); return idem)); -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K - -- TODO: if L is still null, chane the error + -- TODO: if L is still null, change the error error("no idempotent found after ", tries, " attempts. ", "Try using changeBaseField with ", toString L)) @@ -187,7 +201,7 @@ findBasicIdempotent = M -> ( -- this is essentially the Meat-Axe algorithm, -- but the process for finding an idempotent for --- a module over a polynomial ring makes it distict. +-- a module over a polynomial ring makes it distinct. summandsFromIdempotents = method(Options => options findIdempotent) summandsFromIdempotents Module := opts -> M -> ( if rank cover M <= 1 then return {M}; From ef34310a55bf31c51142cb8c184764957194c35f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 21 Apr 2025 15:11:18 -0400 Subject: [PATCH 68/88] updated frobenius.m2 --- .../packages/DirectSummands/frobenius.m2 | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 index 829dfa17edd..623145b7aab 100644 --- a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -53,8 +53,7 @@ frobeniusTwist(ZZ, Ring) := Ring => (e, S) -> ( if char S == 0 or k_0 == 1 then return S; if not S.?cache then S.cache = new CacheTable; -- FIMXE: towers of pushforwards - if S.cache#?(symbol FrobeniusTwist, e) then S.cache#(symbol FrobeniusTwist, e) - else S.cache#(symbol FrobeniusTwist, e) = ( + S.cache#(symbol FrobeniusTwist, e) ??= ( F := frobeniusTwistMap(e, ring ideal S); quotient F ideal S)) frobeniusTwist(ZZ, Ideal) := Ideal => (e, I) -> frobeniusTwist(e, module I) @@ -93,30 +92,21 @@ frobeniusPushforward = method() frobeniusPushforward(ZZ, Ring) := (e, R) -> frobeniusPushforward(e, module R) frobeniusPushforward(ZZ, Ideal) := (e, I) -> frobeniusPushforward(e, quotient I) -- TODO: cache in a way that the second pushforward is the same as applying pushforward twice -frobeniusPushforward(ZZ, Module) := (e, M) -> ( - if M.cache#?(FrobeniusPushforward, e) - then M.cache#(FrobeniusPushforward, e) - else M.cache#(FrobeniusPushforward, e) = ( +frobeniusPushforward(ZZ, Module) := (e, M) -> M.cache#(FrobeniusPushforward, e) ??= ( f := presentation myPushForward( frobeniusMap(e, ring M), frobeniusTwist(e, M)); if not isHomogeneous f then coker f - else directSum apply(decomposeFrobeniusPresentation(e, f), coker))) + else directSum apply(decomposeFrobeniusPresentation(e, f), coker)) -- -frobeniusPushforward(ZZ, Matrix) := (e, f) -> ( - if f.cache#?(FrobeniusPushforward, e) - then f.cache#(FrobeniusPushforward, e) - else f.cache#(FrobeniusPushforward, e) = ( +frobeniusPushforward(ZZ, Matrix) := (e, f) -> f.cache#(FrobeniusPushforward, e) ??= ( g := myPushForward( frobeniusMap(e, ring f), frobeniusTwist(e, f)); if not isHomogeneous g then g - else directSum decomposeFrobeniusPresentation(e, g))) + else directSum decomposeFrobeniusPresentation(e, g)) -frobeniusPushforward(ZZ, SheafMap) := (e, f) -> ( - if f.cache#?(FrobeniusPushforward, e) - then f.cache#(FrobeniusPushforward, e) - else f.cache#(FrobeniusPushforward, e) = ( +frobeniusPushforward(ZZ, SheafMap) := (e, f) -> f.cache#(FrobeniusPushforward, e) ??= ( --if not(isFreeModule module source f and isFreeModule module target f) then error "expected a map between free modules"; g := myPushForward( frobeniusMap(e, ring matrix f), @@ -132,16 +122,11 @@ frobeniusPushforward(ZZ, SheafMap) := (e, f) -> ( (sPrestardegs, sPressrcdegs) := toSequence(-degrees sourcePres // p^e); Fgsource := sheaf coker map(R^sPrestardegs, R^sPressrcdegs, sub(sourcePres, R)); sheaf map(module Fgtarget, module Fgsource, sub(cover Fg, R))) -) - --frobeniusPushforward(ZZ, Complex) := (e, C) -> () -- TODO frobeniusPushforward(ZZ, SheafOfRings) := (e, N0) -> frobeniusPushforward(e, N0^1) -- TODO: is this cached? -frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> ( - if N.cache#?(FrobeniusPushforward, e) - then N.cache#(FrobeniusPushforward, e) - else N.cache#(FrobeniusPushforward, e) = ( +frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> N.cache#(FrobeniusPushforward, e) ??= if e == 1 then ( R := ring variety N; p := char R; FN := first components frobeniusPushforward(e, module N); @@ -151,23 +136,21 @@ frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> ( Fmatrix := sub(presentation FN, R); (tardegs, srcdegs) := toSequence(-degrees Fmatrix // p^e); -- TODO: how long does this take? is it worth caching? - sheaf prune coker map(R^tardegs, R^srcdegs, Fmatrix)) - ) + sheaf prune coker map(R^tardegs, R^srcdegs, Fmatrix)) else ( + frobeniusPushforward(1, frobeniusPushforward(e-1, N))) + protect FrobeniusPullback frobeniusPullback = method() --frobeniusPullback(Thing, ZZ) := (T, e) -> frobeniusPullback(e, T) -frobeniusPullback(ZZ, Module) := (e, M) -> ( - if M.cache#?(FrobeniusPullback, e) - then M.cache#(FrobeniusPullback, e) - else M.cache#(FrobeniusPullback, e) = ( +frobeniusPullback(ZZ, Module) := (e, M) -> M.cache#(FrobeniusPullback, e) ??= ( R := ring M; p := char R; F := frobeniusMap(R, e); R0 := source F; A := presentation M; A0 := sub(A, R0); - coker(F ** map(R0^(-(p^e) * degrees target A0), , A0)))) + coker(F ** map(R0^(-(p^e) * degrees target A0), , A0))) frobeniusPullback(ZZ, CoherentSheaf) := (e, F) -> sheaf frobeniusPullback(e, module F) end-- From 2e5c5c1a06951f78b541712d9718b393328fa4b0 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 21 Apr 2025 15:12:13 -0400 Subject: [PATCH 69/88] updated Grassmannian benchmark data --- M2/Macaulay2/packages/DirectSummands/bench.m2 | 65 +++++++++++++------ .../DirectSummands/large-eigenvalue.m2 | 10 +++ .../packages/DirectSummands/large-tests.m2 | 12 ++++ 3 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/large-eigenvalue.m2 diff --git a/M2/Macaulay2/packages/DirectSummands/bench.m2 b/M2/Macaulay2/packages/DirectSummands/bench.m2 index 8b336fded5e..63596081191 100644 --- a/M2/Macaulay2/packages/DirectSummands/bench.m2 +++ b/M2/Macaulay2/packages/DirectSummands/bench.m2 @@ -1,36 +1,59 @@ restart debug needsPackage "DirectSummands" debugLevel = 1 +errorDepth = 2 -kk = ZZ/3 +kk = ZZ/5 R = quotient Grassmannian(1,3, CoefficientRing => kk) X = Proj R elapsedTime M = module frobeniusPushforward(1, OO_X); -- <1s in char 2 & 3 -elapsedTime L = summands M; - -tally apply(L, N -> (rank N, degrees N)) - --* -Timing results: - q | F_* | Tot | End |basis| misc ------------------------------------ - 2 | <1s | <1s | | | - 4 | ~6s | | | | - 3 | <1s | 43s | 13s | 2.3 | - 5 | 64s | | | | - -ZZ/2 q = 2^1 rk 16: 1:0 14:1 1:2 -ZZ/2 q = 2^2 rk 256 - -ZZ/3 q = 3^1 rk 81: 1:0 44:1 20:2 8:rk 2 bundles +rank M +elapsedTime L = summands(M, Verbose => true); +tally apply(summands M, N -> (rank N, degrees N)) +N = last M.cache#"FreeSummands"; +elapsedTime projs = findProjectors N; +elapsedTime L0 = flatten(summands \ prune \ coker \ projs); +elapsedTime L = summands(keys tallySummands L0, N, Verbose => true); +M.cache.summands = drop(M.cache#"FreeSummands", -1) | L; +-* Timing results: +----------------------------------- + q | F_* | Tot | End | misc +----------------------------------- + 2 | <1s | <1s | <1s | noether + 4 | ~6s | 33m | 8m | Fields + 3 | <1s | 10s | <1s | noether + 5 | 43s | ~3h | ~3h | noether (once End is computed, pretty fast with some manual work) + 7 | | | | + 9 | | | | +----------------------------------- +field q rk free summands non-free summands +----------------------------------- +ZZ/2 2^1 16 1:0 14:1 1:2 +ZZ/2 2^2 256 1:0 99:1 99:2 1:3 28:rk 2 bundles +ZZ/3 3^1 81 1:0 44:1 20:2 8: rk 2 bundles +ZZ/3 3^2 +ZZ/5 5^1 625 1:0 190:1 300:2 6:3 64:rk 2 bundles +ZZ/7 7^1 *- -kk = ZZ/3 +kk = ZZ/2 R = quotient Grassmannian(2,5, CoefficientRing => kk) X = Proj R -elapsedTime M = module frobeniusPushforward(1, OO_X); -- <1s +elapsedTime M = module frobeniusPushforward(1, OO_X); -- ~20min in char 2 rank M -elapsedTime L = summands M; +elapsedTime L = summands(M, Verbose => true); +tally apply(L, N -> (rank N, degrees N)) + +-* Timing results: +----------------------------------- + q | F_* | Tot | End | misc +----------------------------------- + 2 | 20m | | | Fields +----------------------------------- +field q rk free summands non-free summands +----------------------------------- +ZZ/2 2^1 512 (312) (200) +*- diff --git a/M2/Macaulay2/packages/DirectSummands/large-eigenvalue.m2 b/M2/Macaulay2/packages/DirectSummands/large-eigenvalue.m2 new file mode 100644 index 00000000000..3ac0d42fdd5 --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/large-eigenvalue.m2 @@ -0,0 +1,10 @@ +kk = ZZ/5 +R = kk[p_(0,1)..p_(0,2), p_(1,2), p_(0,3), p_(1,3), p_(2,3)]/(p_(1,2)*p_(0,3)-p_(0,2)*p_(1,3)+p_(0,1)*p_(2,3)) +tar = R^{256:{-2}} +src = R^{256:{-3}} +mat = {{0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0},{0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),-2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3),0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),2*p_(1,3),0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),-p_(1,3)},{0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(0,3),0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(2,3),0,2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(2,3),0,-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(0,3)},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(2,3),0,-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(2,3),0,p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,3),2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,3),-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,3),-2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,3),2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,3),-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2*p_(1,3),-2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,3),2*p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2*p_(1,3),2*p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(0,3),0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-p_(1,3),-p_(1,2),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(0,3),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1),0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,p_(1,2),p_(0,2),p_(0,1)}}; +-- non-free summand of F_*(OO_X) on X = Gr(1,3) over F_5 +N = coker map(tar, src, mat); +-- about 3hrs to compute gensEnd0 N +-- after that, manually find one set of projectors in a few seconds, +-- split to get the rank 2 summands, then peel those off in a minute diff --git a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 index 934bf362421..5bd00d7ff91 100644 --- a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 @@ -41,3 +41,15 @@ TEST /// -- TODO: have a flag to test for twists of input summands as well summands({image F.dd_1, coker vars R}, M) /// + +/// + restart + debug needsPackage "DirectSummands" + K = GF(9) + K = ZZ/7 + R = K[b] + f = random(R^95, R^95); + elapsedTime select(7, i -> 0 == det(f - i * id_(target f))) -- only 33s for 250x250 over ZZ/7 + elapsedTime select(K.order, i -> 0 == det(f - K_0^i * id_(target f))) -- only 33s for 250x250 over ZZ/7 + scan(20, n -> elapsedTime eigenvalues' random(R^(n+5), R^(n+5))); +/// From 83618b54eab1c8bbdc0a89fbafefb61b4020f574 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 21 Apr 2025 15:34:20 -0400 Subject: [PATCH 70/88] fixed some comments --- M2/Macaulay2/packages/DirectSummands.m2 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 8a68e121543..85e4252e09b 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -8,6 +8,9 @@ -- 2. implement diagonalize for matrices (and later, complexes) -- 3. find a way to restrict and pass End to the summands -- 4. make summands work over ZZ (currently rank fails) +-- 5. compute a general endomorphism without computing End +-- 6. speed up computing eigenvalues over ZZ/p and GF(q) +-- 7. once an indecomposable summand is found, call summands(N, M) --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -360,7 +363,7 @@ generalEndomorphism(Matrix, Nothing, Matrix) := Matrix => o -> (N, null, inc) -> -- to produce a general endomorphism of N generalEndomorphism(Module, Matrix) := Matrix => o -> (N, pr) -> ( -- assert(N === target pr); - -- TODO: currently this still computed End_0(N) + -- TODO: currently this still computes End_0(N) -- figure out a way to compute the inverse without doing so inv := rightInverse(pr, DegreeLimit => degree 0_N, @@ -419,8 +422,7 @@ Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( DegreeLimit => degree 0_M, MinimalGenerators => true)) --- TODO: can we return cached summands from the closest field extension? --- all cached keys: select(keys M.cache, k -> instance(k, Option) and k#0 === symbol directSummands) +-- TODO: can we return cached summands from the closest subfield? cachedSummands = M -> M.cache.summands ?? components M ----------------------------------------------------------------------------- @@ -428,7 +430,6 @@ cachedSummands = M -> M.cache.summands ?? components M ----------------------------------------------------------------------------- -- Note: M may need to be extended to a field extensions --- TODO: when splitting over a field extension, use cached splitting over summands directSummands = method(Options => DirectSummandsOptions) directSummands Module := List => opts -> M -> M.cache.summands ??= ( checkRecursionDepth(); From a528121e938d089a05ebebbb98f79b6c64aadcf6 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 22 Apr 2025 04:46:16 -0400 Subject: [PATCH 71/88] fixed bug in eigenvalues'' --- M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 0069e634c50..5078fcd6168 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -93,9 +93,10 @@ fieldElements' = memoize fieldElements -- FIXME: don't cache globally eigenvalues'' = A -> ( R := ring A; p := char R; + F := groundField R; I := id_(target A); - if p == 0 or p > 1000 then return eigenvalues' A; - select(fieldElements' groundField R, e -> zero det(A - e * I))) + if p == 0 or not F.?order or F.order > 1000 then return eigenvalues' A; + select(fieldElements' F, e -> zero det(A - e * I))) largePower = (p,l,M) -> ( if p^l < 2^30 then return M^(p^l); From a0ea0e975cbc6e99bf622ec1f916f905d9e4abee Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 22 Apr 2025 04:50:02 -0400 Subject: [PATCH 72/88] some changes in summands(L, M) --- M2/Macaulay2/packages/DirectSummands.m2 | 57 +++++++++++++++---------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 85e4252e09b..506d39f970b 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -274,6 +274,7 @@ smartBasis = (deg, M) -> ( -- matrix of (degree zero) generators of End M -- TODO: rename this +-- also see gensHom0 gensEnd0 = M -> M.cache#"End0" ??= ( -- TODO: need to pass options from Hom + choose the coefficient field zdeg := if isHomogeneous M then degree 0_M; @@ -395,7 +396,6 @@ splitFreeModule = (M, opts) -> apply(numgens M, i -> target(M.cache#(symbol ^, [ -- helper for splitting free summands by observing the degrees of generators splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" ??= ( - if opts.Verbose then stderr << " -- splitting free summands: " << flush; directSummands(R := ring M; apply(-unique degrees M, d -> R^{d}), M, opts)) -- helper for splitting a module with known components @@ -466,54 +466,65 @@ directSummands CoherentSheaf := List => opts -> F -> apply( directSummands(Module, Module) := List => opts -> (L, M) -> ( checkRecursionDepth(); if ring L =!= ring M then error "expected objects over the same ring"; - if rank L >= rank M then return {M}; - n := opts.Limit ?? numgens M; + if rank L > rank M then return {M}; + limit := opts.Limit ?? numgens M; tries := opts.Tries ?? defaultNumTries char ring M; if 1 < #cachedSummands M then return flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); - if 1 < n then ( - -- TODO: can we detect multiple summands of L at once? + if 1 < limit then ( + if opts.Verbose then stderr << " -- splitting free summands: " << flush; comps := new MutableList from {M}; - for i to n - 1 do ( + comps = for i to limit - 1 do ( LL := directSummands(L, M, opts, Limit => 1); - if #LL == 1 then return sort toList comps; + if #LL == 1 then break sort toList comps; comps#(#comps-1) = L; - comps##comps = M = LL#1) + comps##comps = M = LL#1); + if opts.Verbose then stderr << endl << flush; + return comps ); zdeg := degree 0_M; + -- TODO: can we detect multiple summands of L at once? + -- perhaps find a projector onto a summand with several copies? + gensHom0 := (N, M) -> ( + H := Hom(N, M, + DegreeLimit => zdeg, + MinimalGenerators => false); + smartBasis(zdeg, H)); if isFreeModule L then ( - B := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); + B := gensHom0(M, L); if numcols B == 0 then return {M}; -- Previous alternative: - -- h := for i from 0 to numcols B - 1 do ( isSurjective(b := homomorphism B_{i}) ...) + -- h := for i from 0 to numcols B - 1 do ( + -- isSurjective(b := homomorphism B_{i}) ...) h := for i to tries - 1 do ( b := homomorphism(B * random source B); if isSurjective b then break matrix {L_0} // b)) else ( -- we look for a composition L -> M -> L which is the identity - B = smartBasis(zdeg, Hom(L, M, DegreeLimit => zdeg, MinimalGenerators => false)); - if numcols B == 0 then return {M}; - C := smartBasis(zdeg, Hom(M, L, DegreeLimit => zdeg, MinimalGenerators => false)); - if numcols C == 0 then return {M}; + B = gensHom0(L, M); if numcols B == 0 then return {M}; + C := gensHom0(M, L); if numcols C == 0 then return {M}; -- attempt to find a random isomorphism h = for i to tries - 1 do ( b := homomorphism(B * random source B); c := homomorphism(C * random source C); --TODO: change isIsomorphism to isSurjective? if isIsomorphism(c * b) then break b); - --is it worth doing the following lines? when does the random strategy above fail? if h === null then h = ( + -- TODO: is it worth doing the following lines? when does the random strategy above fail? + printerr "summands got to this part, it's probably useful!"; if opts.Strategy & 8 == 8 then ( -- precomputing the Homs can sometimes be a good strategy - Bhoms := apply(numcols B, i -> homomorphism B_{i}); - Choms := select(apply(numcols C, i -> homomorphism C_{i}), j -> isSurjective j); - pos := position'(Choms, Bhoms, - (c, b) -> isIsomorphism(c * b)); + -- TODO: confirm that this injectivity check is worthwhile and not slow + Bhoms := select(apply(numcols B, i -> homomorphism B_{i}), isInjective); + Choms := select(apply(numcols C, i -> homomorphism C_{i}), isSurjective); + pos := position'(Choms, Bhoms, (c, b) -> isIsomorphism(c * b)); if pos =!= null then last pos) else ( -- and sometimes too memory intensive - ind := position'(numcols C, numcols B, - --how to skip c if homomorphism C_{c} is not surjective? - (c, b) -> isSurjective homomorphism C_{c} and isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); - if ind =!= null then homomorphism B_{last ind}));); + ind := position'(numcols C, numcols B, (c, b) -> + isSurjective homomorphism C_{c} + and isInjective homomorphism B_{b} + and isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); + if ind =!= null then homomorphism B_{last ind}) + )); if h === null then return {M}; if opts.Verbose then stderr << concatenate(rank L : ".") << flush; {L, prune coker h}) From 73d11c101cc5ea3584f93dad34a99dc5de939a7d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 22 Apr 2025 06:14:04 -0400 Subject: [PATCH 73/88] exported more methods --- M2/Macaulay2/packages/DirectSummands.m2 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 506d39f970b..093608ad1f0 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -36,11 +36,15 @@ newPackage( export { -- methods + "isIndecomposable", "directSummands", "summands" => "directSummands", "findIdempotent", "findIdem" => "findIdempotent", + "findProjectors", "findSplitInclusion", + "generalEndomorphism", "isomorphismTally", - "isIndecomposable", + "tallySummands", + "isomorphism", -- symbols "Tries", -- frobenius methods From 7de90021da79f9edeb152fe0ff9486d0e3a89530 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 22 Apr 2025 10:51:37 -0400 Subject: [PATCH 74/88] fixed another bug in fieldElements --- M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 5078fcd6168..5524cc7199d 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -85,7 +85,7 @@ eigenvalues' = A -> ( else flatten rationalPoints ideal Chi) fieldElements = method() -fieldElements QuotientRing := ZZp -> toList(0..char ZZp - 1) +fieldElements QuotientRing := ZZp -> apply(ZZp.order, i -> i_ZZp) fieldElements GaloisField := GFq -> prepend_(0_GFq) apply(GFq.order - 1, i -> GFq_0^i) fieldElements' = memoize fieldElements -- FIXME: don't cache globally From 7d6392b1d4f2d67655d027c1eae68495b18d25c1 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 23 Apr 2025 02:08:31 -0400 Subject: [PATCH 75/88] fixed many problems with summands(L, M) --- M2/Macaulay2/packages/DirectSummands.m2 | 94 ++++++++++--------- .../packages/DirectSummands/homogeneous.m2 | 1 + .../packages/DirectSummands/idempotents.m2 | 1 + M2/Macaulay2/packages/DirectSummands/tests.m2 | 73 +++++++++++--- 4 files changed, 114 insertions(+), 55 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 093608ad1f0..c7730350d94 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -116,6 +116,9 @@ position' = method() position'(VisibleList, VisibleList, Function) := (B, C, f) -> for b in B do for c in C do if f(b, c) then return (b, c) position'(ZZ, ZZ, Function) := (B, C, f) -> position'(0..B-1, 0..C-1, f) +scan' = method() +scan'(VisibleList, VisibleList, Function) := (B, C, f) -> for b in B do for c in C do f(b,c) + char SheafOfRings := O -> char variety O GF'ZZ'ZZ = memoize (lookup(GF, ZZ, ZZ)) (options GF) @@ -408,7 +411,7 @@ splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" ??= ( splitComponents = (M, comps, splitfunc) -> ( n := #comps; c := -1; -- summand counter - projs := apply(n, i -> M^[i]); + projs := if 1 < n then apply(n, i -> M^[i]) else { id_M }; flatten apply(comps, projs, (N, p) -> ( L := splitfunc N; -- Projection maps to the summands @@ -447,7 +450,8 @@ directSummands Module := List => opts -> M -> M.cache.summands ??= ( if isDirectSum M then return M.cache.summands = splitComponents(M, components M, directSummands_opts); if isFreeModule M then return M.cache.summands = splitFreeModule(M, opts); if strategy & 1 == 1 then return M.cache.summands = ( - directSummands(directSum splitFreeSummands(M, opts), opts, Strategy => strategy - 1)); + splitComponents(M, splitFreeSummands(M, opts), + directSummands_(opts, Strategy => strategy - 1))); -- TODO: where should indecomposability check happen? -- for now it's here, but once we figure out random endomorphisms -- without computing Hom, this would need to move. @@ -470,20 +474,26 @@ directSummands CoherentSheaf := List => opts -> F -> apply( directSummands(Module, Module) := List => opts -> (L, M) -> ( checkRecursionDepth(); if ring L =!= ring M then error "expected objects over the same ring"; - if rank L > rank M then return {M}; + if rank L > rank M + or rank L >= rank M and isFreeModule M then return {M}; limit := opts.Limit ?? numgens M; tries := opts.Tries ?? defaultNumTries char ring M; if 1 < #cachedSummands M then return flatten apply(cachedSummands M, N -> directSummands(L, N, opts)); if 1 < limit then ( - if opts.Verbose then stderr << " -- splitting free summands: " << flush; + N := M; + M.cache#(symbol ^, [0]) = id_M; + if opts.Verbose then stderr << " -- splitting summands of degree " << degrees L << ": " << flush; comps := new MutableList from {M}; - comps = for i to limit - 1 do ( - LL := directSummands(L, M, opts, Limit => 1); - if #LL == 1 then break sort toList comps; - comps#(#comps-1) = L; - comps##comps = M = LL#1); + for i to limit - 1 do ( + pr := M.cache#(symbol ^, [#comps-1]); + LL := directSummands(L, N, opts, Limit => 1); + if #LL == 1 then break; + M.cache#(symbol ^, [#comps]) = N^[1] * pr; + M.cache#(symbol ^, [#comps-1]) = N^[0] * pr; + comps#(#comps-1) = L; -- === LL#0 + comps##comps = N = LL#1); if opts.Verbose then stderr << endl << flush; - return comps + return toList comps ); zdeg := degree 0_M; -- TODO: can we detect multiple summands of L at once? @@ -493,45 +503,45 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( DegreeLimit => zdeg, MinimalGenerators => false); smartBasis(zdeg, H)); - if isFreeModule L then ( - B := gensHom0(M, L); if numcols B == 0 then return {M}; + h := catch if isFreeModule L then ( + C := gensHom0(M, L); if numcols C == 0 then return {M}; -- Previous alternative: - -- h := for i from 0 to numcols B - 1 do ( - -- isSurjective(b := homomorphism B_{i}) ...) - h := for i to tries - 1 do ( - b := homomorphism(B * random source B); - if isSurjective b then break matrix {L_0} // b)) + -- h := for i from 0 to numcols C - 1 do ( + -- isSurjective(c := homomorphism C_{i}) ...) + for i to tries - 1 do ( + c := homomorphism(C * random source C); + if isSurjective c then throw (c, rightInverse c))) else ( -- we look for a composition L -> M -> L which is the identity - B = gensHom0(L, M); if numcols B == 0 then return {M}; - C := gensHom0(M, L); if numcols C == 0 then return {M}; + B := gensHom0(L, M); if numcols B == 0 then return {M}; + C = gensHom0(M, L); if numcols C == 0 then return {M}; -- attempt to find a random isomorphism - h = for i to tries - 1 do ( + for i to tries - 1 do ( b := homomorphism(B * random source B); c := homomorphism(C * random source C); --TODO: change isIsomorphism to isSurjective? - if isIsomorphism(c * b) then break b); - if h === null then h = ( - -- TODO: is it worth doing the following lines? when does the random strategy above fail? - printerr "summands got to this part, it's probably useful!"; - if opts.Strategy & 8 == 8 then ( - -- precomputing the Homs can sometimes be a good strategy - -- TODO: confirm that this injectivity check is worthwhile and not slow - Bhoms := select(apply(numcols B, i -> homomorphism B_{i}), isInjective); - Choms := select(apply(numcols C, i -> homomorphism C_{i}), isSurjective); - pos := position'(Choms, Bhoms, (c, b) -> isIsomorphism(c * b)); - if pos =!= null then last pos) - else ( - -- and sometimes too memory intensive - ind := position'(numcols C, numcols B, (c, b) -> - isSurjective homomorphism C_{c} - and isInjective homomorphism B_{b} - and isIsomorphism(homomorphism C_{c} * homomorphism B_{b})); - if ind =!= null then homomorphism B_{last ind}) - )); - if h === null then return {M}; + if isIsomorphism(c * b) then throw (c, b)); + -- TODO: is it worth doing the following lines? when does the random strategy above fail? + printerr "summands got to this part, it's probably useful!"; + if opts.Strategy & 8 == 8 then ( + -- precomputing the Homs can sometimes be a good strategy + -- TODO: confirm that this injectivity check is worthwhile and not slow + Bhoms := select(apply(numcols B, i -> homomorphism B_{i}), isInjective); + Choms := select(apply(numcols C, i -> homomorphism C_{i}), isSurjective); + scan'(Choms, Bhoms, (c, b) -> if isIsomorphism(c * b) then throw (c, b))) + -- but sometimes too memory intensive + else scan'(numcols C, numcols B, (ci, bi) -> + if isSurjective(c := homomorphism C_{ci}) + and isInjective(b := homomorphism B_{bi}) + and isIsomorphism(c * b) then throw (c, b)) + ); + (pr, inc) := if h =!= null then h else return {M}; if opts.Verbose then stderr << concatenate(rank L : ".") << flush; - {L, prune coker h}) + N = prune coker inc; + iso := inverse N.cache.pruningMap; + M.cache#(symbol ^, [0]) = pr; + M.cache#(symbol ^, [1]) = iso * inducedMap(coker inc, M); + {L, N}) directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( directSummands(module prune L, module prune G, opts), N -> sheaf(variety L, N)) @@ -540,7 +550,7 @@ directSummands(CoherentSheaf, CoherentSheaf) := List => opts -> (L, G) -> apply( directSummands(List, CoherentSheaf) := directSummands(List, Module) := List => opts -> (Ls, M) -> sort ( if 1 < #cachedSummands M then flatten apply(cachedSummands M, N -> directSummands(Ls, N, opts)) - else fold(Ls, {M}, (L, LL) -> join(drop(LL, -1), directSummands(L, last LL, opts)))) + else fold(Ls, {M}, (L, LL) -> splitComponents(M, LL, directSummands_(opts, L)))) ----------------------------------------------------------------------------- -- isIndecomposable diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index b3fdad84cb5..ad4e0222f7e 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -61,6 +61,7 @@ findBasicProjectors = M -> ( -- hence it is distinct from the Meat-Axe algorithm. summandsFromProjectors = method(Options => options findProjectors) summandsFromProjectors Module := opts -> M -> ( + if opts.Verbose then printerr "splitting summands using projectors"; if rank cover M <= 1 then return {M}; -- TODO: if M.cache.Idempotents is nonempty, should we use it here? -- maps M -> M whose (co)kernel is a (usually indecomposable) summand diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 5524cc7199d..4be81994548 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -205,6 +205,7 @@ findBasicIdempotent = M -> ( -- a module over a polynomial ring makes it distinct. summandsFromIdempotents = method(Options => options findIdempotent) summandsFromIdempotents Module := opts -> M -> ( + if opts.Verbose then printerr "splitting summands using idempotents"; if rank cover M <= 1 then return {M}; M.cache.Idempotents ??= {}; idems := if 0 < #M.cache.Idempotents then M.cache.Idempotents diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index b399644e645..273900ce70e 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -74,6 +74,53 @@ TEST /// -- direct summands over field extensions elapsedTime assert(toList(7:1) == rank \ summands changeBaseField(GF(7, 2), M)) -- 2.18s -> 0.47 /// +TEST /// + debug needsPackage "DirectSummands" + K = GF(7, 2) + R = K[x..z]/(x^3+y^3+z^3) + M = coker map(R^{6:{-1}}, R^{6:{-2}}, { + {(-a-2)*z, -2*y, (-a+1)*y, 0, x, (-2*a+1)*z}, + {(2*a+3)*y, 0, x, (a+2)*z, (3*a-3)*z, y}, + {x, (3*a-2)*z, (-a+2)*z, (-2*a+1)*y, (2*a-3)*y, 0}, + {(2*a-1)*z, (a+1)*y, (-3*a+1)*y, x, 0, (-2*a-1)*z}, + {(-a+3)*y, x, 0, (-2*a-2)*z, z, -3*a*y}, + {0, a*z, -2*z, (a-3)*y, (2*a-1)*y, x}}) + assert({1,1} == rank \ summandsFromProjectors M) + assert({1,1} == rank \ summandsFromIdempotents M) +/// + +TEST /// -- testing the local case + debug needsPackage "DirectSummands" + k = ZZ/2 + -- D_4^1 singularity + R = k[x,y,z]/(x^2*y + x*y^2 + x*y*z + z^2) + M = frobeniusPushforward(1, R) + -- TODO + -- the structure is significantly altered by homogenizing it + -- simpler example: nodal cubic in affine vs projective plane + k = ZZ/2 + R = k[x,y,z,h]/(x^2*y + x*y^2 + x*y*z + z^2*h) + M = frobeniusPushforward(1, R) + elapsedTime assert(toList(8:1) == rank \ summandsFromProjectors M) -- 6s + elapsedTime assert(toList(8:1) == rank \ summandsFromIdempotents M) -- 10s + -- + end + -- FIXME: these don't finish??? + R = k[x,y]/(x^2-y^3-y^2) + M = frobeniusPushforward(1, R) + elapsedTime assert(toList(4:1) == rank \ summandsFromProjectors(M, Verbose => true)) + assert(toList(4:1) == rank \ summandsFromIdempotents M) + R = k[x,y,z]/(x^2*z-y^3-y^2*z) + assert(toList(4:1) == rank \ summandsFromProjectors M) + assert(toList(4:1) == rank \ summandsFromIdempotents M) +/// + +TEST /// -- Grassmannian example + X = Proj quotient Grassmannian(1, 3, CoefficientRing => ZZ/3); + elapsedTime F = frobeniusPushforward(1, OO_X); -- <1s in char 2 & 3 + elapsedTime assert(splice{65:1, 8:2} == rank \ summands F) -- ~8s +/// + TEST /// -- ~1.1s R = ZZ/32003[x,y,z]/(x^3, x^2*y, x*y^2, y^4, y^3*z) @@ -146,6 +193,18 @@ TEST /// -- testing inhomogeneous -- findIdempotent M /// +TEST /// + kk = ZZ/101 + S = kk[x,y,z] + P = Proj S + T = tangentSheaf P + R = S/(x*y-z^2) + M = module T ** R + -- the module doesn't split, but the sheaf does + assert(1 == length summands M) + assert(2 == length summands sheaf M) +/// + TEST /// debug needsPackage "DirectSummands" K = ZZ/7 @@ -160,24 +219,12 @@ TEST /// F2 = frobeniusPushforward(1, L1#1); elapsedTime assert({7} == rank \ summands F2) -- 2s L = potentialExtension F2 - elapsedTime L2 = summands changeBaseField(L, F2); -- 14s + elapsedTime L2 = summands changeBaseField(L, F2); -- 14s projectors, 85s idempotents assert(toList(7:1) == rank \ L2) -- tests largepowers, but is very slow: -- findIdempotent changeBaseField(L, F2) /// -TEST /// - kk = ZZ/101 - S = kk[x,y,z] - P = Proj S - T = tangentSheaf P - R = S/(x*y-z^2) - M = module T ** R - -- the module doesn't split, but the sheaf does - assert(1 == length summands M) - assert(2 == length summands sheaf M) -/// - /// restart debug needsPackage "DirectSummands" From 582e78671d3342a8dbff3329bca825ee5c2fe6fb Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 23 Apr 2025 03:52:47 -0400 Subject: [PATCH 76/88] added splitComponentsBasic, renamed findIdempotents --- M2/Macaulay2/packages/DirectSummands.m2 | 21 +++++++--- M2/Macaulay2/packages/DirectSummands/docs.m2 | 2 +- .../packages/DirectSummands/homogeneous.m2 | 7 +++- .../packages/DirectSummands/idempotents.m2 | 41 ++++++++++++------- M2/Macaulay2/packages/DirectSummands/tests.m2 | 19 ++++++--- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index c7730350d94..cc3f57bf53e 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -26,7 +26,7 @@ newPackage( "Isomorphism", -- for isIsomorphic "Polyhedra", -- for coneFromVData and coneComp "PushForward", -- only for frobenius.m2 - "RationalPoints2", -- for rationalPoints in findIdempotent + "RationalPoints2", -- for rationalPoints in findIdempotents "Truncations", -- for effGenerators "Varieties", }, @@ -38,8 +38,8 @@ export { -- methods "isIndecomposable", "directSummands", "summands" => "directSummands", - "findIdempotent", "findIdem" => "findIdempotent", "findProjectors", + "findIdempotents", "findIdem" => "findIdempotents", "findSplitInclusion", "generalEndomorphism", "isomorphismTally", @@ -72,12 +72,13 @@ importFrom_Core { -- defined here and used in idempotents.m2 and homogeneous.m2 DirectSummandsOptions = new OptionTable from { Limit => null, -- used in directSummands(Module, Module) - Strategy => 7, -- Strategy is a bitwise sum of the following: + Strategy => 3, -- Strategy is a bitwise sum of the following: -- 1 => use degrees of generators as heuristic to peel off line bundles first -- 2 => check generators of End_0 as heuristic for finding idempotents - -- 4 => whether we are splitting coherent sheaves, so skip line bundles + -- 4 => use splitComponentsBasic, skips computing splitting maps -- 8 => precompute Homs before looking for idempotents -- 16 => use summandsFromIdempotents even in graded case + "Splitting" => null, Tries => null, -- see defaultNumTries below Verbose => false -- whether to print extra debugging info } @@ -411,16 +412,24 @@ splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" ??= ( splitComponents = (M, comps, splitfunc) -> ( n := #comps; c := -1; -- summand counter - projs := if 1 < n then apply(n, i -> M^[i]) else { id_M }; + projs := if 1 < n then apply(n, i -> try M^[i]) else { id_M }; flatten apply(comps, projs, (N, p) -> ( L := splitfunc N; -- Projection maps to the summands - if #L > 1 then apply(#L, i -> + try if #L > 1 then apply(#L, i -> M.cache#(symbol ^, [c += 1]) = N^[i] * p) else M.cache#(symbol ^, [c += 1]) = p; -- Inclusion maps are computed on-demand L))) +splitComponentsBasic = (M, ends, opts) -> ( + -- the coker of each idempotent gives a summand, while + L1 := prune \ apply(ends, coker); + -- the image of their composition is the complement. + L2 := prune \ { image product ends }; + -- TODO: call something like summands(L, M) here? + flatten apply(nonzero join(L1, L2), summands_opts)) + -- these are computed on-demand from the cached split surjection oldinclusions = lookup(symbol_, Module, Array) Module _ Array := Matrix => (M, w) -> M.cache#(symbol _, w) ??= ( diff --git a/M2/Macaulay2/packages/DirectSummands/docs.m2 b/M2/Macaulay2/packages/DirectSummands/docs.m2 index 65996787cfc..a0cd8e3e45b 100644 --- a/M2/Macaulay2/packages/DirectSummands/docs.m2 +++ b/M2/Macaulay2/packages/DirectSummands/docs.m2 @@ -60,7 +60,7 @@ Node L = summands M assert first isIsomorphic(M, directSum L) SeeAlso - findIdempotent + findIdempotents /// -- Template: diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index ad4e0222f7e..fa9bf55bf4c 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -1,6 +1,6 @@ needsPackage "RationalPoints2" -findProjectors = method(Options => DirectSummandsOptions ++ { "Splitting" => null }) +findProjectors = method(Options => DirectSummandsOptions) findProjectors Module := opts -> M -> ( R := ring M; p := char R; @@ -61,6 +61,7 @@ findBasicProjectors = M -> ( -- hence it is distinct from the Meat-Axe algorithm. summandsFromProjectors = method(Options => options findProjectors) summandsFromProjectors Module := opts -> M -> ( + if not isHomogeneous M then error "expected homogeneous module"; if opts.Verbose then printerr "splitting summands using projectors"; if rank cover M <= 1 then return {M}; -- TODO: if M.cache.Idempotents is nonempty, should we use it here? @@ -73,6 +74,10 @@ summandsFromProjectors Module := opts -> M -> ( -- chance of splitting the module in a single iteration. summandsFromProjectors(Module, Matrix) := opts -> (M, pr) -> summandsFromProjectors(M, {pr}, opts) summandsFromProjectors(Module, List) := opts -> (M, ends) -> ( + checkRecursionDepth(); + -- in some examples, we use barebones splitComponentsBasic + if opts.Strategy & 4 == 4 or not isHomogeneous M + then return splitComponentsBasic(M, ends, opts); -- maps from kernel summands and to cokernel summands injs := apply(ends, h -> inducedMap(M, ker h)); projs := apply(ends, h -> inducedMap(coker h, M)); diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 4be81994548..c3854d26fb7 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -123,9 +123,9 @@ findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) -- TODO: findIdem right now will fail if K is not L[a]/f(a); -- in general, will need to find a primitive element first -findIdempotent = method(Options => DirectSummandsOptions ++ { "Splitting" => null }) -findIdempotent CoherentSheaf := opts -> M -> findIdempotent(module M, opts) -findIdempotent Module := opts -> M -> ( +findIdempotents = method(Options => DirectSummandsOptions) +findIdempotents CoherentSheaf := opts -> M -> findIdempotents(module M, opts) +findIdempotents Module := opts -> M -> ( R := ring M; p := char R; F := groundField R; @@ -138,6 +138,7 @@ findIdempotent Module := opts -> M -> ( -- this is used in generalEndomorphism -- to avoid recomputing the Hom module (pr, inc) := opts#"Splitting" ?? (id_M, id_M); + limit := opts.Limit ?? numgens M; tries := opts.Tries ?? defaultNumTries p; for c to tries - 1 do ( f := generalEndomorphism(M, pr, inc); @@ -157,21 +158,28 @@ findIdempotent Module := opts -> M -> ( if #eigen == 1 and F === L or #eigen == 0 then continue); -- try to find idempotens from eigenvalues - opers := flatten for y in eigen list ( - if p == 0 then (1, f - y*id_M, fm - y*id_V) else ( - for j from 0 to e list (j, f - y*id_M, largePower'(p, j+1, largePower(p, l, fm - y*id_V))))); - idem := position(opers, (j, g, gm) -> isWeakIdempotent gm and not isSurjective gm and gm != 0); - if idem =!= null then ( - (j, g, gm) := opers_idem; - idem = if p == 0 then g else largePower'(p, j+1, largePower(p, l, g)); - -- for inexact fields, we compose the idempotent until the determinant is zero - if inexactFlag then idem = idem ^ (findErrorMargin idem); - return idem)); + isUsable := gm -> isWeakIdempotent gm and not isSurjective gm and gm != 0; + largePow := (j, g) -> largePower'(p, j+1, largePower(p, l, g)); + -- TODO: use limit here + idems := nonnull flatten for y in eigen list ( + if p > 0 then for j from 0 to e do ( + if isUsable largePow(j, fm - y*id_V) then break (j, f - y*id_M)) + else if isUsable(fm - y*id_V) then (1, f - y*id_M)); + idems = select(idems, (j, f) -> image f != 0 and coker f != 0); + if #idems == 0 then continue; + return apply(idems, (j, g) -> ( + idem := if p == 0 then g else largePow(j, g); + -- for inexact fields, we compose the idempotent until the determinant is zero + if inexactFlag then idem = idem ^ (findErrorMargin idem); + idem))); -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K -- TODO: if L is still null, change the error error("no idempotent found after ", tries, " attempts. ", "Try using changeBaseField with ", toString L)) +-- for backwards compatibility +findIdempotent = options findIdempotents >> opts -> M -> first findIdempotents(M, opts) + protect Idempotents -- only tries to find an idempotent among the generators of End_0(M) @@ -203,19 +211,22 @@ findBasicIdempotent = M -> ( -- this is essentially the Meat-Axe algorithm, -- but the process for finding an idempotent for -- a module over a polynomial ring makes it distinct. -summandsFromIdempotents = method(Options => options findIdempotent) +summandsFromIdempotents = method(Options => options findIdempotents) summandsFromIdempotents Module := opts -> M -> ( if opts.Verbose then printerr "splitting summands using idempotents"; if rank cover M <= 1 then return {M}; M.cache.Idempotents ??= {}; idems := if 0 < #M.cache.Idempotents then M.cache.Idempotents - else try findIdempotent(M, opts) else return {M}; + else try findIdempotents(M, opts) else return {M}; summandsFromIdempotents(M, idems, opts)) -- keep close to summandsFromProjectors summandsFromIdempotents(Module, Matrix) := opts -> (M, h) -> summandsFromIdempotents(M, {h}, opts) summandsFromIdempotents(Module, List) := opts -> (M, ends) -> ( checkRecursionDepth(); + -- in some examples, we use barebones splitComponentsBasic + if opts.Strategy & 4 == 4 or not isHomogeneous M + then return splitComponentsBasic(M, ends, opts); -- maps from kernel summands and to cokernel summands injs := apply(ends, h -> inducedMap(M, ker h)); projs := apply(ends, h -> inducedMap(coker h, M)); diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 273900ce70e..447aa0e7439 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -1,3 +1,9 @@ +/// +restart +debug needsPackage "DirectSummands" +errorDepth = 2 +/// + TEST /// -- basic test -- ~0.2s S = QQ[x,y] @@ -66,7 +72,7 @@ TEST /// -- direct summands over field extensions elapsedTime B = basis({0}, A); -- ~0.23s elapsedTime B = smartBasis({0}, A); -- ~0.03s *- - -- if this test fails, check if "try findIdempotent M" if hiding any unexpected errors + -- if this test fails, check if "try findIdempotents M" if hiding any unexpected errors -- FIXME: this is slow because random homomorphisms shouldn't be over the extended field elapsedTime assert({1, 2, 2, 2} == rank \ summands M) -- 2.28s elapsedTime assert({1, 2, 2, 2} == rank \ summands changeBaseField(GF 7, M)) -- 2.87s -> 2.05 @@ -95,6 +101,9 @@ TEST /// -- testing the local case -- D_4^1 singularity R = k[x,y,z]/(x^2*y + x*y^2 + x*y*z + z^2) M = frobeniusPushforward(1, R) + errorDepth=2 + --elapsedTime summands(M, Verbose => true); + --elapsedTime summandsFromIdempotents(M, Verbose => true); -- TODO -- the structure is significantly altered by homogenizing it -- simpler example: nodal cubic in affine vs projective plane @@ -190,7 +199,7 @@ TEST /// -- testing inhomogeneous M = coker matrix matrix"x,y,z;y,z,x;z,x,y" M = coker matrix matrix"1,y,z;y,1,x;z,x,1" -- findBasicIdempotent M - -- findIdempotent M + -- findIdempotents M /// TEST /// @@ -219,10 +228,10 @@ TEST /// F2 = frobeniusPushforward(1, L1#1); elapsedTime assert({7} == rank \ summands F2) -- 2s L = potentialExtension F2 - elapsedTime L2 = summands changeBaseField(L, F2); -- 14s projectors, 85s idempotents + elapsedTime L2 = summands changeBaseField(L, F2); -- projectors 14s, idempotents 85s->45s assert(toList(7:1) == rank \ L2) -- tests largepowers, but is very slow: - -- findIdempotent changeBaseField(L, F2) + -- findIdempotents changeBaseField(L, F2) /// /// @@ -266,7 +275,7 @@ TEST /// assert(2 == #summands coker matrix {{a, b^3}, {-b^3, a}}) M = coker matrix {{a, b^3}, {-b^3, a}} - findIdempotent M + findIdempotents M summands changeBaseField(2, M) /// From 97abefa741bc67aff27262f0233f039109c0b9ca Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 23 Apr 2025 11:56:05 -0400 Subject: [PATCH 77/88] added kludge for LocalRings --- M2/Macaulay2/packages/DirectSummands.m2 | 10 +++++++--- .../packages/DirectSummands/idempotents.m2 | 17 +++++++++++++++-- M2/Macaulay2/packages/DirectSummands/tests.m2 | 5 ++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index cc3f57bf53e..8614a4b4abe 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -28,7 +28,8 @@ newPackage( "PushForward", -- only for frobenius.m2 "RationalPoints2", -- for rationalPoints in findIdempotents "Truncations", -- for effGenerators - "Varieties", + "LocalRings", -- for the local examples + "Varieties", -- for the geometric examples }, AuxiliaryFiles => true, DebuggingMode => false @@ -310,12 +311,15 @@ random Module := Vector => o -> M -> ( if isHomogeneous M then random(degree 1_(ring M), M, o) else localRandom(M, o)) generalEndomorphism = method(Options => options random) -generalEndomorphism Module := Matrix => o -> M -> ( +generalEndomorphism Module := Matrix => o -> M0 -> ( + R := ring M0; + -- TODO: avoid this hack for local rings + M := if instance(R, LocalRing) then liftUp M0 else M0; B := gensEnd0 M; r := if isHomogeneous M then random(source B, o) else localRandom(source B, o); - homomorphism(B * r)) + R ** homomorphism(B * r)) -- the sheaf needs to be pruned to prevent missing endomorphisms generalEndomorphism CoherentSheaf := SheafMap => o -> F -> ( sheaf generalEndomorphism(module prune F, o)) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index c3854d26fb7..99d01192e14 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -117,6 +117,15 @@ lift(CC, CC_*) := opts -> (r, C) -> numeric(precision C, r) -- adjust as needed LOL findErrorMargin = m -> ceiling(log_10 2^(precision ring m)) +-- TODO: move to LocalRings? +residueField = method() +residueField Ring := R -> quotient ideal vars R +residueField LocalRing := R -> target R.residueMap + +residueMap' = method() +residueMap' Ring := R -> map(quotient ideal vars R, R, vars R % ideal vars R) +residueMap' LocalRing := R -> map(quotient ideal R.maxIdeal, R, vars baseRing R % R.maxIdeal) + ----------------------------------------------------------------------------- -- findIdempotents ----------------------------------------------------------------------------- @@ -130,7 +139,7 @@ findIdempotents Module := opts -> M -> ( p := char R; F := groundField R; e := fieldExponent R; - K := quotient ideal gens R; + K := residueMap' R; V := K ** M; inexactFlag := instance(F, InexactField); l := if p == 0 then e else max(e, ceiling log_p numgens M); @@ -190,8 +199,12 @@ findBasicIdempotent = M -> ( if 0 < #M.cache.Idempotents then return (first M.cache.Idempotents, false); R := ring M; + K := residueMap' R; + -- FIXME: this may not be correct + if instance(R, LocalRing) then ( + M = liftUp M; + K = residueMap' ring M); B := gensEnd0 M; - K := coker vars R; -- whether all non-identity endomorphisms are zero mod m -- if this remains true till the end, the module is -- certifiably indecomposable. diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 447aa0e7439..28816bcc83a 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -189,10 +189,13 @@ TEST /// -- testing inhomogeneous assert(3 == #summandsFromIdempotents M) assert(3 == #summandsFromProjectors M) -- + -- FIXME: broken when using multiple idempotents at once M = coker matrix matrix"1,y,z;y,1,x;z,x,1" assert(2 == #summands M) assert(2 == #summandsFromIdempotents M) - -- assert(3 == #summandsFromProjectors M) -- TODO: why does this return 3? + R = S_(ideal vars S) + M = coker matrix matrix"1,y,z;y,1,x;z,x,1" + assert(1 == #summands M) -- -- FIXME: S = QQ[x,y,z]; From f25f52157f17906a2b503c3119f0b219d62ae8b5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 23 Apr 2025 18:25:49 -0400 Subject: [PATCH 78/88] added prune' for inhomogeneous examples --- M2/Macaulay2/packages/DirectSummands.m2 | 26 ++++++++++++++++--- .../packages/DirectSummands/homogeneous.m2 | 4 +-- .../packages/DirectSummands/idempotents.m2 | 4 +-- M2/Macaulay2/packages/DirectSummands/tests.m2 | 18 +++++++------ 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 8614a4b4abe..713627223e5 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -108,6 +108,12 @@ submatrixByDegrees(Matrix, Sequence) := (m, degs) -> ( row := if tar =!= null then positions(degrees target m, deg -> member(deg, tar)); submatrix(m, row, col)) +-- rankWarn = true +-- rank' = M -> if groundField ring M =!= ZZ then rank M else try rank M else ( +-- if rankWarn then ( rankWarn = false; +-- printerr "warning: rank over integers is not well-defined; returning zero"); +-- 0) + -- this defines sorting on modules and sheaves CoherentSheaf ? CoherentSheaf := Module ? Module := (M, N) -> if rank M != rank N then rank M ? rank N else degrees M ? degrees N @@ -169,6 +175,19 @@ sheaf' = (X, M) -> try sheaf(X, M) else ( changeBaseField(GaloisField, CoherentSheaf) := (L, F) -> sheaf'(variety F, changeBaseField(L, module F)) +importFrom_Varieties "flattenModule" +prune' = method() +prune' Module := M0 -> M0.cache.prune' ??= ( + M := prune M0; + R := ring M; + if isHomogeneous M0 + or instance(R, LocalRing) then return M; + S := ambient R; + MS := flattenModule M; + Sm := localRing(S, ideal gens S); + MSm := prune(MS ** Sm); + R ** liftUp MSm) + nonzero = x -> select(x, i -> i != 0) nonnull = x -> select(x, i -> i =!= null) @@ -428,9 +447,9 @@ splitComponents = (M, comps, splitfunc) -> ( splitComponentsBasic = (M, ends, opts) -> ( -- the coker of each idempotent gives a summand, while - L1 := prune \ apply(ends, coker); + L1 := prune' \ apply(ends, coker); -- the image of their composition is the complement. - L2 := prune \ { image product ends }; + L2 := prune' \ { image product ends }; -- TODO: call something like summands(L, M) here? flatten apply(nonzero join(L1, L2), summands_opts)) @@ -455,6 +474,7 @@ directSummands Module := List => opts -> M -> M.cache.summands ??= ( checkRecursionDepth(); strategy := opts.Strategy; R := ring M; + if prune' M == 0 then return { M }; -- Note: End does not work for WeylAlgebras or AssociativeAlgebras yet, nor does basis if not isCommutative R and not isWeylAlgebra R then error "expected a commutative base ring"; -- Note: rank does weird stuff if R is not a domain @@ -550,7 +570,7 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( ); (pr, inc) := if h =!= null then h else return {M}; if opts.Verbose then stderr << concatenate(rank L : ".") << flush; - N = prune coker inc; + N = prune' coker inc; iso := inverse N.cache.pruningMap; M.cache#(symbol ^, [0]) = pr; M.cache#(symbol ^, [1]) = iso * inducedMap(coker inc, M); diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index fa9bf55bf4c..b9e4fc7dafe 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -63,7 +63,7 @@ summandsFromProjectors = method(Options => options findProjectors) summandsFromProjectors Module := opts -> M -> ( if not isHomogeneous M then error "expected homogeneous module"; if opts.Verbose then printerr "splitting summands using projectors"; - if rank cover M <= 1 then return {M}; + if rank cover M <= 1 or prune' M == 0 then return {M}; -- TODO: if M.cache.Idempotents is nonempty, should we use it here? -- maps M -> M whose (co)kernel is a (usually indecomposable) summand projs := try findProjectors(M, opts) else return {M}; @@ -97,7 +97,7 @@ summandsFromProjectors(Module, List) := opts -> (M, ends) -> ( comps := for n to #ends list ( (pr, inc) := (projs#n, injs#n); (N0, K0) := (target pr, source inc); - if (N := prune N0) == 0 then continue; + if (N := prune' N0) == 0 then continue; -- TODO: can we check if M has multiple copies of N quickly? iso := try isomorphism(K0, N0); p := inverse N.cache.pruningMap * pr; diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 99d01192e14..1975ce2e0dd 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -227,7 +227,7 @@ findBasicIdempotent = M -> ( summandsFromIdempotents = method(Options => options findIdempotents) summandsFromIdempotents Module := opts -> M -> ( if opts.Verbose then printerr "splitting summands using idempotents"; - if rank cover M <= 1 then return {M}; + if rank cover M <= 1 or prune' M == 0 then return {M}; M.cache.Idempotents ??= {}; idems := if 0 < #M.cache.Idempotents then M.cache.Idempotents else try findIdempotents(M, opts) else return {M}; @@ -259,7 +259,7 @@ summandsFromIdempotents(Module, List) := opts -> (M, ends) -> ( comps := for n to #ends list ( (pr, inc) := (projs#n, injs#n); (N0, K0) := (target pr, source inc); - if (N := prune N0) == 0 then continue; + if (N := prune' N0) == 0 then continue; -- TODO: can we check if M has multiple copies of N quickly? iso := try isomorphism(K0, N0); p := inverse N.cache.pruningMap * pr; diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 28816bcc83a..c700a894982 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -180,7 +180,7 @@ TEST /// -- testing in char 0 -- FIXME scan(20, i -> assert(set summands coker matrix {{x,y},{-y,x}} == set {cokernel matrix {{x-ii*y}}, cokernel matrix {{x+ii*y}}})) /// -TEST /// -- testing inhomogeneous +TEST /// -- testing inhomogeneous examples debug needsPackage "DirectSummands" S = GF(2,2)[x,y,z]; -- homogeneous baseline, used as control @@ -189,20 +189,22 @@ TEST /// -- testing inhomogeneous assert(3 == #summandsFromIdempotents M) assert(3 == #summandsFromProjectors M) -- - -- FIXME: broken when using multiple idempotents at once + -- TODO: this is locally zero, but can we diagonalize it? M = coker matrix matrix"1,y,z;y,1,x;z,x,1" - assert(2 == #summands M) - assert(2 == #summandsFromIdempotents M) + assert(summands M == {M}) + assert(summandsFromIdempotents M == {M}) R = S_(ideal vars S) M = coker matrix matrix"1,y,z;y,1,x;z,x,1" - assert(1 == #summands M) + assert(summands M == {M}) + assert(summandsFromIdempotents M == {M}) -- - -- FIXME: S = QQ[x,y,z]; M = coker matrix matrix"x,y,z;y,z,x;z,x,y" + assert(summands M == {M}) + assert(summandsFromIdempotents M == {M}) M = coker matrix matrix"1,y,z;y,1,x;z,x,1" - -- findBasicIdempotent M - -- findIdempotents M + assert(summands M == {M}) + assert(summandsFromIdempotents M == {M}) /// TEST /// From 794d1363b026b431d41528e89a0e614b9fad95e0 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 23 Apr 2025 20:36:51 -0400 Subject: [PATCH 79/88] fixed the local and inhomogeneous tests --- M2/Macaulay2/packages/DirectSummands.m2 | 4 +- .../packages/DirectSummands/idempotents.m2 | 20 ++++---- M2/Macaulay2/packages/DirectSummands/tests.m2 | 50 +++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 713627223e5..22ba43f592f 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -423,7 +423,7 @@ findSplitInclusion(Module, Module) := opts -> (M, N) -> ( if h === null then return "not known" else return h) -- helper for splitting a free module and setting the split surjections -splitFreeModule = (M, opts) -> apply(numgens M, i -> target(M.cache#(symbol ^, [i]) = M^{i})) +splitFreeModule = (M, opts) -> components directSum apply(R := ring M; -degrees M, deg -> R^{deg}) -- helper for splitting free summands by observing the degrees of generators splitFreeSummands = (M, opts) -> M.cache#"FreeSummands" ??= ( @@ -605,7 +605,7 @@ isIndecomposable Module := o -> M -> M.cache.isIndecomposable ??= tryHooks( -- * all degree zero endomorphisms of M are zero mod maximal ideal -- if a non-identity idempotent is found, it is cached in M addHook((isIndecomposable, Module), Strategy => "IdempotentSearch", (opts, M) -> ( - (idemp, certified) := findBasicIdempotent M; + (idemp, certified) := findBasicIdempotents(M, opts); if idemp =!= null then ( if 1 < debugLevel then printerr "module is decomposable!"; false ) else if certified then ( if 1 < debugLevel then printerr "module is indecomposable!"; true ) )) diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index 1975ce2e0dd..ce6aa420c63 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -174,7 +174,8 @@ findIdempotents Module := opts -> M -> ( if p > 0 then for j from 0 to e do ( if isUsable largePow(j, fm - y*id_V) then break (j, f - y*id_M)) else if isUsable(fm - y*id_V) then (1, f - y*id_M)); - idems = select(idems, (j, f) -> image f != 0 and coker f != 0); + idems = select(idems, (j, f) -> + prune' image f != 0 and prune' coker f != 0 and prune' image f != M); if #idems == 0 then continue; return apply(idems, (j, g) -> ( idem := if p == 0 then g else largePow(j, g); @@ -194,10 +195,9 @@ protect Idempotents -- only tries to find an idempotent among the generators of End_0(M) -- which is in general unlikely to be successful, but it often works! -- returns a pair: (idempotent or null, whether M is certified indecomposable) -findBasicIdempotent = M -> ( +findBasicIdempotents = options findIdempotents >> opts -> M -> ( M.cache.Idempotents ??= {}; - if 0 < #M.cache.Idempotents - then return (first M.cache.Idempotents, false); + if 0 < #M.cache.Idempotents then return (M.cache.Idempotents, false); R := ring M; K := residueMap' R; -- FIXME: this may not be correct @@ -218,8 +218,10 @@ findBasicIdempotent = M -> ( or zero(hm := K ** cover h) then return; certified = false; if isWeakIdempotent hm then break h)); - if idemp =!= null then M.cache.Idempotents ??= { idemp }; - (idemp, certified)) + if idemp =!= null then ( + if opts.Verbose then printerr "splitting summands using a basic idempotent"; + M.cache.Idempotents |= { idemp }); + (M.cache.Idempotents, certified)) -- this is essentially the Meat-Axe algorithm, -- but the process for finding an idempotent for @@ -228,9 +230,9 @@ summandsFromIdempotents = method(Options => options findIdempotents) summandsFromIdempotents Module := opts -> M -> ( if opts.Verbose then printerr "splitting summands using idempotents"; if rank cover M <= 1 or prune' M == 0 then return {M}; - M.cache.Idempotents ??= {}; - idems := if 0 < #M.cache.Idempotents then M.cache.Idempotents - else try findIdempotents(M, opts) else return {M}; + idems := try M.cache.Idempotents else {}; + if 0 == #idems then try + idems = findIdempotents(M, opts) else return {M}; summandsFromIdempotents(M, idems, opts)) -- keep close to summandsFromProjectors diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index c700a894982..f31c838170b 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -96,32 +96,32 @@ TEST /// /// TEST /// -- testing the local case + -- the structure is significantly altered by homogenizing modules + -- simpler example: nodal cubic in affine vs projective plane debug needsPackage "DirectSummands" k = ZZ/2 -- D_4^1 singularity R = k[x,y,z]/(x^2*y + x*y^2 + x*y*z + z^2) M = frobeniusPushforward(1, R) - errorDepth=2 - --elapsedTime summands(M, Verbose => true); - --elapsedTime summandsFromIdempotents(M, Verbose => true); - -- TODO - -- the structure is significantly altered by homogenizing it - -- simpler example: nodal cubic in affine vs projective plane + -- uses a basic idem + elapsedTime assert(toList(4:1) == rank \ summands M) -- ~2s + elapsedTime assert(toList(4:1) == rank \ summandsFromIdempotents M) -- ~0s + -- k = ZZ/2 R = k[x,y,z,h]/(x^2*y + x*y^2 + x*y*z + z^2*h) M = frobeniusPushforward(1, R) - elapsedTime assert(toList(8:1) == rank \ summandsFromProjectors M) -- 6s - elapsedTime assert(toList(8:1) == rank \ summandsFromIdempotents M) -- 10s + elapsedTime assert(toList(8:1) == rank \ summands M) -- <2s + --elapsedTime assert(toList(8:1) == rank \ summandsFromProjectors M) -- 6s + --elapsedTime assert(toList(8:1) == rank \ summandsFromIdempotents M) -- 10s -- - end - -- FIXME: these don't finish??? + k = ZZ/2 R = k[x,y]/(x^2-y^3-y^2) M = frobeniusPushforward(1, R) - elapsedTime assert(toList(4:1) == rank \ summandsFromProjectors(M, Verbose => true)) - assert(toList(4:1) == rank \ summandsFromIdempotents M) + elapsedTime assert({1,1} == rank \ summands M) -- 3s + -- R = k[x,y,z]/(x^2*z-y^3-y^2*z) - assert(toList(4:1) == rank \ summandsFromProjectors M) - assert(toList(4:1) == rank \ summandsFromIdempotents M) + M = frobeniusPushforward(1, R) + elapsedTime assert(toList(4:1) == rank \ summands M) -- <2s /// TEST /// -- Grassmannian example @@ -239,25 +239,23 @@ TEST /// -- findIdempotents changeBaseField(L, F2) /// -/// - restart +TEST /// debug needsPackage "DirectSummands" kk = ZZ/13 S = kk[x,y,z] R = S/(x*z-y^2) - L = summands frobeniusPushforward(1, R); - L = summands S^30000; - elapsedTime isomorphismTally L - elapsedTime tallySummands L - set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} - + M = module frobeniusPushforward(1, OO_(Proj R)); + elapsedTime L = summands(M, Verbose => true); + elapsedTime assert({1,12} == last \ isomorphismTally L); + elapsedTime L = summands(S^3000, Verbose => true); + elapsedTime assert({3000} == last \ isomorphismTally L); + -- nonstandard graded case kk = ZZ/11 S = kk[x,y,z, Degrees => {5,1,5}] R = S/(x*z-y^10) - L = summands frobeniusPushforward(1, R); - elapsedTime isomorphismTally L; - elapsedTime tallySummands L; - set(last \ isomorphismTally summands frobeniusPushforward(1,R)) == set{12,13} + M = module frobeniusPushforward(1, OO_(Proj R)); + elapsedTime L = summands(M, Verbose => true); + elapsedTime assert({1,2,2,2,2,2} == last \ isomorphismTally L); /// /// From 37a9fedaacc121eba7a876aa8abefa591afac45f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 24 Apr 2025 01:36:17 -0400 Subject: [PATCH 80/88] fixed isIndecomposable --- M2/Macaulay2/packages/DirectSummands.m2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 22ba43f592f..09d16f6c56c 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -593,7 +593,7 @@ directSummands(List, Module) := List => opts -> (Ls, M) -> sort ( -- (but does _not_ run the full directSummands algorithm) -- returns true if the module is certifiably indecomposable -- returns null for non-conclusive results -isIndecomposable = method(Options => { Strategy => null }) +isIndecomposable = method(Options => { Strategy => null, Verbose => false }) isIndecomposable CoherentSheaf := o -> F -> isIndecomposable(module prune F, o) isIndecomposable Module := o -> M -> M.cache.isIndecomposable ??= tryHooks( (isIndecomposable, Module), (o, M), (o, M) -> ( @@ -606,8 +606,8 @@ isIndecomposable Module := o -> M -> M.cache.isIndecomposable ??= tryHooks( -- if a non-identity idempotent is found, it is cached in M addHook((isIndecomposable, Module), Strategy => "IdempotentSearch", (opts, M) -> ( (idemp, certified) := findBasicIdempotents(M, opts); - if idemp =!= null then ( if 1 < debugLevel then printerr "module is decomposable!"; false ) - else if certified then ( if 1 < debugLevel then printerr "module is indecomposable!"; true ) + if #idemp > 0 then ( if opts.Verbose then printerr "module is decomposable!"; false ) else + if certified then ( if opts.Verbose then printerr "module is indecomposable!"; true ) )) -- TODO From 90402403d1f89867a720a0865bcf0dcfe7b48ecc Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 25 Apr 2025 14:35:03 -0400 Subject: [PATCH 81/88] moved a file to avoid case-insensitive conflicts --- .../DirectSummands/{elimination.m2 => trivial-algorithm.m2} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename M2/Macaulay2/packages/DirectSummands/{elimination.m2 => trivial-algorithm.m2} (100%) diff --git a/M2/Macaulay2/packages/DirectSummands/elimination.m2 b/M2/Macaulay2/packages/DirectSummands/trivial-algorithm.m2 similarity index 100% rename from M2/Macaulay2/packages/DirectSummands/elimination.m2 rename to M2/Macaulay2/packages/DirectSummands/trivial-algorithm.m2 From 7c432586abfd5a4c3d8c42fdf9b061f810221c44 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 26 Apr 2025 23:09:48 -0400 Subject: [PATCH 82/88] fixed a bug in findProjectors --- M2/Macaulay2/packages/DirectSummands/homogeneous.m2 | 6 ++++-- M2/Macaulay2/packages/DirectSummands/tests.m2 | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index b9e4fc7dafe..ce7d6a56f9d 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -27,7 +27,9 @@ findProjectors Module := opts -> M -> ( -- TODO: computing eigenvalues over coefficient field -- would significantly speed up this step eigen := eigenvalues'' f0; -- about 25% of computation - if #eigen <= 1 then ( + projs := select(for y in eigen list (f - y * id_M)^n, + g -> not zero g and not isInjective g); + if #projs < 1 then ( -- to be used as a suggestion in the error -- TODO: is there any way to tell if the module is indecomposable here? -- e.g. based on the characteristic polynomial factoring completely @@ -35,7 +37,7 @@ findProjectors Module := opts -> M -> ( -- TODO: expand for inexact fields if L === null and not instance(F, InexactField) then L = extField { char f0 }; continue); - return for y in eigen list (f - y * id_M)^n + return projs ); -- TODO: skip the "Try using" line if the field is large enough, e.g. L === K -- TODO: if L is still null, change the error diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index f31c838170b..87daa8c3b7e 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -188,6 +188,7 @@ TEST /// -- testing inhomogeneous examples assert(3 == #summands M) assert(3 == #summandsFromIdempotents M) assert(3 == #summandsFromProjectors M) + assert isIsomorphic'(directSum summands M, M, Tries => 10) -- -- TODO: this is locally zero, but can we diagonalize it? M = coker matrix matrix"1,y,z;y,1,x;z,x,1" @@ -200,8 +201,10 @@ TEST /// -- testing inhomogeneous examples -- S = QQ[x,y,z]; M = coker matrix matrix"x,y,z;y,z,x;z,x,y" - assert(summands M == {M}) - assert(summandsFromIdempotents M == {M}) + assert(2 == #summands M) + assert(2 == #summandsFromProjectors M) + assert first isIsomorphic(directSum summands M, M) + -- TODO: this is locally zero, but can we diagonalize it? M = coker matrix matrix"1,y,z;y,1,x;z,x,1" assert(summands M == {M}) assert(summandsFromIdempotents M == {M}) From 44a4d39eac41eaa493ed04640e590e168f619358 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 26 Apr 2025 23:14:42 -0400 Subject: [PATCH 83/88] added test Currently fails to split over large field --- M2/Macaulay2/packages/DirectSummands/tests.m2 | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index 87daa8c3b7e..bdeb77759d4 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -261,6 +261,29 @@ TEST /// elapsedTime assert({1,2,2,2,2,2} == last \ isomorphismTally L); /// +TEST /// + kk = ZZ/13 + R = kk[x,y] + m = matrix {{x, 2*y}, {-y, x}} + assert(1 == # summands coker m) + assert(2 == # summands coker(m ++ m)) + assert(2 == # summands changeBaseField_2 coker m) + assert(4 == # summands changeBaseField_2 coker(m ++ m)) + -- + kk = ZZ/32003 + R = kk[x,y] + m = matrix {{x, y}, {-y, x}} + assert(1 == # summands coker m) + -- FIXME: this seems to almost always fail!!! + --assert(2 == # summands coker(m ++ m)) + assert(2 == # summands changeBaseField_2 coker m) + assert(4 == # summands changeBaseField_2 coker(m ++ m)) + end-- + findProjectors(coker(m ++ m), Tries => 100) + factor char generalEndomorphism(coker m ++ coker m) + eigenvalues'' generalEndomorphism(coker m ++ coker m) +/// + /// restart errorDepth=2 From d27b57acb28e4240ed0390308c9149ecda987827 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 1 May 2025 11:59:00 -0400 Subject: [PATCH 84/88] added changeBaseField(Ring, Matrix) --- M2/Macaulay2/packages/DirectSummands.m2 | 27 +++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 09d16f6c56c..b7e0ab0b1bb 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -11,6 +11,8 @@ -- 5. compute a general endomorphism without computing End -- 6. speed up computing eigenvalues over ZZ/p and GF(q) -- 7. once an indecomposable summand is found, call summands(N, M) +-- 8. add option to split over a field extension +-- 9. add Atlantic City, Las Vegas, or Monte Carlo style strategies? --------------------------------------------------------------------------- newPackage( "DirectSummands", @@ -55,6 +57,7 @@ export { "frobeniusPushforward", "frobeniusTwist", "potentialExtension", + "extendGroundField" => "changeBaseField", "changeBaseField" } @@ -140,17 +143,21 @@ GF(Ring) := GaloisField => opts -> GF'Ring -- R ** S to change coefficient ring -- TODO: can you change the ground field but keep the tower structure? QuotientRing ** GaloisField := -PolynomialRing ** GaloisField := (R, L) -> ( +PolynomialRing ** GaloisField := (R, L) -> R.cache#(map, L, R) ??= ( -- TODO: in general we may want to keep part of the ring tower A := first flattenRing(R, CoefficientRing => null); quotient sub(ideal A, L monoid A)) +changeBaseMap = method() +changeBaseMap(Ring, Ring) := (L, S) -> S.cache#(map, L, S) ??= map(quotient sub(ideal S, L monoid S), S) + changeBaseField = method() -- TODO: add more automated methods, e.g. where minors of the presentation factor changeBaseField(ZZ, Module) := changeBaseField(ZZ, CoherentSheaf) := (e, M) -> changeBaseField(GF(char ring M, e), M) changeBaseField(GaloisField, Module) := (L, M) -> M.cache#(symbol changeBaseField, L) ??= ( - S := first flattenRing(ring M, CoefficientRing => null); + S := first flattenRing(ring M, + CoefficientRing => null); K := coefficientRing S; if class K =!= GaloisField then return ( R0 := quotient sub(ideal S, L monoid S); @@ -166,6 +173,19 @@ changeBaseField(GaloisField, Module) := (L, M) -> M.cache#(symbol changeBaseFiel i := map(R, S, i1); directSum apply(cachedSummands M, N -> i ** N)) +changeBaseField(Ring, Module) := (L, M) -> M.cache#(symbol changeBaseField, L) ??= ( + S := first flattenRing(ring M, + CoefficientRing => null); + psi := changeBaseMap(L, S); + directSum apply(cachedSummands M, + N -> coker(psi ** presentation N))) + +changeBaseField(Ring, Matrix) := (L, f) -> f.cache#(symbol changeBaseField, L) ??= ( + S := first flattenRing(ring f, + CoefficientRing => null); + psi := changeBaseMap(L, S); + psi ** f) + -- TODO: come up with a better way to extend ground field of a variety -- TODO: does this also need to be used for frobenius pushforward? sheaf' = (X, M) -> try sheaf(X, M) else ( @@ -537,6 +557,9 @@ directSummands(Module, Module) := List => opts -> (L, M) -> ( MinimalGenerators => false); smartBasis(zdeg, H)); h := catch if isFreeModule L then ( + -- TODO: for the case of line bundle summands, + -- is it faster if we compute all of Hom and check + -- for line bundles of any degree all at once? C := gensHom0(M, L); if numcols C == 0 then return {M}; -- Previous alternative: -- h := for i from 0 to numcols C - 1 do ( From ace561e74d41daa857211d6ad71be0a9ee5d8893 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 1 May 2025 12:01:40 -0400 Subject: [PATCH 85/88] added pushForward(RingMap, CoherentSheaf) --- M2/Macaulay2/packages/DirectSummands.m2 | 1 + .../packages/DirectSummands/frobenius.m2 | 25 ++-- .../packages/DirectSummands/pushforward2.m2 | 111 ++++++++++++++++++ 3 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 M2/Macaulay2/packages/DirectSummands/pushforward2.m2 diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index b7e0ab0b1bb..5a22f583ccf 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -94,6 +94,7 @@ defaultNumTries = p -> ceiling(0.1 + 100 / log p) -- helpers for computing Frobenius pushforwards of modules and sheaves -- TODO: move to PushForward package? +load "./DirectSummands/pushforward2.m2" load "./DirectSummands/frobenius.m2" -- helpers for finding random idempotents of a module for the local case load "./DirectSummands/idempotents.m2" diff --git a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 index 623145b7aab..008f0cb714e 100644 --- a/M2/Macaulay2/packages/DirectSummands/frobenius.m2 +++ b/M2/Macaulay2/packages/DirectSummands/frobenius.m2 @@ -1,4 +1,3 @@ - --needsPackage "PushForward" --needsPackage "Polyhedra" -- for lattice points --needsPackage "Complexes" @@ -11,6 +10,10 @@ myPushForward = (f, M) -> ( -- pushForward(f, M, UseHilbertFunction => false) ) +----------------------------------------------------------------------------- +-* Frobenius pushforwards *- +----------------------------------------------------------------------------- + protect FrobeniusRing protect FrobeniusFormation frobeniusRing = method(TypicalValue => Ring) @@ -74,17 +77,7 @@ frobeniusMap(ZZ, Ring) := (e, R) -> ( map(Re := frobeniusTwist(e, R), frobeniusRing(e, R), apply(gens Re, g -> g^((char R)^e)))) -decomposeFrobeniusPresentation = (e, f) -> ( - p := char ring f; - tardegrees := degrees target f; - srcdegrees := degrees source f; - cube := flatten \ entries \ latticePoints hypercube(degreeLength ring f, 0, p^e - 1); - tarclasses := apply(cube, i -> positions(tardegrees, deg -> deg % p^e == i)); - srcclasses := apply(cube, i -> positions(srcdegrees, deg -> deg % p^e == i)); - -- sorts the degrees of source and column - tarclasses = apply(tarclasses, ell -> ell_(last \ sort \\ reverse \ toList pairs tardegrees_ell)); - srcclasses = apply(srcclasses, ell -> ell_(last \ sort \\ reverse \ toList pairs srcdegrees_ell)); - apply(tarclasses, srcclasses, (tarclass, srcclass) -> submatrix(f, tarclass, srcclass))) +decomposeFrobeniusPresentation = (e, f) -> decomposePushforwardPresentation((char ring f)^e, f) protect FrobeniusPushforward frobeniusPushforward = method() @@ -98,7 +91,7 @@ frobeniusPushforward(ZZ, Module) := (e, M) -> M.cache#(FrobeniusPushforward, e) frobeniusTwist(e, M)); if not isHomogeneous f then coker f else directSum apply(decomposeFrobeniusPresentation(e, f), coker)) --- + frobeniusPushforward(ZZ, Matrix) := (e, f) -> f.cache#(FrobeniusPushforward, e) ??= ( g := myPushForward( frobeniusMap(e, ring f), @@ -125,7 +118,10 @@ frobeniusPushforward(ZZ, SheafMap) := (e, f) -> f.cache#(FrobeniusPushforward, --frobeniusPushforward(ZZ, Complex) := (e, C) -> () -- TODO -frobeniusPushforward(ZZ, SheafOfRings) := (e, N0) -> frobeniusPushforward(e, N0^1) -- TODO: is this cached? +frobeniusPushforward(ZZ, SheafOfRings) := (e, O) -> ( + X := variety O; + X.cache.FrobeniusPushforward ??= new MutableHashTable; + X.cache.FrobeniusPushforward#e ??= frobeniusPushforward(e, O^1)) frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> N.cache#(FrobeniusPushforward, e) ??= if e == 1 then ( R := ring variety N; p := char R; @@ -139,7 +135,6 @@ frobeniusPushforward(ZZ, CoherentSheaf) := (e, N) -> N.cache#(FrobeniusPushforwa sheaf prune coker map(R^tardegs, R^srcdegs, Fmatrix)) else ( frobeniusPushforward(1, frobeniusPushforward(e-1, N))) - protect FrobeniusPullback frobeniusPullback = method() --frobeniusPullback(Thing, ZZ) := (T, e) -> frobeniusPullback(e, T) diff --git a/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 b/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 new file mode 100644 index 00000000000..abf39c89a23 --- /dev/null +++ b/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 @@ -0,0 +1,111 @@ +----------------------------------------------------------------------------- +-* Pushforwards of coherent sheaves *- +----------------------------------------------------------------------------- + +decomposePushforwardPresentation = method() +decomposePushforwardPresentation(List, Matrix) := (d, f) -> ( + if #d == 1 then decomposePushforwardPresentation(d#0, f) + else error "not implemented for multigraded maps") +decomposePushforwardPresentation(ZZ, Matrix) := (d, f) -> ( + tardegrees := degrees target f; + srcdegrees := degrees source f; + cube := flatten \ entries \ latticePoints hypercube(degreeLength ring f, 0, d - 1); + tarclasses := apply(cube, i -> positions(tardegrees, deg -> deg % d == i)); + srcclasses := apply(cube, i -> positions(srcdegrees, deg -> deg % d == i)); + -- sorts the degrees of source and column + tarclasses = apply(tarclasses, ell -> ell_(last \ sort \\ reverse \ toList pairs tardegrees_ell)); + srcclasses = apply(srcclasses, ell -> ell_(last \ sort \\ reverse \ toList pairs srcdegrees_ell)); + apply(tarclasses, srcclasses, (tarclass, srcclass) -> submatrix(f, tarclass, srcclass))) + +pushForward' = (f, M, opts) -> ( + if not isHomogeneous f + or not isHomogeneous M + then return null; + M = cokernel presentation M; + M1 := M / ideal f.matrix; + M2 := subquotient(matrix basis M1, relations M); + cokernel pushNonLinear(opts, f, M2)) + +protect Pushforwards +pushForward(RingMap, SheafOfRings) := opts -> (f, O) -> ( + X := variety O; + X.cache.Pushforwards ??= new MutableHashTable; + X.cache.Pushforwards#f ??= pushForward(f, O^1, opts)) +pushForward(RingMap, CoherentSheaf) := opts -> (f, G) -> ( + G.cache.Pushforwards ??= new MutableHashTable )#f ??= ( + d := degree f(first gens source f); -- FIXME: this should be the degree of the map + if #d == 1 then d = d#0 else error "not implemented for multigraded maps"; + S := source f; + m0 := presentation pushForward'(f, module G, opts); + m1 := first decomposePushforwardPresentation(d, m0); + m2 := sub(m1, S); -- TODO: do better than sub? + (tardegs, srcdegs) := toSequence(-degrees m2 // d); + -- TODO: how long does this take? is it worth caching? + sheaf prune coker map(S^tardegs, S^srcdegs, m2)) + +----------------------------------------------------------------------------- +-- TODO: fix in Core +----------------------------------------------------------------------------- + +ordertab := new HashTable from { + Eliminate => (nR, nS) -> Eliminate nR, + ProductOrder => (nR, nS) -> ProductOrder{nR, nS}, + Lex => (nR, nS) -> Lex, + } + +pushNonLinear = (opts, f, M) -> ( + -- given f: R --> S, and M an S-module, finite over R, + -- returns R-presentation matrix for the pushforward of M + -- written by Mike Stillman and David Eisenbud + (R, S) := (target f, source f); + deglen := degreeLength S; + n1 := numgens R; -- TODO: what if R is a tower? + + monorder := opts.MonomialOrder; + monorder = if ordertab#?monorder then (ordertab#monorder)(numgens R, numgens S) + else error("pushForward: MonomialOrder option expected one of ", + demark_", " \\ toString \ keys ordertab); + + J := graphIdeal(f, MonomialOrder => monorder, VariableBaseName => local X); + G := ring J; + m := presentation M; + xvars := map(G, R, submatrix(vars G, toList(0..n1-1))); + m1 := presentation (cokernel xvars m ** cokernel generators J); + + if opts.UseHilbertFunction and all({f, m}, isHomogeneous) then ( + -- compare with kernel RingMap + hf := poincare cokernel m; + T := degreesRing G; + hf = hf * product(degrees source generators J, d -> 1 - T_d); + -- cache poincare + poincare cokernel m1 = hf); + + mapbackdeg := d -> take(d, -deglen); + -- that choice of degree map was chosen to make the symmetricPower functor homogeneous, but it doesn't have much + -- else to recommend it. + -- we should really be *lifting* the result to S along the natural map S ---> G + S' := newRing(S, Degrees => take(degrees G, n1 - numgens G)); + mapback := map(S', G, map(S'^1, S'^n1, 0) | vars S', DegreeMap => mapbackdeg ); + + -- let's at least check it splits f's degree map: + for i from 0 to numgens S - 1 do ( + e := degree S'_i; + if mapbackdeg f.cache.DegreeMap e =!= e + then error "not implemented yet: unexpected degree map of ring map"); + + g := gb(m1, + StopBeforeComputation => opts.StopBeforeComputation, + DegreeLimit => opts.DegreeLimit, + PairLimit => opts.PairLimit); + -- MES: check if the monomial order restricts to S. If so, then do `` forceGB result '' + mapback selectInSubring(if numgens target f > 0 then 1 else 0, generators g)) + +-- addHook((pushForward, RingMap, Module), Strategy => "DefaultFixed", +-- (opts, f, M) -> ( +-- if not isHomogeneous f +-- or not isHomogeneous M +-- then return null; +-- M = cokernel presentation M; +-- M1 := M / ideal f.matrix; +-- M2 := subquotient(matrix basis M1, relations M); +-- cokernel pushNonLinear(opts, f, M2))) From e12aa09f7ee53e3daf7d931c0171e6c55eb5b005 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 14 May 2025 00:26:24 -0400 Subject: [PATCH 86/88] extracted methods that moved to Isomorphism --- M2/Macaulay2/packages/DirectSummands.m2 | 45 ++----------------------- 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands.m2 b/M2/Macaulay2/packages/DirectSummands.m2 index 5a22f583ccf..3ab4c5d01a3 100644 --- a/M2/Macaulay2/packages/DirectSummands.m2 +++ b/M2/Macaulay2/packages/DirectSummands.m2 @@ -47,9 +47,6 @@ export { "generalEndomorphism", "isomorphismTally", "tallySummands", - "isomorphism", - -- symbols - "Tries", -- frobenius methods "frobeniusMap", "frobeniusRing", @@ -59,6 +56,7 @@ export { "potentialExtension", "extendGroundField" => "changeBaseField", "changeBaseField" + -- symbols } importFrom_Core { @@ -221,43 +219,6 @@ checkRecursionDepth = () -> if recursionDepth() > recursionLimit - 20 then print module Module := identity --- give an isomorphism between two free modules with same degrees --- FIXME: because of https://github.com/Macaulay2/M2/issues/3719, --- this might not give the most "natural" isomorphism -isisofree = o -> (M, N0) -> ( - (d1, d2) := (degrees M, degrees N0); - if #d1 =!= #d2 then return (false, null); - if o.Strict then N := N0 else d2 = degrees( - N = N0 ** (ring N0)^{min d2 - min d1}); - if sort d1 != sort d2 then return (false, null); - p1 := first \ (sortBy last) toList pairs d1; - p2 := first \ (sortBy last) toList pairs d2; - (true, map(M, N0, id_N^p2 // id_M^p1))) - -isiso = lookup(isIsomorphic, Module, Module) -isIsomorphic(Module, Module) := Sequence => o -> (M, N) -> ( - if isFreeModule M and isFreeModule N - then (isisofree o)(M, N) - else (isiso o)(M, N)) - -protect Isomorphisms -isIsomorphic' = method(Options => options isIsomorphic ++ { Tries => null }) -isIsomorphic'(Module, Module) := opts -> (M, N) -> ( - M.cache.Isomorphisms ??= new MutableHashTable; - if M.cache.Isomorphisms#?N then return true; - tries := opts.Tries ?? defaultNumTries char ring M; - opts' := selectKeys(opts, k -> (options isIsomorphic)#?k); - -- TODO: parallelize - for c to tries - 1 do ( - (bool, isom) := isIsomorphic(M, N, opts'); - if bool then ( M.cache.Isomorphisms#N = isom; return true )); - false) - -isomorphism = method(Options => options isIsomorphic ++ { Tries => null }) -isomorphism(Module, Module) := Matrix => o -> (M, N) -> ( - if not isIsomorphic'(M, N, o) then error "modules not isomorphic"; - M.cache.Isomorphisms#N) - -- TODO: speed this up -- TODO: implement isIsomorphic for sheaves -- TODO: add strict option @@ -266,7 +227,7 @@ tallySummands = L -> tally ( L = new MutableList from module \ L; b := new MutableList from #L : true; for i to #L-2 do if b#i then for j from i+1 to #L-1 do if b#j then ( - if isIsomorphic'(L#i, L#j, opts) + if isIsomorphic(L#i, L#j, opts) then ( b#j = false; L#j = L#i )); new List from L) @@ -281,7 +242,7 @@ isomorphismTally List := L -> ( i := j + 1; c := 1; while i < #L do ( - if isIsomorphic'(L#j, L#i, opts) + if isIsomorphic(L#j, L#i, opts) then ( L = drop(L, {i, i}); c = c + 1) From c16365cf0febd3be62d9a232de92b976a81223f6 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 14 May 2025 00:27:07 -0400 Subject: [PATCH 87/88] fixed decomposePushforwardPresentation for degree 1 maps --- M2/Macaulay2/packages/DirectSummands/pushforward2.m2 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 b/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 index abf39c89a23..cb986cac31a 100644 --- a/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 +++ b/M2/Macaulay2/packages/DirectSummands/pushforward2.m2 @@ -7,8 +7,11 @@ decomposePushforwardPresentation(List, Matrix) := (d, f) -> ( if #d == 1 then decomposePushforwardPresentation(d#0, f) else error "not implemented for multigraded maps") decomposePushforwardPresentation(ZZ, Matrix) := (d, f) -> ( + if d <= 0 then error "expected positive degree"; + if d == 1 then return {f}; tardegrees := degrees target f; srcdegrees := degrees source f; + -- TODO: hypercube(n, 0, 0) gives an error, but it should return the origin cube := flatten \ entries \ latticePoints hypercube(degreeLength ring f, 0, d - 1); tarclasses := apply(cube, i -> positions(tardegrees, deg -> deg % d == i)); srcclasses := apply(cube, i -> positions(srcdegrees, deg -> deg % d == i)); @@ -20,7 +23,7 @@ decomposePushforwardPresentation(ZZ, Matrix) := (d, f) -> ( pushForward' = (f, M, opts) -> ( if not isHomogeneous f or not isHomogeneous M - then return null; + then error "expected homogeneous inputs"; M = cokernel presentation M; M1 := M / ideal f.matrix; M2 := subquotient(matrix basis M1, relations M); From e00526a401cd0b6a6b2b9b33732a0b8c83678dc4 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 14 May 2025 00:47:54 -0400 Subject: [PATCH 88/88] updated package for new version of Isomorphism --- M2/Macaulay2/packages/DirectSummands/docs.m2 | 2 +- M2/Macaulay2/packages/DirectSummands/homogeneous.m2 | 2 +- M2/Macaulay2/packages/DirectSummands/idempotents.m2 | 2 +- M2/Macaulay2/packages/DirectSummands/large-tests.m2 | 2 +- M2/Macaulay2/packages/DirectSummands/tests.m2 | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/M2/Macaulay2/packages/DirectSummands/docs.m2 b/M2/Macaulay2/packages/DirectSummands/docs.m2 index a0cd8e3e45b..0ced42fb882 100644 --- a/M2/Macaulay2/packages/DirectSummands/docs.m2 +++ b/M2/Macaulay2/packages/DirectSummands/docs.m2 @@ -58,7 +58,7 @@ Node S = QQ[x,y] M = coker matrix{{x,y},{x,x}} L = summands M - assert first isIsomorphic(M, directSum L) + assert isIsomorphic(M, directSum L) SeeAlso findIdempotents /// diff --git a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 index ce7d6a56f9d..5b2ded09b4c 100644 --- a/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 +++ b/M2/Macaulay2/packages/DirectSummands/homogeneous.m2 @@ -89,7 +89,7 @@ summandsFromProjectors(Module, List) := opts -> (M, ends) -> ( projs = append(projs, inducedMap(image comp, M, comp)); -- assert(0 == intersect apply(ends, ker)); -- assert(0 == intersect apply(injs, image)); - -- assert first isIsomorphic(M, directSum apply(projs, target)); + -- assert isIsomorphic(M, directSum apply(projs, target)); -- this is the splitting (surjection, inclusion) of M to a module -- whose degree zero endomorphisms have already been computed. (pr0, inc0) := opts#"Splitting" ?? (id_M, id_M); diff --git a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 index ce6aa420c63..c96e34bba2c 100644 --- a/M2/Macaulay2/packages/DirectSummands/idempotents.m2 +++ b/M2/Macaulay2/packages/DirectSummands/idempotents.m2 @@ -251,7 +251,7 @@ summandsFromIdempotents(Module, List) := opts -> (M, ends) -> ( projs = append(projs, inducedMap(image comp, M, comp)); -- assert(0 == intersect apply(ends, ker)); -- assert(0 == intersect apply(injs, image)); - -- assert first isIsomorphic(M, directSum apply(projs, target)); + -- assert isIsomorphic(M, directSum apply(projs, target)); -- this is the splitting (surjection, inclusion) of M to a module -- whose degree zero endomorphisms have already been computed. (pr0, inc0) := opts#"Splitting" ?? (id_M, id_M); diff --git a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 index 5bd00d7ff91..299775099ca 100644 --- a/M2/Macaulay2/packages/DirectSummands/large-tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/large-tests.m2 @@ -26,7 +26,7 @@ TEST /// debugLevel = 1 elapsedTime L5 = summands M; -- takes ~30min on Fields' server assert(#L5 == 75); -- could be wrong - assert first isIsomorphic(M, directSum L5) + assert isIsomorphic(M, directSum L5) /// /// diff --git a/M2/Macaulay2/packages/DirectSummands/tests.m2 b/M2/Macaulay2/packages/DirectSummands/tests.m2 index bdeb77759d4..4530508456e 100644 --- a/M2/Macaulay2/packages/DirectSummands/tests.m2 +++ b/M2/Macaulay2/packages/DirectSummands/tests.m2 @@ -139,7 +139,7 @@ TEST /// elapsedTime L = summands M assert(8 == #L) assert all(L, isHomogeneous) - assert first isIsomorphic(M, directSum L) + assert isIsomorphic(M, directSum L) assert all(8, i -> same { M, target M_[i], source M^[i] } and same { L#i, target M^[i], source M_[i] }) --elapsedTime profile summands M; @@ -188,7 +188,7 @@ TEST /// -- testing inhomogeneous examples assert(3 == #summands M) assert(3 == #summandsFromIdempotents M) assert(3 == #summandsFromProjectors M) - assert isIsomorphic'(directSum summands M, M, Tries => 10) + assert isIsomorphic(directSum summands M, M, Tries => 10) -- -- TODO: this is locally zero, but can we diagonalize it? M = coker matrix matrix"1,y,z;y,1,x;z,x,1" @@ -203,7 +203,7 @@ TEST /// -- testing inhomogeneous examples M = coker matrix matrix"x,y,z;y,z,x;z,x,y" assert(2 == #summands M) assert(2 == #summandsFromProjectors M) - assert first isIsomorphic(directSum summands M, M) + assert isIsomorphic(directSum summands M, M) -- TODO: this is locally zero, but can we diagonalize it? M = coker matrix matrix"1,y,z;y,1,x;z,x,1" assert(summands M == {M})