Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
07c4f98
src/sage/env.py: Set MPMATH_NOSAGE
mkoeppe May 29, 2024
0f6e4b0
src/sage/libs/mpmath/utils.pyx: Remove 'patches some mpmath functions…
mkoeppe Aug 11, 2024
3ed5cca
src/sage/libs/mpmath/ext_libmp.pyx: Remove
mkoeppe May 29, 2024
d52b7ec
src/sage/libs/mpmath/ext_libmp.p*: Remove
mkoeppe May 29, 2024
ec767ec
src/sage/libs/mpmath/ext_main.p*: Remove
mkoeppe Jul 25, 2024
cf0a7a1
src/sage/libs/mpmath/utils.pyx: Use gmpy2
mkoeppe May 30, 2024
b57e0a4
src/sage/rings/real_mpfr.pyx: Remove duplicate cimport
mkoeppe May 30, 2024
125ae4e
src/sage/structure/coerce.pyx (is_mpmath_type, py_scalar_parent): Han…
mkoeppe May 30, 2024
b713521
src/sage/tests/books/computational-mathematics-with-sagemath/integrat…
mkoeppe May 31, 2024
84f280c
src/sage/libs/mpmath/utils.pyx: Update doctest output for 'ei'
mkoeppe Jul 27, 2024
ed6d0c2
build/pkgs/mpmath/version_requirements.txt: Reject 1.4
mkoeppe Aug 25, 2024
12c6b9f
Merge branch 'no_mpmath_sage' into mpmath-1.4
mkoeppe Aug 26, 2024
81d3cce
build/pkgs/mpmath: Update to 1.4.0a1
mkoeppe Aug 26, 2024
e5cb262
Merge branch 'develop' into mpmath-1.4
kiwifb Aug 23, 2025
7714d6c
bump required version to 1.4.0a5 which has many fixes
kiwifb Aug 23, 2025
e7f5fb5
update meson build for new layout
kiwifb Aug 23, 2025
65a3fcb
Merge branch 'develop' into mpmath-1.4
kiwifb Sep 21, 2025
691d91b
Merge remote-tracking branch 'kiwifb/mpmath-1.4' into mpmath-1.4
antonio-rojas Feb 27, 2026
23e6179
Revert mpmath upgrade
antonio-rojas Feb 27, 2026
99fb2b7
Additional mpmath 1.4 fixes
antonio-rojas Feb 27, 2026
695f378
Additional mpmath 1.4 fixes
antonio-rojas Feb 27, 2026
f75f163
Make test pass with older mpmath
antonio-rojas Feb 28, 2026
12b41d0
Remove abs-tol from tests
antonio-rojas Feb 28, 2026
a1a9f84
Drop mpmath feature tests
antonio-rojas Feb 28, 2026
458d0e0
Make update-meson happy
antonio-rojas Feb 28, 2026
d7314e9
Add back abs tol for error.py
antonio-rojas Feb 28, 2026
7cf21c9
Improvements to the construction of subalgebras.
tscrim Feb 6, 2026
e7c6726
Making the inputs explicit.
tscrim Feb 10, 2026
86514a7
Making the ideals also use the same algorithm.
tscrim Feb 10, 2026
d5737bf
test: default to algorithm=basis
orlitzky Mar 3, 2026
876a29d
test: default to algorithm=generators
orlitzky Mar 3, 2026
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
223 changes: 182 additions & 41 deletions src/sage/categories/finite_dimensional_algebras_with_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,12 +418,95 @@ def center(self):
center.rename("Center of {}".format(self))
return center

