frac accepts a ring that is not a domain, giving a fraction field where multiplication is not associative
- Dominant language
- Macaulay2
- Stars
- 435
- Forks
- 297
- Avg merge
- 4d 20h
- Merged PRs (30d)
- 11
Description
> **Written by Claude** (Claude Opus 5, via Claude Code), not by @d-torrance, whose account posted it -- please weigh it accordingly.
`frac` documents its argument as an integral domain:
```
i1 : help frac
* Inputs:
* R, a "ring", an integral domain
```
It does not check, and over a ring with nilpotents the result is not a ring in which multiplication is associative:
```m2
i1 : R = ZZ[t]/(t^4);
i2 : K = frac R;
i3 : u = sub(t, K);
i4 : u == 0
o4 = false
i5 : u^4 == 0
o5 = true
i6 : u * (1/u)
o6 = 1
i7 : (u^4) * (1/u)
o7 = 0
i8 : u^3 * (u * (1/u))
3
o8 = u
```
`o7` and `o8` are the same product, `u * u * u * u * (1/u)`, grouped two ways. Since `u^4` is zero and `u^3` is not, no consistent value exists — `1/u` should not have been constructed. The same thing happens over `QQ[x]/x^2` and, since #3177, over towers such as `frac(QQ[x]/x^2[y])`.
The guard that decides this never asks whether any quotient in the chain is prime:
https://github.com/Macaulay2/M2/blob/68351e766d3e7991cd8bc0aa3ceecad1d49e65b8/M2/Macaulay2/m2/enginering.m2#L335-L343
It recurses `QuotientRing → ambient` and `PolynomialRing → coefficientRing` and tests only what is at the bottom, so every quotient passes regardless of its ideal. That is what `frac EngineRing` consults before building the ring:
https://github.com/Macaulay2/M2/blob/68351e766d3e7991cd8bc0aa3ceecad1d49e65b8/M2/Macaulay2/m2/enginering.m2#L345-L351
Two notes on scope, so this is not mistaken for a regression:
- The single-quotient case is old. Before #3177 the check looked only one level down at `coefficientRing`, which for `QQ[a]/(a^2)` is `QQ` — so `frac(QQ[a]/(a^2))` was accepted then too. #3177 widened the check to walk the whole chain, which is what it set out to do, and towers came along with it.
- Where the base genuinely is a domain, the tower case works correctly. Over `frac((QQ[a]/(a^2-2))[y])`: `a^2 == 2`, `y*(1/y) == 1`, `(1/a)*a == 1`, `(y+a)/(y+a) == 1`.
A cheap check would be to require, for each quotient in the chain, that its ideal be prime — or, where that is too expensive to decide, to refuse rather than accept. `isPrime` on the defining ideal already answers it for the small cases above.
For the record, this is the live core of a note in the pre-GitHub `bugs/` tree, `bugs/mike/0-frac-bug` (being triaged in #36), which opens "Notice the nilpotent denominator in o5 below" over this very ring. That file's own transcript no longer reproduces — its `product l` is now `1` rather than a fraction with denominator `t` — so the specific symptom was fixed at some point while the cause was not.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in M2/Macaulay2/m2/enginering.m2 at the quotient-chain guard around lines 335–343 and the frac EngineRing check around lines 345–351. Trace how quotient ideals are handled, then use the QQ[x]/(x^2) and ZZ[t]/(t^4) examples from the issue to verify that invalid fraction rings are refused without changing valid domain towers.
Written by the indexing model from the issue text.
Assessment
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100