passagemath-symbolics 10.6.40__cp314-cp314t-macosx_13_0_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-symbolics might be problematic. Click here for more details.

Files changed (172) hide show
  1. passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
  2. passagemath_symbolics/__init__.py +3 -0
  3. passagemath_symbolics-10.6.40.dist-info/METADATA +187 -0
  4. passagemath_symbolics-10.6.40.dist-info/RECORD +172 -0
  5. passagemath_symbolics-10.6.40.dist-info/WHEEL +6 -0
  6. passagemath_symbolics-10.6.40.dist-info/top_level.txt +3 -0
  7. sage/all__sagemath_symbolics.py +17 -0
  8. sage/calculus/all.py +14 -0
  9. sage/calculus/calculus.py +2826 -0
  10. sage/calculus/desolvers.py +1866 -0
  11. sage/calculus/predefined.py +51 -0
  12. sage/calculus/tests.py +225 -0
  13. sage/calculus/var.cpython-314t-darwin.so +0 -0
  14. sage/calculus/var.pyx +401 -0
  15. sage/dynamics/all__sagemath_symbolics.py +6 -0
  16. sage/dynamics/complex_dynamics/all.py +5 -0
  17. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-darwin.so +0 -0
  19. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
  20. sage/ext/all__sagemath_symbolics.py +1 -0
  21. sage/ext_data/kenzo/CP2.txt +45 -0
  22. sage/ext_data/kenzo/CP3.txt +349 -0
  23. sage/ext_data/kenzo/CP4.txt +4774 -0
  24. sage/ext_data/kenzo/README.txt +49 -0
  25. sage/ext_data/kenzo/S4.txt +20 -0
  26. sage/ext_data/magma/latex/latex.m +1021 -0
  27. sage/ext_data/magma/latex/latex.spec +1 -0
  28. sage/ext_data/magma/sage/basic.m +356 -0
  29. sage/ext_data/magma/sage/sage.spec +1 -0
  30. sage/ext_data/magma/spec +9 -0
  31. sage/geometry/all__sagemath_symbolics.py +8 -0
  32. sage/geometry/hyperbolic_space/all.py +5 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  39. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  40. sage/geometry/riemannian_manifolds/all.py +7 -0
  41. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  42. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  43. sage/interfaces/all__sagemath_symbolics.py +1 -0
  44. sage/interfaces/magma.py +3017 -0
  45. sage/interfaces/magma_free.py +92 -0
  46. sage/interfaces/maple.py +1397 -0
  47. sage/interfaces/mathematica.py +1345 -0
  48. sage/interfaces/mathics.py +1312 -0
  49. sage/interfaces/sympy.py +1398 -0
  50. sage/interfaces/sympy_wrapper.py +197 -0
  51. sage/interfaces/tides.py +938 -0
  52. sage/libs/all__sagemath_symbolics.py +6 -0
  53. sage/manifolds/all.py +7 -0
  54. sage/manifolds/calculus_method.py +555 -0
  55. sage/manifolds/catalog.py +437 -0
  56. sage/manifolds/chart.py +4019 -0
  57. sage/manifolds/chart_func.py +3419 -0
  58. sage/manifolds/continuous_map.py +2183 -0
  59. sage/manifolds/continuous_map_image.py +155 -0
  60. sage/manifolds/differentiable/affine_connection.py +2475 -0
  61. sage/manifolds/differentiable/all.py +1 -0
  62. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  63. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  64. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  65. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  66. sage/manifolds/differentiable/chart.py +1241 -0
  67. sage/manifolds/differentiable/curve.py +1028 -0
  68. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  69. sage/manifolds/differentiable/degenerate.py +559 -0
  70. sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
  71. sage/manifolds/differentiable/diff_form.py +1658 -0
  72. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  73. sage/manifolds/differentiable/diff_map.py +1315 -0
  74. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  75. sage/manifolds/differentiable/examples/all.py +1 -0
  76. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  77. sage/manifolds/differentiable/examples/real_line.py +897 -0
  78. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  79. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  80. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  81. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  82. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  83. sage/manifolds/differentiable/manifold.py +4254 -0
  84. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  85. sage/manifolds/differentiable/metric.py +3032 -0
  86. sage/manifolds/differentiable/mixed_form.py +1507 -0
  87. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  88. sage/manifolds/differentiable/multivector_module.py +800 -0
  89. sage/manifolds/differentiable/multivectorfield.py +1520 -0
  90. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  91. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  92. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  93. sage/manifolds/differentiable/scalarfield.py +1343 -0
  94. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  95. sage/manifolds/differentiable/symplectic_form.py +910 -0
  96. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  97. sage/manifolds/differentiable/tangent_space.py +412 -0
  98. sage/manifolds/differentiable/tangent_vector.py +616 -0
  99. sage/manifolds/differentiable/tensorfield.py +4665 -0
  100. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  101. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  102. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  103. sage/manifolds/differentiable/vector_bundle.py +1728 -0
  104. sage/manifolds/differentiable/vectorfield.py +1717 -0
  105. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  106. sage/manifolds/differentiable/vectorframe.py +1832 -0
  107. sage/manifolds/family.py +270 -0
  108. sage/manifolds/local_frame.py +1490 -0
  109. sage/manifolds/manifold.py +3090 -0
  110. sage/manifolds/manifold_homset.py +452 -0
  111. sage/manifolds/operators.py +359 -0
  112. sage/manifolds/point.py +994 -0
  113. sage/manifolds/scalarfield.py +3718 -0
  114. sage/manifolds/scalarfield_algebra.py +629 -0
  115. sage/manifolds/section.py +3111 -0
  116. sage/manifolds/section_module.py +831 -0
  117. sage/manifolds/structure.py +229 -0
  118. sage/manifolds/subset.py +2764 -0
  119. sage/manifolds/subsets/all.py +1 -0
  120. sage/manifolds/subsets/closure.py +131 -0
  121. sage/manifolds/subsets/pullback.py +885 -0
  122. sage/manifolds/topological_submanifold.py +891 -0
  123. sage/manifolds/trivialization.py +733 -0
  124. sage/manifolds/utilities.py +1348 -0
  125. sage/manifolds/vector_bundle.py +1342 -0
  126. sage/manifolds/vector_bundle_fiber.py +332 -0
  127. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  128. sage/matrix/all__sagemath_symbolics.py +1 -0
  129. sage/matrix/matrix_symbolic_dense.cpython-314t-darwin.so +0 -0
  130. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  131. sage/matrix/matrix_symbolic_dense.pyx +1022 -0
  132. sage/matrix/matrix_symbolic_sparse.cpython-314t-darwin.so +0 -0
  133. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  134. sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
  135. sage/modules/all__sagemath_symbolics.py +1 -0
  136. sage/modules/vector_callable_symbolic_dense.py +105 -0
  137. sage/modules/vector_symbolic_dense.py +116 -0
  138. sage/modules/vector_symbolic_sparse.py +118 -0
  139. sage/rings/all__sagemath_symbolics.py +4 -0
  140. sage/rings/asymptotic/all.py +6 -0
  141. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  142. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  143. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
  144. sage/rings/asymptotic/growth_group.py +5373 -0
  145. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  146. sage/rings/asymptotic/term_monoid.py +5237 -0
  147. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  148. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  149. sage/symbolic/all.py +15 -0
  150. sage/symbolic/assumptions.py +985 -0
  151. sage/symbolic/benchmark.py +93 -0
  152. sage/symbolic/callable.py +459 -0
  153. sage/symbolic/complexity_measures.py +35 -0
  154. sage/symbolic/constants.py +1287 -0
  155. sage/symbolic/expression_conversion_algebraic.py +310 -0
  156. sage/symbolic/expression_conversion_sympy.py +317 -0
  157. sage/symbolic/expression_conversions.py +1713 -0
  158. sage/symbolic/function_factory.py +355 -0
  159. sage/symbolic/integration/all.py +1 -0
  160. sage/symbolic/integration/external.py +270 -0
  161. sage/symbolic/integration/integral.py +1115 -0
  162. sage/symbolic/maxima_wrapper.py +162 -0
  163. sage/symbolic/operators.py +267 -0
  164. sage/symbolic/random_tests.py +462 -0
  165. sage/symbolic/relation.py +1907 -0
  166. sage/symbolic/ring.cpython-314t-darwin.so +0 -0
  167. sage/symbolic/ring.pxd +5 -0
  168. sage/symbolic/ring.pyx +1396 -0
  169. sage/symbolic/subring.py +1025 -0
  170. sage/symbolic/symengine.py +19 -0
  171. sage/symbolic/tests.py +40 -0
  172. sage/symbolic/units.py +1470 -0
