Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LeftIdeal as a Module #3032

Draft
wants to merge 9 commits into
base: development
Choose a base branch
from
Draft

Conversation

mahrud
Copy link
Member

@mahrud mahrud commented Dec 7, 2023

This is an alternative to #2912 and #3020 which implements the following:

  • adds LeftIdeal as a type of Module
  • forces the constructor ideal to return a LeftIdeal if the ring is a Weyl algebra
  • implements a handful of methods of Ideal which are not defined for Module
  • reimplements a handful of methods that are different from Ideal

I mostly wanted to see if this was feasible, but it was pretty straightforward (thanks to Anton and Mike's fixes to Dmodules, which I more or less copied), so I figured I'd turn it into a PR to present this option.

Pros:

  • Ideal is intact

Cons:

  • some duplication because LeftIdeal and Ideal are now unrelated

@mahrud mahrud marked this pull request as draft December 7, 2023 10:31
@mahrud
Copy link
Member Author

mahrud commented Dec 7, 2023

@antonleykin @mikestillman this is now passing all tests, and by virtue of inheriting from Module, I believe everything else should work out of the box.

Incidentally, I think it would also make a lot of sense to have Ideal inherit from Module. The only real difference is between HashTable currently and MutableHashTable if we make this change, which would only affect hash-related behavior.

Let me know what you think!

@DanGrayson
Copy link
Member

Having Ideal inherit from Module doesn't seem appropriate, because an instance of Module is a right module and and instance of Ideal is to be a 2-sided ideal. At least as proposed by Anton.

@mahrud
Copy link
Member Author

mahrud commented Dec 7, 2023

Why is an instance of Module a right module necessarily?

@DanGrayson
Copy link
Member

Why is an instance of Module a right module necessarily?

Because when representing a map between free modules as a matrix, the matrix acts on the left side of the vector.

@mahrud
Copy link
Member Author

mahrud commented Dec 8, 2023

This sounds like programming detail that can be fixed by introducing covectors. Regardless, this is irrelevant to this PR.

@DanGrayson
Copy link
Member

Well, no, it's an argument for introducing LeftModule and RightModule, so that LeftIdeal can inherit from LeftModule.

@mahrud
Copy link
Member Author

mahrud commented Dec 8, 2023

This PR isn't introducing LeftModule or RightModule, and there are no maps between LeftIdeals, so there can be no confusion here.

@DanGrayson
Copy link
Member

But if LeftIdeal inherits from Module, as is the case in your PrR, then when I and J are two left ideals, Hom(I,J) will try to do something, because they will both be modules.

@mahrud
Copy link
Member Author

mahrud commented Dec 8, 2023

Thanks, I'll add Hom methods for LeftIdeal to give a "not yet implemented" error.

@DanGrayson
Copy link
Member

Or better, just don't make a left ideal a type of module.

@mahrud
Copy link
Member Author

mahrud commented Dec 9, 2023

All ideals are modules.

@DanGrayson
Copy link
Member

Then Hom(I,J) should work.

@mahrud
Copy link
Member Author

mahrud commented Dec 9, 2023

Do you expect me to implement everything in one PR? Later, when left and right modules are in place, you can change one line and have LeftIdeal inherit from LeftModule instead, with Him(I, J) working as expected.

@DanGrayson
Copy link
Member

You're the one who said "All ideals are modules."

@pzinn
Copy link
Contributor

pzinn commented Dec 9, 2023

Why is an instance of Module a right module necessarily?

Because when representing a map between free modules as a matrix, the matrix acts on the left side of the vector.

not related to this PR but:
I'm confused about this. I know this has been discussed many times already, but multiplication is on the right, not on the left? as in

i1 : needsPackage "AssociativeAlgebras"

o1 = AssociativeAlgebras

o1 : Package

i2 : R=QQ<|a,b|>

o2 = R

o2 : FreeAlgebra

i3 : a*b

o3 = a*b

o3 : R

i4 : matrix{{a}}*matrix{{b}}

o4 = | ba |

             1      1
o4 : Matrix R  <-- R

@d-torrance
Copy link
Member

I'm torn about whether Ideal should inherit from Module (or any of the various Left/Right iterations).

Mathematically, yes, an ideal of $R$ is an $R$-module. So it makes perfect sense.

But in Macaulay2, certain methods have different behaviors when passed an Ideal object v. a Module object. For example, res I gives us the resolution of $R/I$ as an $R$-module and not $I$. So we'd violate the Liskov substitution principle if Ideal inherited from Module.

@mahrud
Copy link
Member Author

mahrud commented Dec 9, 2023

For example, res I gives us the resolution of $R/I$ as an $R$-module and not $I$. So we'd violate the Liskov substitution principle if Ideal inherited from Module.

I think situations like this are rare enough that they can be considered exceptions forced by mathematical conventions.

Also, I'm not necessarily suggesting that everything under the sun that has a module structure should inherit from modules, but for ideals in particular, almost every method turns the input into a module and sometimes the output back into an ideal.

@mikestillman
Copy link
Member

I have a few comments here.

Why is an instance of Module a right module necessarily?

Because when representing a map between free modules as a matrix, the matrix acts on the left side of the vector.

This is not quite correct I think, as we have changed multiplication of matrices to use the opposite side. We did this precisely so people could have left modules (e.g. people working with D-modules are pretty used to thinking in these terms). See @pzinn 's comments above.

Also

Then Hom(I,J) should work.

Why is this? In general, Hom only returns a result over the center of the ring, if the modules do not have extra bi-module structure?

I'm torn about whether Ideal should inherit from Module (or any of the various Left/Right iterations).

Mathematically, yes, an ideal of R is an R-module. So it makes perfect sense.

But in Macaulay2, certain methods have different behaviors when passed an Ideal object v. a Module object. For example, res I gives us the resolution of R/I as an R-module and not I. So we'd violate the Liskov substitution principle if Ideal inherited from Module.

In commutative algebra it is fairly confusing to have some operations on ideals work as if for the corresponding quotient module (e.g. depth, free resolution even, etc). But the difference is so ingrained it is hard to change these notions. And from an efficiency view, these are almost always better than treating an ideal as a module directly. I think I am against making an ideal be a module directly (although I could be convinced perhaps).

@mahrud Are there really alot of cases where Ideal's are considered as Module's directly? I haven't gone to look, but I would think it is fairly small (Hom, tensor are the ones I think of).

One reason I am against it is more pedagogical: I don't want to make people learning, e.g. undergraduates in a first class, need to know about these things completely. Ideals are just simpler than modules (I know that they will still see free modules, etc., and I used to think that this was not a good argument. Until I tried teaching younger undergraduates using Macaulay2).

(Here is another thing we don't want to do: just make all modules into complexes? )

@mahrud
Copy link
Member Author

mahrud commented Dec 10, 2023

Like I said, I'm not suggesting turning everything to inherit from the most general type.

Re: pedagogy, I also didn't learn what a Groebner basis of an ideal is by first learning what a Groebner basis of a module is, but here we are:

i1 : code(gb, Ideal)

o1 = -- code for method: gb(Ideal)
     ../linuxbrew/.linuxbrew/share/Macaulay2/Core/gb.m2:307:35-307:57: --source code:
     gb Ideal  := GroebnerBasis => opts -> I -> gb (module I, opts)

Re: efficiency, I think operations for ideals and submodules of R^1 should be identical in efficiency, since they are mathematically the same, no? I don't see why having them also be programatically the same would slow things down.

The biggest reason I'm advocating for LeftIdeals inheriting from Modules (and, eventually, perhaps all Ideals, though that's not the point of this PR) is that there will be much less code duplication, therefore the code will be simpler to read and easier to maintain, because you only need to explicitly implement the methods where LeftIdeals are special, not every method.

@pzinn
Copy link
Contributor

pzinn commented Jan 23, 2024

wrong thread sry

@mahrud
Copy link
Member Author

mahrud commented Jan 23, 2024

@antonleykin I couldn't attend the meeting because of the MSRI workshop, could you summarize your objections to this approach? I don't think it's possible to switch back without significant confusion in the future.

@antonleykin
Copy link
Contributor

@antonleykin I couldn't attend the meeting because of the MSRI workshop, could you summarize your objections to this approach? I don't think it's possible to switch back without significant confusion in the future.

We haven't discussed this PR in any detail, but my personal opinion is that one should lay out a strategic vision of what one would do after this PR. Creating LeftIdeal as a type derived from the de-facto right Module (seems to be hacky) but does achieve a local goal. What is your global vision?

Please post to zulipchat in "M2internals:Noncommutativity".

@mahrud
Copy link
Member Author

mahrud commented Jan 25, 2024

Maybe I don't understand what you mean by a "strategic vision". The vision is the same as yours, only the implementation is different.

LeftIdeal being derived from Module is a temporary solution until we have LeftModule and RightModule types that are truly differentiated. Currently I have:

LeftIdeal = new Type of Module -- or LeftModule, or ImmutableType?

Which can be simply changed when we have that type. Like I said above, given the portion of func(Ideal) methods that simply call func(module I) I think this is much less work than your solution.

(I'll elaborate in zulip, but wanted to give a quick answer here)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants