Suppose that I is the image of a free module FI in a quotient module G, and J is the image of the free module FJ in G.
Available strategies for the computation can be listed using the function hooks:
i1 : hooks methods(quotient, Ideal, Ideal)
o1 = {0 => (quotient, Ideal, Ideal, Strategy => Quotient)}
{1 => (quotient, Ideal, Ideal, Strategy => Iterate) }
{2 => (quotient, Ideal, Ideal, Strategy => Monomial)}
o1 : NumberedVerticalList
|
The strategy Quotient computes the first components of the syzygies of the map $R\oplus(FJ^\vee\otimes FI) \to FJ^\vee \otimes G$. The Macaulay2 code for each strategy can be viewed using the function code:
i2 : code(quotient, Ideal, Ideal, Strategy => Quotient)
o2 = -- code for method: quotient(Ideal,Ideal)
/usr/share/Macaulay2/Saturation.m2:222:30-233:23: --source code:
Quotient => (opts, I, J) -> (
R := ring I;
mR := transpose generators J ** (R / I);
-- if J is a single element, this is the same as
-- computing syz gb(matrix{{f}} | generators I, ...)
g := syz gb(mR, opts,
Strategy => LongPolynomial,
Syzygies => true,
SyzygyRows => 1);
-- The degrees of g are not correct, so we fix that here:
-- g = map(R^1, null, g);
lift(ideal g, R)),
|
If Strategy => Iterate then quotient first computes the quotient I1 by the first generator of J. It then checks whether this quotient already annihilates the second generator of J mod I. If so, it goes on to the third generator; else it intersects I1 with the quotient of I by the second generator to produce a new I1. It then iterates this process, working through the generators one at a time.
To use Strategy=>Linear the argument J must be a principal ideal, generated by a linear form. A change of variables is made so that this linear form becomes the last variable. Then a reverse lex Gröbner basis is used, and the quotient of the initial ideal by the last variable is computed combinatorially. This set of monomial is then lifted back to a set of generators for the quotient.
The following examples show timings for the different strategies. Strategy => Iterate is sometimes faster for ideals with a small number of generators:
i3 : n = 6 o3 = 6 |
i4 : S = ZZ/101[vars(0..n-1)]; |
i5 : I = monomialCurveIdeal(S, 1..n-1); o5 : Ideal of S |
i6 : J = ideal(map(S^1, S^n, (p, q) -> S_q^5)); o6 : Ideal of S |
i7 : time quotient(I^3, J^2, Strategy => Iterate);
-- used 0.558153 seconds
o7 : Ideal of S
|
i8 : time quotient(I^3, J^2, Strategy => Quotient);
-- used 0.784608 seconds
o8 : Ideal of S
|
Strategy => Quotient is faster in other cases:
i9 : S = ZZ/101[vars(0..4)]; |
i10 : I = ideal vars S; o10 : Ideal of S |
i11 : time quotient(I^5, I^3, Strategy => Iterate);
-- used 0.0546585 seconds
o11 : Ideal of S
|
i12 : time quotient(I^5, I^3, Strategy => Quotient);
-- used 0.00829957 seconds
o12 : Ideal of S
|
For further information see for example Exercise 15.41 in Eisenbud's Commutative Algebra with a View Towards Algebraic Geometry.