@@ -0,0 +1,3017 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Interface to Magma
4
+
5
+ Sage provides an interface to the Magma computational algebra
6
+ system. This system provides extensive functionality for number
7
+ theory, group theory, combinatorics and algebra.
8
+
9
+ .. NOTE::
10
+
11
+ You must have Magma installed on your
12
+ computer for this interface to work. Magma is not free, so it is
13
+ not included with Sage, but you can obtain it from
14
+ https://magma.maths.usyd.edu.au/.
15
+
16
+ The Magma interface offers three pieces of functionality:
17
+
18
+ #. ``magma_console()`` -- a function that dumps you into an interactive command-line Magma session.
19
+
20
+ #. ``magma.new(obj)`` and alternatively ``magma(obj)`` -- creation of a Magma object from a Sage object ``obj``.
21
+ This provides a Pythonic interface to Magma. For example, if ``f=magma.new(10)``, then
22
+ ``f.Factors()`` returns the prime factorization of 10 computed using Magma. If obj is a string containing
23
+ an arbitrary Magma expression, then the expression is evaluated in Magma to create a Magma object. An example
24
+ is ``magma.new('10 div 3')``, which returns Magma integer 3.
25
+
26
+ #. ``magma.eval(expr)`` -- evaluation of the Magma expression ``expr``, with the result returned as a string.
27
+
28
+ Type ``magma.[tab]`` for a list of all functions available from your Magma.
29
+ Type ``magma.Function?`` for Magma's help about the Magma ``Function``.
30
+
31
+ Parameters
32
+ ----------
33
+
34
+ Some Magma functions have optional "parameters", which are
35
+ arguments that in Magma go after a colon. In Sage, you pass these
36
+ using named function arguments. For example,
37
+
38
+ ::
39
+
40
+ sage: E = magma('EllipticCurve([0,1,1,-1,0])') # optional - magma
41
+ sage: E.Rank(Bound = 5) # optional - magma
42
+ 0
43
+
44
+ Multiple Return Values
45
+ ----------------------
46
+
47
+ Some Magma functions return more than one value. You can control
48
+ how many you get using the ``nvals`` named parameter to
49
+ a function call::
50
+
51
+ sage: # optional - magma
52
+ sage: n = magma(100)
53
+ sage: n.IsSquare(nvals = 1)
54
+ true
55
+ sage: n.IsSquare(nvals = 2)
56
+ (true, 10)
57
+ sage: n = magma(-2006)
58
+ sage: n.Factorization()
59
+ [ <2, 1>, <17, 1>, <59, 1> ]
60
+ sage: n.Factorization(nvals=2)
61
+ ([ <2, 1>, <17, 1>, <59, 1> ], -1)
62
+
63
+ We verify that an obviously principal ideal is principal::
64
+
65
+ sage: # optional - magma
66
+ sage: _ = magma.eval('R<x> := PolynomialRing(RationalField())')
67
+ sage: O = magma.NumberField('x^2+23').MaximalOrder()
68
+ sage: I = magma('ideal<%s|%s.1>'%(O.name(),O.name()))
69
+ sage: I.IsPrincipal(nvals=2)
70
+ (true, [1, 0])
71
+
72
+ Long Input
73
+ ----------
74
+
75
+ The Magma interface reads in even very long input (using files) in
76
+ a robust manner.
77
+
78
+ ::
79
+
80
+ sage: t = '"%s"'%10^10000 # ten thousand character string. # optional - magma
81
+ sage: a = magma.eval(t) # optional - magma
82
+ sage: a = magma(t) # optional - magma
83
+
84
+ Garbage Collection
85
+ ------------------
86
+
87
+ There is a subtle point with the Magma interface, which arises from
88
+ how garbage collection works. Consider the following session:
89
+
90
+ First, create a matrix m in Sage::
91
+
92
+ sage: m=matrix(ZZ,2,[1,2,3,4]) # optional - magma
93
+
94
+ Then I create a corresponding matrix A in Magma::
95
+
96
+ sage: A = magma(m) # optional - magma
97
+
98
+ It is called _sage_[...] in Magma::
99
+
100
+ sage: s = A.name(); s # optional - magma
101
+ '_sage_[...]'
102
+
103
+ It's there::
104
+
105
+ sage: magma.eval(s) # optional - magma
106
+ '[1 2]\n[3 4]'
107
+
108
+ Now I delete the reference to that matrix::
109
+
110
+ sage: del A # optional - magma
111
+
112
+ Now _sage_[...] is "zeroed out" in the Magma session::
113
+
114
+ sage: magma.eval(s) # optional - magma
115
+ '0'
116
+
117
+ If Sage did not do this garbage collection, then every single time you
118
+ ever create any magma object from a sage object, e.g., by doing
119
+ magma(m), you would use up a lot of memory in that Magma session.
120
+ This would lead to a horrible memory leak situation, which would make
121
+ the Magma interface nearly useless for serious work.
122
+
123
+
124
+ Other Examples
125
+ --------------
126
+
127
+ We compute a space of modular forms with character.
128
+
129
+ ::
130
+
131
+ sage: N = 20
132
+ sage: D = 20
133
+ sage: eps_top = fundamental_discriminant(D)
134
+ sage: eps = magma.KroneckerCharacter(eps_top, RationalField()) # optional - magma
135
+ sage: M2 = magma.ModularForms(eps) # optional - magma
136
+ sage: print(M2) # optional - magma
137
+ Space of modular forms on Gamma_1(5) ...
138
+ sage: print(M2.Basis()) # optional - magma
139
+ [
140
+ 1 + 10*q^2 + 20*q^3 + 20*q^5 + 60*q^7 + ...
141
+ q + q^2 + 2*q^3 + 3*q^4 + 5*q^5 + 2*q^6 + ...
142
+ ]
143
+
144
+ In Sage/Python (and sort of C++) coercion of an element x into a
145
+ structure S is denoted by S(x). This also works for the Magma
146
+ interface::
147
+
148
+ sage: # optional - magma
149
+ sage: G = magma.DirichletGroup(20)
150
+ sage: G.AssignNames(['a', 'b'])
151
+ sage: (G.1).Modulus()
152
+ 20
153
+ sage: e = magma.DirichletGroup(40)(G.1)
154
+ sage: print(e)
155
+ Kronecker character -4 in modulus 40
156
+ sage: print(e.Modulus())
157
+ 40
158
+
159
+ We coerce some polynomial rings into Magma::
160
+
161
+ sage: R.<y> = PolynomialRing(QQ)
162
+ sage: S = magma(R) # optional - magma
163
+ sage: print(S) # optional - magma
164
+ Univariate Polynomial Ring in y over Rational Field
165
+ sage: S.1 # optional - magma
166
+ y
167
+
168
+ This example illustrates that Sage doesn't magically extend how
169
+ Magma implicit coercion (what there is, at least) works. The errors
170
+ below are the result of Magma having a rather limited automatic
171
+ coercion system compared to Sage's::
172
+
173
+ sage: R.<x> = ZZ[]
174
+ sage: x * 5
175
+ 5*x
176
+ sage: x * 1.0
177
+ x
178
+ sage: x * (2/3)
179
+ 2/3*x
180
+ sage: y = magma(x) # optional - magma
181
+ sage: y * 5 # optional - magma
182
+ 5*x
183
+ sage: y * 1.0 # optional - magma
184
+ $.1
185
+ sage: y * (2/3) # optional - magma
186
+ Traceback (most recent call last):
187
+ ...
188
+ TypeError: Error evaluating Magma code.
189
+ ...
190
+ Argument types given: RngUPolElt[RngInt], FldRatElt
191
+
192
+
193
+ AUTHORS:
194
+
195
+ - William Stein (2005): initial version
196
+
197
+ - William Stein (2006-02-28): added extensive tab completion and
198
+ interactive IPython documentation support.
199
+
200
+ - William Stein (2006-03-09): added nvals argument for
201
+ magma.functions...
202
+ """
203
+
204
+ # ****************************************************************************
205
+ # Copyright (C) 2005 William Stein <wstein@gmail.com>
206
+ #
207
+ # Distributed under the terms of the GNU General Public License (GPL)
208
+ #
209
+ # This code is distributed in the hope that it will be useful,
210
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
211
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
212
+ # General Public License for more details.
213
+ #
214
+ # The full text of the GPL is available at:
215
+ #
216
+ # https://www.gnu.org/licenses/
217
+ # ****************************************************************************
218
+ from pathlib import Path
219
+ import re
220
+ import sys
221
+ import os
222
+
223
+ from sage.structure.parent import Parent
224
+ from .expect import Expect, ExpectElement, ExpectFunction, FunctionElement
225
+ from sage.env import SAGE_EXTCODE, DOT_SAGE
226
+ import sage.misc.misc
227
+ import sage.misc.sage_eval
228
+ import sage.interfaces.abc
229
+ from sage.interfaces.tab_completion import ExtraTabCompletion
230
+ from sage.misc.instancedoc import instancedoc
231
+
232
+ PROMPT = ">>>"
233
+
234
+ SAGE_REF = "_sage_ref"
235
+ SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF)
236
+
237
+ INTRINSIC_CACHE = '%s/magma_intrinsic_cache.sobj' % DOT_SAGE
238
+ EXTCODE_DIR = None
239
+
240
+
241
+ def extcode_dir(iface=None) -> str:
242
+ """
243
+ Return directory that contains all the Magma extcode.
244
+
245
+ This is put in a writable directory owned by the user, since when
246
+ attached, Magma has to write sig and lck files.
247
+
248
+ EXAMPLES::
249
+
250
+ sage: from sage.interfaces.magma import extcode_dir
251
+ sage: extcode_dir()
252
+ '...dir_.../data/'
253
+ """
254
+ global EXTCODE_DIR
255
+ if not EXTCODE_DIR:
256
+ if iface is None or iface._server is None:
257
+ import shutil
258
+ tmp = sage.misc.temporary_file.tmp_dir()
259
+ shutil.copytree('%s/magma/' % SAGE_EXTCODE, tmp + '/data')
260
+ EXTCODE_DIR = "%s/data/" % tmp
261
+ else:
262
+ tmp = iface._remote_tmpdir()
263
+ command = 'scp -q -r "%s/magma/" "%s:%s/data" 1>&2 2>/dev/null' % (SAGE_EXTCODE, iface._server, tmp)
264
+ try:
265
+ ans = os.system(command)
266
+ EXTCODE_DIR = "%s/data/" % tmp
267
+ if ans != 0:
268
+ raise OSError
269
+ except OSError:
270
+ out_str = 'Tried to copy the file structure in "%s/magma/" to "%s:%s/data" and failed (possibly because scp is not installed in the system).\nFor the remote Magma to work you should populate the remote directory by some other method, or install scp in the system and retry.' % (SAGE_EXTCODE, iface._server, tmp)
271
+ from warnings import warn
272
+ warn(out_str)
273
+ return EXTCODE_DIR
274
+
275
+
276
+ class Magma(ExtraTabCompletion, Expect):
277
+ r"""
278
+ Interface to the Magma interpreter.
279
+
280
+ Type ``magma.[tab]`` for a list of all the functions
281
+ available from your Magma install. Type
282
+ ``magma.Function?`` for Magma's help about a given ``Function``
283
+ Type ``magma(...)`` to create a new Magma
284
+ object, and ``magma.eval(...)`` to run a string using
285
+ Magma (and get the result back as a string).
286
+
287
+ .. NOTE::
288
+
289
+ If you do not own a local copy of Magma, try using the
290
+ ``magma_free`` command instead, which uses the free demo web
291
+ interface to Magma.
292
+
293
+ If you have ssh access to a remote installation of Magma, you can
294
+ also set the ``server`` parameter to use it.
295
+
296
+ EXAMPLES:
297
+
298
+ You must use nvals = 0 to call a function that doesn't return
299
+ anything, otherwise you'll get an error. (nvals is the number of
300
+ return values.)
301
+
302
+ ::
303
+
304
+ sage: magma.SetDefaultRealFieldPrecision(200, nvals=0) # magma >= v2.12; optional - magma
305
+ sage: magma.eval('1.1') # optional - magma
306
+ '1.1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
307
+ sage: magma.SetDefaultRealFieldPrecision(30, nvals=0) # optional - magma
308
+ """
309
+ def __init__(self, script_subdirectory=None,
310
+ logfile=None, server=None, server_tmpdir=None,
311
+ user_config=False, seed=None, command=None):
312
+ """
313
+ INPUT:
314
+
315
+ - ``script_subdirectory`` -- directory where scripts
316
+ are read from
317
+
318
+ - ``logfile`` -- output logged to this file
319
+
320
+ - ``server`` -- address of remote server
321
+
322
+ - ``server_tmpdir`` -- temporary directory to use in remote server
323
+
324
+ - ``user_config`` -- if ``True``, then local user
325
+ configuration files will be read by Magma. If ``False`` (the default),
326
+ then Magma is started with the -n option which suppresses user
327
+ configuration files.
328
+
329
+ - ``seed`` -- seed to use in the random number generator
330
+
331
+ - ``command`` -- (default: ``'magma'``) the command to execute to start Magma
332
+
333
+ EXAMPLES::
334
+
335
+ sage: Magma(logfile=tmp_filename())
336
+ Magma
337
+ """
338
+ if command is None:
339
+ command = os.getenv('SAGE_MAGMA_COMMAND') or 'magma'
340
+
341
+ if not user_config:
342
+ command += ' -n'
343
+
344
+ # Obtain the parameters from the environment, to allow the magma = Magma() phrase
345
+ # to work with non-default parameters.
346
+ if seed is None:
347
+ seed = os.getenv('SAGE_MAGMA_SEED')
348
+
349
+ Expect.__init__(self,
350
+ name='magma',
351
+ prompt='>>SAGE>>',
352
+ command=command,
353
+ server=server,
354
+ server_tmpdir=server_tmpdir,
355
+ script_subdirectory=script_subdirectory,
356
+ restart_on_ctrlc=False,
357
+ logfile=logfile,
358
+ eval_using_file_cutoff=100)
359
+ # We use "-n" above in the Magma startup command so
360
+ # local user startup configuration is not read.
361
+
362
+ self.__seq = 0
363
+ self.__ref = 0
364
+ self.__available_var = []
365
+ self.__cache = {}
366
+ self._preparse_colon_equals = False # if set to True, all "=" become ":=" (some users really appreciate this)
367
+ self._seed = seed
368
+
369
+ def set_seed(self, seed=None):
370
+ """
371
+ Set the seed for the Magma interpreter.
372
+
373
+ The seed should be an integer.
374
+
375
+ EXAMPLES::
376
+
377
+ sage: m = Magma() # optional - magma
378
+ sage: m.set_seed(1) # optional - magma
379
+ 1
380
+ sage: [m.Random(100) for i in range(5)] # optional - magma
381
+ [14, 81, 45, 75, 67]
382
+ """
383
+ if seed is None:
384
+ seed = self.rand_seed()
385
+ self.eval('SetSeed(%d)' % seed)
386
+ self._seed = seed
387
+ return seed
388
+
389
+ def __reduce__(self):
390
+ """
391
+ Used to pickle a magma interface instance.
392
+
393
+ Unpickling results in the default magma interpreter; this is a
394
+ choice, and perhaps not the most logical one! It means that if you
395
+ make two distinct magma interfaces, pickle both, then unpickle
396
+ them, you get back exactly the same one. We illustrate this
397
+ behavior below.
398
+
399
+ OUTPUT: function, empty tuple
400
+
401
+ EXAMPLES::
402
+
403
+ sage: from sage.interfaces.magma import magma
404
+ sage: loads(dumps(magma)) is magma
405
+ True
406
+
407
+ Unpickling always gives the default global magma interpreter::
408
+
409
+ sage: m1 = Magma(); m2 = Magma()
410
+ sage: m1 is m2
411
+ False
412
+ sage: loads(dumps(m1)) is loads(dumps(m2))
413
+ True
414
+ sage: loads(dumps(m1)) is magma
415
+ True
416
+ """
417
+ return reduce_load_Magma, tuple([])
418
+
419
+ def _read_in_file_command(self, filename) -> str:
420
+ """
421
+ Return the command in Magma that reads in the contents of the given
422
+ file.
423
+
424
+ INPUT:
425
+
426
+ - ``filename`` -- string
427
+
428
+ OUTPUT:
429
+
430
+ - ``string`` -- a magma command
431
+
432
+ EXAMPLES::
433
+
434
+ sage: magma._read_in_file_command('file.m')
435
+ 'load "file.m";'
436
+ """
437
+ return 'load "%s";' % filename
438
+
439
+ def _post_process_from_file(self, s) -> str:
440
+ r"""
441
+ Used internally in the Magma interface to post-process the result
442
+ of evaluating a string using a file. For Magma what this does is
443
+ delete the first output line, since that is a verbose output line
444
+ that Magma displays about loading a file.
445
+
446
+ INPUT:
447
+
448
+ - ``s`` -- string
449
+
450
+ OUTPUT: string
451
+
452
+ EXAMPLES::
453
+
454
+ sage: magma._post_process_from_file("Loading ...\nHello")
455
+ 'Hello'
456
+ sage: magma._post_process_from_file("Hello")
457
+ ''
458
+ """
459
+ if not isinstance(s, str):
460
+ raise RuntimeError("Error evaluating object in %s:\n%s" % (self, s))
461
+ # Chop off the annoying "Loading ... " message that Magma
462
+ # always outputs no matter what.
463
+ i = s.find('\n')
464
+ if i == -1: # special case -- command produced no output, so no \n
465
+ return ''
466
+ return s[i + 1:]
467
+
468
+ def __getattr__(self, attrname):
469
+ """
470
+ Return a formal wrapper around a Magma function, or raise an
471
+ :exc:`AttributeError` if attrname starts with an underscore.
472
+
473
+ INPUT:
474
+
475
+ - ``attrname`` -- string
476
+
477
+ OUTPUT: :class:`MagmaFunction` instance
478
+
479
+ EXAMPLES::
480
+
481
+ sage: g = magma.__getattr__('EllipticCurve')
482
+ sage: g
483
+ EllipticCurve
484
+ sage: type(g)
485
+ <class 'sage.interfaces.magma.MagmaFunction'>
486
+
487
+ In fact, __getattr__ is called implicitly in the following
488
+ case::
489
+
490
+ sage: f = magma.EllipticCurve
491
+ sage: type(f)
492
+ <class 'sage.interfaces.magma.MagmaFunction'>
493
+ sage: f
494
+ EllipticCurve
495
+ """
496
+ if attrname[:1] == "_":
497
+ raise AttributeError
498
+ return MagmaFunction(self, attrname)
499
+
500
+ def eval(self, x, strip=True, **kwds) -> str:
501
+ """
502
+ Evaluate the given block x of code in Magma and return the output
503
+ as a string.
504
+
505
+ INPUT:
506
+
507
+ - ``x`` -- string of code
508
+
509
+ - ``strip`` -- ignored
510
+
511
+ OUTPUT: string
512
+
513
+ EXAMPLES:
514
+
515
+ We evaluate a string that involves assigning to a
516
+ variable and printing.
517
+
518
+ ::
519
+
520
+ sage: magma.eval("a := 10;print 2+a;") # optional - magma
521
+ '12'
522
+
523
+ We evaluate a large input line (note that no weird output appears
524
+ and that this works quickly).
525
+
526
+ ::
527
+
528
+ sage: magma.eval("a := %s;"%(10^10000)) # optional - magma
529
+ ''
530
+
531
+ Verify that :issue:`9705` is fixed::
532
+
533
+ sage: nl=chr(10) # newline character
534
+ sage: magma.eval( # optional - magma
535
+ ....: "_<x>:=PolynomialRing(Rationals());"+nl+
536
+ ....: "repeat"+nl+
537
+ ....: " g:=3*b*x^4+18*c*x^3-6*b^2*x^2-6*b*c*x-b^3-9*c^2 where b:=Random([-10..10]) where c:=Random([-10..10]);"+nl+
538
+ ....: "until g ne 0 and Roots(g) ne [];"+nl+
539
+ ....: "print \"success\";")
540
+ 'success'
541
+
542
+ Verify that :issue:`11401` is fixed::
543
+
544
+ sage: nl=chr(10) # newline character
545
+ sage: magma.eval("a:=3;"+nl+"b:=5;") == nl # optional - magma
546
+ True
547
+ sage: magma.eval("[a,b];") # optional - magma
548
+ '[ 3, 5 ]'
549
+ """
550
+ x = self._preparse(x)
551
+ x = str(x).rstrip()
552
+ if len(x) == 0 or x[len(x) - 1] != ';':
553
+ x += ';'
554
+ ans = Expect.eval(self, x, **kwds).replace('\\\n', '')
555
+ if 'Runtime error' in ans or 'User error' in ans:
556
+ raise RuntimeError("Error evaluating Magma code.\nIN:%s\nOUT:%s" % (x, ans))
557
+ return ans
558
+
559
+ def _preparse(self, s) -> str:
560
+ """
561
+ All input gets preparsed by calling this function before it gets evaluated.
562
+
563
+ EXAMPLES::
564
+
565
+ sage: magma = Magma()
566
+ sage: magma._preparse_colon_equals = False
567
+ sage: magma._preparse('a = 5')
568
+ 'a = 5'
569
+ sage: magma._preparse_colon_equals = True
570
+ sage: magma._preparse('a = 5')
571
+ 'a := 5'
572
+ sage: magma._preparse('a = 5; b := 7; c =a+b;')
573
+ 'a := 5; b := 7; c :=a+b;'
574
+ """
575
+ try:
576
+ # this is in a try/except only because of the possibility
577
+ # of old pickled Magma interfaces.
578
+ if self._preparse_colon_equals:
579
+ s = s.replace(':=', '=').replace('=', ':=')
580
+ except AttributeError:
581
+ pass
582
+ return s
583
+
584
+ def _start(self) -> None:
585
+ """
586
+ Initialize a Magma interface instance. This involves (1) setting up
587
+ an obfuscated prompt, and (2) attaching the MAGMA_SPEC file (see
588
+ EXTCODE_DIR/spec file (see sage.interfaces.magma.EXTCODE_DIR/spec).
589
+
590
+ EXAMPLES: This is not too exciting::
591
+
592
+ sage: magma._start() # optional - magma
593
+ """
594
+ self._change_prompt('>')
595
+ Expect._start(self)
596
+ self.eval('SetPrompt("%s"); SetLineEditor(false); SetColumns(0);' % PROMPT)
597
+ self._change_prompt(PROMPT)
598
+ self.expect().expect(PROMPT)
599
+ self.expect().expect(PROMPT)
600
+ self.expect().expect(PROMPT)
601
+ self.attach_spec(extcode_dir(self) + '/spec')
602
+ # set random seed
603
+ self.set_seed(self._seed)
604
+
605
+ def set(self, var, value):
606
+ """
607
+ Set the variable var to the given value in the Magma interpreter.
608
+
609
+ INPUT:
610
+
611
+ - ``var`` -- string; a variable name
612
+
613
+ - ``value`` -- string; what to set var equal to
614
+
615
+ EXAMPLES::
616
+
617
+ sage: magma.set('abc', '2 + 3/5') # optional - magma
618
+ sage: magma('abc') # optional - magma
619
+ 13/5
620
+ """
621
+ out = self.eval("%s:=%s" % (var, value))
622
+ if out.lower().find("error") != -1:
623
+ raise TypeError("Error executing Magma code:\n%s" % out)
624
+
625
+ def get(self, var) -> str:
626
+ """
627
+ Get the value of the variable var.
628
+
629
+ INPUT:
630
+
631
+ - ``var`` -- string; name of a variable defined in the
632
+ Magma session
633
+
634
+ OUTPUT: string representation of the value of the variable
635
+
636
+ EXAMPLES::
637
+
638
+ sage: magma.set('abc', '2 + 3/5') # optional - magma
639
+ sage: magma.get('abc') # optional - magma
640
+ '13/5'
641
+ """
642
+ return self.eval("%s" % var)
643
+
644
+ def objgens(self, value, gens):
645
+ """
646
+ Create a new object with given value and gens.
647
+
648
+ INPUT:
649
+
650
+ - ``value`` -- something coercible to an element of this Magma
651
+ interface
652
+
653
+ - ``gens`` -- string; comma separated list of variable names
654
+
655
+ OUTPUT: new Magma element that is equal to value with given gens
656
+
657
+ EXAMPLES::
658
+
659
+ sage: R = magma.objgens('PolynomialRing(Rationals(),2)', 'alpha,beta') # optional - magma
660
+ sage: R.gens() # optional - magma
661
+ (alpha, beta)
662
+
663
+ Because of how Magma works you can use this to change the variable
664
+ names of the generators of an object::
665
+
666
+ sage: S = magma.objgens(R, 'X,Y') # optional - magma
667
+ sage: R # optional - magma
668
+ Polynomial ring of rank 2 over Rational Field
669
+ Order: Lexicographical
670
+ Variables: X, Y
671
+ sage: S # optional - magma
672
+ Polynomial ring of rank 2 over Rational Field
673
+ Order: Lexicographical
674
+ Variables: X, Y
675
+ """
676
+ var = self._next_var_name()
677
+ value = self(value)
678
+ out = self.eval("_zsage_<%s> := %s; %s := _zsage_" % (gens,
679
+ value.name(),
680
+ var))
681
+ if out.lower().find("error") != -1:
682
+ raise TypeError("Error executing Magma code:\n%s" % out)
683
+ return self(var)
684
+
685
+ def __call__(self, x, gens=None):
686
+ """
687
+ Coerce x into this Magma interpreter interface.
688
+
689
+ INPUT:
690
+
691
+ - ``x`` -- object
692
+
693
+ - ``gens`` -- string; names of generators of self,
694
+ separated by commas
695
+
696
+ OUTPUT: :class:`MagmaElement`
697
+
698
+ EXAMPLES::
699
+
700
+ sage: # optional - magma
701
+ sage: magma(EllipticCurve('37a'))
702
+ Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
703
+ sage: magma('EllipticCurve([GF(5)|1,2,3,4,1])')
704
+ Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 1 over GF(5)
705
+ sage: magma('PowerSeriesRing(Rationals())', 't')
706
+ Power series ring in t over Rational Field
707
+ sage: magma('PolynomialRing(RationalField(), 3)', 'x,y,z')
708
+ Polynomial ring of rank 3 over Rational Field
709
+ Order: Lexicographical
710
+ Variables: x, y, z
711
+
712
+ We test a coercion between different Magma instances::
713
+
714
+ sage: m = Magma()
715
+ sage: n = Magma()
716
+ sage: a = n(m(2)) # optional - magma
717
+ sage: a.parent() is n # optional - magma
718
+ True
719
+ sage: a.parent() is m # optional - magma
720
+ False
721
+
722
+ We test caching::
723
+
724
+ sage: # optional - magma
725
+ sage: R.<x> = ZZ[]
726
+ sage: magma(R) is magma(R)
727
+ True
728
+ sage: m = Magma()
729
+ sage: m(R)
730
+ Univariate Polynomial Ring in x over Integer Ring
731
+ sage: m(R) is magma(R)
732
+ False
733
+ sage: R._magma_cache
734
+ {Magma: Univariate Polynomial Ring in x over Integer Ring,
735
+ Magma: Univariate Polynomial Ring in x over Integer Ring}
736
+
737
+ sage: # optional - magma
738
+ sage: P.<x,y> = PolynomialRing(GF(127))
739
+ sage: m = Magma()
740
+ sage: m(P)
741
+ Polynomial ring of rank 2 over GF(127)
742
+ Order: Graded Reverse Lexicographical
743
+ Variables: x, y
744
+ sage: P._magma_cache
745
+ {Magma: Polynomial ring of rank 2 over GF(127)
746
+ Order: Graded Reverse Lexicographical
747
+ Variables: x, y}
748
+ """
749
+ if isinstance(x, bool):
750
+ return Expect.__call__(self, 'true' if x else 'false')
751
+
752
+ if gens is not None: # get rid of this at some point -- it's weird
753
+ return self.objgens(x, gens)
754
+
755
+ # This is mostly about caching the Magma element in the object
756
+ # itself below. Note that it is *very* important that caching
757
+ # happen on the object itself, and not in a dictionary that is
758
+ # held by the Magma interface, since we want garbage collection
759
+ # of the objects in the Magma interface to work correctly.
760
+ has_cache = hasattr(x, '_magma_cache')
761
+ try:
762
+ if has_cache and self in x._magma_cache:
763
+ A = x._magma_cache[self]
764
+ if A._session_number == self._session_number:
765
+ return A
766
+ except AttributeError:
767
+ # This happens when x has _magma_cache as a cdef public object attribute.
768
+ x._magma_cache = {}
769
+
770
+ try:
771
+ if x in self.__cache:
772
+ A = self.__cache[x]
773
+ if A._session_number == self._session_number:
774
+ return A
775
+ except TypeError: # if x isn't hashable
776
+ pass
777
+
778
+ A = Expect.__call__(self, x)
779
+ if has_cache:
780
+ x._magma_cache[self] = A
781
+ else:
782
+ try: # use try/except here, because if x is cdef'd we won't be able to set this.
783
+ x._magma_cache = {self: A}
784
+ except AttributeError:
785
+ # Unfortunately, we *have* do have this __cache
786
+ # attribute, which can lead to "leaks" in the working
787
+ # Magma session. This is because it is critical that
788
+ # parent objects get cached, but sometimes they can't
789
+ # be cached in the object itself, because the object
790
+ # doesn't have a _magma_cache attribute. So in such
791
+ # cases when the object is a parent we cache it in
792
+ # the magma interface.
793
+ if isinstance(x, Parent):
794
+ self.__cache[x] = A
795
+ return A
796
+
797
+ def _coerce_from_special_method(self, x):
798
+ """
799
+ Try to coerce to ``self`` by calling a special underscore method.
800
+
801
+ If no such method is defined, raises an :exc:`AttributeError` instead
802
+ of a :exc:`TypeError`.
803
+
804
+ EXAMPLES::
805
+
806
+ sage: magma._coerce_from_special_method(-3/5) # optional - magma
807
+ -3/5
808
+
809
+ Note that AttributeError::
810
+
811
+ sage: magma._coerce_from_special_method('2 + 3') # optional - magma
812
+ Traceback (most recent call last):
813
+ ...
814
+ AttributeError: 'str' object has no attribute '_magma_init_'...
815
+ """
816
+ s = x._magma_init_(self)
817
+ a = self(s)
818
+
819
+ # dereference all _sage_ref's used in this string.
820
+ while True:
821
+ z = SAGE_REF_RE.search(s)
822
+ if not z:
823
+ break
824
+ self.eval('delete %s;' % s[z.start():z.end()])
825
+ s = s[z.end()+1:]
826
+ return a
827
+
828
+ def _with_names(self, s, names):
829
+ """
830
+ Return s but wrapped by a call to SageCreateWithNames. This is just
831
+ a very simple convenience function so that code is cleaner.
832
+
833
+ INPUT:
834
+
835
+ - ``s`` -- string
836
+
837
+ - ``names`` -- list of strings
838
+
839
+ OUTPUT: string
840
+
841
+ EXAMPLES::
842
+
843
+ sage: magma._with_names('PolynomialRing(RationalField())', ['y']) # optional - magma
844
+ 'SageCreateWithNames(PolynomialRing(RationalField()),["y"])'
845
+ """
846
+ return 'SageCreateWithNames(%s,[%s])' % (s, ','.join('"%s"' % x
847
+ for x in names))
848
+
849
+ def clear(self, var):
850
+ """
851
+ Clear the variable named var and make it available to be used
852
+ again.
853
+
854
+ INPUT:
855
+
856
+ - ``var`` -- string
857
+
858
+ EXAMPLES::
859
+
860
+ sage: magma = Magma() # optional - magma
861
+ sage: magma.clear('foo') # sets foo to 0 in magma; optional - magma
862
+ sage: magma.eval('foo') # optional - magma
863
+ '0'
864
+
865
+ Because we cleared foo, it is set to be used as a variable name in
866
+ the future::
867
+
868
+ sage: a = magma('10') # optional - magma
869
+ sage: a.name() # optional - magma
870
+ 'foo'
871
+
872
+ The following tests that the whole variable clearing and freeing
873
+ system is working correctly.
874
+
875
+ ::
876
+
877
+ sage: # optional - magma
878
+ sage: magma = Magma()
879
+ sage: a = magma('100')
880
+ sage: a.name()
881
+ '_sage_[1]'
882
+ sage: del a
883
+ sage: b = magma('257')
884
+ sage: b.name()
885
+ '_sage_[1]'
886
+ sage: del b
887
+ sage: magma('_sage_[1]')
888
+ 0
889
+ """
890
+ self.__available_var.insert(0, var) # adds var to front of list
891
+ self.eval("%s:=0" % var)
892
+
893
+ def cputime(self, t=None):
894
+ """
895
+ Return the CPU time in seconds that has elapsed since this Magma
896
+ session started. This is a floating point number, computed by
897
+ Magma.
898
+
899
+ If t is given, then instead return the floating point time from
900
+ when t seconds had elapsed. This is useful for computing elapsed
901
+ times between two points in a running program.
902
+
903
+ INPUT:
904
+
905
+ - ``t`` -- float (default: ``None``); if not None, return
906
+ cputime since t
907
+
908
+ OUTPUT:
909
+
910
+ - ``float`` -- seconds
911
+
912
+ EXAMPLES::
913
+
914
+ sage: # optional - magma
915
+ sage: type(magma.cputime())
916
+ <... 'float'>
917
+ sage: magma.cputime() # random
918
+ 1.9399999999999999
919
+ sage: t = magma.cputime()
920
+ sage: magma.cputime(t) # random
921
+ 0.02
922
+ """
923
+ if t:
924
+ return float(self.eval('Cputime(%s)' % t))
925
+ return float(self.eval('Cputime()'))
926
+
927
+ def chdir(self, dir):
928
+ """
929
+ Change the Magma interpreter's current working directory.
930
+
931
+ INPUT:
932
+
933
+ - ``dir`` -- string
934
+
935
+ EXAMPLES::
936
+
937
+ sage: magma.chdir('/') # optional - magma
938
+ sage: magma.eval('System("pwd")') # optional - magma
939
+ '/'
940
+ """
941
+ self.eval('ChangeDirectory("%s")' % dir, strip=False)
942
+
943
+ def attach(self, filename):
944
+ r"""
945
+ Attach the given file to the running instance of Magma.
946
+
947
+ Attaching a file in Magma makes all intrinsics defined in the file
948
+ available to the shell. Moreover, if the file doesn't start with
949
+ the ``freeze;`` command, then the file is reloaded
950
+ whenever it is changed. Note that functions and procedures defined
951
+ in the file are *not* available. For only those, use
952
+ ``magma.load(filename)``.
953
+
954
+ INPUT:
955
+
956
+ - ``filename`` -- string
957
+
958
+ EXAMPLES: Attaching a file that exists is fine::
959
+
960
+ sage: SAGE_EXTCODE = SAGE_ENV['SAGE_EXTCODE'] # optional - magma
961
+ sage: magma.attach('%s/magma/sage/basic.m'%SAGE_EXTCODE) # optional - magma
962
+
963
+ Attaching a file that doesn't exist raises an exception::
964
+
965
+ sage: SAGE_EXTCODE = SAGE_ENV['SAGE_EXTCODE'] # optional - magma
966
+ sage: magma.attach('%s/magma/sage/basic2.m'%SAGE_EXTCODE) # optional - magma
967
+ Traceback (most recent call last):
968
+ ...
969
+ RuntimeError: Error evaluating Magma code...
970
+ """
971
+ self.eval('Attach("%s")' % filename)
972
+
973
+ Attach = attach
974
+
975
+ def attach_spec(self, filename):
976
+ r"""
977
+ Attach the given spec file to the running instance of Magma.
978
+
979
+ This can attach numerous other files to the running Magma (see the
980
+ Magma documentation for more details).
981
+
982
+ INPUT:
983
+
984
+ - ``filename`` -- string
985
+
986
+ EXAMPLES::
987
+
988
+ sage: SAGE_EXTCODE = SAGE_ENV['SAGE_EXTCODE'] # optional - magma
989
+ sage: magma.attach_spec('%s/magma/spec'%SAGE_EXTCODE) # optional - magma
990
+ sage: magma.attach_spec('%s/magma/spec2'%SAGE_EXTCODE) # optional - magma
991
+ Traceback (most recent call last):
992
+ ...
993
+ RuntimeError: Can't open package spec file .../magma/spec2 for reading (No such file or directory)
994
+ """
995
+ s = self.eval('AttachSpec("%s")' % filename)
996
+ if s:
997
+ raise RuntimeError(s.strip())
998
+
999
+ AttachSpec = attach_spec
1000
+
1001
+ def load(self, filename):
1002
+ r"""
1003
+ Load the file with given filename using the 'load' command in the
1004
+ Magma shell.
1005
+
1006
+ Loading a file in Magma makes all the functions and procedures in
1007
+ the file available. The file should not contain any intrinsics (or
1008
+ you will get errors). It also runs code in the file, which can
1009
+ produce output.
1010
+
1011
+ INPUT:
1012
+
1013
+ - ``filename`` -- string
1014
+
1015
+ OUTPUT: output printed when loading the file
1016
+
1017
+ EXAMPLES::
1018
+
1019
+ sage: from tempfile import NamedTemporaryFile as NTF
1020
+ sage: with NTF(mode='w+t', suffix='.m') as f: # optional - magma
1021
+ ....: _ = f.write('function f(n) return n^2; end function;\nprint "hi";')
1022
+ ....: print(magma.load(f.name))
1023
+ Loading "....m"
1024
+ hi
1025
+ sage: magma('f(12)') # optional - magma
1026
+ 144
1027
+ """
1028
+ p = Path(filename)
1029
+ return self.eval('load "%s"' % p.absolute())
1030
+
1031
+ def _next_var_name(self) -> str:
1032
+ """
1033
+ Return the next available variable name in Magma.
1034
+
1035
+ OUTPUT: string
1036
+
1037
+ EXAMPLES::
1038
+
1039
+ sage: m = Magma()
1040
+ sage: m._next_var_name() # optional - magma
1041
+ '_sage_[1]'
1042
+ sage: m._next_var_name() # optional - magma
1043
+ '_sage_[2]'
1044
+ sage: a = m(3/8); a # optional - magma
1045
+ 3/8
1046
+ sage: a.name() # optional - magma
1047
+ '_sage_[3]'
1048
+ sage: m._next_var_name() # optional - magma
1049
+ '_sage_[4]'
1050
+ """
1051
+ if self.__seq == 0:
1052
+ self.eval('_sage_ := [* *];')
1053
+ else:
1054
+ try:
1055
+ self.eval('Append(~_sage_, 0);')
1056
+ except Exception:
1057
+ # this exception could happen if the Magma process
1058
+ # was interrupted during startup / initialization.
1059
+ self.eval('_sage_ := [* 0 : i in [1..%s] *];' % self.__seq)
1060
+ try:
1061
+ return self.__available_var.pop()
1062
+ except IndexError:
1063
+ self.__seq += 1
1064
+ return '_sage_[%s]' % self.__seq
1065
+
1066
+ def _next_ref_name(self):
1067
+ """
1068
+ Return the next reference name. This is used internally to deal
1069
+ with Magma objects that would be deallocated before they are used
1070
+ in constructing another object.
1071
+
1072
+ OUTPUT: string
1073
+
1074
+ EXAMPLES::
1075
+
1076
+ sage: magma._next_ref_name()
1077
+ '_sage_ref...'
1078
+ """
1079
+ self.__ref += 1
1080
+ return '%s%s' % (SAGE_REF, self.__ref)
1081
+
1082
+ def function_call(self, function, args=[], params={}, nvals=1):
1083
+ """
1084
+ Return result of evaluating a Magma function with given input,
1085
+ parameters, and asking for nvals as output.
1086
+
1087
+ INPUT:
1088
+
1089
+ - ``function`` -- string, a Magma function name
1090
+
1091
+ - ``args`` -- list of objects coercible into this magma
1092
+ interface
1093
+
1094
+ - ``params`` -- Magma parameters, passed in after a
1095
+ colon
1096
+
1097
+ - ``nvals`` -- number of return values from the
1098
+ function to ask Magma for
1099
+
1100
+ OUTPUT: instance of :class:`MagmaElement` or a tuple of ``nvals`` many
1101
+ :class:`MagmaElement` instances
1102
+
1103
+ EXAMPLES::
1104
+
1105
+ sage: magma.function_call('Factorization', 100) # optional - magma
1106
+ [ <2, 2>, <5, 2> ]
1107
+ sage: magma.function_call('NextPrime', 100, {'Proof':False}) # optional - magma
1108
+ 101
1109
+ sage: magma.function_call('PolynomialRing', [QQ,2]) # optional - magma
1110
+ Polynomial ring of rank 2 over Rational Field
1111
+ Order: Lexicographical
1112
+ Variables: $.1, $.2
1113
+
1114
+ Next, we illustrate multiple return values::
1115
+
1116
+ sage: magma.function_call('IsSquare', 100) # optional - magma
1117
+ true
1118
+ sage: magma.function_call('IsSquare', 100, nvals=2) # optional - magma
1119
+ (true, 10)
1120
+ sage: magma.function_call('IsSquare', 100, nvals=3) # optional - magma
1121
+ Traceback (most recent call last):
1122
+ ...
1123
+ RuntimeError: Error evaluating Magma code...
1124
+ Runtime error in :=: Expected to assign 3 value(s) but only computed 2 value(s)
1125
+ """
1126
+ args, params = self._convert_args_kwds(args, params)
1127
+ nvals = int(nvals)
1128
+ if len(params) == 0:
1129
+ par = ''
1130
+ else:
1131
+ par = ' : ' + ','.join('%s:=%s' % (a, b.name())
1132
+ for a, b in params.items())
1133
+
1134
+ fun = "%s(%s%s)" % (function, ",".join(s.name() for s in args), par)
1135
+
1136
+ return self._do_call(fun, nvals)
1137
+
1138
+ def _do_call(self, code, nvals):
1139
+ """
1140
+ Evaluate the given code expression assuming that it outputs nvals
1141
+ distinct values. Return the resulting values as a tuple if nvals =
1142
+ 2.
1143
+
1144
+ INPUT:
1145
+
1146
+ - ``code`` -- string; code to evaluate
1147
+
1148
+ - ``nvals`` -- integer; number of return values
1149
+
1150
+ OUTPUT: nvals distinct values
1151
+
1152
+ EXAMPLES::
1153
+
1154
+ sage: magma._do_call('SetVerbose("Groebner",2)', 0) # optional - magma
1155
+ sage: magma._do_call('Factorization(-5)', 1) # optional - magma
1156
+ [ <5, 1> ]
1157
+
1158
+ Here we get two outputs, as a tuple.
1159
+
1160
+ ::
1161
+
1162
+ sage: magma._do_call('Factorization(-5)', 2) # optional - magma
1163
+ ([ <5, 1> ], -1)
1164
+
1165
+ You can also do this::
1166
+
1167
+ sage: F, sign = magma._do_call('Factorization(-5)', 2) # optional - magma
1168
+ sage: F # optional - magma
1169
+ [ <5, 1> ]
1170
+ sage: sign # optional - magma
1171
+ -1
1172
+
1173
+ An expression that has one value.
1174
+
1175
+ ::
1176
+
1177
+ sage: magma._do_call('3^5', 1) # optional - magma
1178
+ 243
1179
+ """
1180
+ if nvals <= 0:
1181
+ out = self.eval(code)
1182
+ ans = None
1183
+ elif nvals == 1:
1184
+ return self(code)
1185
+ else:
1186
+ v = [self._next_var_name() for _ in range(nvals)]
1187
+ vars = ", ".join(v)
1188
+ cmd = "%s := %s;" % (vars, code)
1189
+ out = self.eval(cmd)
1190
+ ans = tuple([MagmaElement(self, x, is_name=True) for x in v])
1191
+
1192
+ if out.lower().find("error") != -1:
1193
+ raise TypeError("Error executing Magma code:\n%s" % out)
1194
+ return ans
1195
+
1196
+ def bar_call(self, left, name, gens, nvals=1):
1197
+ """
1198
+ This is a wrapper around the Magma constructor.
1199
+
1200
+ nameleft gens
1201
+
1202
+ returning nvals.
1203
+
1204
+ INPUT:
1205
+
1206
+ - ``left`` -- something coerceable to a magma object
1207
+
1208
+ - ``name`` -- name of the constructor, e.g., sub, quo,
1209
+ ideal, etc.
1210
+
1211
+ - ``gens`` -- if a list/tuple, each item is coerced to
1212
+ magma; otherwise gens itself is converted to magma
1213
+
1214
+ - ``nvals`` -- positive integer; number of return
1215
+ values
1216
+
1217
+ OUTPUT: a single magma object if nvals == 1; otherwise a tuple of
1218
+ nvals magma objects.
1219
+
1220
+ EXAMPLES: The bar_call function is used by the sub, quo, and ideal
1221
+ methods of Magma elements. Here we illustrate directly using
1222
+ bar_call to create quotients::
1223
+
1224
+ sage: # optional - magma
1225
+ sage: V = magma.RModule(ZZ,3)
1226
+ sage: V
1227
+ RModule(IntegerRing(), 3)
1228
+ sage: magma.bar_call(V, 'quo', [[1,2,3]], nvals=1)
1229
+ RModule(IntegerRing(), 2)
1230
+ sage: magma.bar_call(V, 'quo', [[1,2,3]], nvals=2)
1231
+ (RModule(IntegerRing(), 2),
1232
+ Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 2))
1233
+ sage: magma.bar_call(V, 'quo', V, nvals=2)
1234
+ (RModule(IntegerRing(), 0),
1235
+ Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 0))
1236
+ """
1237
+ magma = self
1238
+ # coerce each arg to be a Magma element
1239
+ if isinstance(gens, (list, tuple)):
1240
+ gens = [magma(z) for z in gens]
1241
+ # make comma separated list of names (in Magma) of each of the gens
1242
+ v = ', '.join(w.name() for w in gens)
1243
+ else:
1244
+ gens = magma(gens)
1245
+ v = gens.name()
1246
+ # construct the string that evaluates in Magma to define the subobject,
1247
+ # and return it evaluated in Magma.
1248
+ s = '%s< %s | %s >' % (name, left.name(), v)
1249
+ return self._do_call(s, nvals)
1250
+
1251
+ def _object_class(self):
1252
+ """
1253
+ Return the Python class of elements of the Magma interface.
1254
+
1255
+ OUTPUT: a Python class
1256
+
1257
+ EXAMPLES::
1258
+
1259
+ sage: magma._object_class()
1260
+ <class 'sage.interfaces.magma.MagmaElement'>
1261
+ """
1262
+ return MagmaElement
1263
+
1264
+ # Usually "Sequences" are what you want in Magma, not "lists".
1265
+ # It's very painful using the interface without this.
1266
+ def _left_list_delim(self):
1267
+ """
1268
+ Return the left sequence delimiter in Magma.
1269
+
1270
+ Despite the name in this function, this is really the least
1271
+ painful choice.
1272
+
1273
+ EXAMPLES::
1274
+
1275
+ sage: magma._left_list_delim()
1276
+ '['
1277
+ """
1278
+ return "["
1279
+
1280
+ def _right_list_delim(self):
1281
+ """
1282
+ Return the right sequence delimiter in Magma.
1283
+
1284
+ Despite the name in this function, this is really the least
1285
+ painful choice.
1286
+
1287
+ EXAMPLES::
1288
+
1289
+ sage: magma._right_list_delim()
1290
+ ']'
1291
+ """
1292
+ return "]"
1293
+
1294
+ def _assign_symbol(self):
1295
+ """
1296
+ Return the assignment symbol in Magma.
1297
+
1298
+ EXAMPLES::
1299
+
1300
+ sage: magma._assign_symbol()
1301
+ ':='
1302
+ """
1303
+ return ":="
1304
+
1305
+ def _equality_symbol(self):
1306
+ """
1307
+ Return the equality testing logical symbol in Magma.
1308
+
1309
+ EXAMPLES::
1310
+
1311
+ sage: magma._equality_symbol()
1312
+ 'eq'
1313
+ """
1314
+ return 'eq'
1315
+
1316
+ def _lessthan_symbol(self):
1317
+ """
1318
+ Return the less than testing logical symbol in Magma.
1319
+
1320
+ EXAMPLES::
1321
+
1322
+ sage: magma._lessthan_symbol()
1323
+ ' lt '
1324
+ """
1325
+ return ' lt '
1326
+
1327
+ def _greaterthan_symbol(self):
1328
+ """
1329
+ Return the greater than testing logical symbol in Magma.
1330
+
1331
+ EXAMPLES::
1332
+
1333
+ sage: magma._greaterthan_symbol()
1334
+ ' gt '
1335
+ """
1336
+ return ' gt '
1337
+
1338
+ # For efficiency purposes, you should definitely override these
1339
+ # in your derived class.
1340
+ def _true_symbol(self):
1341
+ """
1342
+ Return the string representation of "truth" in Magma.
1343
+
1344
+ EXAMPLES::
1345
+
1346
+ sage: magma._true_symbol()
1347
+ 'true'
1348
+ """
1349
+ return 'true'
1350
+
1351
+ def _false_symbol(self):
1352
+ """
1353
+ Return the string representation of "false" in Magma.
1354
+
1355
+ EXAMPLES::
1356
+
1357
+ sage: magma._false_symbol()
1358
+ 'false'
1359
+ """
1360
+ return 'false'
1361
+
1362
+ def console(self):
1363
+ """
1364
+ Run a command line Magma session. This session is completely
1365
+ separate from this Magma interface.
1366
+
1367
+ EXAMPLES::
1368
+
1369
+ sage: magma.console() # not tested
1370
+ Magma V2.14-9 Sat Oct 11 2008 06:36:41 on one [Seed = 1157408761]
1371
+ Type ? for help. Type <Ctrl>-D to quit.
1372
+ >
1373
+ Total time: 2.820 seconds, Total memory usage: 3.95MB
1374
+ """
1375
+ magma_console()
1376
+
1377
+ def version(self):
1378
+ """
1379
+ Return the version of Magma that you have in your PATH on your
1380
+ computer.
1381
+
1382
+ OUTPUT:
1383
+
1384
+ - ``numbers`` -- 3-tuple: major, minor, etc.
1385
+
1386
+ - ``string`` -- version as a string
1387
+
1388
+ EXAMPLES::
1389
+
1390
+ sage: magma.version() # random, optional - magma
1391
+ ((2, 14, 9), 'V2.14-9')
1392
+ """
1393
+ t = tuple([int(n) for n in self.eval('GetVersion()').split()])
1394
+ return t, 'V%s.%s-%s' % t
1395
+
1396
+ def help(self, s):
1397
+ """
1398
+ Return Magma help on string s.
1399
+
1400
+ This returns what typing ?s would return in Magma.
1401
+
1402
+ INPUT:
1403
+
1404
+ - ``s`` -- string
1405
+
1406
+ OUTPUT: string
1407
+
1408
+ EXAMPLES::
1409
+
1410
+ sage: magma.help("NextPrime") # optional - magma
1411
+ ===============================================================================
1412
+ PATH: /magma/ring-field-algebra/integer/prime/next-previous/NextPrime
1413
+ KIND: Intrinsic
1414
+ ===============================================================================
1415
+ NextPrime(n) : RngIntElt -> RngIntElt
1416
+ NextPrime(n: parameter) : RngIntElt -> RngIntElt
1417
+ ...
1418
+ """
1419
+ print(self.eval('? %s' % s))
1420
+
1421
+ def _tab_completion(self, verbose=True, use_disk_cache=True):
1422
+ """
1423
+ Return a list of all Magma commands.
1424
+
1425
+ This is used as a hook to enable custom command completion.
1426
+
1427
+ Magma doesn't provide any fast way to make a list of all commands,
1428
+ which is why caching is done by default. Note that an adverse
1429
+ impact of caching is that *new* commands are not picked up, e.g.,
1430
+ user defined variables or functions.
1431
+
1432
+ INPUT:
1433
+
1434
+ - ``verbose`` -- boolean (default: ``True``); whether to
1435
+ verbosely output status info the first time the command list is
1436
+ built
1437
+
1438
+ - ``use_disk_cache`` -- boolean (default: ``True``); use
1439
+ cached command list, which is saved to disk
1440
+
1441
+ OUTPUT: list of strings
1442
+
1443
+ EXAMPLES::
1444
+
1445
+ sage: len(magma._tab_completion(verbose=False)) # random, optional - magma
1446
+ 7261
1447
+ """
1448
+ try:
1449
+ return self.__tab_completion
1450
+ except AttributeError:
1451
+ import sage.misc.persist
1452
+ if use_disk_cache:
1453
+ try:
1454
+ self.__tab_completion = sage.misc.persist.load(INTRINSIC_CACHE)
1455
+ return self.__tab_completion
1456
+ except OSError:
1457
+ pass
1458
+ if verbose:
1459
+ print("\nCreating list of all Magma intrinsics for use in tab completion.")
1460
+ print("This takes a few minutes the first time, but is saved to the")
1461
+ print("file '%s' for future instant use." % INTRINSIC_CACHE)
1462
+ print("Magma may produce errors during this process, which are safe to ignore.")
1463
+ print("Delete that file to force recreation of this cache.")
1464
+ print("Scanning Magma types ...")
1465
+ tm = sage.misc.misc.cputime()
1466
+ T = self.eval('ListTypes()').split()
1467
+ N = []
1468
+ for t in T:
1469
+ if verbose:
1470
+ print(t, " ", end="")
1471
+ sys.stdout.flush()
1472
+ try:
1473
+ s = self.eval('ListSignatures(%s)' % t)
1474
+ for x in s.split('\n'):
1475
+ i = x.find('(')
1476
+ N.append(x[:i])
1477
+ except RuntimeError as msg: # weird internal problems in Magma type system
1478
+ print('Error -- %s' % msg)
1479
+ if verbose:
1480
+ print("Done! (%s seconds)" % sage.misc.misc.cputime(tm))
1481
+ N = sorted(set(N))
1482
+ print("Saving cache to '%s' for future instant use." % INTRINSIC_CACHE)
1483
+ print("Delete the above file to force re-creation of the cache.")
1484
+ sage.misc.persist.save(N, INTRINSIC_CACHE)
1485
+ self.__tab_completion = N
1486
+ return N
1487
+
1488
+ def ideal(self, L):
1489
+ """
1490
+ Return the Magma ideal defined by L.
1491
+
1492
+ INPUT:
1493
+
1494
+ - ``L`` -- list of elements of a Sage multivariate
1495
+ polynomial ring
1496
+
1497
+ OUTPUT: the magma ideal generated by the elements of L
1498
+
1499
+ EXAMPLES::
1500
+
1501
+ sage: R.<x,y> = QQ[]
1502
+ sage: magma.ideal([x^2, y^3*x]) # optional - magma
1503
+ Ideal of Polynomial ring of rank 2 over Rational Field
1504
+ Order: Graded Reverse Lexicographical
1505
+ Variables: x, y
1506
+ Homogeneous
1507
+ Basis:
1508
+ [
1509
+ x^2,
1510
+ x*y^3
1511
+ ]
1512
+ """
1513
+ P = next(iter(L)).parent()
1514
+ Pn = self(P).name()
1515
+ k = P.base_ring()
1516
+ if k.degree() > 1:
1517
+ i = str(k.gen())
1518
+ o = self("BaseRing(%s).1" % Pn).name()
1519
+ self.eval("%s := %s" % (i, o))
1520
+ mlist = self(L)
1521
+ return self("ideal<%s|%s>" % (Pn, mlist.name()))
1522
+
1523
+ def set_verbose(self, type, level):
1524
+ """
1525
+ Set the verbosity level for a given algorithm, class, etc. in
1526
+ Magma.
1527
+
1528
+ INPUT:
1529
+
1530
+ - ``type`` -- string (e.g. 'Groebner')
1531
+
1532
+ - ``level`` -- integer >= 0
1533
+
1534
+ EXAMPLES::
1535
+
1536
+ sage: magma.set_verbose("Groebner", 2) # optional - magma
1537
+ sage: magma.get_verbose("Groebner") # optional - magma
1538
+ 2
1539
+ """
1540
+ if level < 0:
1541
+ raise TypeError("level must be >= 0")
1542
+ self.eval('SetVerbose("%s",%d)' % (type, level))
1543
+
1544
+ SetVerbose = set_verbose
1545
+
1546
+ def get_verbose(self, type):
1547
+ """
1548
+ Get the verbosity level of a given algorithm class etc. in Magma.
1549
+
1550
+ INPUT:
1551
+
1552
+ - ``type`` -- string (e.g. 'Groebner'), see Magma
1553
+ documentation
1554
+
1555
+ EXAMPLES::
1556
+
1557
+ sage: magma.set_verbose("Groebner", 2) # optional - magma
1558
+ sage: magma.get_verbose("Groebner") # optional - magma
1559
+ 2
1560
+ """
1561
+ return int(self.eval('GetVerbose("%s")' % type))
1562
+
1563
+ GetVerbose = get_verbose
1564
+
1565
+ def set_nthreads(self, n):
1566
+ """
1567
+ Set the number of threads used for parallelized algorithms in Magma.
1568
+
1569
+ INPUT:
1570
+
1571
+ - ``n`` -- number of threads
1572
+
1573
+ EXAMPLES::
1574
+
1575
+ sage: magma.set_nthreads(2) #optional - magma
1576
+ sage: magma.get_nthreads() #optional - magma
1577
+ 2
1578
+ """
1579
+ if n < 1:
1580
+ raise TypeError("no. of threads must be >= 1")
1581
+ self.eval('SetNthreads(%d)' % (n))
1582
+
1583
+ SetNthreads = set_nthreads
1584
+
1585
+ def get_nthreads(self):
1586
+ """
1587
+ Get the number of threads used in Magma.
1588
+
1589
+ EXAMPLES::
1590
+
1591
+ sage: magma.set_nthreads(2) #optional - magma
1592
+ sage: magma.get_nthreads() #optional - magma
1593
+ 2
1594
+ """
1595
+ return int(self.eval('GetNthreads()'))
1596
+
1597
+ GetNthreads = get_nthreads
1598
+
1599
+
1600
+ @instancedoc
1601
+ class MagmaFunctionElement(FunctionElement):
1602
+ def __call__(self, *args, **kwds):
1603
+ """
1604
+ Return the result of calling this Magma function at given inputs.
1605
+
1606
+ Use the optional nvals keyword argument to specify that there are
1607
+ multiple return values.
1608
+
1609
+ EXAMPLES: We create a MagmaFunctionElement::
1610
+
1611
+ sage: # optional - magma
1612
+ sage: n = magma(-15)
1613
+ sage: f = n.Factorisation
1614
+ sage: type(f)
1615
+ <class 'sage.interfaces.magma.MagmaFunctionElement'>
1616
+ sage: f()
1617
+ [ <3, 1>, <5, 1> ]
1618
+
1619
+ We verify that the nvals argument works.
1620
+
1621
+ ::
1622
+
1623
+ sage: f(nvals=2) # optional - magma
1624
+ ([ <3, 1>, <5, 1> ], -1)
1625
+
1626
+ This illustrates the more conventional way of calling a method on
1627
+ an object. It's equivalent to the above, but done in all in one
1628
+ step.
1629
+
1630
+ ::
1631
+
1632
+ sage: n.Factorization(nvals = 2) # optional - magma
1633
+ ([ <3, 1>, <5, 1> ], -1)
1634
+ """
1635
+ nvals = 1
1636
+ if len(kwds) > 0:
1637
+ if 'nvals' in kwds:
1638
+ nvals = kwds['nvals']
1639
+ del kwds['nvals']
1640
+ M = self._obj.parent()
1641
+ return M.function_call(self._name,
1642
+ [self._obj.name()] + list(args),
1643
+ params=kwds,
1644
+ nvals=nvals)
1645
+
1646
+ def _instancedoc_(self):
1647
+ """
1648
+ Return the docstring for this function of an element.
1649
+
1650
+ OUTPUT: string
1651
+
1652
+ EXAMPLES::
1653
+
1654
+ sage: # optional - magma
1655
+ sage: n = magma(-15)
1656
+ sage: f = n.Factorisation
1657
+ sage: print(f.__doc__)
1658
+ (n::RngIntElt) -> RngIntEltFact, RngIntElt, SeqEnum
1659
+ ...
1660
+ sage: print(n.Factorisation.__doc__)
1661
+ (n::RngIntElt) -> RngIntEltFact, RngIntElt, SeqEnum
1662
+ ...
1663
+ """
1664
+ M = self._obj.parent()
1665
+ t = str(self._obj.Type())
1666
+ s = M.eval(self._name)
1667
+ Z = s.split('\n(')[1:]
1668
+ W = []
1669
+ tt = '::%s' % t
1670
+ for X in Z:
1671
+ X = '(' + X
1672
+ if '(<All>' in X or tt in X:
1673
+ W.append(X)
1674
+ s = '\n'.join(W)
1675
+ s = sage.misc.misc.word_wrap(s)
1676
+ return s
1677
+
1678
+ def _repr_(self):
1679
+ """
1680
+ Return string representation of this partially evaluated function.
1681
+
1682
+ This is basically the docstring (as returned by ``_instancedoc_``)
1683
+ unless self._name is the name of an attribute of the object, in
1684
+ which case this returns the value of that attribute.
1685
+
1686
+ EXAMPLES::
1687
+
1688
+ sage: magma(-15).Factorisation # optional - magma
1689
+ Partially evaluated Magma function or intrinsic 'Factorisation'
1690
+ ...
1691
+
1692
+ We create a vector space, set its M attribute to a number, then
1693
+ display/get the attribute as a string.
1694
+
1695
+ ::
1696
+
1697
+ sage: # optional - magma
1698
+ sage: V = magma('VectorSpace(RationalField(),2)')
1699
+ sage: V.set_magma_attribute('M', 290398)
1700
+ sage: V.M
1701
+ 290398
1702
+ sage: type(V.M)
1703
+ <class 'sage.interfaces.magma.MagmaFunctionElement'>
1704
+ sage: type(V.M.__repr__())
1705
+ <... 'str'>
1706
+
1707
+ Displaying a non-attribute function works as above.
1708
+
1709
+ ::
1710
+
1711
+ sage: V.Dimension # optional - magma
1712
+ Partially evaluated Magma function or intrinsic 'Dimension'
1713
+ ...
1714
+ """
1715
+ M = self._obj.parent()
1716
+ try:
1717
+ return M.eval('%s`%s' % (self._obj.name(), self._name))
1718
+ except RuntimeError:
1719
+ return "Partially evaluated Magma function or intrinsic '%s'\n\nSignature:\n\n%s" % (self._name, self._instancedoc_())
1720
+
1721
+
1722
+ @instancedoc
1723
+ class MagmaFunction(ExpectFunction):
1724
+ def __call__(self, *args, **kwds):
1725
+ """
1726
+ Return the result of calling this Magma function at given inputs.
1727
+
1728
+ Use the optional nvals keyword argument to specify that there are
1729
+ multiple return values.
1730
+
1731
+ EXAMPLES: We create a MagmaFunction::
1732
+
1733
+ sage: f = magma.Factorisation # optional - magma
1734
+ sage: type(f) # optional - magma
1735
+ <class 'sage.interfaces.magma.MagmaFunction'>
1736
+ sage: f(-15) # optional - magma
1737
+ [ <3, 1>, <5, 1> ]
1738
+
1739
+ We verify that the nvals argument works.
1740
+
1741
+ ::
1742
+
1743
+ sage: f(-15, nvals=2) # optional - magma
1744
+ ([ <3, 1>, <5, 1> ], -1)
1745
+ sage: f.__call__(-15, nvals=2) # optional - magma
1746
+ ([ <3, 1>, <5, 1> ], -1)
1747
+ """
1748
+ nvals = 1
1749
+ if len(kwds) > 0:
1750
+ if 'nvals' in kwds:
1751
+ nvals = kwds['nvals']
1752
+ del kwds['nvals']
1753
+ M = self._parent
1754
+ return M.function_call(self._name,
1755
+ list(args),
1756
+ params=kwds,
1757
+ nvals=nvals)
1758
+
1759
+ def _instancedoc_(self):
1760
+ """
1761
+ Return docstring about this function.
1762
+
1763
+ OUTPUT: string
1764
+
1765
+ EXAMPLES::
1766
+
1767
+ sage: f = magma.Factorisation
1768
+ sage: type(f)
1769
+ <class 'sage.interfaces.magma.MagmaFunction'>
1770
+ sage: print(f.__doc__) # optional - magma
1771
+ Intrinsic 'Factorisation'
1772
+ ...
1773
+ """
1774
+ M = self._parent
1775
+ s = M.eval(self._name)
1776
+ s = sage.misc.misc.word_wrap(s, 80)
1777
+ return s
1778
+
1779
+
1780
+ def is_MagmaElement(x):
1781
+ """
1782
+ Return ``True`` if ``x`` is of type :class:`MagmaElement`, and ``False``
1783
+ otherwise.
1784
+
1785
+ INPUT:
1786
+
1787
+ - ``x`` -- any object
1788
+
1789
+ OUTPUT: boolean
1790
+
1791
+ EXAMPLES::
1792
+
1793
+ sage: from sage.interfaces.magma import is_MagmaElement
1794
+ sage: is_MagmaElement(2)
1795
+ doctest:...: DeprecationWarning: the function is_MagmaElement is deprecated; use isinstance(x, sage.interfaces.abc.MagmaElement) instead
1796
+ See https://github.com/sagemath/sage/issues/34804 for details.
1797
+ False
1798
+ sage: is_MagmaElement(magma(2)) # optional - magma
1799
+ True
1800
+ """
1801
+ from sage.misc.superseded import deprecation
1802
+ deprecation(34804, "the function is_MagmaElement is deprecated; use isinstance(x, sage.interfaces.abc.MagmaElement) instead")
1803
+
1804
+ return isinstance(x, MagmaElement)
1805
+
1806
+
1807
+ @instancedoc
1808
+ class MagmaElement(ExtraTabCompletion, ExpectElement, sage.interfaces.abc.MagmaElement):
1809
+ def _ref(self):
1810
+ """
1811
+ Return a variable name that is a new reference to this particular
1812
+ MagmaElement in Magma. This keeps this object from being garbage
1813
+ collected by Magma, even if all the Sage references to it are
1814
+ freed.
1815
+
1816
+ Important special behavior: When _ref is used during an implicit
1817
+ call to _magma_init_, then the reference disappears after the
1818
+ coercion is done. More precisely, if the output of _ref() appears
1819
+ as part of the output of a call to _magma_init_ that is then
1820
+ going to be input to magma(...), then it is deleted in the Magma
1821
+ interface. The main use for this behavior is that in
1822
+ _magma_init_ it allows you to get a reference to one object, and
1823
+ use it exactly once in constructing a string to evaluate in Magma,
1824
+ without having to worry about that object being deallocated. There
1825
+ are more sophisticated ways that the same problem (with
1826
+ _magma_init_ and references) could have been solved, but this
1827
+ solution is much simpler and easier to understand than all others I
1828
+ came up with. If this doesn't make sense, read the source code to
1829
+ _coerce_from_special_method, which is much shorter than this
1830
+ paragraph.
1831
+
1832
+ .. warning::
1833
+
1834
+ Use _ref sparingly, since it involves a full call to Magma,
1835
+ which can be slow.
1836
+
1837
+ OUTPUT: string
1838
+
1839
+ EXAMPLES::
1840
+
1841
+ sage: a = magma('-2/3') # optional - magma
1842
+ sage: s = a._ref(); s # optional - magma
1843
+ '_sage_ref...'
1844
+ sage: magma(s) # optional - magma
1845
+ -2/3
1846
+ """
1847
+ P = self._check_valid()
1848
+ n = P._next_ref_name()
1849
+ P.set(n, self.name())
1850
+ return n
1851
+
1852
+ def __getattr__(self, attrname):
1853
+ """
1854
+ INPUT:
1855
+
1856
+ - ``attrname`` -- string
1857
+
1858
+ OUTPUT: a Magma function partially evaluated with ``self`` as the first
1859
+ input
1860
+
1861
+ .. NOTE::
1862
+
1863
+ If the input ``attrname`` starts with an underscore, an
1864
+ :exc:`AttributeError` is raised so that the actual
1865
+ Python _ method/value can be accessed.
1866
+
1867
+ EXAMPLES::
1868
+
1869
+ sage: # optional - magma
1870
+ sage: n = magma(-15)
1871
+ sage: type(n)
1872
+ <class 'sage.interfaces.magma.MagmaElement'>
1873
+ sage: f = n.__getattr__('Factorization')
1874
+ sage: type(f)
1875
+ <class 'sage.interfaces.magma.MagmaFunctionElement'>
1876
+ sage: f
1877
+ Partially evaluated Magma function or intrinsic 'Factorization'
1878
+ ...
1879
+ """
1880
+ if attrname[:1] == "_":
1881
+ raise AttributeError
1882
+ return MagmaFunctionElement(self, attrname)
1883
+
1884
+ def _sage_(self):
1885
+ """
1886
+ Return Sage version of this object.
1887
+
1888
+ Use self.sage() to get the Sage version.
1889
+
1890
+ Edit ``src/sage/ext_data/magma/sage/basic.m`` to add functionality.
1891
+
1892
+ EXAMPLES: Enumerated Sets::
1893
+
1894
+ sage: # optional - magma
1895
+ sage: a = magma('{1,2/3,-5/9}')
1896
+ sage: a.sage()
1897
+ {1, -5/9, 2/3}
1898
+ sage: a._sage_()
1899
+ {1, -5/9, 2/3}
1900
+ sage: type(a.sage())
1901
+ <class 'sage.sets.set.Set_object_enumerated_with_category'>
1902
+ sage: a = magma('{1,2/3,-5/9}'); a
1903
+ { -5/9, 2/3, 1 }
1904
+ sage: a.Type()
1905
+ SetEnum
1906
+ sage: b = a.sage(); b
1907
+ {1, -5/9, 2/3}
1908
+ sage: type(b)
1909
+ <class 'sage.sets.set.Set_object_enumerated_with_category'>
1910
+ sage: c = magma(b); c
1911
+ { -5/9, 2/3, 1 }
1912
+ sage: c.Type()
1913
+ SetEnum
1914
+
1915
+ Multisets are converted to lists::
1916
+
1917
+ sage: m = magma('{* 1,2,2,2,4^^2,3 *}') # optional - magma
1918
+ sage: z = m.sage(); z # optional - magma
1919
+ [1, 2, 2, 2, 3, 4, 4]
1920
+ sage: type(z) # optional - magma
1921
+ <... 'list'>
1922
+
1923
+ Tuples get converted to tuples::
1924
+
1925
+ sage: m = magma('<1,2,<3>>') # optional - magma
1926
+ sage: z = m.sage(); z # optional - magma
1927
+ (1, 2, (3,))
1928
+ sage: type(z) # optional - magma
1929
+ <... 'tuple'>
1930
+
1931
+ Sequences get converted to lists::
1932
+
1933
+ sage: m = magma('[<1>,<2>]') # optional - magma
1934
+ sage: z = m.sage(); z # optional - magma
1935
+ [(1,), (2,)]
1936
+ sage: type(z) # optional - magma
1937
+ <... 'list'>
1938
+
1939
+ Matrices::
1940
+
1941
+ sage: a = matrix(ZZ,3,3,[1..9])
1942
+ sage: m = magma(a) # optional - magma
1943
+ sage: b = m.sage(); b # optional - magma
1944
+ [1 2 3]
1945
+ [4 5 6]
1946
+ [7 8 9]
1947
+ sage: b == a # optional - magma
1948
+ True
1949
+
1950
+ A nonsquare matrix::
1951
+
1952
+ sage: a = matrix(ZZ,2,3,[1..6])
1953
+ sage: m = magma(a) # optional - magma
1954
+ sage: m.sage() # optional - magma
1955
+ [1 2 3]
1956
+ [4 5 6]
1957
+
1958
+ Multivariate polynomials::
1959
+
1960
+ sage: # optional - magma
1961
+ sage: R.<x,y,z> = QQ[]
1962
+ sage: f = x^2+3*y
1963
+ sage: g = magma(f).sage(); g
1964
+ x^2 + 3*y
1965
+ sage: parent(f) == parent(g)
1966
+ True
1967
+
1968
+ Real and complex numbers::
1969
+
1970
+ sage: # optional - magma
1971
+ sage: m = magma(RealField(200)(1/3))
1972
+ sage: m.sage()
1973
+ 0.33333333333333333333333333333333333333333333333333333333333
1974
+ sage: m = magma(RealField(1000)(1/3))
1975
+ sage: m.sage()
1976
+ 0.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
1977
+
1978
+ sage: m = magma(ComplexField(200)).1; m # optional - magma
1979
+ 1.00000000000000000000000000000000000000000000000000000000000*$.1
1980
+ sage: s = m.Sqrt(); s # optional - magma
1981
+ 0.707106781186547524400844362104849039284835937688474036588340 + 0.707106781186547524400844362104849039284835937688474036588340*$.1
1982
+ sage: s.sage() # indirect doctest, optional - magma
1983
+ 0.70710678118654752440084436210484903928483593768847403658834 + 0.70710678118654752440084436210484903928483593768847403658834*I
1984
+
1985
+ Number fields and their elements::
1986
+
1987
+ sage: x = var('x')
1988
+ sage: L.<alpha> = NumberField(x^3+2*x+2)
1989
+ sage: K = magma(L) # optional - magma
1990
+ sage: K.sage() # optional - magma
1991
+ Number Field in alpha with defining polynomial x^3 + 2*x + 2
1992
+ sage: K.sage() is L # optional - magma
1993
+ True
1994
+ sage: magma(alpha).sage() # optional - magma
1995
+ alpha
1996
+
1997
+ Relative number field elements can be converted from Magma
1998
+ to Sage, but the other direction has not yet been implemented.::
1999
+
2000
+ sage: # needs sage.libs.pari
2001
+ sage: P.<y> = L[]
2002
+ sage: N.<b> = NumberField(y^2-alpha)
2003
+ sage: M = magma(N) # optional - magma
2004
+ sage: M.1.sage() # optional - magma
2005
+ b
2006
+ sage: _^2 # optional - magma
2007
+ alpha
2008
+ sage: magma(b) # optional - magma
2009
+ Traceback (most recent call last):
2010
+ ...
2011
+ TypeError: coercion of relative number field elements to Magma is not implemented
2012
+
2013
+ Sage does not have absolute number fields defined by
2014
+ two polynomials, like Magma does. They are converted
2015
+ to relative number fields. Conversion of their elements
2016
+ has not yet been implemented.::
2017
+
2018
+ sage: # optional - magma
2019
+ sage: magma.eval('P<x> := PolynomialRing(Rationals());')
2020
+ ''
2021
+ sage: K = magma('NumberField([x^2-2,x^2-3]:Abs);')
2022
+ sage: L = K.sage(); L
2023
+ Number Field in K1 with defining polynomial x^2 - 2 over its base field
2024
+ sage: L.base_field()
2025
+ Number Field in K2 with defining polynomial x^2 - 3
2026
+ sage: K.GeneratorsSequence()[1].sage()
2027
+ Traceback (most recent call last):
2028
+ ...
2029
+ NameError: name 'K' is not defined
2030
+
2031
+ Finite quotients of ZZ::
2032
+
2033
+ sage: R = Zmod(137)
2034
+ sage: magma(R).sage() # optional - magma
2035
+ Ring of integers modulo 137
2036
+
2037
+ TESTS:
2038
+
2039
+ Tests for :issue:`30341`::
2040
+
2041
+ sage: P.<t> = PolynomialRing(QQ)
2042
+ sage: l = [-27563611963/4251528, -48034411/104976, -257/54, 1]
2043
+ sage: u = P(l)
2044
+ sage: u == P(magma(u).sage()) # optional - magma
2045
+ True
2046
+
2047
+ sage: P.<x,y> = PolynomialRing(QQ, 2)
2048
+ sage: u = x + 27563611963/4251528*y
2049
+ sage: magma(u).sage() # optional - magma
2050
+ x + 27563611963/4251528*y
2051
+ """
2052
+ z, preparse = self.Sage(nvals=2)
2053
+ s = str(z)
2054
+ preparse = str(preparse) == 'true'
2055
+ return sage.misc.sage_eval.sage_eval(s, preparse=preparse)
2056
+
2057
+ def AssignNames(self, names):
2058
+ """
2059
+ EXAMPLES::
2060
+
2061
+ sage: # optional - magma
2062
+ sage: S = magma.PolynomialRing(magma.Integers(), 2)
2063
+ sage: S.AssignNames(['a', 'b'])
2064
+ sage: S.1
2065
+ a
2066
+ sage: S.1^2 + S.2
2067
+ a^2 + b
2068
+ """
2069
+ P = self._check_valid()
2070
+ cmd = 'AssignNames(~%s, [%s])' % (self.name(),
2071
+ ','.join('"%s"' % x for x in names))
2072
+ P.eval(cmd)
2073
+
2074
+ assign_names = AssignNames
2075
+
2076
+ def gen(self, n):
2077
+ """
2078
+ Return the `n`-th generator of this Magma element.
2079
+
2080
+ Note that generators are 1-based in Magma rather than 0-based!
2081
+
2082
+ INPUT:
2083
+
2084
+ - ``n`` -- *positive* integer
2085
+
2086
+ OUTPUT: :class:`MagmaElement`
2087
+
2088
+ EXAMPLES::
2089
+
2090
+ sage: # needs sage.rings.finite_rings
2091
+ sage: k.<a> = GF(9)
2092
+ sage: magma(k).gen(1) # optional -- magma
2093
+ a
2094
+ sage: R.<s,t,w> = k[]
2095
+ sage: m = magma(R) # optional -- magma
2096
+ sage: m.gen(1) # optional -- magma
2097
+ s
2098
+ sage: m.gen(2) # optional -- magma
2099
+ t
2100
+ sage: m.gen(3) # optional -- magma
2101
+ w
2102
+ sage: m.gen(0) # optional -- magma
2103
+ Traceback (most recent call last):
2104
+ ...
2105
+ IndexError: index must be positive since Magma indexes are 1-based
2106
+ sage: m.gen(4) # optional -- magma
2107
+ Traceback (most recent call last):
2108
+ ...
2109
+ IndexError: tuple index out of range
2110
+ """
2111
+ if n <= 0:
2112
+ raise IndexError("index must be positive since Magma indexes are 1-based")
2113
+ return self.gens()[n - 1]
2114
+
2115
+ def gens(self) -> tuple:
2116
+ """
2117
+ Return generators for ``self``.
2118
+
2119
+ If ``self`` is named X in Magma, this function evaluates X.1, X.2,
2120
+ etc., in Magma until an error occurs. It then returns a Sage tuple
2121
+ of the resulting X.i. Note - I do not think there is a Magma command
2122
+ that returns the list of valid X.i. There are numerous ad hoc
2123
+ functions for various classes but nothing systematic. This function
2124
+ gets around that problem. Again, this is something that should
2125
+ probably be reported to the Magma group and fixed there.
2126
+
2127
+ AUTHORS:
2128
+
2129
+ - William Stein (2006-07-02)
2130
+
2131
+ EXAMPLES::
2132
+
2133
+ sage: magma("VectorSpace(RationalField(),3)").gens() # optional - magma
2134
+ ((1 0 0), (0 1 0), (0 0 1))
2135
+ sage: magma("AbelianGroup(EllipticCurve([1..5]))").gens() # optional - magma
2136
+ ($.1,)
2137
+ """
2138
+ try:
2139
+ return self._magma_gens
2140
+ except AttributeError:
2141
+ pass
2142
+ G = []
2143
+ i = 1
2144
+ P = self._check_valid()
2145
+ n = self.name()
2146
+ while True:
2147
+ try:
2148
+ G.append(P('%s.%s' % (n, i)))
2149
+ except (RuntimeError, TypeError):
2150
+ break
2151
+ i += 1
2152
+ tG = tuple(G)
2153
+ self._magma_gens = tG
2154
+ return tG
2155
+
2156
+ def gen_names(self):
2157
+ """
2158
+ Return list of Magma variable names of the generators of ``self``.
2159
+
2160
+ .. NOTE::
2161
+
2162
+ As illustrated below, these are not the print names of the
2163
+ the generators of the Magma object, but special variable
2164
+ names in the Magma session that reference the generators.
2165
+
2166
+ EXAMPLES::
2167
+
2168
+ sage: R.<x,zw> = QQ[]
2169
+ sage: S = magma(R) # optional - magma
2170
+ sage: S.gen_names() # optional - magma
2171
+ ('_sage_[...]', '_sage_[...]')
2172
+ sage: magma(S.gen_names()[1]) # optional - magma
2173
+ zw
2174
+ """
2175
+ try:
2176
+ return self.__gen_names
2177
+ except AttributeError:
2178
+ self.__gen_names = tuple([x.name() for x in self.gens()])
2179
+ return self.__gen_names
2180
+
2181
+ def evaluate(self, *args):
2182
+ r"""
2183
+ Evaluate ``self`` at the inputs.
2184
+
2185
+ INPUT:
2186
+
2187
+ - ``*args`` -- import arguments
2188
+
2189
+ OUTPUT: self(\*args)
2190
+
2191
+ EXAMPLES::
2192
+
2193
+ sage: # optional - magma
2194
+ sage: f = magma('Factorization')
2195
+ sage: f.evaluate(15)
2196
+ [ <3, 1>, <5, 1> ]
2197
+ sage: f(15)
2198
+ [ <3, 1>, <5, 1> ]
2199
+ sage: f = magma('GCD')
2200
+ sage: f.evaluate(15,20)
2201
+ 5
2202
+
2203
+ sage: m = matrix(QQ, 2, 2, [2,3,5,7]) # optional - magma
2204
+ sage: f = magma('ElementaryDivisors') # optional - magma
2205
+ sage: f.evaluate(m) # optional - magma
2206
+ [ 1, 1 ]
2207
+ """
2208
+ P = self._check_valid()
2209
+ names = ','.join(a._magma_init_(P) for a in args)
2210
+ return P('%s(%s)' % (self.name(), names))
2211
+
2212
+ eval = evaluate
2213
+
2214
+ def __call__(self, *args):
2215
+ """
2216
+ Coerce something into the object (using the Magma ! notation).
2217
+
2218
+ For function calls, use self.eval(...).
2219
+
2220
+ EXAMPLES::
2221
+
2222
+ sage: # optional - magma
2223
+ sage: M = magma.RMatrixSpace(magma.IntegerRing(), 2, 2)
2224
+ sage: A = M([1,2,3,4]); A
2225
+ [1 2]
2226
+ [3 4]
2227
+ sage: type(A)
2228
+ <class 'sage.interfaces.magma.MagmaElement'>
2229
+ sage: A.Type()
2230
+ ModMatRngElt
2231
+ """
2232
+ if len(args) > 1:
2233
+ return self.evaluate(*args)
2234
+ P = self._check_valid()
2235
+ x = P(args[0])
2236
+ try:
2237
+ return P('%s!%s' % (self.name(), x.name()))
2238
+ except (RuntimeError, TypeError):
2239
+ return self.evaluate(*args)
2240
+
2241
+ def __iter__(self):
2242
+ """
2243
+ Return iterator over this Magma element.
2244
+
2245
+ OUTPUT: generator object
2246
+
2247
+ .. warning::
2248
+
2249
+ Internally this constructs the list of elements in ``self`` in
2250
+ Magma, which is not a lazy operation. This is because Magma
2251
+ doesn't have a notion of lazy iterators, unfortunately.
2252
+
2253
+ EXAMPLES::
2254
+
2255
+ sage: # optional - magma
2256
+ sage: V = magma('VectorSpace(GF(3),2)')
2257
+ sage: V
2258
+ Full Vector space of degree 2 over GF(3)
2259
+ sage: w = V.__iter__(); w
2260
+ <generator object ...__iter__ at ...>
2261
+ sage: next(w)
2262
+ (0 0)
2263
+ sage: next(w)
2264
+ (1 0)
2265
+ sage: list(w)
2266
+ [(2 0), (0 1), (1 1), (2 1), (0 2), (1 2), (2 2)]
2267
+ """
2268
+ P = self._check_valid()
2269
+ z = P('[_a : _a in %s]' % self.name())
2270
+ for i in range(1, len(z) + 1):
2271
+ yield z[i]
2272
+
2273
+ def __len__(self):
2274
+ r"""
2275
+ Return cardinality of this Magma element.
2276
+
2277
+ This is the same as ``#self`` in Magma.
2278
+
2279
+ EXAMPLES::
2280
+
2281
+ sage: # optional - magma
2282
+ sage: V = magma('VectorSpace(GF(3),2)')
2283
+ sage: V
2284
+ Full Vector space of degree 2 over GF(3)
2285
+ sage: len(V)
2286
+ 9
2287
+ sage: V.__len__()
2288
+ 9
2289
+ """
2290
+ P = self._check_valid()
2291
+ return int(P.eval('#%s' % self.name()))
2292
+
2293
+ def _polynomial_(self, R):
2294
+ """
2295
+ Try to convert ``self`` into a polynomial in the univariate polynomial
2296
+ ring `R`.
2297
+
2298
+ EXAMPLES::
2299
+
2300
+ sage: R.<x> = QQ[]
2301
+ sage: f = magma(x^2 + 2/3*x + 5) # optional - magma
2302
+ sage: f # optional - magma
2303
+ x^2 + 2/3*x + 5
2304
+ sage: f.Type() # optional - magma
2305
+ RngUPolElt
2306
+ sage: f._polynomial_(R) # optional - magma
2307
+ x^2 + 2/3*x + 5
2308
+ """
2309
+ return R(list(self.Eltseq()))
2310
+
2311
+ def _latex_(self) -> str:
2312
+ r"""
2313
+ Return latex representation of ``self``.
2314
+
2315
+ AUTHORS:
2316
+
2317
+ - Jennifer Balakrishnan
2318
+
2319
+ Types that are nicely latexed include:
2320
+
2321
+ - rationals
2322
+
2323
+ - matrices
2324
+
2325
+ - polynomials
2326
+
2327
+ - binary quadratic forms
2328
+
2329
+ - elements of quadratic, cyclotomic number fields, and general
2330
+ number fields
2331
+
2332
+ - points
2333
+
2334
+ - elliptic curves
2335
+
2336
+ - power series
2337
+
2338
+ IMPLEMENTATION: Calls latex.m, which is in
2339
+ SAGE_EXTCODE/magma/latex.m
2340
+
2341
+ EXAMPLES::
2342
+
2343
+ sage: latex(magma('-2/3')) # optional - magma
2344
+ \frac{-2}{3}
2345
+ sage: magma('-2/3')._latex_() # optional - magma
2346
+ '\\frac{-2}{3}'
2347
+
2348
+ ::
2349
+
2350
+ sage: magma.eval('R<x> := PolynomialRing(RationalField()); f := (x-17/2)^3;') # optional - magma
2351
+ ''
2352
+ sage: latex(magma('f')) # optional - magma
2353
+ x^{3}-\frac{51}{2}x^{2}+\frac{867}{4}x-\frac{4913}{8}
2354
+
2355
+ ::
2356
+
2357
+ sage: latex(magma('(MatrixAlgebra(RationalField(),3)![0,2,3,4,5,6,7,8,9])^(-1)')) # optional - magma
2358
+ \left(\begin{array}{ccc}-1&2&-1\\2&-7&4\\-1&\frac{14}{3}&\frac{-8}{3}\end{array}\right)
2359
+
2360
+ ::
2361
+
2362
+ sage: magma.eval('K<a> := CyclotomicField(11)') # optional - magma
2363
+ ''
2364
+ sage: latex(magma('a^3 + a - 17/3')) # optional - magma
2365
+ \frac{-17}{3}+\zeta_{11}+\zeta_{11}^{3}
2366
+
2367
+ ::
2368
+
2369
+ sage: latex(magma('EllipticCurve([1,2/3,3/4,4/5,-5/6])')) # optional - magma
2370
+ y^2+xy+\frac{3}{4}y=x^3+\frac{2}{3}x^2+\frac{4}{5}x-\frac{5}{6}
2371
+
2372
+ ::
2373
+
2374
+ sage: _=magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
2375
+ sage: _=magma.eval('K<a> := NumberField(x^3+17*x+2)') # optional - magma
2376
+ sage: latex(magma('(1/3)*a^2 - 17/3*a + 2')) # optional - magma
2377
+ 2-\frac{17}{3}a+\frac{1}{3}a^{2}
2378
+
2379
+ Sage auto-detects the greek letters and puts backslashes in::
2380
+
2381
+ sage: _=magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
2382
+ sage: _=magma.eval('K<alpha> := NumberField(x^3+17*x+2)') # optional - magma
2383
+ sage: latex(magma('(1/3)*alpha^2 - 17/3*alpha + 2')) # optional - magma
2384
+ 2-\frac{17}{3}\alpha+\frac{1}{3}\alpha^{2}
2385
+
2386
+ ::
2387
+
2388
+ sage: _=magma.eval('R<alpha> := PolynomialRing(RationalField())') # optional - magma
2389
+ sage: latex(magma('alpha^3-1/7*alpha + 3')) # optional - magma
2390
+ \alpha^{3}-\frac{1}{7}\alpha+3
2391
+
2392
+ Finite field elements::
2393
+
2394
+ sage: _=magma.eval('K<a> := GF(27)') # optional - magma
2395
+ sage: latex(magma('a^2+2')) # optional - magma
2396
+ 2+a^{2}
2397
+
2398
+ Printing of unnamed (dollar sign) generators works correctly::
2399
+
2400
+ sage: latex(magma('FiniteField(81).1^2+1')) # optional - magma
2401
+ 1+\$.1^{2}
2402
+
2403
+ Finite fields::
2404
+
2405
+ sage: latex(magma('FiniteField(3)')) # optional - magma
2406
+ \mathbf{F}_{{3}}
2407
+ sage: latex(magma('FiniteField(27)')) # optional - magma
2408
+ \mathbf{F}_{{3}^{3}}
2409
+
2410
+ Power Series::
2411
+
2412
+ sage: # optional - magma
2413
+ sage: _=magma.eval('R<x> := PowerSeriesRing(RationalField())')
2414
+ sage: latex(magma('(1/(1+x))'))
2415
+ 1-x+x^{2}-x^{3}+x^{4}-x^{5}+x^{6}-x^{7}+x^{8}-x^{9}+x^{10}-x^{11}+x^{12}-x^{13}+x^{14}-x^{15}+x^{16}-x^{17}+x^{18}-x^{19}+O(x^{20})
2416
+ sage: _=magma.eval('R<x> := PowerSeriesRing(RationalField())')
2417
+ sage: latex(magma('(-1/(2+x + O(x^3)))'))
2418
+ \frac{-1}{2}+\frac{1}{4}x-\frac{1}{8}x^{2}+O(x^{3})
2419
+
2420
+ `p`-adic Numbers::
2421
+
2422
+ sage: latex(magma('pAdicField(7,4)!9333294394/49')) # optional - magma
2423
+ 4\cdot{}7^{-2} + 5\cdot{}7^{-1} + 5+ 6\cdot{}7^{1} + O(7^{2})
2424
+ """
2425
+ P = self._check_valid()
2426
+ s = str(P.eval('Latex(%s)' % self.name()))
2427
+ v = '\\mathrm{'
2428
+ if s[:len(v)] == v:
2429
+ raise AttributeError
2430
+ return s
2431
+
2432
+ def set_magma_attribute(self, attrname, value):
2433
+ """
2434
+ INPUT:
2435
+
2436
+ - ``attrname`` -- string
2437
+ - ``value`` -- something coercible to a MagmaElement
2438
+
2439
+ EXAMPLES::
2440
+
2441
+ sage: # optional - magma
2442
+ sage: V = magma("VectorSpace(RationalField(),2)")
2443
+ sage: V.set_magma_attribute('M',10)
2444
+ sage: V.get_magma_attribute('M')
2445
+ 10
2446
+ sage: V.M
2447
+ 10
2448
+ """
2449
+ P = self.parent() # instance of Magma that contains this element.
2450
+ if not (isinstance(value, MagmaElement) and value.parent() is P):
2451
+ value = P(value)
2452
+ P.eval('%s`%s := %s' % (self.name(), attrname, value.name()))
2453
+
2454
+ def get_magma_attribute(self, attrname):
2455
+ """
2456
+ Return value of a given Magma attribute. This is like selfattrname
2457
+ in Magma.
2458
+
2459
+ OUTPUT: :class:`MagmaElement`
2460
+
2461
+ EXAMPLES::
2462
+
2463
+ sage: # optional - magma
2464
+ sage: V = magma("VectorSpace(RationalField(),10)")
2465
+ sage: V.set_magma_attribute('M','"hello"')
2466
+ sage: V.get_magma_attribute('M')
2467
+ hello
2468
+ sage: V.M
2469
+ hello
2470
+ """
2471
+ P = self.parent()
2472
+ return P('%s`%s' % (self.name(), attrname))
2473
+
2474
+ def list_attributes(self):
2475
+ """
2476
+ Return the attributes of self, obtained by calling the
2477
+ ListAttributes function in Magma.
2478
+
2479
+ OUTPUT: list of strings
2480
+
2481
+ EXAMPLES: We observe that vector spaces in Magma have numerous
2482
+ funny and mysterious attributes. ::
2483
+
2484
+ sage: V = magma("VectorSpace(RationalField(),2)") # optional - magma
2485
+ sage: v = V.list_attributes(); v.sort() # optional - magma
2486
+ sage: print(v) # optional - magma
2487
+ ['Coroots', 'Involution', ..., 'p', 'ssbasis', 'weights']
2488
+ """
2489
+ return magma.eval('ListAttributes(Type(%s))' % self.name()).split()
2490
+
2491
+ def _tab_completion(self):
2492
+ """
2493
+ Return all Magma functions that have this Magma element as first
2494
+ input. This is used for tab completion.
2495
+
2496
+ .. NOTE::
2497
+
2498
+ This function can unfortunately be slow if there are a very
2499
+ large number of functions, e.g., when ``self`` is an
2500
+ integer. (This could be fixed by the addition of an
2501
+ appropriate function to the Magma kernel, which is
2502
+ something that can only be done by the Magma developers.)
2503
+
2504
+ OUTPUT:
2505
+
2506
+ - ``list`` -- sorted list of distinct strings
2507
+
2508
+ EXAMPLES::
2509
+
2510
+ sage: R.<x> = ZZ[] # optional - magma
2511
+ sage: v = magma(R)._tab_completion() # optional - magma
2512
+ sage: v # optional - magma
2513
+ ["'*'", "'+'", "'.'", "'/'", "'eq'", "'meet'", "'subset'", ...]
2514
+ """
2515
+ M = self.methods()
2516
+ N = []
2517
+ for x in M:
2518
+ i = x.find('(')
2519
+ N.append(x[:i])
2520
+ v = sorted(set(N + self.list_attributes()))
2521
+ return v
2522
+
2523
+ def methods(self, any=False):
2524
+ """
2525
+ Return signatures of all Magma intrinsics that can take ``self`` as the
2526
+ first argument, as strings.
2527
+
2528
+ INPUT:
2529
+
2530
+ - ``any`` -- boolean (default: ``False``); if ``True``, also
2531
+ include signatures with Any as first argument
2532
+
2533
+ OUTPUT: list of strings
2534
+
2535
+ EXAMPLES::
2536
+
2537
+ sage: v = magma('2/3').methods() # optional - magma
2538
+ sage: v[0] # optional - magma
2539
+ "'*'..."
2540
+ """
2541
+ t = str(self.Type())
2542
+ X = self.parent().eval('ListSignatures(%s)' % self.Type()).split('\n')
2543
+ X = X[2:] # because the first 2 lines are not relevant
2544
+ t0 = t + ',' # t as first argument among several
2545
+ t1 = t + ')' # t as only argument
2546
+ result = []
2547
+ for x in X:
2548
+ x1 = x.split('::')[1] # typical line starts (f::Elt, g::Elt)
2549
+ if x1.startswith(t0) or x1.startswith(t1):
2550
+ result.append(x)
2551
+ elif any and x1.startswith("Any"):
2552
+ result.append(x)
2553
+ return result
2554
+
2555
+ def __floordiv__(self, x):
2556
+ """
2557
+ Quotient of division of ``self`` by ``other``. This is denoted ``//``
2558
+ (``div`` in magma).
2559
+
2560
+ EXAMPLES::
2561
+
2562
+ sage: R.<x,y,z> = QQ[]
2563
+ sage: magma(5)//magma(2) # optional - magma
2564
+ 2
2565
+ sage: m = magma(x*z + x*y) # optional - magma
2566
+ sage: n = magma(x) # optional - magma
2567
+ sage: m//n # optional - magma
2568
+ y + z
2569
+ """
2570
+ return self.parent()('%s div %s' % (self.name(), x.name()))
2571
+
2572
+ def __bool__(self):
2573
+ """
2574
+ Return ``True`` if ``self`` is nonzero according to Magma.
2575
+
2576
+ If Magma cannot decide, i.e., is raising an error
2577
+ then also return ``True``.
2578
+
2579
+ EXAMPLES: We define a Magma vector space::
2580
+
2581
+ sage: V = magma('VectorSpace(GF(3),2)'); V # optional - magma
2582
+ Full Vector space of degree 2 over GF(3)
2583
+
2584
+ The first generator is nonzero::
2585
+
2586
+ sage: bool(V.gen(1)) # optional - magma
2587
+ True
2588
+
2589
+ The zero element is zero::
2590
+
2591
+ sage: bool(V(0)) # optional - magma
2592
+ False
2593
+
2594
+ The space itself is nonzero (the default - in Magma no comparison
2595
+ to 0 is possible)::
2596
+
2597
+ sage: bool(V) # optional - magma
2598
+ True
2599
+
2600
+ Note that ``bool`` calls ``__bool__`` in Python 3.
2601
+
2602
+ Test use in bool conversions of bools::
2603
+
2604
+ sage: # optional - magma
2605
+ sage: bool(magma(False))
2606
+ False
2607
+ sage: bool(magma(True))
2608
+ True
2609
+ sage: bool(magma(1))
2610
+ True
2611
+ sage: bool(magma(0))
2612
+ False
2613
+
2614
+ TESTS:
2615
+
2616
+ Verify that :issue:`32602` is fixed::
2617
+
2618
+ sage: magma("1 eq 0").bool() # optional - magma
2619
+ False
2620
+ sage: magma("1 eq 1").bool() # optional - magma
2621
+ True
2622
+ sage: Q.<x> = PolynomialRing(GF(3))
2623
+ sage: u = x^6+x^4+2*x^3+2*x+1
2624
+ sage: F0 = magma.FunctionField(GF(3)) # optional - magma
2625
+ sage: bool(F0.1) # optional - magma
2626
+ True
2627
+ """
2628
+ try:
2629
+ return str(self.parent()("%s eq 0" % self.name())) == "false"
2630
+ except TypeError:
2631
+ # comparing with 0 didn't work; try comparing with false
2632
+ try:
2633
+ return str(self.parent()("%s eq false" % self.name())) == "false"
2634
+ except TypeError:
2635
+ pass
2636
+ return True
2637
+
2638
+ def sub(self, gens):
2639
+ """
2640
+ Return the sub-object of ``self`` with given gens.
2641
+
2642
+ INPUT:
2643
+
2644
+ - ``gens`` -- object or list/tuple of generators
2645
+
2646
+ EXAMPLES::
2647
+
2648
+ sage: V = magma('VectorSpace(RationalField(),3)') # optional - magma
2649
+ sage: W = V.sub([ [1,2,3], [1,1,2] ]); W # optional - magma
2650
+ Vector space of degree 3, dimension 2 over Rational Field
2651
+ Generators:
2652
+ (1 2 3)
2653
+ (1 1 2)
2654
+ Echelonized basis:
2655
+ (1 0 1)
2656
+ (0 1 1)
2657
+ """
2658
+ return self.parent().bar_call(self, 'sub', gens)
2659
+
2660
+ def quo(self, gens, **args):
2661
+ """
2662
+ Return the quotient of ``self`` by the given object or list of
2663
+ generators.
2664
+
2665
+ INPUT:
2666
+
2667
+ - ``gens`` -- object or list/tuple of generators
2668
+ - further named arguments that are ignored
2669
+
2670
+ OUTPUT:
2671
+
2672
+ - ``magma element`` -- the quotient object
2673
+
2674
+ - ``magma element`` -- mapping from ``self`` to the
2675
+ quotient object
2676
+
2677
+ EXAMPLES::
2678
+
2679
+ sage: V = magma('VectorSpace(RationalField(),3)') # optional - magma
2680
+ sage: V.quo([[1,2,3], [1,1,2]]) # optional - magma
2681
+ (Full Vector space of degree 1 over Rational Field, Mapping from: Full Vector space of degree 3 over Rational Field to Full Vector space of degree 1 over Rational Field)
2682
+
2683
+ We illustrate quotienting out by an object instead of a list of
2684
+ generators::
2685
+
2686
+ sage: W = V.sub([ [1,2,3], [1,1,2] ]) # optional - magma
2687
+ sage: V.quo(W) # optional - magma
2688
+ (Full Vector space of degree 1 over Rational Field, Mapping from: Full Vector space of degree 3 over Rational Field to Full Vector space of degree 1 over Rational Field)
2689
+
2690
+ We quotient a ZZ module out by a submodule.
2691
+
2692
+ ::
2693
+
2694
+ sage: # optional - magma
2695
+ sage: V = magma.RModule(ZZ,3); V
2696
+ RModule(IntegerRing(), 3)
2697
+ sage: W, phi = V.quo([[1,2,3]])
2698
+ sage: W
2699
+ RModule(IntegerRing(), 2)
2700
+ sage: phi
2701
+ Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 2)
2702
+ """
2703
+ return self.parent().bar_call(self, 'quo', gens, nvals=2)
2704
+
2705
+ def ideal(self, gens):
2706
+ """
2707
+ Return the ideal of ``self`` with given list of generators.
2708
+
2709
+ INPUT:
2710
+
2711
+ - ``gens`` -- object or list/tuple of generators
2712
+
2713
+ OUTPUT:
2714
+
2715
+ - ``magma element`` -- a Magma ideal
2716
+
2717
+ EXAMPLES::
2718
+
2719
+ sage: # optional - magma
2720
+ sage: R = magma('PolynomialRing(RationalField())')
2721
+ sage: R.assign_names(['x'])
2722
+ sage: x = R.1
2723
+ sage: R.ideal([x^2 - 1, x^3 - 1])
2724
+ Ideal of Univariate Polynomial Ring in x over Rational Field generated by x - 1
2725
+ """
2726
+ return self.parent().bar_call(self, 'ideal', gens, nvals=1)
2727
+
2728
+
2729
+ magma = Magma()
2730
+
2731
+
2732
+ def reduce_load_Magma():
2733
+ """
2734
+ Used in unpickling a Magma interface.
2735
+
2736
+ This functions just returns the global default Magma interface.
2737
+
2738
+ EXAMPLES::
2739
+
2740
+ sage: sage.interfaces.magma.reduce_load_Magma()
2741
+ Magma
2742
+ """
2743
+ return magma
2744
+
2745
+
2746
+ def magma_console():
2747
+ """
2748
+ Run a command line Magma session.
2749
+
2750
+ EXAMPLES::
2751
+
2752
+ sage: magma_console() # not tested
2753
+ Magma V2.14-9 Sat Oct 11 2008 06:36:41 on one [Seed = 1157408761]
2754
+ Type ? for help. Type <Ctrl>-D to quit.
2755
+ >
2756
+ Total time: 2.820 seconds, Total memory usage: 3.95MB
2757
+ """
2758
+ from sage.repl.rich_output.display_manager import get_display_manager
2759
+ if not get_display_manager().is_in_terminal():
2760
+ raise RuntimeError('Can use the console only in the terminal. Try %%magma magics instead.')
2761
+ os.system('magma')
2762
+
2763
+
2764
+ class MagmaGBLogPrettyPrinter:
2765
+ """
2766
+ A device which filters Magma Groebner basis computation logs.
2767
+ """
2768
+ cmd_inpt = re.compile("^>>>$")
2769
+ app_inpt = re.compile("^Append\\(~_sage_, 0\\);$")
2770
+
2771
+ deg_curr = re.compile(
2772
+ "^Basis length\\: (\\d+), queue length\\: (\\d+), step degree\\: (\\d+), num pairs\\: (\\d+)$"
2773
+ )
2774
+ pol_curr = re.compile("^Number of pair polynomials\\: (\\d+), at (\\d+) column\\(s\\), .*")
2775
+
2776
+ def __init__(self, verbosity=1, style='magma'):
2777
+ """
2778
+ Construct a new Magma Groebner Basis log pretty printer.
2779
+
2780
+ INPUT:
2781
+
2782
+ - ``verbosity`` -- how much information should be printed
2783
+ (between 0 and 1)
2784
+
2785
+ - ``style`` -- if "magma" the full Magma log is printed; if
2786
+ 'sage' only the current degree and the number of pairs in
2787
+ the queue is printed (default: ``'magma'``).
2788
+
2789
+ EXAMPLES::
2790
+
2791
+ sage: # needs sage.libs.singular
2792
+ sage: P.<x,y,z> = GF(32003)[]
2793
+ sage: I = sage.rings.ideal.Cyclic(P)
2794
+ sage: _ = I.groebner_basis('magma',prot='sage') # indirect doctest, optional - magma, not tested
2795
+
2796
+ Leading term degree: 2. Critical pairs: 2.
2797
+ Leading term degree: 3. Critical pairs: 1.
2798
+
2799
+ Highest degree reached during computation: 3.
2800
+
2801
+ sage: # needs sage.libs.singular
2802
+ sage: P.<x,y,z> = GF(32003)[]
2803
+ sage: I = sage.rings.ideal.Cyclic(P)
2804
+ sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma, not tested
2805
+ ********************
2806
+ FAUGERE F4 ALGORITHM
2807
+ ********************
2808
+ Coefficient ring: GF(32003)
2809
+ Rank: 3
2810
+ Order: Graded Reverse Lexicographical
2811
+ NEW hash table
2812
+ Matrix kind: Modular FP
2813
+ Datum size: 4
2814
+ No queue sort
2815
+ Initial length: 3
2816
+ Inhomogeneous
2817
+
2818
+ Initial queue setup time: 0.000
2819
+ Initial queue length: 2
2820
+
2821
+ *******
2822
+ STEP 1
2823
+ Basis length: 3, queue length: 2, step degree: 2, num pairs: 1
2824
+ Basis total mons: 8, average length: 2.667
2825
+ Number of pair polynomials: 1, at 4 column(s), 0.000
2826
+ ...
2827
+ Total Faugere F4 time: 0.000, real time: 0.000
2828
+
2829
+ sage: # needs sage.libs.pari
2830
+ sage: set_random_seed(1)
2831
+ sage: sr = mq.SR(1,1,2,4)
2832
+ sage: F,s = sr.polynomial_system()
2833
+ sage: I = F.ideal()
2834
+ sage: _ = I.groebner_basis('magma',prot='sage') # indirect doctest, optional - magma, not tested
2835
+ Leading term degree: 1. Critical pairs: 40.
2836
+ Leading term degree: 2. Critical pairs: 40.
2837
+ Leading term degree: 3. Critical pairs: 38.
2838
+ Leading term degree: 2. Critical pairs: 327.
2839
+ Leading term degree: 2. Critical pairs: 450.
2840
+ Leading term degree: 2. Critical pairs: 416.
2841
+ Leading term degree: 3. Critical pairs: 415.
2842
+ Leading term degree: 4. Critical pairs: 98 (all pairs of current degree eliminated by criteria).
2843
+ Leading term degree: 5. Critical pairs: 3 (all pairs of current degree eliminated by criteria).
2844
+
2845
+ Highest degree reached during computation: 3.
2846
+ """
2847
+ self.verbosity = verbosity
2848
+ if style not in ['sage', 'magma']:
2849
+ raise ValueError('style must be sage or magma')
2850
+ self.style = style
2851
+
2852
+ self.curr_deg = 0 # current degree
2853
+ self.curr_npairs = 0 # current number of pairs to be considered
2854
+ self.max_deg = 0 # maximal degree in total
2855
+
2856
+ self.storage = "" # stores incomplete strings
2857
+ self.sync = None # should we expect a sync integer?
2858
+
2859
+ def write(self, s):
2860
+ """
2861
+ EXAMPLES::
2862
+
2863
+ sage: # needs sage.libs.singular
2864
+ sage: P.<x,y,z> = GF(32003)[]
2865
+ sage: I = sage.rings.ideal.Katsura(P)
2866
+ sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma
2867
+ ...
2868
+ ********************
2869
+ FAUGERE F4 ALGORITHM
2870
+ ********************
2871
+ ...
2872
+ Total Faugere F4 time: ..., real time: ...
2873
+ """
2874
+ verbosity, style = self.verbosity, self.style
2875
+
2876
+ if isinstance(s, bytes):
2877
+ # sys.stdout.encoding can be None or something else
2878
+ if isinstance(sys.stdout.encoding, str):
2879
+ s = s.decode(sys.stdout.encoding)
2880
+ else:
2881
+ s = s.decode("UTF-8")
2882
+
2883
+ if self.storage:
2884
+ s = self.storage + s
2885
+ self.storage = ""
2886
+
2887
+ for line in s.splitlines():
2888
+ # deal with the Sage <-> Magma syncing code
2889
+ match = re.match(MagmaGBLogPrettyPrinter.cmd_inpt, line)
2890
+ if match:
2891
+ self.sync = 1
2892
+ continue
2893
+
2894
+ if self.sync:
2895
+ if self.sync == 1:
2896
+ self.sync = line
2897
+ continue
2898
+ else:
2899
+ if line == '':
2900
+ continue
2901
+ self.sync = None
2902
+ continue
2903
+
2904
+ if re.match(MagmaGBLogPrettyPrinter.app_inpt, line):
2905
+ continue
2906
+
2907
+ if re.match(MagmaGBLogPrettyPrinter.deg_curr, line):
2908
+ match = re.match(MagmaGBLogPrettyPrinter.deg_curr, line)
2909
+
2910
+ nbasis, npairs, deg, npairs_deg = map(int, match.groups())
2911
+
2912
+ self.curr_deg = deg
2913
+ self.curr_npairs = npairs
2914
+
2915
+ if re.match(MagmaGBLogPrettyPrinter.pol_curr, line):
2916
+ match = re.match(MagmaGBLogPrettyPrinter.pol_curr, line)
2917
+ pol_curr, col_curr = map(int, match.groups())
2918
+
2919
+ if pol_curr != 0:
2920
+ self.max_deg = max(self.max_deg, self.curr_deg)
2921
+
2922
+ if style == "sage" and verbosity >= 1:
2923
+ print("Leading term degree: %2d. Critical pairs: %d." %
2924
+ (self.curr_deg, self.curr_npairs))
2925
+ else:
2926
+ if style == "sage" and verbosity >= 1:
2927
+ print("Leading term degree: %2d. Critical pairs: %d (all pairs of current degree eliminated by criteria)." %
2928
+ (self.curr_deg, self.curr_npairs))
2929
+
2930
+ if style == "magma" and verbosity >= 1:
2931
+ print(line)
2932
+
2933
+ def flush(self):
2934
+ """
2935
+ EXAMPLES::
2936
+
2937
+ sage: from sage.interfaces.magma import MagmaGBLogPrettyPrinter
2938
+ sage: logs = MagmaGBLogPrettyPrinter()
2939
+ sage: logs.flush()
2940
+ """
2941
+ import sys
2942
+ sys.stdout.flush()
2943
+
2944
+
2945
+ class MagmaGBDefaultContext:
2946
+ """
2947
+ Context to force preservation of verbosity options for Magma's
2948
+ Groebner basis computation.
2949
+ """
2950
+ def __init__(self, magma=None):
2951
+ """
2952
+ INPUT:
2953
+
2954
+ - ``magma`` -- (default: ``magma_default``)
2955
+
2956
+ EXAMPLES::
2957
+
2958
+ sage: from sage.interfaces.magma import MagmaGBDefaultContext
2959
+ sage: magma.SetVerbose('Groebner',1) # optional - magma
2960
+ sage: with MagmaGBDefaultContext(): magma.GetVerbose('Groebner') # optional - magma
2961
+ 0
2962
+ """
2963
+ if magma is None:
2964
+ from sage.interfaces.magma import magma as magma_default
2965
+ magma = magma_default
2966
+
2967
+ self.magma = magma
2968
+
2969
+ def __enter__(self):
2970
+ """
2971
+ EXAMPLES::
2972
+
2973
+ sage: from sage.interfaces.magma import MagmaGBDefaultContext
2974
+ sage: magma.SetVerbose('Groebner',1) # optional - magma
2975
+ sage: with MagmaGBDefaultContext(): magma.GetVerbose('Groebner') # optional - magma
2976
+ 0
2977
+ """
2978
+ self.groebner_basis_verbose = self.magma.GetVerbose('Groebner')
2979
+ self.magma.SetVerbose('Groebner', 0)
2980
+
2981
+ def __exit__(self, typ, value, tb):
2982
+ """
2983
+ EXAMPLES::
2984
+
2985
+ sage: from sage.interfaces.magma import MagmaGBDefaultContext
2986
+ sage: magma.SetVerbose('Groebner',1) # optional - magma
2987
+ sage: with MagmaGBDefaultContext(): magma.GetVerbose('Groebner') # optional - magma
2988
+ 0
2989
+ sage: magma.GetVerbose('Groebner') # optional - magma
2990
+ 1
2991
+ """
2992
+ self.magma.SetVerbose('Groebner', self.groebner_basis_verbose)
2993
+
2994
+
2995
+ def magma_gb_standard_options(func):
2996
+ """
2997
+ Decorator to force default options for Magma.
2998
+
2999
+ EXAMPLES::
3000
+
3001
+ sage: # needs sage.libs.singular
3002
+ sage: P.<a,b,c,d,e> = PolynomialRing(GF(127))
3003
+ sage: J = sage.rings.ideal.Cyclic(P).homogenize()
3004
+ sage: from sage.misc.sageinspect import sage_getsource
3005
+ sage: "mself" in sage_getsource(J._groebner_basis_magma)
3006
+ True
3007
+ """
3008
+ from sage.misc.decorators import sage_wraps
3009
+
3010
+ @sage_wraps(func)
3011
+ def wrapper(*args, **kwds):
3012
+ """
3013
+ Execute function in ``MagmaGBDefaultContext``.
3014
+ """
3015
+ with MagmaGBDefaultContext():
3016
+ return func(*args, **kwds)
3017
+ return wrapper