next | previous | forward | backward | up | top | index | toc | Macaulay2 website
ExactCouples :: Encoding diagrams as modules

Encoding diagrams as modules -- building graded modules with specified modules in certain degrees, and with specified action maps

Many algorithms in computer algebra accept as input a finite commuting diagram of modules. This can pose a challenge to programmers, since specifying such a diagram can be unwieldy. For example, to input a commuting cube, a user could specify twelve maps in a list... but... these maps come without any preferred ordering, which makes any convention hard to remember, and moreover, the programmer will be forced to include many compatibility checks in order to supply useful error messages.

This package takes the following approach, which we illustrate in the case of a commuting square (a cube would be similar).

Let R be the base ring, and build a new ring

S = R[f,g,Degrees=>{{1,0},{0,1}}]

A graded S-module is an infinite grid of R-modules connected by maps induced by multiplication by the variables f and g. In particular, it encodes an infinite number of commuting squares! To specify a single commuting square, we restrict attention to the four bidegrees $(0,0)$, $(1,0)$, $(0,1)$, and $(1,1)$. (We could ask that the module vanish away from these degrees, but in practice it is more efficient to just say "we don't care what happens in other degrees".)

Internal and external degrees

Some terminology, since the ring R may itself have some grading. We call this grading "internal" since it happens inside the coefficients. The variables f and g have internal degree zero, even though their external degrees are $(1,0)$ and $(0,1)$ respectively. When building a ring, the Degrees option specifies external degrees. Suppose that R has degree length three so that deg(1_R) = $(0,0,0)$, for example. We have then

deg(f) = {1,0,0,0,0}
deg(g) = {0,1,0,0,0}

The first two coordinates are the external degree, and the last three are internal. To obtain this information about a ring, you can use internalDegreeIndices and externalDegreeIndices. For example:

i1 : R = QQ[x,y,Degrees=>{{1,2,3},{4,5,6}}]

o1 = R

o1 : PolynomialRing
i2 : S = R[f,g,Degrees=>{{1,0},{0,1}}]

o2 = S

o2 : PolynomialRing
i3 : internal = internalDegreeIndices S

o3 = {2, 3, 4}

o3 : List
i4 : external = externalDegreeIndices S

o4 = {0, 1}

o4 : List

Later, given a multidegree, it is easy to find the internal and external degrees

i5 : deg = {2,3,4,5,6}

o5 = {2, 3, 4, 5, 6}

o5 : List
i6 : deg_internal

o6 = {4, 5, 6}

o6 : List
i7 : deg_external

o7 = {2, 3}

o7 : List

We generally wish to accommodate a wide range of coefficient rings R, which in particular means we accommodate any number of internal degrees, including towers of rings where the coefficients themselves have coefficients, etc. In such cases, all degrees that are not external count as internal.

Example: encoding a commuting square

In this example, we take R=QQ[z].

i8 : R = QQ[z]

o8 = R

o8 : PolynomialRing
i9 : S = R[f,g,Degrees=>{{1,0},{0,1}}]

o9 = S

o9 : PolynomialRing

We wish to encode a commuting square with the general layout

|   A - -g- -> B
|   |          |
|   f          f
|   |          |
|   v          v
|   C - -g- -> D

The downward maps will be encoded by the action of f, and the rightward maps by g. Here is a particular example.

|  cokernel {3} | z13 | - z^2 -> cokernel {1} | z15 |
|           |                             |
|           z                             z
|           |                             |
|           v                             v
|  cokernel {2} | z6 |  - x^2 -> cokernel {0} | z8 |

We aim to encode this commuting square as an S-module. The external degree will give the position in the commuting square, and the internal degree will record the R-degree. Then, in a multidegree $(r,c,d)$, r gives the row (0 or 1 in our case), c gives the column (also 0 or 1), and d gives the internal degree (usual grading on R=QQ[z]).

Declaring generators

We begin the process in the upper left corner, with the module

|  A = cokernel {3} | z13 |

Let us name the generator of this module $a$. Since we are in the upper left corner, the external degree is $(0,0)$. And since the generator appears in R-degree 3, the internal degree is $(3)$. In total, then, the degree of $a$ is $(0,0,3)$.

Similarly, let $b$, $c$, and $d$ be generators with external degrees $(0,1)$, $(1,0)$, and $(1,1)$, and with internal degrees 1, 2, and 0. This information can be given to M2 using the function declareGenerators.

i10 : declareGenerators(S, {a => {0,0,3}, b => {0,1,1}, c => {1,0,2}, d => {1,1,0}})

       4
o10 = S

