Macaulay2 / M2

The primary source code repository for Macaulay2, a system for computing in commutative algebra, algebraic geometry and related fields.
https://macaulay2.com
333 stars 228 forks source link

Liftability of modules #2103

Open mahrud opened 3 years ago

mahrud commented 3 years ago

What's the procedure for determining whether objects from one ring can be lifted to another ring by Macaulay2? The documentation for lift and liftable seem to be lacking. Here is an example:

i1 : S = kk[x,y,a,b,c,d];

i2 : f = ideal(x * a + y * b, x * c + y * d)

o2 = ideal (x*a + y*b, x*c + y*d)

o2 : Ideal of S

i3 : R = S/f

o3 = R

o3 : QuotientRing

i4 : lift(R^2, S)

      2
o4 = S

o4 : S-module, free

i5 : lift(S^2, R)
stdio:5:1:(3): error: no method found for applying lift to:
                    2
     argument 1 :  S  (of class Module)
     argument 2 :  S
     argument 3 :  R

i6 : liftable(R^2, S)
stdio:6:1:(3): error: no method found for applying liftable to:
                    2
     argument 1 :  R  (of class Module)
     argument 2 :  S

i7 : liftable(S^2, R)
stdio:7:1:(3): error: no method found for applying liftable to:
                    2
     argument 1 :  S  (of class Module)
     argument 2 :  R

i8 : liftable(1_R, S)

o8 = true

i9 : liftable(1_S, R)
stdio:9:1:(3): error: no method found for applying lift to:
     argument 1 :  1 (of class S)
     argument 2 :  R

i10 : promote(R^2, S )
stdio:10:1:(3): error: no method found for applying promote to:
                    2
     argument 1 :  R  (of class Module)
     argument 2 :  R
     argument 3 :  S

i11 : promote(S^2, R)

       2
o11 = R

o11 : R-module, free

i12 : promote(1_S, R)

o12 = 1

o12 : R

i13 : promote(1_R, S)
stdio:13:1:(3): error: no method found for applying promote to:
     argument 1 :  1 (of class R)
     argument 2 :  S

cc: @jchen419 this seems to break the pushforward routine we wrote a few months back.

DanGrayson commented 3 years ago

When lifting is possible, new method functions are added to lift and to liftable. So the criterion is whether a method for doing it is installed.

mahrud commented 3 years ago

Why have liftable then?

Is there a method for automatically doing either lift or promote depending on the situation?

DanGrayson commented 3 years ago

Because you might be trying to "lift" a ring element to a subring, which is not always possible, but might be possible.

There is no method that does either lift or promote.

i1 : liftable(2/3,ZZ)

o1 = false

i2 : liftable(4/2,ZZ)

o2 = true
mahrud commented 3 years ago

It seems like very poor design:

i1 : liftable(2, QQ)
stdio:1:1:(3): error: no method found for applying lift to:
     argument 1 :  2 (of class ZZ)
     argument 2 :  QQ

Do you expect programmers to check whether a method is installed for two rings in the middle of a mathematical algorithm?

DanGrayson commented 3 years ago

No, I don't expect them to check whether a method is installed. I expect them to know that there is no ring homomorphism from QQ to ZZ.

mahrud commented 3 years ago

I expect them to know that there is no ring homomorphism from QQ to ZZ.

That's a useless answer. If you're given a R-module M and some element s in another ring S, how do you implement an algorithm to return s*M, or how do you check whether this is mathematically possible? Do you promote s to R or lift s to R? It doesn't seem mathematical to have to check whether a method is installed for it, so if that's your intention I think this is not a good design.

@mikestillman what would you do in this scenario?

mikestillman commented 3 years ago

The idea of lift and promote is that when there is a natural ring map A --> B. (e.g. ZZ --> QQ, kk --> kk[x,y,z]/I, etc), and a is in A and b is in B, then lift(b, A) will lift b to an element of A that maps to b, and promote(a, B) will give the image under this natural map. In order to compute ab, one cannot lift the result to A, as that might not make sense. Instead, one does promote(a,B) b. promote will always work, at least for what we (as implementors) thought was a natural map: from coefficient ring to polynomial rings, from polynomial rings to quotients, or fraction fields, etc (and including composites of these). liftable returns true if it is possible to lift the element.

Perhaps the names could have been chosen better, but that is what we chose. Also, if the documentation doesn't make this clear, we should fix that!