def subalgebra(self, gens, category=None, *args, **opts):
def _build_basis_by_generators(self, S, gens, order=None, side=2):
"""
Build a basis of elements of ``self`` that contains ``S``
and closed under left/right multiplication by ``gens``.

INPUT:

- ``S`` -- list of elements of ``self``
- ``gens`` -- list of generators
- ``order`` -- (optional) iterable defining an ordering of
the basis elements of ``self``
- ``side`` -- (default: 2) one of the following

* 0 - closed under left multiplication
* 1 - closed under right multiplication
* 2 - closed under twosided multiplication

EXAMPLES::

sage: E.<w,x,y,z> = ExteriorAlgebra(QQ)
sage: gens = E.algebra_generators()
sage: elts = [x*y + z]
sage: E._build_basis_by_generators(elts, gens, side=0)
[x*y + z, w*x*y + w*z, x*z, y*z, w*x*z, w*y*z, x*y*z, w*x*y*z]
sage: E._build_basis_by_generators(elts, gens, side=1)
[x*y + z, -w*x*y + w*z, x*z, y*z, w*x*z, w*y*z, x*y*z, w*x*y*z]
sage: E._build_basis_by_generators(elts, gens, side=2)
[x*y + z, w*z, x*z, y*z, w*x*y, w*x*z, w*y*z, x*y*z, w*x*y*z]
sage: E.ideal_submodule(elts, side="left", algorithm="basis").dimension()
8
sage: E.ideal_submodule(elts, side="right", algorithm="basis").dimension()
8
sage: E.ideal_submodule(elts, side="twosided", algorithm="basis").dimension()
9
"""
if order is None:
try:
order = self.get_order()
except (ValueError, TypeError, NotImplementedError, AttributeError):
order = list(self.basis().keys())

order_dict = {k: i for i, k in enumerate(order)}
key = order_dict.__getitem__

def reduce_pivots(elt, trailsupp, sortsupp):
if not elt:
return elt
return elt - self.linear_combination((trailsupp[s], c // trailsupp[s][s])
for s in sortsupp if (c := elt[s]))

dim = self.dimension()
basis = []
new_elts = list(S)
trailsupp = {}
while new_elts:
basis = self.echelon_form(basis + new_elts, order=order)
if len(basis) == dim: # already the full algebra
break
prevsupp = set(trailsupp)
trailsupp = {b.trailing_support(key=key): b for b in basis}
sortsupp = sorted(trailsupp, key=key)
new_elts = []
# We (re)implement the reduction here
for s in trailsupp:
if s in prevsupp:
continue
b = trailsupp[s]
for g in gens:
if side != 0:
elt = reduce_pivots(b * g, trailsupp, sortsupp)
if elt:
new_elts.append(elt)
if side != 1:
elt = reduce_pivots(g * b, trailsupp, sortsupp)
if elt:
new_elts.append(elt)
return basis

def subalgebra(self, gens, category=None, order=None, *args, **opts):
r"""
Return the subalgebra of ``self`` generated by ``gens``.

Here, ``gens`` is an iterable containing elements of
``self``.
INPUT:

- ``gens`` -- iterable containing elements of ``self``
- ``category`` -- (optional) a subcategory of finite dimensional
algebras with basis
- ``order`` -- (optional) iterable defining an ordering of
the basis elements of ``self``

EXAMPLES::

Expand All @@ -443,39 +526,54 @@ def subalgebra(self, gens, category=None, *args, **opts):
sage: A = MS.subalgebra(gens)
sage: A.dimension()
5

sage: WA = SignedPermutations(3).algebra(QQ)
sage: s1,s2,s3 = WA.algebra_generators()
sage: J2 = s1
sage: J3 = s2 + s1*s2*s1
sage: J4 = (s3 + s2*s3*s2 + s3*s2*s3 + s1*s2*s3*s2*s1
....: + s1*s3*s2*s3*s1 + s2*s1*s3*s2*s3*s1*s2)
sage: SA = WA.subalgebra([J2, J3, J4])
sage: SA.dimension()
14
"""
if order is None:
try:
order = self.get_order()
except (ValueError, TypeError, NotImplementedError, AttributeError):
order = list(self.basis().keys())

gens = self.echelon_form([self(g) for g in gens], order=order)
# add the unit to make sure it is unital
basis = []
new_elts = [self(g) for g in gens] + [self.one()]
while new_elts:
basis = self.echelon_form(basis + new_elts)
trailsupp = {b.trailing_support(): b for b in basis}
sortsupp = sorted(trailsupp)
new_elts = []
# We (re)implement the reduction here
for b in basis:
for bp in basis:
elt = b * bp
for s in sortsupp:
c = elt[s]
if c:
elt -= c / trailsupp[s].trailing_coefficient() * trailsupp[s]
if elt:
new_elts.append(elt)
basis = self._build_basis_by_generators(gens + [self.one()], gens, order=order, side=2)

C = FiniteDimensionalAlgebrasWithBasis(self.category().base_ring())
category = C.Subobjects().or_subcategory(category)
return self.submodule(basis, check=False, already_echelonized=True,
category=category)
category=category, support_order=order, *args, **opts)