o10 : S-module, free, degrees {{1, 0, 2}, {1, 1, 0}, {0, 0, 3}, {0, 1, 1}}

We must now impose relations on these four generators so that the four modules match our intent, and same for the maps.

The first four relations come from the original descriptions of A, B, C, and D:

z^13*a
z^15*b
 z^6*c
 z^8*d

The next four relations come from the descriptions of the four maps:

g*a - z^2*b
g*c - z^2*d
f*a  -  z*c
f*b  -  z*d

The first of these, for example, forces ga = z^2b, and this is what we want since g is supposed to act by the horizontal map, which sends the generator for A to z^2 times the generator for B. With these four relations, the action of f and g is determined in the four degrees of interest.

i11 : M = cospan(z^13*a, z^15*b, z^6*c, z^8*d,
                 g*a - z^2*b, g*c - z^2*d, f*a - z*c, f*b - z*d)

o11 = cokernel {1, 0, 2} | 0   0   z6 0  0   g   -z 0  |
               {1, 1, 0} | 0   0   0  z8 0   -z2 0  -z |
               {0, 0, 3} | z13 0   0  0  g   0   f  0  |
               {0, 1, 1} | 0   z15 0  0  -z2 0   0  f  |

                             4
o11 : S-module, quotient of S

The module M now contains a complete description of the commuting square.

Evaluating a module at various external degrees

In order to check that M is correct, we can use the function evaluateInDegree to make sure the proper R-module lives in each of the four external degrees.

i12 : netList apply(2, r -> apply(2, c -> prune evaluateInDegree({r,c}, M)))

      +--------------------+--------------------+
o12 = |cokernel {3} | z13 ||cokernel {1} | z15 ||
      +--------------------+--------------------+
      |cokernel {2} | z6 | |cokernel | z8 |     |
      +--------------------+--------------------+

Evaluating a module at a structure map

In order to check the action of f and g, we use another form of evaluateInDegree.

i13 : prune structureMap({0,0},,g,M)

o13 = {1} | z2 |

o13 : Matrix
i14 : prune structureMap({1,0},,g,M)

o14 = | z2 |

o14 : Matrix
i15 : prune structureMap({0,0},,f,M)

o15 = {2} | z |

o15 : Matrix
i16 : prune structureMap({0,1},,f,M)

o16 = | z |

o16 : Matrix

Example calculation: computing kernels of cokernels

In order to perform calculations on a diagram encoded as above, one main strategy involves changing which variables are internal and which are external. In this example, we take the cokernel of the downward maps, and then take the kernel of the induced rightward map, resulting in a single R-module

ker( coker(A - -g- -> B) - - -> coker(C - -g- -> D) )

We want to take the cokernel of the g action map in a way that retains the action of f. So build a ring where f is an internal variable, and only g is external:

i17 : S' = R[f][g]

o17 = S'

o17 : PolynomialRing
i18 : phi = map(S',S,DegreeMap=>deg->deg_{1,0,2})

o18 = map(S',S,{f, g, z})

o18 : RingMap S' <--- S
i19 : isHomogeneous phi

o19 = true
i20 : M' = phi ** M

o20 = cokernel {0, 1, 2} | 0   0   z6 0  0   g   -z 0  |
               {1, 1, 0} | 0   0   0  z8 0   -z2 0  -z |
               {0, 0, 3} | z13 0   0  0  g   0   f  0  |
               {1, 0, 1} | 0   z15 0  0  -z2 0   0  f  |

                               4
o20 : S'-module, quotient of S'

Since only g is external, we may evaluate to obtain a map of R[f]-modules, and then take its cokernel:

i21 : cokerg = coker structureMap({0},,g,M')

o21 = cokernel {1, 2} | 1 0 0  1   -z 0   z6 0   |
               {1, 0} | 0 0 -z -z2 0  0   0  0   |
               {0, 3} | 0 1 0  0   f  1   0  z13 |
               {0, 1} | 0 0 f  0   0  -z2 0  0   |

                                     4
o21 : R[f]-module, quotient of (R[f])

Now g is gone, and f is an external variable. We may evaluate to obtain the map on cokernels, and take the kernel:

i22 : ker structureMap({0},,f,cokerg)

o22 = subquotient ({3} | 0 |, {3} | 1 0  |)
                   {1} | z |  {1} | 0 z2 |

                                2
o22 : R-module, subquotient of R

Switching the ring to S' is called "restacking", and it is easier to do when variables are adjoined in smaller batches. It would have been better to use R[f][g] in the first place. For more information, see restackRing and restackModule.

See also