The idea is that this respects the mathematics. If there is a natural map for which it is not installed, that is a bug, not (in my opinion) a bad design.

However, we only consider natural maps, not maps from one ring to another that e.g. have the same names of variables. We also don't automatically construct all such, e.g. I think that if A = ZZ[x], B = ZZ/5[x], I don't think that promote(1_A, B) is defined. It would be nice to add this, but when we create the lift and promote routines (during construction of the ring), we don't know about the other one...

@mahrud Does this answer your question?

mikestillman commented 3 years ago

Actually, looking at your question again, perhaps that doesn't answer it. In general, in order for aM to make sense, and a is in some ring A, then M must be an A-module. If instead, M is a B-module, one way to get the A-module structure is by the promote mechanism, if there is an obvious A-module structure (obvious is up to interpretation, and implementation). Another is to use a ring map f : A --> B, where M is a B-module. In this case, you need to use f(a) M.

DanGrayson commented 3 years ago

That's right. And if f : A -> B is a ring homomorphism, M is an A-module and b is an element of B, it doesn't make sense to try lifting b to A for the purpose of evaluating b*M, because M need not be a module over the image of f.

jchen419 commented 3 years ago

I do think it is a bit strange that liftable would give an error: intuitively I would expect liftable to be "safe", and would want to use it in an attempt to prevent errors. Why wouldn't liftable simply return false if no suitable lift is found?

@mahrud: for purposes of pushForward, I was always thinking of lifting from a QuotientRing to an ambient PolynomialRing. Do you have an example where this breaks?

mikestillman commented 3 years ago

I agree with @jchen419 : I think that liftable should return a boolean. In fact, I'm not sure I realized it didn't do that...

DanGrayson commented 3 years ago

It does:

i19 : liftable(3/2,ZZ)

o19 = false

The error in this case is explained by the rings being inappropriate -- lifting would never be possible:

i20 : liftable(3,QQ)
stdio:20:1:(3): error: no method found for applying lift to:
     argument 1 :  3 (of class ZZ)
     argument 2 :  QQ
mahrud commented 3 years ago

Thanks, Mike. I definitely did not get this intuition about how to use lift and liftable from their respective documentation nodes.

I do think it is a bit strange that liftable would give an error: intuitively I would expect liftable to be "safe", and would want to use it in an attempt to prevent errors. Why wouldn't liftable simply return false if no suitable lift is found?

This is my point exactly. I think this makes programming unsafe in some situations.

A safe alternative (in my opinion), would be to have a function liftable(Ring, Ring) that always returns a boolean and never an error, such that if liftable(A, B) is true then:

Notice that checking member(A, B.baseRings) doesn't do all that I want. The benefit of my suggestion is that it allows users to install a method lift(S/(f), S/(f, g)) even when neither is a base ring of the other, and be able to use lift and liftable without risking an error.

Note that methods(lift, ZZ, QQ) and methods(lift, QQ, ZZ) return the same thing, so that can't be used to check if lift and promote are defined between them either. The only way I know of is using lookup, which is too hard for average mathematician.

@mahrud: for purposes of pushForward, I was always thinking of lifting from a QuotientRing to an ambient PolynomialRing. Do you have an example where this breaks?

Yes, this is the situation in almost all examples from CompleteIntersectionResolutions, where in matrixFactorization there's a call to push forward across a map S/(f, g) <--- S/(f). Mathematically, the target is a quotient of the source, but not from the perspective of lift.

Basically, when presentation ring of the source and target match and that the presentation of the source is a summand of the presentation of the target, then I think lift should work even if one ring isn't a base ring of the other.

mahrud commented 3 years ago

@mikestillman do you have any thoughts on this part?

Basically, when presentation ring of the A and B match and that the presentation of the A is a summand of the presentation of the B, then I think lift should work even if one ring isn't a base ring of the other.

Here is an example:

i1 : S = kk[x,y,z,w];

i2 : A = S/(z^2-y*w);

i3 : use S;

i4 : B = S/(z^2-y*w, y*z-x*w);

i5 : ring presentation A === ring presentation B

o5 = true

i6 : presentation A % presentation B == 0

o6 = true

It's mathematically correct to lift in this case, no?

In particular, isn't this incorrect?

i11 : isQuotientOf(A, B)

o11 = false

I understand the difference between math and programming, but this seems to be a simple improvement. (Also, isQuotientOf should have a caveat).