Skip to content

Commit fad9f15

Browse files
author
Release Manager
committed
sagemathgh-38794: convert magma power series rings 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
2 parents 80fec75 + 6f957f1 commit fad9f15

File tree

11 files changed

+178
-62
lines changed

11 files changed

+178
-62
lines changed

src/sage/ext_data/magma/sage/basic.m

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function PreparseElts(R)
88
end function;
99

1010
intrinsic Sage(X::.) -> MonStgElt, BoolElt
11-
{Default way to convert a Magma object to Sage if we haven't
11+
{Default way to convert a Magma object to Sage if we have not
1212
written anything better.}
1313
return Sprintf("%o", X), true;
1414
end intrinsic;
@@ -153,16 +153,20 @@ intrinsic SageNamesHelper(X::.) -> MonStgElt
153153
{}
154154
/* XXX */
155155
i := NumberOfNames(X);
156-
if i ge 2 then
157-
return (&* [ Sprintf("%o, ", X.j) : j in [ 1..i-1 ] ]) * Sprintf("%o", X.i);
156+
if "$" in Sprint(X.i) then
157+
/* unnamed variables */
158+
return "(" * (&* [ Sprintf("'x%o', ", j) : j in [ 1..i ] ]) * ")";
158159
else
159-
return Sprintf("%o", X.i);
160-
end if;
160+
/* named variables */
161+
return "(" * (&* [ Sprintf("'%o'.replace('.', ''), ", X.j) : j in [ 1..i ] ]) * ")";
162+
163+
end if;
161164
end intrinsic;
162165

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

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

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

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

188193
intrinsic Sage(K::FldNum) -> MonStgElt, BoolElt
189194
{}
190-
names := [Sprintf("'%o'.replace('$.', 'a').replace('.', '')", a) : a in GeneratorsSequence(K)];
191-
polynomials := DefiningPolynomial(K);
192-
return Sprintf("NumberField(%o, %o)", Sage(polynomials), names), false;
195+
gens := GeneratorsSequence(K);
196+
if "$" in Sprint(gens[1]) then
197+
/* unnamed variables */
198+
names := "(" * (&* [ Sprintf("'a%o', ", j) : j in [ 1..#gens ] ]) * ")";
199+
else
200+
/* named variables */
201+
names := "(" * (&* [ Sprintf("'%o'.replace('.', ''), ", a) : a in gens]) * ")";
202+
end if;
203+
polynomials := DefiningPolynomial(K);
204+
return Sprintf("NumberField(%o, %o)", Sage(polynomials), names), false;
193205
end intrinsic;
194206

195207
intrinsic Sage(A::FldNumElt) -> MonStgElt, BoolElt
@@ -264,3 +276,26 @@ intrinsic Sage(X::ModTupRngElt) -> MonStgElt, BoolElt
264276
{}
265277
return Sprintf("%o(%o)", Sage(Parent(X)), Sage(ElementToSequence(X))), true;
266278
end intrinsic;
279+
280+
/* Power series rings */
281+
282+
intrinsic Sage(X::RngSerPow) -> MonStgElt, BoolElt
283+
{}
284+
txt := "PowerSeriesRing(%o, %o)";
285+
var := Sprintf("['%o']", X.1);
286+
return Sprintf(txt, Sage(BaseRing(X)), var), false;
287+
end intrinsic;
288+
289+
intrinsic Sage(X::RngSerLaur) -> MonStgElt, BoolElt
290+
{}
291+
txt := "LaurentSeriesRing(%o, %o)";
292+
var := Sprintf("['%o']", X.1);
293+
return Sprintf(txt, Sage(BaseRing(X)), var), false;
294+
end intrinsic;
295+
296+
intrinsic Sage(X::RngSerPuis) -> MonStgElt, BoolElt
297+
{}
298+
txt := "PuiseuxSeriesRing(%o, %o)";
299+
var := Sprintf("['%o']", X.1);
300+
return Sprintf(txt, Sage(BaseRing(X)), var), false;
301+
end intrinsic;

src/sage/interfaces/magma.py

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
You must have Magma installed on your
1111
computer for this interface to work. Magma is not free, so it is
1212
not included with Sage, but you can obtain it from
13-
http://magma.maths.usyd.edu.au/.
13+
https://magma.maths.usyd.edu.au/.
1414
1515
The Magma interface offers three pieces of functionality:
1616
@@ -214,34 +214,35 @@
214214
#
215215
# https://www.gnu.org/licenses/
216216
# ****************************************************************************
217-
from __future__ import annotations
217+
from pathlib import Path
218218
import re
219219
import sys
220220
import os
221221

222222
from sage.structure.parent import Parent
223223
from .expect import Expect, ExpectElement, ExpectFunction, FunctionElement
224-
PROMPT = ">>>"
225-
226-
SAGE_REF = "_sage_ref"
227-
SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF)
228-
229224
from sage.env import SAGE_EXTCODE, DOT_SAGE
230225
import sage.misc.misc
231226
import sage.misc.sage_eval
232227
import sage.interfaces.abc
233228
from sage.interfaces.tab_completion import ExtraTabCompletion
234229
from sage.misc.instancedoc import instancedoc
235230

231+
PROMPT = ">>>"
232+
233+
SAGE_REF = "_sage_ref"
234+
SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF)
235+
236236
INTRINSIC_CACHE = '%s/magma_intrinsic_cache.sobj' % DOT_SAGE
237237
EXTCODE_DIR = None
238238

239239

240-
def extcode_dir(iface=None):
240+
def extcode_dir(iface=None) -> str:
241241
"""
242-
Return directory that contains all the Magma extcode. This is put
243-
in a writable directory owned by the user, since when attached,
244-
Magma has to write sig and lck files.
242+
Return directory that contains all the Magma extcode.
243+
244+
This is put in a writable directory owned by the user, since when
245+
attached, Magma has to write sig and lck files.
245246
246247
EXAMPLES::
247248
@@ -413,7 +414,7 @@ def __reduce__(self):
413414
"""
414415
return reduce_load_Magma, tuple([])
415416

416-
def _read_in_file_command(self, filename):
417+
def _read_in_file_command(self, filename) -> str:
417418
"""
418419
Return the command in Magma that reads in the contents of the given
419420
file.
@@ -433,7 +434,7 @@ def _read_in_file_command(self, filename):
433434
"""
434435
return 'load "%s";' % filename
435436

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

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

556-
def _preparse(self, s):
557+
def _preparse(self, s) -> str:
557558
"""
558559
All input gets preparsed by calling this function before it gets evaluated.
559560
@@ -578,7 +579,7 @@ def _preparse(self, s):
578579
pass
579580
return s
580581

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

622-
def get(self, var):
623+
def get(self, var) -> str:
623624
"""
624625
Get the value of the variable var.
625626
@@ -655,7 +656,7 @@ def objgens(self, value, gens):
655656
656657
sage: R = magma.objgens('PolynomialRing(Rationals(),2)', 'alpha,beta') # optional - magma
657658
sage: R.gens() # optional - magma
658-
[alpha, beta]
659+
(alpha, beta)
659660
660661
Because of how Magma works you can use this to change the variable
661662
names of the generators of an object::
@@ -911,16 +912,15 @@ def cputime(self, t=None):
911912
sage: # optional - magma
912913
sage: type(magma.cputime())
913914
<... 'float'>
914-
sage: magma.cputime()
915+
sage: magma.cputime() # random
915916
1.9399999999999999
916917
sage: t = magma.cputime()
917-
sage: magma.cputime(t)
918+
sage: magma.cputime(t) # random
918919
0.02
919920
"""
920921
if t:
921922
return float(self.eval('Cputime(%s)' % t))
922-
else:
923-
return float(self.eval('Cputime()'))
923+
return float(self.eval('Cputime()'))
924924

925925
def chdir(self, dir):
926926
"""
@@ -1003,7 +1003,7 @@ def load(self, filename):
10031003
10041004
Loading a file in Magma makes all the functions and procedures in
10051005
the file available. The file should not contain any intrinsics (or
1006-
you'll get errors). It also runs code in the file, which can
1006+
you will get errors). It also runs code in the file, which can
10071007
produce output.
10081008
10091009
INPUT:
@@ -1018,14 +1018,15 @@ def load(self, filename):
10181018
sage: with NTF(mode='w+t', suffix='.m') as f: # optional - magma
10191019
....: _ = f.write('function f(n) return n^2; end function;\nprint "hi";')
10201020
....: print(magma.load(f.name))
1021-
Loading ".../a.m"
1021+
Loading "....m"
10221022
hi
10231023
sage: magma('f(12)') # optional - magma
10241024
144
10251025
"""
1026-
return self.eval('load "%s"' % filename)
1026+
p = Path(filename)
1027+
return self.eval('load "%s"' % p.absolute())
10271028

1028-
def _next_var_name(self):
1029+
def _next_var_name(self) -> str:
10291030
"""
10301031
Return the next available variable name in Magma.
10311032
@@ -1234,7 +1235,7 @@ def bar_call(self, left, name, gens, nvals=1):
12341235
magma = self
12351236
# coerce each arg to be a Magma element
12361237
if isinstance(gens, (list, tuple)):
1237-
gens = (magma(z) for z in gens)
1238+
gens = [magma(z) for z in gens]
12381239
# make comma separated list of names (in Magma) of each of the gens
12391240
v = ', '.join(w.name() for w in gens)
12401241
else:
@@ -1880,10 +1881,11 @@ def __getattr__(self, attrname):
18801881

18811882
def _sage_(self):
18821883
"""
1883-
Return Sage version of this object. Use self.sage() to get the Sage
1884-
version.
1884+
Return Sage version of this object.
1885+
1886+
Use self.sage() to get the Sage version.
18851887
1886-
Edit src/ext/magma/sage/basic.m to add functionality.
1888+
Edit ``src/sage/ext_data/magma/sage/basic.m`` to add functionality.
18871889
18881890
EXAMPLES: Enumerated Sets::
18891891
@@ -2070,14 +2072,14 @@ def AssignNames(self, names):
20702072

20712073
def gen(self, n):
20722074
"""
2073-
Return the `n`-th generator of this Magma element. Note that
2074-
generators are 1-based in Magma rather than 0-based!
2075+
Return the `n`-th generator of this Magma element.
2076+
2077+
Note that generators are 1-based in Magma rather than 0-based!
20752078
20762079
INPUT:
20772080
20782081
- ``n`` -- *positive* integer
20792082
2080-
20812083
OUTPUT: :class:`MagmaElement`
20822084
20832085
EXAMPLES::
@@ -2100,7 +2102,7 @@ def gen(self, n):
21002102
sage: m.gen(4) # optional -- magma
21012103
Traceback (most recent call last):
21022104
...
2103-
IndexError: list index out of range
2105+
IndexError: tuple index out of range
21042106
"""
21052107
if n <= 0:
21062108
raise IndexError("index must be positive since Magma indexes are 1-based")
@@ -2112,7 +2114,7 @@ def gens(self) -> tuple:
21122114
21132115
If ``self`` is named X in Magma, this function evaluates X.1, X.2,
21142116
etc., in Magma until an error occurs. It then returns a Sage tuple
2115-
of the resulting X.i. Note - I don't think there is a Magma command
2117+
of the resulting X.i. Note - I do not think there is a Magma command
21162118
that returns the list of valid X.i. There are numerous ad hoc
21172119
functions for various classes but nothing systematic. This function
21182120
gets around that problem. Again, this is something that should
@@ -2294,15 +2296,15 @@ def _polynomial_(self, R):
22942296
sage: R.<x> = QQ[]
22952297
sage: f = magma(x^2 + 2/3*x + 5) # optional - magma
22962298
sage: f # optional - magma
2297-
t^2 + 2/3*t + 5
2299+
x^2 + 2/3*x + 5
22982300
sage: f.Type() # optional - magma
22992301
RngUPolElt
23002302
sage: f._polynomial_(R) # optional - magma
23012303
x^2 + 2/3*x + 5
23022304
"""
23032305
return R(list(self.Eltseq()))
23042306

2305-
def _latex_(self):
2307+
def _latex_(self) -> str:
23062308
r"""
23072309
Return latex representation of ``self``.
23082310
@@ -2854,6 +2856,7 @@ def write(self, s):
28542856
sage: P.<x,y,z> = GF(32003)[]
28552857
sage: I = sage.rings.ideal.Katsura(P)
28562858
sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma
2859+
...
28572860
********************
28582861
FAUGERE F4 ALGORITHM
28592862
********************

src/sage/matrix/matrix_mod2_dense.pyx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,14 +1393,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
13931393

13941394
def _magma_init_(self, magma):
13951395
"""
1396-
Return a string of ``self`` in ``Magma`` form. Does not return
1397-
``Magma`` object but string.
1396+
Return a string of ``self`` in ``Magma`` form.
1397+
1398+
This does not return a ``Magma`` object but a string.
13981399
13991400
EXAMPLES::
14001401
14011402
sage: A = random_matrix(GF(2),3,3)
14021403
sage: A._magma_init_(magma) # optional - magma
1403-
'Matrix(GF(2),3,3,StringToIntegerSequence("0 1 0 0 1 1 0 0 0"))'
1404+
'Matrix(GF(2),3,3,StringToIntegerSequence("..."))'
14041405
sage: A = random_matrix(GF(2),100,100)
14051406
sage: B = random_matrix(GF(2),100,100)
14061407
sage: magma(A*B) == magma(A) * magma(B) # optional - magma

src/sage/modular/btquotients/btquotient.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from collections import deque
4343

4444
from sage.arith.misc import gcd, xgcd, kronecker_symbol, fundamental_discriminant
45+
from sage.interfaces.magma import magma
4546
from sage.matrix.constructor import Matrix
4647
from sage.matrix.matrix_space import MatrixSpace
4748
from sage.misc.cachefunc import cached_method
@@ -2307,7 +2308,7 @@ def get_extra_embedding_matrices(self):
23072308
[1 0 2 0]
23082309
[0 0 2 0]
23092310
[0 0 0 0]
2310-
[1 0 2 2]
2311+
[1 2 2 0]
23112312
]
23122313
"""
23132314
if not self._use_magma or len(self._extra_level) == 0:
@@ -2700,7 +2701,7 @@ def get_splitting_field(self):
27002701
27012702
sage: X = BruhatTitsQuotient(5,11,use_magma=True) # optional - magma
27022703
sage: X.get_splitting_field() # optional - magma
2703-
Number Field in a with defining polynomial X1^2 + 11
2704+
Number Field in a with defining polynomial x^2 + 11
27042705
"""
27052706
if not self._use_magma:
27062707
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.')

0 commit comments

Comments
 (0)