Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions M2/Macaulay2/e/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ set(SRCLIST
basic-rings/aring-ZZp-flint
basic-rings/aring-ZZp
basic-rings/aring
basic-rings/ring-structure
rings/frac
rings/GF
rings/localring
Expand Down
1 change: 1 addition & 0 deletions M2/Macaulay2/e/Makefile.files.in
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ INTERFACE = \
basic-mutable-matrices/lapack \
basic-mutable-matrices/mat \
basic-rings/aring \
basic-rings/ring-structure \
basic-rings/aring-CC \
basic-rings/aring-CCC \
basic-rings/aring-CCi \
Expand Down
3 changes: 3 additions & 0 deletions M2/Macaulay2/e/basic-rings/aring-glue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "basic-rings/aring.hpp"
#include "basic-rings/aring-translate.hpp"
#include "basic-rings/ring-structure.hpp"
#include "rings/ring.hpp"

#include "mutable-matrices/mutablemat.hpp"
Expand Down Expand Up @@ -620,6 +621,7 @@ bool ConcreteRing<RingType>::promote(const Ring *R,
resultS = copy(fR);
return true;
}
if (!M2::hasCanonicalMap(R, S)) return false;
switch (R->ringID())
{
case M2::ring_ZZp:
Expand Down Expand Up @@ -771,6 +773,7 @@ bool ConcreteRing<RingType>::lift(const Ring *R,
// MES:TODO!! WRITE ME
return false;
}
if (!M2::hasCanonicalMap(R, S)) return false;
switch (R->ringID())
{
case M2::ring_ZZp:
Expand Down
1 change: 1 addition & 0 deletions M2/Macaulay2/e/basic-rings/aring-m2-GF.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class ARingGFM2 : public SimpleARing<ARingGFM2>
ARingGFM2(const PolynomialRing &R, const ring_elem a);

GFElement characteristic() const { return mGF.characteristic(); }
GFElement dimension() const { return mGF.dimension(); }
void text_out(buffer &o) const;

const PolynomialRing &originalRing() const { return mGF.ring(); }
Expand Down
116 changes: 116 additions & 0 deletions M2/Macaulay2/e/basic-rings/ring-structure.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "basic-rings/ring-structure.hpp"
#include "rings/ring.hpp"
#include "basic-rings/aring-glue.hpp"

namespace M2 {

namespace {

/// Direct edges in the canonical-map DAG.
/// directEdge(a, b) iff there is an atomic canonical map a → b.
/// Edges: ZZ→QQ, ZZ→PrimeField, QQ→RealField, RealField→ComplexField,
/// RealField→RealInterval, ComplexField→ComplexInterval,
/// RealInterval→ComplexInterval, PrimeField→FiniteField.
bool directEdge(RingStructure a, RingStructure b)
{
using S = RingStructure;
return (a == S::ZZ && (b == S::QQ || b == S::PrimeField)) ||
(a == S::QQ && b == S::RealField) ||
(a == S::RealField && (b == S::ComplexField || b == S::RealInterval)) ||
(a == S::ComplexField && b == S::ComplexInterval) ||
(a == S::RealInterval && b == S::ComplexInterval) ||
(a == S::PrimeField && b == S::FiniteField);
}

constexpr int numRingStructures = 8; // excludes Unknown
constexpr RingStructure allStructures[numRingStructures] = {
RingStructure::ZZ, RingStructure::QQ,
RingStructure::RealField, RingStructure::ComplexField,
RingStructure::RealInterval, RingStructure::ComplexInterval,
RingStructure::PrimeField, RingStructure::FiniteField
};

// Returns n for GF(p^n) — the dimension as a vector space over GF(p).
// Only meaningful when structureOf(R->ringID()) == FiniteField.
long gfDimension(const Ring* R)
{
switch (R->ringID()) {
case ring_GFFlintZech:
return static_cast<const ConcreteRing<ARingGFFlint>*>(R)->ring().dimension();
case ring_GFFlintBig:
return static_cast<const ConcreteRing<ARingGFFlintBig>*>(R)->ring().dimension();
case ring_GFM2:
return static_cast<long>(
static_cast<const ConcreteRing<ARingGFM2>*>(R)->ring().dimension());
default:
return 0;
}
}

} // namespace

RingStructure structureOf(RingID id)
{
switch (id) {
case ring_ZZ: case ring_ZZFlint: return RingStructure::ZZ;
case ring_QQ: case ring_QQFlint: return RingStructure::QQ;
case ring_RR: case ring_RRR: return RingStructure::RealField;
case ring_CC: case ring_CCC: return RingStructure::ComplexField;
case ring_RRi: return RingStructure::RealInterval;
case ring_CCi: return RingStructure::ComplexInterval;
case ring_ZZp: case ring_ZZpFfpack:
case ring_ZZpFlint: case ring_tower_ZZp: return RingStructure::PrimeField;
case ring_GFM2: case ring_GFFlintBig:
case ring_GFFlintZech: return RingStructure::FiniteField;
default: return RingStructure::Unknown;
}
}

bool structurePrecedes(RingStructure a, RingStructure b)
{
if (a == RingStructure::Unknown || b == RingStructure::Unknown) return false;
if (a == b) return true;
// BFS over the 8-node DAG.
bool reachable[numRingStructures] = {};
for (int i = 0; i < numRingStructures; ++i)
if (directEdge(a, allStructures[i])) reachable[i] = true;
bool changed = true;
while (changed) {
changed = false;
for (int i = 0; i < numRingStructures; ++i) {
if (!reachable[i]) continue;
for (int j = 0; j < numRingStructures; ++j) {
if (!reachable[j] && directEdge(allStructures[i], allStructures[j])) {
reachable[j] = true;
changed = true;
}
}
}
}
for (int i = 0; i < numRingStructures; ++i)
if (allStructures[i] == b && reachable[i]) return true;
return false;
}

bool hasCanonicalMap(const Ring* R, const Ring* S)
{
auto a = structureOf(R->ringID());
auto b = structureOf(S->ringID());
if (!structurePrecedes(a, b)) return false;
// Finite-field families require matching characteristic.
if (a == RingStructure::PrimeField || a == RingStructure::FiniteField)
if (R->characteristic() != S->characteristic()) return false;
// GF(p^m) → GF(p^n) requires m | n.
if (a == RingStructure::FiniteField && b == RingStructure::FiniteField) {
long dimR = gfDimension(R), dimS = gfDimension(S);
if (dimR <= 0 || dimS % dimR != 0) return false;
}
return true;
}

} // namespace M2

// Local Variables:
// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
// indent-tabs-mode: nil
// End:
46 changes: 46 additions & 0 deletions M2/Macaulay2/e/basic-rings/ring-structure.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef M2_BASIC_RINGS_RING_STRUCTURE_HPP_
#define M2_BASIC_RINGS_RING_STRUCTURE_HPP_

#include "basic-rings/aring.hpp" // for M2::RingID

class Ring;

namespace M2 {

/// Mathematical structure of a ring, collapsing precision and backend.
/// RR and RRR both collapse to RealField; all ZZp backends collapse to
/// PrimeField; etc. Unknown covers ring_old (polynomial rings, fraction
/// fields, etc.) for which no structural order is defined.
enum class RingStructure : int {
ZZ = 0,
QQ = 1,
RealField = 2,
ComplexField = 3,
RealInterval = 4,
ComplexInterval = 5,
PrimeField = 6,
FiniteField = 7,
Unknown = 8
};

/// Maps a concrete RingID to its mathematical structure.
RingStructure structureOf(RingID id);

/// True iff there is a canonical ring map from structure a to structure b
/// (reflexive-transitive closure of the canonical-map DAG).
/// Returns false when either argument is Unknown.
bool structurePrecedes(RingStructure a, RingStructure b);

/// True iff there exists a canonical ring map R → S.
/// Combines the structural partial order with characteristic matching (for
/// finite-field families) and dimension divisibility (for GF → GF).
bool hasCanonicalMap(const Ring* R, const Ring* S);

} // namespace M2

#endif

// Local Variables:
// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
// indent-tabs-mode: nil
// End:
3 changes: 2 additions & 1 deletion M2/Macaulay2/e/unit-tests/Makefile.files
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ UNITTEST_CCFILES := \
OverflowTest \
BRP-test \
RingCCCTest \
LatticePointsTest
LatticePointsTest \
RingStructureTest

# ARingGFTest \

Expand Down
122 changes: 122 additions & 0 deletions M2/Macaulay2/e/unit-tests/RingStructureTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include <gtest/gtest.h>
#include "basic-rings/ring-structure.hpp"

using M2::RingStructure;
using M2::structurePrecedes;
using M2::structureOf;
using M2::RingID;

// structureOf: verify RingID → RingStructure mapping

TEST(StructureOf, CharZeroTower)
{
EXPECT_EQ(structureOf(M2::ring_ZZ), RingStructure::ZZ);
EXPECT_EQ(structureOf(M2::ring_ZZFlint), RingStructure::ZZ);
EXPECT_EQ(structureOf(M2::ring_QQ), RingStructure::QQ);
EXPECT_EQ(structureOf(M2::ring_QQFlint), RingStructure::QQ);
EXPECT_EQ(structureOf(M2::ring_RR), RingStructure::RealField);
EXPECT_EQ(structureOf(M2::ring_RRR), RingStructure::RealField);
EXPECT_EQ(structureOf(M2::ring_CC), RingStructure::ComplexField);
EXPECT_EQ(structureOf(M2::ring_CCC), RingStructure::ComplexField);
EXPECT_EQ(structureOf(M2::ring_RRi), RingStructure::RealInterval);
EXPECT_EQ(structureOf(M2::ring_CCi), RingStructure::ComplexInterval);
}

TEST(StructureOf, FiniteFields)
{
EXPECT_EQ(structureOf(M2::ring_ZZp), RingStructure::PrimeField);
EXPECT_EQ(structureOf(M2::ring_ZZpFfpack), RingStructure::PrimeField);
EXPECT_EQ(structureOf(M2::ring_ZZpFlint), RingStructure::PrimeField);
EXPECT_EQ(structureOf(M2::ring_tower_ZZp), RingStructure::PrimeField);
EXPECT_EQ(structureOf(M2::ring_GFM2), RingStructure::FiniteField);
EXPECT_EQ(structureOf(M2::ring_GFFlintBig), RingStructure::FiniteField);
EXPECT_EQ(structureOf(M2::ring_GFFlintZech), RingStructure::FiniteField);
}

TEST(StructureOf, Unknown)
{
EXPECT_EQ(structureOf(M2::ring_old), RingStructure::Unknown);
}

// structurePrecedes: reflexivity

TEST(StructurePrecedes, Reflexive)
{
EXPECT_TRUE(structurePrecedes(RingStructure::ZZ, RingStructure::ZZ));
EXPECT_TRUE(structurePrecedes(RingStructure::QQ, RingStructure::QQ));
EXPECT_TRUE(structurePrecedes(RingStructure::RealField, RingStructure::RealField));
EXPECT_TRUE(structurePrecedes(RingStructure::ComplexField, RingStructure::ComplexField));
EXPECT_TRUE(structurePrecedes(RingStructure::RealInterval, RingStructure::RealInterval));
EXPECT_TRUE(structurePrecedes(RingStructure::ComplexInterval,RingStructure::ComplexInterval));
EXPECT_TRUE(structurePrecedes(RingStructure::PrimeField, RingStructure::PrimeField));
EXPECT_TRUE(structurePrecedes(RingStructure::FiniteField, RingStructure::FiniteField));
}

// structurePrecedes: valid canonical maps (promote direction)

TEST(StructurePrecedes, CharZeroTowerPromote)
{
using S = RingStructure;
// ZZ maps to everything
EXPECT_TRUE(structurePrecedes(S::ZZ, S::QQ));
EXPECT_TRUE(structurePrecedes(S::ZZ, S::RealField));
EXPECT_TRUE(structurePrecedes(S::ZZ, S::ComplexField));
EXPECT_TRUE(structurePrecedes(S::ZZ, S::RealInterval));
EXPECT_TRUE(structurePrecedes(S::ZZ, S::ComplexInterval));
EXPECT_TRUE(structurePrecedes(S::ZZ, S::PrimeField));
EXPECT_TRUE(structurePrecedes(S::ZZ, S::FiniteField));

// QQ chain
EXPECT_TRUE(structurePrecedes(S::QQ, S::RealField));
EXPECT_TRUE(structurePrecedes(S::QQ, S::ComplexField));
EXPECT_TRUE(structurePrecedes(S::QQ, S::RealInterval));
EXPECT_TRUE(structurePrecedes(S::QQ, S::ComplexInterval));

// RealField chain
EXPECT_TRUE(structurePrecedes(S::RealField, S::ComplexField));
EXPECT_TRUE(structurePrecedes(S::RealField, S::RealInterval));
EXPECT_TRUE(structurePrecedes(S::RealField, S::ComplexInterval));

// ComplexField → ComplexInterval
EXPECT_TRUE(structurePrecedes(S::ComplexField, S::ComplexInterval));

// RealInterval → ComplexInterval
EXPECT_TRUE(structurePrecedes(S::RealInterval, S::ComplexInterval));

// PrimeField → FiniteField
EXPECT_TRUE(structurePrecedes(S::PrimeField, S::FiniteField));
}

// structurePrecedes: invalid maps — the "nonsense" cases

TEST(StructurePrecedes, InvalidMaps)
{
using S = RingStructure;
// QQ does not map to ZZ (no ring hom QQ → ZZ)
EXPECT_FALSE(structurePrecedes(S::QQ, S::ZZ));

// QQ does not map to finite-characteristic rings
EXPECT_FALSE(structurePrecedes(S::QQ, S::PrimeField));
EXPECT_FALSE(structurePrecedes(S::QQ, S::FiniteField));

// RealField does not map down to QQ or ZZ
EXPECT_FALSE(structurePrecedes(S::RealField, S::QQ));
EXPECT_FALSE(structurePrecedes(S::RealField, S::ZZ));

// ComplexField does not map to RealField (non-trivial hom impossible)
EXPECT_FALSE(structurePrecedes(S::ComplexField, S::RealField));

// Interval types do not map to non-interval real/complex
EXPECT_FALSE(structurePrecedes(S::RealInterval, S::RealField));
EXPECT_FALSE(structurePrecedes(S::ComplexInterval, S::ComplexField));

// FiniteField does not map to char-0 rings
EXPECT_FALSE(structurePrecedes(S::FiniteField, S::ZZ));
EXPECT_FALSE(structurePrecedes(S::FiniteField, S::QQ));
EXPECT_FALSE(structurePrecedes(S::FiniteField, S::RealField));

// Unknown blocks everything
EXPECT_FALSE(structurePrecedes(S::Unknown, S::ZZ));
EXPECT_FALSE(structurePrecedes(S::ZZ, S::Unknown));
EXPECT_FALSE(structurePrecedes(S::Unknown, S::Unknown));
}
7 changes: 0 additions & 7 deletions M2/Macaulay2/m2/enginering.m2
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ raw EngineRing := R -> R.RawRing
raw Ring := R -> if R.?RawRing then R.RawRing else error "no raw engine ring associated with this ring"
isField EngineRing := R -> rawIsField raw R
hasEngineLinearAlgebra Ring := (R) -> instance(R, InexactField) or R#?"EngineLinearAlgebra" -- used to decide which algorithm to use
-----------------------------------------------------------------------------
-- rational promotion to any engine ring
promote(QQ,RingElement) := (r,S) -> (
a := promote(numerator r,S);
b := promote(denominator r,S);
if a % b == 0 then a // b
else error ("promotion of this rational number to the ring ", toString S, " not possible"))

--- new lift and promote, version 3
basicLift = opts -> (r,Brawring,Bclass) -> (
Expand Down
8 changes: 8 additions & 0 deletions M2/Macaulay2/m2/quotring.m2
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,18 @@ ZZp Ideal := opts -> (I) -> (
fraction(S,S) := S / S := (x,y) -> if y === 0_S then error "division by zero" else x//y;
S.frac = S; -- ZZ/n with n PRIME!
sqrt S := x -> promote(tonelliShanks(lift(x, ZZ), n), S);
promote(S, RingElement) := basicPromote;
promote(Matrix, S, RingElement) := (m, R, T) -> basicPromoteMatrix(m, T, identity);
promote(Module, S, RingElement) := (M, R, T) -> basicPromoteModule(M, T, identity);
promote(MutableMatrix, S, RingElement) := (m, R, T) -> basicPromoteMutableMatrix(m, T);
savedQuotients#(typ, n) = S;
lift(S,QQ) := opts -> liftZZmodQQ;
S))

isPromotable(QuotientRing, Ring) := (R, S) -> (
if isFinitePrimeField R then char R == char S
else lookup(promote, R, S) =!= null)

initializeEngineLinearAlgebra = method()
initializeEngineLinearAlgebra Ring := (R) -> (
R#"EngineLinearAlgebra" = true;
Expand Down
5 changes: 5 additions & 0 deletions M2/Macaulay2/m2/reals.m2
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ promote(RR,CCi') := (i,K) -> toCCi(toRRi(precision i,i,i),toRRi(precision i, 0,0
promote(RRi,CCi') := (i,K) -> toCCi(i, interval 0)
promote(CC,CCi') := (i,K) -> toCCi(toRRi(precision i,realPart i,realPart i),toRRi(precision i, imaginaryPart i, imaginaryPart i))
promote(CCi,CCi') := (i,K) -> toCCi(realPart i, imaginaryPart i) -- this should be fixed

-- e.g., isPromotable(RR_53, ...) = isPromotable(RR, ...)
-- promote is set up in commonEngineRingInitializations in enginering.m2
isPromotable(InexactField, Ring) := (R, S) -> isPromotable(class 0_R, S)

lift(RingElement, InexactNumber) :=
lift(Number, InexactNumber) := opts -> (x, K) -> lift(x, default K, opts)

Expand Down
Loading