def ideal_submodule(self, gens, side='left', category=None, *args, **opts):
def ideal_submodule(self, gens, side='left', category=None, algorithm='generators', *args, **opts):
r"""
Return the ``side`` ideal of ``self`` generated by ``gens``
as a submodule.

Here, ``gens`` is an iterable containing elements of
``self`` or a single element of ``self``,
and ``side`` is either ``'left'`` or
``'right'`` or ``'twosided'``.
INPUT:

- ``gens`` -- iterable containing elements of ``self``
or a single element of ``self``
- ``side`` -- string; either ``'left'`` or ``'right'``
or ``'twosided'``
- ``algorithm`` -- string (optional); must be one of the following:

* ``"generators"`` -- generate the ideal by successively
enlarging the basis by multiplying by the generators of the
algebra
* ``"basis" -- multiply all elements in ``gens`` by the basis
of ``self``

If ``algorithm`` is not specified, if the dimension of ``self``
is `\leq 10`, then this uses the ``"basis"`` algorithm and
otherwise it uses the ``"generators"`` algorithm.

.. TODO::

Expand All @@ -486,32 +584,75 @@ def ideal_submodule(self, gens, side='left', category=None, *args, **opts):

EXAMPLES::

sage: # needs sage.modules

sage: scoeffs = {('a','e'): {'a':1}, ('b','e'): {'a':1, 'b':1},
....: ('c','d'): {'a':1}, ('c','e'): {'c':1}}
sage: L.<a,b,c,d,e> = LieAlgebra(QQ, scoeffs)
sage: MS = MatrixSpace(QQ, 5)
sage: I = MS.ideal_submodule([bg.adjoint_matrix() for bg in L.lie_algebra_generators()])
sage: gens = [bg.adjoint_matrix() for bg in L.lie_algebra_generators()]
sage: I = MS.ideal_submodule(gens)
sage: I.dimension()
25

sage: MS.ideal_submodule(gens, algorithm="basis").dimension()
25
"""
C = AssociativeAlgebras(self.category().base_ring()).WithBasis().FiniteDimensional()
category = C.Subobjects().or_subcategory(category)
alggens = self.algebra_generators()
if gens in self:
gens = [self(gens)]
else:
gens = [self(g) for g in gens]
if side == 'left':
return self.submodule([b * g for b in self.basis() for g in gens],
category=category, *args, **opts)
if side == 'right':
return self.submodule([g * b for b in self.basis() for g in gens],
category=category, *args, **opts)
if side == 'twosided':
return self.submodule([b * g * bp for b in self.basis()
for bp in self.basis() for g in gens],
category=category, *args, **opts)
raise ValueError("side must be either 'left', 'right', or 'twosided'")

if algorithm is None:
if self.dimension() <= 10:
algorithm = "basis"
else:
algorithm = "generators"

