Skip to content

Commit

Permalink
sagemathgh-38794: convert magma power series rings
Browse files Browse the repository at this point in the history
    
this is trying to allow the conversion of magma's series rings

also refactors the conversion of polynomial rings

This needs to be tested, if you have magma.

### 📝 Checklist

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.
    
URL: sagemath#38794
Reported by: Frédéric Chapoton
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Jan 30, 2025
2 parents 80fec75 + 6f957f1 commit fad9f15
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 62 deletions.
55 changes: 45 additions & 10 deletions src/sage/ext_data/magma/sage/basic.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function PreparseElts(R)
end function;

intrinsic Sage(X::.) -> MonStgElt, BoolElt
{Default way to convert a Magma object to Sage if we haven't
{Default way to convert a Magma object to Sage if we have not
written anything better.}
return Sprintf("%o", X), true;
end intrinsic;
Expand Down Expand Up @@ -153,16 +153,20 @@ intrinsic SageNamesHelper(X::.) -> MonStgElt
{}
/* XXX */
i := NumberOfNames(X);
if i ge 2 then
return (&* [ Sprintf("%o, ", X.j) : j in [ 1..i-1 ] ]) * Sprintf("%o", X.i);
if "$" in Sprint(X.i) then
/* unnamed variables */
return "(" * (&* [ Sprintf("'x%o', ", j) : j in [ 1..i ] ]) * ")";
else
return Sprintf("%o", X.i);
end if;
/* named variables */
return "(" * (&* [ Sprintf("'%o'.replace('.', ''), ", X.j) : j in [ 1..i ] ]) * ")";

end if;
end intrinsic;

intrinsic Sage(X::RngUPol) -> MonStgElt, BoolElt
{}
return Sprintf("%o['%o'.replace('$.', 'x').replace('.', '')]", Sage(BaseRing(X)), SageNamesHelper(X)), false;
txt := "PolynomialRing(%o, %o)";
return Sprintf(txt, Sage(BaseRing(X)), SageNamesHelper(X)), false;
end intrinsic;

intrinsic Sage(X::RngUPolElt) -> MonStgElt, BoolElt
Expand All @@ -173,7 +177,8 @@ intrinsic Sage(X::RngUPolElt) -> MonStgElt, BoolElt

intrinsic Sage(X::RngMPol) -> MonStgElt, BoolElt
{}
return Sprintf("%o['%o'.replace('$.', 'x').replace('.', '')]", Sage(BaseRing(X)), SageNamesHelper(X)), false;
txt := "PolynomialRing(%o, %o)";
return Sprintf(txt, Sage(BaseRing(X)), SageNamesHelper(X)), false;
end intrinsic;

intrinsic Sage(X::RngMPolElt) -> MonStgElt, BoolElt
Expand All @@ -187,9 +192,16 @@ intrinsic Sage(X::RngMPolElt) -> MonStgElt, BoolElt

intrinsic Sage(K::FldNum) -> MonStgElt, BoolElt
{}
names := [Sprintf("'%o'.replace('$.', 'a').replace('.', '')", a) : a in GeneratorsSequence(K)];
polynomials := DefiningPolynomial(K);
return Sprintf("NumberField(%o, %o)", Sage(polynomials), names), false;
gens := GeneratorsSequence(K);
if "$" in Sprint(gens[1]) then
/* unnamed variables */
names := "(" * (&* [ Sprintf("'a%o', ", j) : j in [ 1..#gens ] ]) * ")";
else
/* named variables */
names := "(" * (&* [ Sprintf("'%o'.replace('.', ''), ", a) : a in gens]) * ")";
end if;
polynomials := DefiningPolynomial(K);
return Sprintf("NumberField(%o, %o)", Sage(polynomials), names), false;
end intrinsic;

intrinsic Sage(A::FldNumElt) -> MonStgElt, BoolElt
Expand Down Expand Up @@ -264,3 +276,26 @@ intrinsic Sage(X::ModTupRngElt) -> MonStgElt, BoolElt
{}
return Sprintf("%o(%o)", Sage(Parent(X)), Sage(ElementToSequence(X))), true;
end intrinsic;

/* Power series rings */

intrinsic Sage(X::RngSerPow) -> MonStgElt, BoolElt
{}
txt := "PowerSeriesRing(%o, %o)";
var := Sprintf("['%o']", X.1);
return Sprintf(txt, Sage(BaseRing(X)), var), false;
end intrinsic;

intrinsic Sage(X::RngSerLaur) -> MonStgElt, BoolElt
{}
txt := "LaurentSeriesRing(%o, %o)";
var := Sprintf("['%o']", X.1);
return Sprintf(txt, Sage(BaseRing(X)), var), false;
end intrinsic;

intrinsic Sage(X::RngSerPuis) -> MonStgElt, BoolElt
{}
txt := "PuiseuxSeriesRing(%o, %o)";
var := Sprintf("['%o']", X.1);
return Sprintf(txt, Sage(BaseRing(X)), var), false;
end intrinsic;
77 changes: 40 additions & 37 deletions src/sage/interfaces/magma.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
You must have Magma installed on your
computer for this interface to work. Magma is not free, so it is
not included with Sage, but you can obtain it from
http://magma.maths.usyd.edu.au/.
https://magma.maths.usyd.edu.au/.
The Magma interface offers three pieces of functionality:
Expand Down Expand Up @@ -214,34 +214,35 @@
#
# https://www.gnu.org/licenses/
# ****************************************************************************
from __future__ import annotations
from pathlib import Path
import re
import sys
import os

from sage.structure.parent import Parent
from .expect import Expect, ExpectElement, ExpectFunction, FunctionElement
PROMPT = ">>>"

SAGE_REF = "_sage_ref"
SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF)

from sage.env import SAGE_EXTCODE, DOT_SAGE
import sage.misc.misc
import sage.misc.sage_eval
import sage.interfaces.abc
from sage.interfaces.tab_completion import ExtraTabCompletion
from sage.misc.instancedoc import instancedoc

PROMPT = ">>>"

SAGE_REF = "_sage_ref"
SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF)

INTRINSIC_CACHE = '%s/magma_intrinsic_cache.sobj' % DOT_SAGE
EXTCODE_DIR = None


def extcode_dir(iface=None):
def extcode_dir(iface=None) -> str:
"""
Return directory that contains all the Magma extcode. This is put
in a writable directory owned by the user, since when attached,
Magma has to write sig and lck files.
Return directory that contains all the Magma extcode.
This is put in a writable directory owned by the user, since when
attached, Magma has to write sig and lck files.
EXAMPLES::
Expand Down Expand Up @@ -413,7 +414,7 @@ def __reduce__(self):
"""
return reduce_load_Magma, tuple([])

def _read_in_file_command(self, filename):
def _read_in_file_command(self, filename) -> str:
"""
Return the command in Magma that reads in the contents of the given
file.
Expand All @@ -433,7 +434,7 @@ def _read_in_file_command(self, filename):
"""
return 'load "%s";' % filename

def _post_process_from_file(self, s):
def _post_process_from_file(self, s) -> str:
r"""
Used internally in the Magma interface to post-process the result
of evaluating a string using a file. For Magma what this does is
Expand Down Expand Up @@ -494,7 +495,7 @@ def __getattr__(self, attrname):
raise AttributeError
return MagmaFunction(self, attrname)

def eval(self, x, strip=True, **kwds):
def eval(self, x, strip=True, **kwds) -> str:
"""
Evaluate the given block x of code in Magma and return the output
as a string.
Expand Down Expand Up @@ -553,7 +554,7 @@ def eval(self, x, strip=True, **kwds):
raise RuntimeError("Error evaluating Magma code.\nIN:%s\nOUT:%s" % (x, ans))
return ans

def _preparse(self, s):
def _preparse(self, s) -> str:
"""
All input gets preparsed by calling this function before it gets evaluated.
Expand All @@ -578,7 +579,7 @@ def _preparse(self, s):
pass
return s

def _start(self):
def _start(self) -> None:
"""
Initialize a Magma interface instance. This involves (1) setting up
an obfuscated prompt, and (2) attaching the MAGMA_SPEC file (see
Expand Down Expand Up @@ -619,7 +620,7 @@ def set(self, var, value):
if out.lower().find("error") != -1:
raise TypeError("Error executing Magma code:\n%s" % out)

def get(self, var):
def get(self, var) -> str:
"""
Get the value of the variable var.
Expand Down Expand Up @@ -655,7 +656,7 @@ def objgens(self, value, gens):
sage: R = magma.objgens('PolynomialRing(Rationals(),2)', 'alpha,beta') # optional - magma
sage: R.gens() # optional - magma
[alpha, beta]
(alpha, beta)
Because of how Magma works you can use this to change the variable
names of the generators of an object::
Expand Down Expand Up @@ -911,16 +912,15 @@ def cputime(self, t=None):
sage: # optional - magma
sage: type(magma.cputime())
<... 'float'>
sage: magma.cputime()
sage: magma.cputime() # random
1.9399999999999999
sage: t = magma.cputime()
sage: magma.cputime(t)
sage: magma.cputime(t) # random
0.02
"""
if t:
return float(self.eval('Cputime(%s)' % t))
else:
return float(self.eval('Cputime()'))
return float(self.eval('Cputime()'))

def chdir(self, dir):
"""
Expand Down Expand Up @@ -1003,7 +1003,7 @@ def load(self, filename):
Loading a file in Magma makes all the functions and procedures in
the file available. The file should not contain any intrinsics (or
you'll get errors). It also runs code in the file, which can
you will get errors). It also runs code in the file, which can
produce output.
INPUT:
Expand All @@ -1018,14 +1018,15 @@ def load(self, filename):
sage: with NTF(mode='w+t', suffix='.m') as f: # optional - magma
....: _ = f.write('function f(n) return n^2; end function;\nprint "hi";')
....: print(magma.load(f.name))
Loading ".../a.m"
Loading "....m"
hi
sage: magma('f(12)') # optional - magma
144
"""
return self.eval('load "%s"' % filename)
p = Path(filename)
return self.eval('load "%s"' % p.absolute())

def _next_var_name(self):
def _next_var_name(self) -> str:
"""
Return the next available variable name in Magma.
Expand Down Expand Up @@ -1234,7 +1235,7 @@ def bar_call(self, left, name, gens, nvals=1):
magma = self
# coerce each arg to be a Magma element
if isinstance(gens, (list, tuple)):
gens = (magma(z) for z in gens)
gens = [magma(z) for z in gens]
# make comma separated list of names (in Magma) of each of the gens
v = ', '.join(w.name() for w in gens)
else:
Expand Down Expand Up @@ -1880,10 +1881,11 @@ def __getattr__(self, attrname):

def _sage_(self):
"""
Return Sage version of this object. Use self.sage() to get the Sage
version.
Return Sage version of this object.
Use self.sage() to get the Sage version.
Edit src/ext/magma/sage/basic.m to add functionality.
Edit ``src/sage/ext_data/magma/sage/basic.m`` to add functionality.
EXAMPLES: Enumerated Sets::
Expand Down Expand Up @@ -2070,14 +2072,14 @@ def AssignNames(self, names):

def gen(self, n):
"""
Return the `n`-th generator of this Magma element. Note that
generators are 1-based in Magma rather than 0-based!
Return the `n`-th generator of this Magma element.
Note that generators are 1-based in Magma rather than 0-based!
INPUT:
- ``n`` -- *positive* integer
OUTPUT: :class:`MagmaElement`
EXAMPLES::
Expand All @@ -2100,7 +2102,7 @@ def gen(self, n):
sage: m.gen(4) # optional -- magma
Traceback (most recent call last):
...
IndexError: list index out of range
IndexError: tuple index out of range
"""
if n <= 0:
raise IndexError("index must be positive since Magma indexes are 1-based")
Expand All @@ -2112,7 +2114,7 @@ def gens(self) -> tuple:
If ``self`` is named X in Magma, this function evaluates X.1, X.2,
etc., in Magma until an error occurs. It then returns a Sage tuple
of the resulting X.i. Note - I don't think there is a Magma command
of the resulting X.i. Note - I do not think there is a Magma command
that returns the list of valid X.i. There are numerous ad hoc
functions for various classes but nothing systematic. This function
gets around that problem. Again, this is something that should
Expand Down Expand Up @@ -2294,15 +2296,15 @@ def _polynomial_(self, R):
sage: R.<x> = QQ[]
sage: f = magma(x^2 + 2/3*x + 5) # optional - magma
sage: f # optional - magma
t^2 + 2/3*t + 5
x^2 + 2/3*x + 5
sage: f.Type() # optional - magma
RngUPolElt
sage: f._polynomial_(R) # optional - magma
x^2 + 2/3*x + 5
"""
return R(list(self.Eltseq()))

def _latex_(self):
def _latex_(self) -> str:
r"""
Return latex representation of ``self``.
Expand Down Expand Up @@ -2854,6 +2856,7 @@ def write(self, s):
sage: P.<x,y,z> = GF(32003)[]
sage: I = sage.rings.ideal.Katsura(P)
sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma
...
********************
FAUGERE F4 ALGORITHM
********************
Expand Down
7 changes: 4 additions & 3 deletions src/sage/matrix/matrix_mod2_dense.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1393,14 +1393,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse

def _magma_init_(self, magma):
"""
Return a string of ``self`` in ``Magma`` form. Does not return
``Magma`` object but string.
Return a string of ``self`` in ``Magma`` form.
This does not return a ``Magma`` object but a string.
EXAMPLES::
sage: A = random_matrix(GF(2),3,3)
sage: A._magma_init_(magma) # optional - magma
'Matrix(GF(2),3,3,StringToIntegerSequence("0 1 0 0 1 1 0 0 0"))'
'Matrix(GF(2),3,3,StringToIntegerSequence("..."))'
sage: A = random_matrix(GF(2),100,100)
sage: B = random_matrix(GF(2),100,100)
sage: magma(A*B) == magma(A) * magma(B) # optional - magma
Expand Down
5 changes: 3 additions & 2 deletions src/sage/modular/btquotients/btquotient.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from collections import deque

from sage.arith.misc import gcd, xgcd, kronecker_symbol, fundamental_discriminant
from sage.interfaces.magma import magma
from sage.matrix.constructor import Matrix
from sage.matrix.matrix_space import MatrixSpace
from sage.misc.cachefunc import cached_method
Expand Down Expand Up @@ -2307,7 +2308,7 @@ def get_extra_embedding_matrices(self):
[1 0 2 0]
[0 0 2 0]
[0 0 0 0]
[1 0 2 2]
[1 2 2 0]
]
"""
if not self._use_magma or len(self._extra_level) == 0:
Expand Down Expand Up @@ -2700,7 +2701,7 @@ def get_splitting_field(self):
sage: X = BruhatTitsQuotient(5,11,use_magma=True) # optional - magma
sage: X.get_splitting_field() # optional - magma
Number Field in a with defining polynomial X1^2 + 11
Number Field in a with defining polynomial x^2 + 11
"""
if not self._use_magma:
raise NotImplementedError('Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.')
Expand Down
Loading

0 comments on commit fad9f15

Please sign in to comment.