# If the generators is "large" compared to the dimension
if len(gens) > self.dimension() // 2:
# Then we perform a linear reduction
gens = self.echelon_form(gens)
# Special case: gens is a basis for the algebra
if len(gens) == self.dimension():
return self.submodule(gens, already_echelonized=True)

if algorithm == "basis":
if side == 'left':
return self.submodule([b * g for b in self.basis() for g in gens],
category=category, *args, **opts)
if side == 'right':
return self.submodule([g * b for b in self.basis() for g in gens],
category=category, *args, **opts)
if side == 'twosided':
spanset = [b * g for b in self.basis() for g in gens]
spanset.extend(g * b for b in self.basis() for g in gens)
return self.submodule([b * g * bp for b in self.basis()
for bp in self.basis() for g in gens],
category=category, *args, **opts)
raise ValueError("side must be either 'left', 'right', or 'twosided'")

if algorithm == "generators":
try:
order = self.get_order()
except (ValueError, TypeError, NotImplementedError, AttributeError):
order = list(self.basis().keys())

if side == 'left':
basis = self._build_basis_by_generators(gens, alggens, order=order, side=0)
elif side == 'right':
basis = self._build_basis_by_generators(gens, alggens, order=order, side=1)
elif side == 'twosided':
basis = self._build_basis_by_generators(gens, alggens, order=order, side=2)
else:
raise ValueError("side must be either 'left', 'right', or 'twosided'")

C = AssociativeAlgebras(self.category().base_ring()).WithBasis().FiniteDimensional()
category = C.Subobjects().or_subcategory(category)
return self.submodule(basis, category=category, already_echelonized=True,
support_order=order, *args, **opts)

raise ValueError("invalid algorithm")

def principal_ideal(self, a, side='left', *args, **opts):
r"""
Expand Down
5 changes: 3 additions & 2 deletions src/sage/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,9 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st
OPENMP_CFLAGS = var("OPENMP_CFLAGS", "")
OPENMP_CXXFLAGS = var("OPENMP_CXXFLAGS", "")

# Make sure mpmath uses Sage types
os.environ['MPMATH_SAGE'] = '1'
# Make sure that mpmath < 1.4 does not try to use Sage types
os.environ.pop('MPMATH_SAGE', None)
os.environ['MPMATH_NOSAGE'] = '1'

# misc
SAGE_BANNER = var("SAGE_BANNER", "")
Expand Down
4 changes: 2 additions & 2 deletions src/sage/functions/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ def _evalf_(self, x, parent=None, algorithm=None):
0.995322265018953
sage: erf(2).n(200) # needs sage.symbolic
0.99532226501895273416206925636725292861089179704006007673835
sage: erf(pi - 1/2*I).n(100) # needs sage.symbolic
1.0000111669099367825726058952 + 1.6332655417638522934072124547e-6*I
sage: erf(pi - 1/2*I).n(100) # needs sage.symbolic # abs tol 1e-28
1.0000111669099367825726058952 + 1.6332655417638522934072124548e-6*I

TESTS:

Expand Down
2 changes: 1 addition & 1 deletion src/sage/functions/exp_integral.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ def _evalf_(self, z, parent=None, algorithm=None):
sage: N(cos_integral(10^-10), digits=30) # needs sage.symbolic
-22.4486352650389239795759024568
sage: cos_integral(ComplexField(100)(I)) # needs sage.symbolic
0.83786694098020824089467857943 + 1.5707963267948966192313216916*I
0.83786694098020824089467857944 + 1.5707963267948966192313216916*I
"""
return _mpmath_utils_call(_mpmath_ci, z, parent=parent)

Expand Down
2 changes: 1 addition & 1 deletion src/sage/libs/mpmath/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@


def eval_constant(name, ring):
prec = ring.precision() + 20
prec = int(ring.precision() + 20)
return ring(_constants_funcs[name](prec)) >> prec
66 changes: 0 additions & 66 deletions src/sage/libs/mpmath/ext_impl.pxd

This file was deleted.

Loading
Loading