passagemath-symbolics 10.8.1a1__cp314-cp314t-musllinux_1_2_aarch64.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.
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
- passagemath_symbolics-10.8.1a1.dist-info/RECORD +181 -0
- passagemath_symbolics-10.8.1a1.dist-info/WHEEL +5 -0
- passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2838 -0
- sage/calculus/desolvers.py +1864 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +2991 -0
- sage/interfaces/magma_free.py +90 -0
- sage/interfaces/maple.py +1402 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +553 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4010 -0
- sage/manifolds/chart_func.py +3416 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
- sage/manifolds/differentiable/diff_form.py +1660 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1522 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +912 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1725 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2721 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +883 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1347 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1030 -0
- sage/matrix/matrix_symbolic_sparse.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5205 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +987 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +456 -0
- sage/symbolic/callable.pyi +66 -0
- sage/symbolic/comparison_impl.pyi +38 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1286 -0
- sage/symbolic/constants_c_impl.pyi +10 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1727 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/function_factory.pyi +41 -0
- sage/symbolic/getitem_impl.pyi +24 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +271 -0
- sage/symbolic/integration/integral.py +1075 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/operators.pyi +61 -0
- sage/symbolic/pynac_constant_impl.pyi +13 -0
- sage/symbolic/pynac_function_impl.pyi +8 -0
- sage/symbolic/random_tests.py +461 -0
- sage/symbolic/relation.py +2062 -0
- sage/symbolic/ring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyi +110 -0
- sage/symbolic/ring.pyx +1393 -0
- sage/symbolic/series_impl.pyi +10 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- sage/symbolic/units.py +1468 -0
sage/interfaces/magma.py
ADDED
|
@@ -0,0 +1,2991 @@
|
|
|
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
|
+
@instancedoc
|
|
1781
|
+
class MagmaElement(ExtraTabCompletion, ExpectElement,
|
|
1782
|
+
sage.interfaces.abc.MagmaElement):
|
|
1783
|
+
def _ref(self):
|
|
1784
|
+
"""
|
|
1785
|
+
Return a variable name that is a new reference to this particular
|
|
1786
|
+
MagmaElement in Magma. This keeps this object from being garbage
|
|
1787
|
+
collected by Magma, even if all the Sage references to it are
|
|
1788
|
+
freed.
|
|
1789
|
+
|
|
1790
|
+
Important special behavior: When _ref is used during an implicit
|
|
1791
|
+
call to _magma_init_, then the reference disappears after the
|
|
1792
|
+
coercion is done. More precisely, if the output of _ref() appears
|
|
1793
|
+
as part of the output of a call to _magma_init_ that is then
|
|
1794
|
+
going to be input to magma(...), then it is deleted in the Magma
|
|
1795
|
+
interface. The main use for this behavior is that in
|
|
1796
|
+
_magma_init_ it allows you to get a reference to one object, and
|
|
1797
|
+
use it exactly once in constructing a string to evaluate in Magma,
|
|
1798
|
+
without having to worry about that object being deallocated. There
|
|
1799
|
+
are more sophisticated ways that the same problem (with
|
|
1800
|
+
_magma_init_ and references) could have been solved, but this
|
|
1801
|
+
solution is much simpler and easier to understand than all others I
|
|
1802
|
+
came up with. If this doesn't make sense, read the source code to
|
|
1803
|
+
_coerce_from_special_method, which is much shorter than this
|
|
1804
|
+
paragraph.
|
|
1805
|
+
|
|
1806
|
+
.. warning::
|
|
1807
|
+
|
|
1808
|
+
Use _ref sparingly, since it involves a full call to Magma,
|
|
1809
|
+
which can be slow.
|
|
1810
|
+
|
|
1811
|
+
OUTPUT: string
|
|
1812
|
+
|
|
1813
|
+
EXAMPLES::
|
|
1814
|
+
|
|
1815
|
+
sage: a = magma('-2/3') # optional - magma
|
|
1816
|
+
sage: s = a._ref(); s # optional - magma
|
|
1817
|
+
'_sage_ref...'
|
|
1818
|
+
sage: magma(s) # optional - magma
|
|
1819
|
+
-2/3
|
|
1820
|
+
"""
|
|
1821
|
+
P = self._check_valid()
|
|
1822
|
+
n = P._next_ref_name()
|
|
1823
|
+
P.set(n, self.name())
|
|
1824
|
+
return n
|
|
1825
|
+
|
|
1826
|
+
def __getattr__(self, attrname):
|
|
1827
|
+
"""
|
|
1828
|
+
INPUT:
|
|
1829
|
+
|
|
1830
|
+
- ``attrname`` -- string
|
|
1831
|
+
|
|
1832
|
+
OUTPUT: a Magma function partially evaluated with ``self`` as the first
|
|
1833
|
+
input
|
|
1834
|
+
|
|
1835
|
+
.. NOTE::
|
|
1836
|
+
|
|
1837
|
+
If the input ``attrname`` starts with an underscore, an
|
|
1838
|
+
:exc:`AttributeError` is raised so that the actual
|
|
1839
|
+
Python _ method/value can be accessed.
|
|
1840
|
+
|
|
1841
|
+
EXAMPLES::
|
|
1842
|
+
|
|
1843
|
+
sage: # optional - magma
|
|
1844
|
+
sage: n = magma(-15)
|
|
1845
|
+
sage: type(n)
|
|
1846
|
+
<class 'sage.interfaces.magma.MagmaElement'>
|
|
1847
|
+
sage: f = n.__getattr__('Factorization')
|
|
1848
|
+
sage: type(f)
|
|
1849
|
+
<class 'sage.interfaces.magma.MagmaFunctionElement'>
|
|
1850
|
+
sage: f
|
|
1851
|
+
Partially evaluated Magma function or intrinsic 'Factorization'
|
|
1852
|
+
...
|
|
1853
|
+
"""
|
|
1854
|
+
if attrname[:1] == "_":
|
|
1855
|
+
raise AttributeError
|
|
1856
|
+
return MagmaFunctionElement(self, attrname)
|
|
1857
|
+
|
|
1858
|
+
def _sage_(self):
|
|
1859
|
+
"""
|
|
1860
|
+
Return Sage version of this object.
|
|
1861
|
+
|
|
1862
|
+
Use self.sage() to get the Sage version.
|
|
1863
|
+
|
|
1864
|
+
Edit ``src/sage/ext_data/magma/sage/basic.m`` to add functionality.
|
|
1865
|
+
|
|
1866
|
+
EXAMPLES: Enumerated Sets::
|
|
1867
|
+
|
|
1868
|
+
sage: # optional - magma
|
|
1869
|
+
sage: a = magma('{1,2/3,-5/9}')
|
|
1870
|
+
sage: a.sage()
|
|
1871
|
+
{1, -5/9, 2/3}
|
|
1872
|
+
sage: a._sage_()
|
|
1873
|
+
{1, -5/9, 2/3}
|
|
1874
|
+
sage: type(a.sage())
|
|
1875
|
+
<class 'sage.sets.set.Set_object_enumerated_with_category'>
|
|
1876
|
+
sage: a = magma('{1,2/3,-5/9}'); a
|
|
1877
|
+
{ -5/9, 2/3, 1 }
|
|
1878
|
+
sage: a.Type()
|
|
1879
|
+
SetEnum
|
|
1880
|
+
sage: b = a.sage(); b
|
|
1881
|
+
{1, -5/9, 2/3}
|
|
1882
|
+
sage: type(b)
|
|
1883
|
+
<class 'sage.sets.set.Set_object_enumerated_with_category'>
|
|
1884
|
+
sage: c = magma(b); c
|
|
1885
|
+
{ -5/9, 2/3, 1 }
|
|
1886
|
+
sage: c.Type()
|
|
1887
|
+
SetEnum
|
|
1888
|
+
|
|
1889
|
+
Multisets are converted to lists::
|
|
1890
|
+
|
|
1891
|
+
sage: m = magma('{* 1,2,2,2,4^^2,3 *}') # optional - magma
|
|
1892
|
+
sage: z = m.sage(); z # optional - magma
|
|
1893
|
+
[1, 2, 2, 2, 3, 4, 4]
|
|
1894
|
+
sage: type(z) # optional - magma
|
|
1895
|
+
<... 'list'>
|
|
1896
|
+
|
|
1897
|
+
Tuples get converted to tuples::
|
|
1898
|
+
|
|
1899
|
+
sage: m = magma('<1,2,<3>>') # optional - magma
|
|
1900
|
+
sage: z = m.sage(); z # optional - magma
|
|
1901
|
+
(1, 2, (3,))
|
|
1902
|
+
sage: type(z) # optional - magma
|
|
1903
|
+
<... 'tuple'>
|
|
1904
|
+
|
|
1905
|
+
Sequences get converted to lists::
|
|
1906
|
+
|
|
1907
|
+
sage: m = magma('[<1>,<2>]') # optional - magma
|
|
1908
|
+
sage: z = m.sage(); z # optional - magma
|
|
1909
|
+
[(1,), (2,)]
|
|
1910
|
+
sage: type(z) # optional - magma
|
|
1911
|
+
<... 'list'>
|
|
1912
|
+
|
|
1913
|
+
Matrices::
|
|
1914
|
+
|
|
1915
|
+
sage: a = matrix(ZZ,3,3,[1..9])
|
|
1916
|
+
sage: m = magma(a) # optional - magma
|
|
1917
|
+
sage: b = m.sage(); b # optional - magma
|
|
1918
|
+
[1 2 3]
|
|
1919
|
+
[4 5 6]
|
|
1920
|
+
[7 8 9]
|
|
1921
|
+
sage: b == a # optional - magma
|
|
1922
|
+
True
|
|
1923
|
+
|
|
1924
|
+
A nonsquare matrix::
|
|
1925
|
+
|
|
1926
|
+
sage: a = matrix(ZZ,2,3,[1..6])
|
|
1927
|
+
sage: m = magma(a) # optional - magma
|
|
1928
|
+
sage: m.sage() # optional - magma
|
|
1929
|
+
[1 2 3]
|
|
1930
|
+
[4 5 6]
|
|
1931
|
+
|
|
1932
|
+
Multivariate polynomials::
|
|
1933
|
+
|
|
1934
|
+
sage: # optional - magma
|
|
1935
|
+
sage: R.<x,y,z> = QQ[]
|
|
1936
|
+
sage: f = x^2+3*y
|
|
1937
|
+
sage: g = magma(f).sage(); g
|
|
1938
|
+
x^2 + 3*y
|
|
1939
|
+
sage: parent(f) == parent(g)
|
|
1940
|
+
True
|
|
1941
|
+
|
|
1942
|
+
Real and complex numbers::
|
|
1943
|
+
|
|
1944
|
+
sage: # optional - magma
|
|
1945
|
+
sage: m = magma(RealField(200)(1/3))
|
|
1946
|
+
sage: m.sage()
|
|
1947
|
+
0.33333333333333333333333333333333333333333333333333333333333
|
|
1948
|
+
sage: m = magma(RealField(1000)(1/3))
|
|
1949
|
+
sage: m.sage()
|
|
1950
|
+
0.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
|
|
1951
|
+
|
|
1952
|
+
sage: m = magma(ComplexField(200)).1; m # optional - magma
|
|
1953
|
+
1.00000000000000000000000000000000000000000000000000000000000*$.1
|
|
1954
|
+
sage: s = m.Sqrt(); s # optional - magma
|
|
1955
|
+
0.707106781186547524400844362104849039284835937688474036588340 + 0.707106781186547524400844362104849039284835937688474036588340*$.1
|
|
1956
|
+
sage: s.sage() # indirect doctest, optional - magma
|
|
1957
|
+
0.70710678118654752440084436210484903928483593768847403658834 + 0.70710678118654752440084436210484903928483593768847403658834*I
|
|
1958
|
+
|
|
1959
|
+
Number fields and their elements::
|
|
1960
|
+
|
|
1961
|
+
sage: x = var('x')
|
|
1962
|
+
sage: L.<alpha> = NumberField(x^3+2*x+2)
|
|
1963
|
+
sage: K = magma(L) # optional - magma
|
|
1964
|
+
sage: K.sage() # optional - magma
|
|
1965
|
+
Number Field in alpha with defining polynomial x^3 + 2*x + 2
|
|
1966
|
+
sage: K.sage() is L # optional - magma
|
|
1967
|
+
True
|
|
1968
|
+
sage: magma(alpha).sage() # optional - magma
|
|
1969
|
+
alpha
|
|
1970
|
+
|
|
1971
|
+
Relative number field elements can be converted from Magma
|
|
1972
|
+
to Sage, but the other direction has not yet been implemented.::
|
|
1973
|
+
|
|
1974
|
+
sage: # needs sage.libs.pari
|
|
1975
|
+
sage: P.<y> = L[]
|
|
1976
|
+
sage: N.<b> = NumberField(y^2-alpha)
|
|
1977
|
+
sage: M = magma(N) # optional - magma
|
|
1978
|
+
sage: M.1.sage() # optional - magma
|
|
1979
|
+
b
|
|
1980
|
+
sage: _^2 # optional - magma
|
|
1981
|
+
alpha
|
|
1982
|
+
sage: magma(b) # optional - magma
|
|
1983
|
+
Traceback (most recent call last):
|
|
1984
|
+
...
|
|
1985
|
+
TypeError: coercion of relative number field elements to Magma is not implemented
|
|
1986
|
+
|
|
1987
|
+
Sage does not have absolute number fields defined by
|
|
1988
|
+
two polynomials, like Magma does. They are converted
|
|
1989
|
+
to relative number fields. Conversion of their elements
|
|
1990
|
+
has not yet been implemented.::
|
|
1991
|
+
|
|
1992
|
+
sage: # optional - magma
|
|
1993
|
+
sage: magma.eval('P<x> := PolynomialRing(Rationals());')
|
|
1994
|
+
''
|
|
1995
|
+
sage: K = magma('NumberField([x^2-2,x^2-3]:Abs);')
|
|
1996
|
+
sage: L = K.sage(); L
|
|
1997
|
+
Number Field in K1 with defining polynomial x^2 - 2 over its base field
|
|
1998
|
+
sage: L.base_field()
|
|
1999
|
+
Number Field in K2 with defining polynomial x^2 - 3
|
|
2000
|
+
sage: K.GeneratorsSequence()[1].sage()
|
|
2001
|
+
Traceback (most recent call last):
|
|
2002
|
+
...
|
|
2003
|
+
NameError: name 'K' is not defined
|
|
2004
|
+
|
|
2005
|
+
Finite quotients of ZZ::
|
|
2006
|
+
|
|
2007
|
+
sage: R = Zmod(137)
|
|
2008
|
+
sage: magma(R).sage() # optional - magma
|
|
2009
|
+
Ring of integers modulo 137
|
|
2010
|
+
|
|
2011
|
+
TESTS:
|
|
2012
|
+
|
|
2013
|
+
Tests for :issue:`30341`::
|
|
2014
|
+
|
|
2015
|
+
sage: P.<t> = PolynomialRing(QQ)
|
|
2016
|
+
sage: l = [-27563611963/4251528, -48034411/104976, -257/54, 1]
|
|
2017
|
+
sage: u = P(l)
|
|
2018
|
+
sage: u == P(magma(u).sage()) # optional - magma
|
|
2019
|
+
True
|
|
2020
|
+
|
|
2021
|
+
sage: P.<x,y> = PolynomialRing(QQ, 2)
|
|
2022
|
+
sage: u = x + 27563611963/4251528*y
|
|
2023
|
+
sage: magma(u).sage() # optional - magma
|
|
2024
|
+
x + 27563611963/4251528*y
|
|
2025
|
+
"""
|
|
2026
|
+
z, preparse = self.Sage(nvals=2)
|
|
2027
|
+
s = str(z)
|
|
2028
|
+
preparse = str(preparse) == 'true'
|
|
2029
|
+
return sage.misc.sage_eval.sage_eval(s, preparse=preparse)
|
|
2030
|
+
|
|
2031
|
+
def AssignNames(self, names):
|
|
2032
|
+
"""
|
|
2033
|
+
EXAMPLES::
|
|
2034
|
+
|
|
2035
|
+
sage: # optional - magma
|
|
2036
|
+
sage: S = magma.PolynomialRing(magma.Integers(), 2)
|
|
2037
|
+
sage: S.AssignNames(['a', 'b'])
|
|
2038
|
+
sage: S.1
|
|
2039
|
+
a
|
|
2040
|
+
sage: S.1^2 + S.2
|
|
2041
|
+
a^2 + b
|
|
2042
|
+
"""
|
|
2043
|
+
P = self._check_valid()
|
|
2044
|
+
cmd = 'AssignNames(~%s, [%s])' % (self.name(),
|
|
2045
|
+
','.join('"%s"' % x for x in names))
|
|
2046
|
+
P.eval(cmd)
|
|
2047
|
+
|
|
2048
|
+
assign_names = AssignNames
|
|
2049
|
+
|
|
2050
|
+
def gen(self, n):
|
|
2051
|
+
"""
|
|
2052
|
+
Return the `n`-th generator of this Magma element.
|
|
2053
|
+
|
|
2054
|
+
Note that generators are 1-based in Magma rather than 0-based!
|
|
2055
|
+
|
|
2056
|
+
INPUT:
|
|
2057
|
+
|
|
2058
|
+
- ``n`` -- *positive* integer
|
|
2059
|
+
|
|
2060
|
+
OUTPUT: :class:`MagmaElement`
|
|
2061
|
+
|
|
2062
|
+
EXAMPLES::
|
|
2063
|
+
|
|
2064
|
+
sage: # needs sage.rings.finite_rings
|
|
2065
|
+
sage: k.<a> = GF(9)
|
|
2066
|
+
sage: magma(k).gen(1) # optional -- magma
|
|
2067
|
+
a
|
|
2068
|
+
sage: R.<s,t,w> = k[]
|
|
2069
|
+
sage: m = magma(R) # optional -- magma
|
|
2070
|
+
sage: m.gen(1) # optional -- magma
|
|
2071
|
+
s
|
|
2072
|
+
sage: m.gen(2) # optional -- magma
|
|
2073
|
+
t
|
|
2074
|
+
sage: m.gen(3) # optional -- magma
|
|
2075
|
+
w
|
|
2076
|
+
sage: m.gen(0) # optional -- magma
|
|
2077
|
+
Traceback (most recent call last):
|
|
2078
|
+
...
|
|
2079
|
+
IndexError: index must be positive since Magma indexes are 1-based
|
|
2080
|
+
sage: m.gen(4) # optional -- magma
|
|
2081
|
+
Traceback (most recent call last):
|
|
2082
|
+
...
|
|
2083
|
+
IndexError: tuple index out of range
|
|
2084
|
+
"""
|
|
2085
|
+
if n <= 0:
|
|
2086
|
+
raise IndexError("index must be positive since Magma indexes are 1-based")
|
|
2087
|
+
return self.gens()[n - 1]
|
|
2088
|
+
|
|
2089
|
+
def gens(self) -> tuple:
|
|
2090
|
+
"""
|
|
2091
|
+
Return generators for ``self``.
|
|
2092
|
+
|
|
2093
|
+
If ``self`` is named X in Magma, this function evaluates X.1, X.2,
|
|
2094
|
+
etc., in Magma until an error occurs. It then returns a Sage tuple
|
|
2095
|
+
of the resulting X.i. Note - I do not think there is a Magma command
|
|
2096
|
+
that returns the list of valid X.i. There are numerous ad hoc
|
|
2097
|
+
functions for various classes but nothing systematic. This function
|
|
2098
|
+
gets around that problem. Again, this is something that should
|
|
2099
|
+
probably be reported to the Magma group and fixed there.
|
|
2100
|
+
|
|
2101
|
+
AUTHORS:
|
|
2102
|
+
|
|
2103
|
+
- William Stein (2006-07-02)
|
|
2104
|
+
|
|
2105
|
+
EXAMPLES::
|
|
2106
|
+
|
|
2107
|
+
sage: magma("VectorSpace(RationalField(),3)").gens() # optional - magma
|
|
2108
|
+
((1 0 0), (0 1 0), (0 0 1))
|
|
2109
|
+
sage: magma("AbelianGroup(EllipticCurve([1..5]))").gens() # optional - magma
|
|
2110
|
+
($.1,)
|
|
2111
|
+
"""
|
|
2112
|
+
try:
|
|
2113
|
+
return self._magma_gens
|
|
2114
|
+
except AttributeError:
|
|
2115
|
+
pass
|
|
2116
|
+
G = []
|
|
2117
|
+
i = 1
|
|
2118
|
+
P = self._check_valid()
|
|
2119
|
+
n = self.name()
|
|
2120
|
+
while True:
|
|
2121
|
+
try:
|
|
2122
|
+
G.append(P('%s.%s' % (n, i)))
|
|
2123
|
+
except (RuntimeError, TypeError):
|
|
2124
|
+
break
|
|
2125
|
+
i += 1
|
|
2126
|
+
tG = tuple(G)
|
|
2127
|
+
self._magma_gens = tG
|
|
2128
|
+
return tG
|
|
2129
|
+
|
|
2130
|
+
def gen_names(self):
|
|
2131
|
+
"""
|
|
2132
|
+
Return list of Magma variable names of the generators of ``self``.
|
|
2133
|
+
|
|
2134
|
+
.. NOTE::
|
|
2135
|
+
|
|
2136
|
+
As illustrated below, these are not the print names of the
|
|
2137
|
+
the generators of the Magma object, but special variable
|
|
2138
|
+
names in the Magma session that reference the generators.
|
|
2139
|
+
|
|
2140
|
+
EXAMPLES::
|
|
2141
|
+
|
|
2142
|
+
sage: R.<x,zw> = QQ[]
|
|
2143
|
+
sage: S = magma(R) # optional - magma
|
|
2144
|
+
sage: S.gen_names() # optional - magma
|
|
2145
|
+
('_sage_[...]', '_sage_[...]')
|
|
2146
|
+
sage: magma(S.gen_names()[1]) # optional - magma
|
|
2147
|
+
zw
|
|
2148
|
+
"""
|
|
2149
|
+
try:
|
|
2150
|
+
return self.__gen_names
|
|
2151
|
+
except AttributeError:
|
|
2152
|
+
self.__gen_names = tuple([x.name() for x in self.gens()])
|
|
2153
|
+
return self.__gen_names
|
|
2154
|
+
|
|
2155
|
+
def evaluate(self, *args):
|
|
2156
|
+
r"""
|
|
2157
|
+
Evaluate ``self`` at the inputs.
|
|
2158
|
+
|
|
2159
|
+
INPUT:
|
|
2160
|
+
|
|
2161
|
+
- ``*args`` -- import arguments
|
|
2162
|
+
|
|
2163
|
+
OUTPUT: self(\*args)
|
|
2164
|
+
|
|
2165
|
+
EXAMPLES::
|
|
2166
|
+
|
|
2167
|
+
sage: # optional - magma
|
|
2168
|
+
sage: f = magma('Factorization')
|
|
2169
|
+
sage: f.evaluate(15)
|
|
2170
|
+
[ <3, 1>, <5, 1> ]
|
|
2171
|
+
sage: f(15)
|
|
2172
|
+
[ <3, 1>, <5, 1> ]
|
|
2173
|
+
sage: f = magma('GCD')
|
|
2174
|
+
sage: f.evaluate(15,20)
|
|
2175
|
+
5
|
|
2176
|
+
|
|
2177
|
+
sage: m = matrix(QQ, 2, 2, [2,3,5,7]) # optional - magma
|
|
2178
|
+
sage: f = magma('ElementaryDivisors') # optional - magma
|
|
2179
|
+
sage: f.evaluate(m) # optional - magma
|
|
2180
|
+
[ 1, 1 ]
|
|
2181
|
+
"""
|
|
2182
|
+
P = self._check_valid()
|
|
2183
|
+
names = ','.join(a._magma_init_(P) for a in args)
|
|
2184
|
+
return P('%s(%s)' % (self.name(), names))
|
|
2185
|
+
|
|
2186
|
+
eval = evaluate
|
|
2187
|
+
|
|
2188
|
+
def __call__(self, *args):
|
|
2189
|
+
"""
|
|
2190
|
+
Coerce something into the object (using the Magma ! notation).
|
|
2191
|
+
|
|
2192
|
+
For function calls, use self.eval(...).
|
|
2193
|
+
|
|
2194
|
+
EXAMPLES::
|
|
2195
|
+
|
|
2196
|
+
sage: # optional - magma
|
|
2197
|
+
sage: M = magma.RMatrixSpace(magma.IntegerRing(), 2, 2)
|
|
2198
|
+
sage: A = M([1,2,3,4]); A
|
|
2199
|
+
[1 2]
|
|
2200
|
+
[3 4]
|
|
2201
|
+
sage: type(A)
|
|
2202
|
+
<class 'sage.interfaces.magma.MagmaElement'>
|
|
2203
|
+
sage: A.Type()
|
|
2204
|
+
ModMatRngElt
|
|
2205
|
+
"""
|
|
2206
|
+
if len(args) > 1:
|
|
2207
|
+
return self.evaluate(*args)
|
|
2208
|
+
P = self._check_valid()
|
|
2209
|
+
x = P(args[0])
|
|
2210
|
+
try:
|
|
2211
|
+
return P('%s!%s' % (self.name(), x.name()))
|
|
2212
|
+
except (RuntimeError, TypeError):
|
|
2213
|
+
return self.evaluate(*args)
|
|
2214
|
+
|
|
2215
|
+
def __iter__(self):
|
|
2216
|
+
"""
|
|
2217
|
+
Return iterator over this Magma element.
|
|
2218
|
+
|
|
2219
|
+
OUTPUT: generator object
|
|
2220
|
+
|
|
2221
|
+
.. warning::
|
|
2222
|
+
|
|
2223
|
+
Internally this constructs the list of elements in ``self`` in
|
|
2224
|
+
Magma, which is not a lazy operation. This is because Magma
|
|
2225
|
+
doesn't have a notion of lazy iterators, unfortunately.
|
|
2226
|
+
|
|
2227
|
+
EXAMPLES::
|
|
2228
|
+
|
|
2229
|
+
sage: # optional - magma
|
|
2230
|
+
sage: V = magma('VectorSpace(GF(3),2)')
|
|
2231
|
+
sage: V
|
|
2232
|
+
Full Vector space of degree 2 over GF(3)
|
|
2233
|
+
sage: w = V.__iter__(); w
|
|
2234
|
+
<generator object ...__iter__ at ...>
|
|
2235
|
+
sage: next(w)
|
|
2236
|
+
(0 0)
|
|
2237
|
+
sage: next(w)
|
|
2238
|
+
(1 0)
|
|
2239
|
+
sage: list(w)
|
|
2240
|
+
[(2 0), (0 1), (1 1), (2 1), (0 2), (1 2), (2 2)]
|
|
2241
|
+
"""
|
|
2242
|
+
P = self._check_valid()
|
|
2243
|
+
z = P('[_a : _a in %s]' % self.name())
|
|
2244
|
+
for i in range(1, len(z) + 1):
|
|
2245
|
+
yield z[i]
|
|
2246
|
+
|
|
2247
|
+
def __len__(self):
|
|
2248
|
+
r"""
|
|
2249
|
+
Return cardinality of this Magma element.
|
|
2250
|
+
|
|
2251
|
+
This is the same as ``#self`` in Magma.
|
|
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: len(V)
|
|
2260
|
+
9
|
|
2261
|
+
sage: V.__len__()
|
|
2262
|
+
9
|
|
2263
|
+
"""
|
|
2264
|
+
P = self._check_valid()
|
|
2265
|
+
return int(P.eval('#%s' % self.name()))
|
|
2266
|
+
|
|
2267
|
+
def _polynomial_(self, R):
|
|
2268
|
+
"""
|
|
2269
|
+
Try to convert ``self`` into a polynomial in the univariate polynomial
|
|
2270
|
+
ring `R`.
|
|
2271
|
+
|
|
2272
|
+
EXAMPLES::
|
|
2273
|
+
|
|
2274
|
+
sage: R.<x> = QQ[]
|
|
2275
|
+
sage: f = magma(x^2 + 2/3*x + 5) # optional - magma
|
|
2276
|
+
sage: f # optional - magma
|
|
2277
|
+
x^2 + 2/3*x + 5
|
|
2278
|
+
sage: f.Type() # optional - magma
|
|
2279
|
+
RngUPolElt
|
|
2280
|
+
sage: f._polynomial_(R) # optional - magma
|
|
2281
|
+
x^2 + 2/3*x + 5
|
|
2282
|
+
"""
|
|
2283
|
+
return R(list(self.Eltseq()))
|
|
2284
|
+
|
|
2285
|
+
def _latex_(self) -> str:
|
|
2286
|
+
r"""
|
|
2287
|
+
Return latex representation of ``self``.
|
|
2288
|
+
|
|
2289
|
+
AUTHORS:
|
|
2290
|
+
|
|
2291
|
+
- Jennifer Balakrishnan
|
|
2292
|
+
|
|
2293
|
+
Types that are nicely latexed include:
|
|
2294
|
+
|
|
2295
|
+
- rationals
|
|
2296
|
+
|
|
2297
|
+
- matrices
|
|
2298
|
+
|
|
2299
|
+
- polynomials
|
|
2300
|
+
|
|
2301
|
+
- binary quadratic forms
|
|
2302
|
+
|
|
2303
|
+
- elements of quadratic, cyclotomic number fields, and general
|
|
2304
|
+
number fields
|
|
2305
|
+
|
|
2306
|
+
- points
|
|
2307
|
+
|
|
2308
|
+
- elliptic curves
|
|
2309
|
+
|
|
2310
|
+
- power series
|
|
2311
|
+
|
|
2312
|
+
IMPLEMENTATION: Calls latex.m, which is in
|
|
2313
|
+
SAGE_EXTCODE/magma/latex.m
|
|
2314
|
+
|
|
2315
|
+
EXAMPLES::
|
|
2316
|
+
|
|
2317
|
+
sage: latex(magma('-2/3')) # optional - magma
|
|
2318
|
+
\frac{-2}{3}
|
|
2319
|
+
sage: magma('-2/3')._latex_() # optional - magma
|
|
2320
|
+
'\\frac{-2}{3}'
|
|
2321
|
+
|
|
2322
|
+
::
|
|
2323
|
+
|
|
2324
|
+
sage: magma.eval('R<x> := PolynomialRing(RationalField()); f := (x-17/2)^3;') # optional - magma
|
|
2325
|
+
''
|
|
2326
|
+
sage: latex(magma('f')) # optional - magma
|
|
2327
|
+
x^{3}-\frac{51}{2}x^{2}+\frac{867}{4}x-\frac{4913}{8}
|
|
2328
|
+
|
|
2329
|
+
::
|
|
2330
|
+
|
|
2331
|
+
sage: latex(magma('(MatrixAlgebra(RationalField(),3)![0,2,3,4,5,6,7,8,9])^(-1)')) # optional - magma
|
|
2332
|
+
\left(\begin{array}{ccc}-1&2&-1\\2&-7&4\\-1&\frac{14}{3}&\frac{-8}{3}\end{array}\right)
|
|
2333
|
+
|
|
2334
|
+
::
|
|
2335
|
+
|
|
2336
|
+
sage: magma.eval('K<a> := CyclotomicField(11)') # optional - magma
|
|
2337
|
+
''
|
|
2338
|
+
sage: latex(magma('a^3 + a - 17/3')) # optional - magma
|
|
2339
|
+
\frac{-17}{3}+\zeta_{11}+\zeta_{11}^{3}
|
|
2340
|
+
|
|
2341
|
+
::
|
|
2342
|
+
|
|
2343
|
+
sage: latex(magma('EllipticCurve([1,2/3,3/4,4/5,-5/6])')) # optional - magma
|
|
2344
|
+
y^2+xy+\frac{3}{4}y=x^3+\frac{2}{3}x^2+\frac{4}{5}x-\frac{5}{6}
|
|
2345
|
+
|
|
2346
|
+
::
|
|
2347
|
+
|
|
2348
|
+
sage: _=magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
|
|
2349
|
+
sage: _=magma.eval('K<a> := NumberField(x^3+17*x+2)') # optional - magma
|
|
2350
|
+
sage: latex(magma('(1/3)*a^2 - 17/3*a + 2')) # optional - magma
|
|
2351
|
+
2-\frac{17}{3}a+\frac{1}{3}a^{2}
|
|
2352
|
+
|
|
2353
|
+
Sage auto-detects the greek letters and puts backslashes in::
|
|
2354
|
+
|
|
2355
|
+
sage: _=magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
|
|
2356
|
+
sage: _=magma.eval('K<alpha> := NumberField(x^3+17*x+2)') # optional - magma
|
|
2357
|
+
sage: latex(magma('(1/3)*alpha^2 - 17/3*alpha + 2')) # optional - magma
|
|
2358
|
+
2-\frac{17}{3}\alpha+\frac{1}{3}\alpha^{2}
|
|
2359
|
+
|
|
2360
|
+
::
|
|
2361
|
+
|
|
2362
|
+
sage: _=magma.eval('R<alpha> := PolynomialRing(RationalField())') # optional - magma
|
|
2363
|
+
sage: latex(magma('alpha^3-1/7*alpha + 3')) # optional - magma
|
|
2364
|
+
\alpha^{3}-\frac{1}{7}\alpha+3
|
|
2365
|
+
|
|
2366
|
+
Finite field elements::
|
|
2367
|
+
|
|
2368
|
+
sage: _=magma.eval('K<a> := GF(27)') # optional - magma
|
|
2369
|
+
sage: latex(magma('a^2+2')) # optional - magma
|
|
2370
|
+
2+a^{2}
|
|
2371
|
+
|
|
2372
|
+
Printing of unnamed (dollar sign) generators works correctly::
|
|
2373
|
+
|
|
2374
|
+
sage: latex(magma('FiniteField(81).1^2+1')) # optional - magma
|
|
2375
|
+
1+\$.1^{2}
|
|
2376
|
+
|
|
2377
|
+
Finite fields::
|
|
2378
|
+
|
|
2379
|
+
sage: latex(magma('FiniteField(3)')) # optional - magma
|
|
2380
|
+
\mathbf{F}_{{3}}
|
|
2381
|
+
sage: latex(magma('FiniteField(27)')) # optional - magma
|
|
2382
|
+
\mathbf{F}_{{3}^{3}}
|
|
2383
|
+
|
|
2384
|
+
Power Series::
|
|
2385
|
+
|
|
2386
|
+
sage: # optional - magma
|
|
2387
|
+
sage: _=magma.eval('R<x> := PowerSeriesRing(RationalField())')
|
|
2388
|
+
sage: latex(magma('(1/(1+x))'))
|
|
2389
|
+
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})
|
|
2390
|
+
sage: _=magma.eval('R<x> := PowerSeriesRing(RationalField())')
|
|
2391
|
+
sage: latex(magma('(-1/(2+x + O(x^3)))'))
|
|
2392
|
+
\frac{-1}{2}+\frac{1}{4}x-\frac{1}{8}x^{2}+O(x^{3})
|
|
2393
|
+
|
|
2394
|
+
`p`-adic Numbers::
|
|
2395
|
+
|
|
2396
|
+
sage: latex(magma('pAdicField(7,4)!9333294394/49')) # optional - magma
|
|
2397
|
+
4\cdot{}7^{-2} + 5\cdot{}7^{-1} + 5+ 6\cdot{}7^{1} + O(7^{2})
|
|
2398
|
+
"""
|
|
2399
|
+
P = self._check_valid()
|
|
2400
|
+
s = str(P.eval('Latex(%s)' % self.name()))
|
|
2401
|
+
v = '\\mathrm{'
|
|
2402
|
+
if s[:len(v)] == v:
|
|
2403
|
+
raise AttributeError
|
|
2404
|
+
return s
|
|
2405
|
+
|
|
2406
|
+
def set_magma_attribute(self, attrname, value):
|
|
2407
|
+
"""
|
|
2408
|
+
INPUT:
|
|
2409
|
+
|
|
2410
|
+
- ``attrname`` -- string
|
|
2411
|
+
- ``value`` -- something coercible to a MagmaElement
|
|
2412
|
+
|
|
2413
|
+
EXAMPLES::
|
|
2414
|
+
|
|
2415
|
+
sage: # optional - magma
|
|
2416
|
+
sage: V = magma("VectorSpace(RationalField(),2)")
|
|
2417
|
+
sage: V.set_magma_attribute('M',10)
|
|
2418
|
+
sage: V.get_magma_attribute('M')
|
|
2419
|
+
10
|
|
2420
|
+
sage: V.M
|
|
2421
|
+
10
|
|
2422
|
+
"""
|
|
2423
|
+
P = self.parent() # instance of Magma that contains this element.
|
|
2424
|
+
if not (isinstance(value, MagmaElement) and value.parent() is P):
|
|
2425
|
+
value = P(value)
|
|
2426
|
+
P.eval('%s`%s := %s' % (self.name(), attrname, value.name()))
|
|
2427
|
+
|
|
2428
|
+
def get_magma_attribute(self, attrname):
|
|
2429
|
+
"""
|
|
2430
|
+
Return value of a given Magma attribute. This is like selfattrname
|
|
2431
|
+
in Magma.
|
|
2432
|
+
|
|
2433
|
+
OUTPUT: :class:`MagmaElement`
|
|
2434
|
+
|
|
2435
|
+
EXAMPLES::
|
|
2436
|
+
|
|
2437
|
+
sage: # optional - magma
|
|
2438
|
+
sage: V = magma("VectorSpace(RationalField(),10)")
|
|
2439
|
+
sage: V.set_magma_attribute('M','"hello"')
|
|
2440
|
+
sage: V.get_magma_attribute('M')
|
|
2441
|
+
hello
|
|
2442
|
+
sage: V.M
|
|
2443
|
+
hello
|
|
2444
|
+
"""
|
|
2445
|
+
P = self.parent()
|
|
2446
|
+
return P('%s`%s' % (self.name(), attrname))
|
|
2447
|
+
|
|
2448
|
+
def list_attributes(self):
|
|
2449
|
+
"""
|
|
2450
|
+
Return the attributes of self, obtained by calling the
|
|
2451
|
+
ListAttributes function in Magma.
|
|
2452
|
+
|
|
2453
|
+
OUTPUT: list of strings
|
|
2454
|
+
|
|
2455
|
+
EXAMPLES: We observe that vector spaces in Magma have numerous
|
|
2456
|
+
funny and mysterious attributes. ::
|
|
2457
|
+
|
|
2458
|
+
sage: V = magma("VectorSpace(RationalField(),2)") # optional - magma
|
|
2459
|
+
sage: v = V.list_attributes(); v.sort() # optional - magma
|
|
2460
|
+
sage: print(v) # optional - magma
|
|
2461
|
+
['Coroots', 'Involution', ..., 'p', 'ssbasis', 'weights']
|
|
2462
|
+
"""
|
|
2463
|
+
return magma.eval('ListAttributes(Type(%s))' % self.name()).split()
|
|
2464
|
+
|
|
2465
|
+
def _tab_completion(self):
|
|
2466
|
+
"""
|
|
2467
|
+
Return all Magma functions that have this Magma element as first
|
|
2468
|
+
input. This is used for tab completion.
|
|
2469
|
+
|
|
2470
|
+
.. NOTE::
|
|
2471
|
+
|
|
2472
|
+
This function can unfortunately be slow if there are a very
|
|
2473
|
+
large number of functions, e.g., when ``self`` is an
|
|
2474
|
+
integer. (This could be fixed by the addition of an
|
|
2475
|
+
appropriate function to the Magma kernel, which is
|
|
2476
|
+
something that can only be done by the Magma developers.)
|
|
2477
|
+
|
|
2478
|
+
OUTPUT:
|
|
2479
|
+
|
|
2480
|
+
- ``list`` -- sorted list of distinct strings
|
|
2481
|
+
|
|
2482
|
+
EXAMPLES::
|
|
2483
|
+
|
|
2484
|
+
sage: R.<x> = ZZ[] # optional - magma
|
|
2485
|
+
sage: v = magma(R)._tab_completion() # optional - magma
|
|
2486
|
+
sage: v # optional - magma
|
|
2487
|
+
["'*'", "'+'", "'.'", "'/'", "'eq'", "'meet'", "'subset'", ...]
|
|
2488
|
+
"""
|
|
2489
|
+
M = self.methods()
|
|
2490
|
+
N = []
|
|
2491
|
+
for x in M:
|
|
2492
|
+
i = x.find('(')
|
|
2493
|
+
N.append(x[:i])
|
|
2494
|
+
v = sorted(set(N + self.list_attributes()))
|
|
2495
|
+
return v
|
|
2496
|
+
|
|
2497
|
+
def methods(self, any=False):
|
|
2498
|
+
"""
|
|
2499
|
+
Return signatures of all Magma intrinsics that can take ``self`` as the
|
|
2500
|
+
first argument, as strings.
|
|
2501
|
+
|
|
2502
|
+
INPUT:
|
|
2503
|
+
|
|
2504
|
+
- ``any`` -- boolean (default: ``False``); if ``True``, also
|
|
2505
|
+
include signatures with Any as first argument
|
|
2506
|
+
|
|
2507
|
+
OUTPUT: list of strings
|
|
2508
|
+
|
|
2509
|
+
EXAMPLES::
|
|
2510
|
+
|
|
2511
|
+
sage: v = magma('2/3').methods() # optional - magma
|
|
2512
|
+
sage: v[0] # optional - magma
|
|
2513
|
+
"'*'..."
|
|
2514
|
+
"""
|
|
2515
|
+
t = str(self.Type())
|
|
2516
|
+
X = self.parent().eval('ListSignatures(%s)' % self.Type()).split('\n')
|
|
2517
|
+
X = X[2:] # because the first 2 lines are not relevant
|
|
2518
|
+
t0 = t + ',' # t as first argument among several
|
|
2519
|
+
t1 = t + ')' # t as only argument
|
|
2520
|
+
result = []
|
|
2521
|
+
for x in X:
|
|
2522
|
+
x1 = x.split('::')[1] # typical line starts (f::Elt, g::Elt)
|
|
2523
|
+
if x1.startswith(t0) or x1.startswith(t1):
|
|
2524
|
+
result.append(x)
|
|
2525
|
+
elif any and x1.startswith("Any"):
|
|
2526
|
+
result.append(x)
|
|
2527
|
+
return result
|
|
2528
|
+
|
|
2529
|
+
def __floordiv__(self, x):
|
|
2530
|
+
"""
|
|
2531
|
+
Quotient of division of ``self`` by ``other``. This is denoted ``//``
|
|
2532
|
+
(``div`` in magma).
|
|
2533
|
+
|
|
2534
|
+
EXAMPLES::
|
|
2535
|
+
|
|
2536
|
+
sage: R.<x,y,z> = QQ[]
|
|
2537
|
+
sage: magma(5)//magma(2) # optional - magma
|
|
2538
|
+
2
|
|
2539
|
+
sage: m = magma(x*z + x*y) # optional - magma
|
|
2540
|
+
sage: n = magma(x) # optional - magma
|
|
2541
|
+
sage: m//n # optional - magma
|
|
2542
|
+
y + z
|
|
2543
|
+
"""
|
|
2544
|
+
return self.parent()('%s div %s' % (self.name(), x.name()))
|
|
2545
|
+
|
|
2546
|
+
def __bool__(self):
|
|
2547
|
+
"""
|
|
2548
|
+
Return ``True`` if ``self`` is nonzero according to Magma.
|
|
2549
|
+
|
|
2550
|
+
If Magma cannot decide, i.e., is raising an error
|
|
2551
|
+
then also return ``True``.
|
|
2552
|
+
|
|
2553
|
+
EXAMPLES: We define a Magma vector space::
|
|
2554
|
+
|
|
2555
|
+
sage: V = magma('VectorSpace(GF(3),2)'); V # optional - magma
|
|
2556
|
+
Full Vector space of degree 2 over GF(3)
|
|
2557
|
+
|
|
2558
|
+
The first generator is nonzero::
|
|
2559
|
+
|
|
2560
|
+
sage: bool(V.gen(1)) # optional - magma
|
|
2561
|
+
True
|
|
2562
|
+
|
|
2563
|
+
The zero element is zero::
|
|
2564
|
+
|
|
2565
|
+
sage: bool(V(0)) # optional - magma
|
|
2566
|
+
False
|
|
2567
|
+
|
|
2568
|
+
The space itself is nonzero (the default - in Magma no comparison
|
|
2569
|
+
to 0 is possible)::
|
|
2570
|
+
|
|
2571
|
+
sage: bool(V) # optional - magma
|
|
2572
|
+
True
|
|
2573
|
+
|
|
2574
|
+
Note that ``bool`` calls ``__bool__`` in Python 3.
|
|
2575
|
+
|
|
2576
|
+
Test use in bool conversions of bools::
|
|
2577
|
+
|
|
2578
|
+
sage: # optional - magma
|
|
2579
|
+
sage: bool(magma(False))
|
|
2580
|
+
False
|
|
2581
|
+
sage: bool(magma(True))
|
|
2582
|
+
True
|
|
2583
|
+
sage: bool(magma(1))
|
|
2584
|
+
True
|
|
2585
|
+
sage: bool(magma(0))
|
|
2586
|
+
False
|
|
2587
|
+
|
|
2588
|
+
TESTS:
|
|
2589
|
+
|
|
2590
|
+
Verify that :issue:`32602` is fixed::
|
|
2591
|
+
|
|
2592
|
+
sage: magma("1 eq 0").bool() # optional - magma
|
|
2593
|
+
False
|
|
2594
|
+
sage: magma("1 eq 1").bool() # optional - magma
|
|
2595
|
+
True
|
|
2596
|
+
sage: Q.<x> = PolynomialRing(GF(3))
|
|
2597
|
+
sage: u = x^6+x^4+2*x^3+2*x+1
|
|
2598
|
+
sage: F0 = magma.FunctionField(GF(3)) # optional - magma
|
|
2599
|
+
sage: bool(F0.1) # optional - magma
|
|
2600
|
+
True
|
|
2601
|
+
"""
|
|
2602
|
+
try:
|
|
2603
|
+
return str(self.parent()("%s eq 0" % self.name())) == "false"
|
|
2604
|
+
except TypeError:
|
|
2605
|
+
# comparing with 0 didn't work; try comparing with false
|
|
2606
|
+
try:
|
|
2607
|
+
return str(self.parent()("%s eq false" % self.name())) == "false"
|
|
2608
|
+
except TypeError:
|
|
2609
|
+
pass
|
|
2610
|
+
return True
|
|
2611
|
+
|
|
2612
|
+
def sub(self, gens):
|
|
2613
|
+
"""
|
|
2614
|
+
Return the sub-object of ``self`` with given gens.
|
|
2615
|
+
|
|
2616
|
+
INPUT:
|
|
2617
|
+
|
|
2618
|
+
- ``gens`` -- object or list/tuple of generators
|
|
2619
|
+
|
|
2620
|
+
EXAMPLES::
|
|
2621
|
+
|
|
2622
|
+
sage: V = magma('VectorSpace(RationalField(),3)') # optional - magma
|
|
2623
|
+
sage: W = V.sub([ [1,2,3], [1,1,2] ]); W # optional - magma
|
|
2624
|
+
Vector space of degree 3, dimension 2 over Rational Field
|
|
2625
|
+
Generators:
|
|
2626
|
+
(1 2 3)
|
|
2627
|
+
(1 1 2)
|
|
2628
|
+
Echelonized basis:
|
|
2629
|
+
(1 0 1)
|
|
2630
|
+
(0 1 1)
|
|
2631
|
+
"""
|
|
2632
|
+
return self.parent().bar_call(self, 'sub', gens)
|
|
2633
|
+
|
|
2634
|
+
def quo(self, gens, **args):
|
|
2635
|
+
"""
|
|
2636
|
+
Return the quotient of ``self`` by the given object or list of
|
|
2637
|
+
generators.
|
|
2638
|
+
|
|
2639
|
+
INPUT:
|
|
2640
|
+
|
|
2641
|
+
- ``gens`` -- object or list/tuple of generators
|
|
2642
|
+
- further named arguments that are ignored
|
|
2643
|
+
|
|
2644
|
+
OUTPUT:
|
|
2645
|
+
|
|
2646
|
+
- ``magma element`` -- the quotient object
|
|
2647
|
+
|
|
2648
|
+
- ``magma element`` -- mapping from ``self`` to the
|
|
2649
|
+
quotient object
|
|
2650
|
+
|
|
2651
|
+
EXAMPLES::
|
|
2652
|
+
|
|
2653
|
+
sage: V = magma('VectorSpace(RationalField(),3)') # optional - magma
|
|
2654
|
+
sage: V.quo([[1,2,3], [1,1,2]]) # optional - magma
|
|
2655
|
+
(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)
|
|
2656
|
+
|
|
2657
|
+
We illustrate quotienting out by an object instead of a list of
|
|
2658
|
+
generators::
|
|
2659
|
+
|
|
2660
|
+
sage: W = V.sub([ [1,2,3], [1,1,2] ]) # optional - magma
|
|
2661
|
+
sage: V.quo(W) # optional - magma
|
|
2662
|
+
(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)
|
|
2663
|
+
|
|
2664
|
+
We quotient a ZZ module out by a submodule.
|
|
2665
|
+
|
|
2666
|
+
::
|
|
2667
|
+
|
|
2668
|
+
sage: # optional - magma
|
|
2669
|
+
sage: V = magma.RModule(ZZ,3); V
|
|
2670
|
+
RModule(IntegerRing(), 3)
|
|
2671
|
+
sage: W, phi = V.quo([[1,2,3]])
|
|
2672
|
+
sage: W
|
|
2673
|
+
RModule(IntegerRing(), 2)
|
|
2674
|
+
sage: phi
|
|
2675
|
+
Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 2)
|
|
2676
|
+
"""
|
|
2677
|
+
return self.parent().bar_call(self, 'quo', gens, nvals=2)
|
|
2678
|
+
|
|
2679
|
+
def ideal(self, gens):
|
|
2680
|
+
"""
|
|
2681
|
+
Return the ideal of ``self`` with given list of generators.
|
|
2682
|
+
|
|
2683
|
+
INPUT:
|
|
2684
|
+
|
|
2685
|
+
- ``gens`` -- object or list/tuple of generators
|
|
2686
|
+
|
|
2687
|
+
OUTPUT:
|
|
2688
|
+
|
|
2689
|
+
- ``magma element`` -- a Magma ideal
|
|
2690
|
+
|
|
2691
|
+
EXAMPLES::
|
|
2692
|
+
|
|
2693
|
+
sage: # optional - magma
|
|
2694
|
+
sage: R = magma('PolynomialRing(RationalField())')
|
|
2695
|
+
sage: R.assign_names(['x'])
|
|
2696
|
+
sage: x = R.1
|
|
2697
|
+
sage: R.ideal([x^2 - 1, x^3 - 1])
|
|
2698
|
+
Ideal of Univariate Polynomial Ring in x over Rational Field generated by x - 1
|
|
2699
|
+
"""
|
|
2700
|
+
return self.parent().bar_call(self, 'ideal', gens, nvals=1)
|
|
2701
|
+
|
|
2702
|
+
|
|
2703
|
+
magma = Magma()
|
|
2704
|
+
|
|
2705
|
+
|
|
2706
|
+
def reduce_load_Magma():
|
|
2707
|
+
"""
|
|
2708
|
+
Used in unpickling a Magma interface.
|
|
2709
|
+
|
|
2710
|
+
This functions just returns the global default Magma interface.
|
|
2711
|
+
|
|
2712
|
+
EXAMPLES::
|
|
2713
|
+
|
|
2714
|
+
sage: sage.interfaces.magma.reduce_load_Magma()
|
|
2715
|
+
Magma
|
|
2716
|
+
"""
|
|
2717
|
+
return magma
|
|
2718
|
+
|
|
2719
|
+
|
|
2720
|
+
def magma_console():
|
|
2721
|
+
"""
|
|
2722
|
+
Run a command line Magma session.
|
|
2723
|
+
|
|
2724
|
+
EXAMPLES::
|
|
2725
|
+
|
|
2726
|
+
sage: magma_console() # not tested
|
|
2727
|
+
Magma V2.14-9 Sat Oct 11 2008 06:36:41 on one [Seed = 1157408761]
|
|
2728
|
+
Type ? for help. Type <Ctrl>-D to quit.
|
|
2729
|
+
>
|
|
2730
|
+
Total time: 2.820 seconds, Total memory usage: 3.95MB
|
|
2731
|
+
"""
|
|
2732
|
+
from sage.repl.rich_output.display_manager import get_display_manager
|
|
2733
|
+
if not get_display_manager().is_in_terminal():
|
|
2734
|
+
raise RuntimeError('Can use the console only in the terminal. Try %%magma magics instead.')
|
|
2735
|
+
os.system('magma')
|
|
2736
|
+
|
|
2737
|
+
|
|
2738
|
+
class MagmaGBLogPrettyPrinter:
|
|
2739
|
+
"""
|
|
2740
|
+
A device which filters Magma Groebner basis computation logs.
|
|
2741
|
+
"""
|
|
2742
|
+
cmd_inpt = re.compile("^>>>$")
|
|
2743
|
+
app_inpt = re.compile("^Append\\(~_sage_, 0\\);$")
|
|
2744
|
+
|
|
2745
|
+
deg_curr = re.compile(
|
|
2746
|
+
"^Basis length\\: (\\d+), queue length\\: (\\d+), step degree\\: (\\d+), num pairs\\: (\\d+)$"
|
|
2747
|
+
)
|
|
2748
|
+
pol_curr = re.compile("^Number of pair polynomials\\: (\\d+), at (\\d+) column\\(s\\), .*")
|
|
2749
|
+
|
|
2750
|
+
def __init__(self, verbosity=1, style='magma'):
|
|
2751
|
+
"""
|
|
2752
|
+
Construct a new Magma Groebner Basis log pretty printer.
|
|
2753
|
+
|
|
2754
|
+
INPUT:
|
|
2755
|
+
|
|
2756
|
+
- ``verbosity`` -- how much information should be printed
|
|
2757
|
+
(between 0 and 1)
|
|
2758
|
+
|
|
2759
|
+
- ``style`` -- if "magma" the full Magma log is printed; if
|
|
2760
|
+
'sage' only the current degree and the number of pairs in
|
|
2761
|
+
the queue is printed (default: ``'magma'``).
|
|
2762
|
+
|
|
2763
|
+
EXAMPLES::
|
|
2764
|
+
|
|
2765
|
+
sage: # needs sage.libs.singular
|
|
2766
|
+
sage: P.<x,y,z> = GF(32003)[]
|
|
2767
|
+
sage: I = sage.rings.ideal.Cyclic(P)
|
|
2768
|
+
sage: _ = I.groebner_basis('magma',prot='sage') # indirect doctest, optional - magma, not tested
|
|
2769
|
+
|
|
2770
|
+
Leading term degree: 2. Critical pairs: 2.
|
|
2771
|
+
Leading term degree: 3. Critical pairs: 1.
|
|
2772
|
+
|
|
2773
|
+
Highest degree reached during computation: 3.
|
|
2774
|
+
|
|
2775
|
+
sage: # needs sage.libs.singular
|
|
2776
|
+
sage: P.<x,y,z> = GF(32003)[]
|
|
2777
|
+
sage: I = sage.rings.ideal.Cyclic(P)
|
|
2778
|
+
sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma, not tested
|
|
2779
|
+
********************
|
|
2780
|
+
FAUGERE F4 ALGORITHM
|
|
2781
|
+
********************
|
|
2782
|
+
Coefficient ring: GF(32003)
|
|
2783
|
+
Rank: 3
|
|
2784
|
+
Order: Graded Reverse Lexicographical
|
|
2785
|
+
NEW hash table
|
|
2786
|
+
Matrix kind: Modular FP
|
|
2787
|
+
Datum size: 4
|
|
2788
|
+
No queue sort
|
|
2789
|
+
Initial length: 3
|
|
2790
|
+
Inhomogeneous
|
|
2791
|
+
|
|
2792
|
+
Initial queue setup time: 0.000
|
|
2793
|
+
Initial queue length: 2
|
|
2794
|
+
|
|
2795
|
+
*******
|
|
2796
|
+
STEP 1
|
|
2797
|
+
Basis length: 3, queue length: 2, step degree: 2, num pairs: 1
|
|
2798
|
+
Basis total mons: 8, average length: 2.667
|
|
2799
|
+
Number of pair polynomials: 1, at 4 column(s), 0.000
|
|
2800
|
+
...
|
|
2801
|
+
Total Faugere F4 time: 0.000, real time: 0.000
|
|
2802
|
+
|
|
2803
|
+
sage: # needs sage.libs.pari
|
|
2804
|
+
sage: set_random_seed(1)
|
|
2805
|
+
sage: sr = mq.SR(1,1,2,4)
|
|
2806
|
+
sage: F,s = sr.polynomial_system()
|
|
2807
|
+
sage: I = F.ideal()
|
|
2808
|
+
sage: _ = I.groebner_basis('magma',prot='sage') # indirect doctest, optional - magma, not tested
|
|
2809
|
+
Leading term degree: 1. Critical pairs: 40.
|
|
2810
|
+
Leading term degree: 2. Critical pairs: 40.
|
|
2811
|
+
Leading term degree: 3. Critical pairs: 38.
|
|
2812
|
+
Leading term degree: 2. Critical pairs: 327.
|
|
2813
|
+
Leading term degree: 2. Critical pairs: 450.
|
|
2814
|
+
Leading term degree: 2. Critical pairs: 416.
|
|
2815
|
+
Leading term degree: 3. Critical pairs: 415.
|
|
2816
|
+
Leading term degree: 4. Critical pairs: 98 (all pairs of current degree eliminated by criteria).
|
|
2817
|
+
Leading term degree: 5. Critical pairs: 3 (all pairs of current degree eliminated by criteria).
|
|
2818
|
+
|
|
2819
|
+
Highest degree reached during computation: 3.
|
|
2820
|
+
"""
|
|
2821
|
+
self.verbosity = verbosity
|
|
2822
|
+
if style not in ['sage', 'magma']:
|
|
2823
|
+
raise ValueError('style must be sage or magma')
|
|
2824
|
+
self.style = style
|
|
2825
|
+
|
|
2826
|
+
self.curr_deg = 0 # current degree
|
|
2827
|
+
self.curr_npairs = 0 # current number of pairs to be considered
|
|
2828
|
+
self.max_deg = 0 # maximal degree in total
|
|
2829
|
+
|
|
2830
|
+
self.storage = "" # stores incomplete strings
|
|
2831
|
+
self.sync = None # should we expect a sync integer?
|
|
2832
|
+
|
|
2833
|
+
def write(self, s):
|
|
2834
|
+
"""
|
|
2835
|
+
EXAMPLES::
|
|
2836
|
+
|
|
2837
|
+
sage: # needs sage.libs.singular
|
|
2838
|
+
sage: P.<x,y,z> = GF(32003)[]
|
|
2839
|
+
sage: I = sage.rings.ideal.Katsura(P)
|
|
2840
|
+
sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma
|
|
2841
|
+
...
|
|
2842
|
+
********************
|
|
2843
|
+
FAUGERE F4 ALGORITHM
|
|
2844
|
+
********************
|
|
2845
|
+
...
|
|
2846
|
+
Total Faugere F4 time: ..., real time: ...
|
|
2847
|
+
"""
|
|
2848
|
+
verbosity, style = self.verbosity, self.style
|
|
2849
|
+
|
|
2850
|
+
if isinstance(s, bytes):
|
|
2851
|
+
# sys.stdout.encoding can be None or something else
|
|
2852
|
+
if isinstance(sys.stdout.encoding, str):
|
|
2853
|
+
s = s.decode(sys.stdout.encoding)
|
|
2854
|
+
else:
|
|
2855
|
+
s = s.decode("UTF-8")
|
|
2856
|
+
|
|
2857
|
+
if self.storage:
|
|
2858
|
+
s = self.storage + s
|
|
2859
|
+
self.storage = ""
|
|
2860
|
+
|
|
2861
|
+
for line in s.splitlines():
|
|
2862
|
+
# deal with the Sage <-> Magma syncing code
|
|
2863
|
+
match = re.match(MagmaGBLogPrettyPrinter.cmd_inpt, line)
|
|
2864
|
+
if match:
|
|
2865
|
+
self.sync = 1
|
|
2866
|
+
continue
|
|
2867
|
+
|
|
2868
|
+
if self.sync:
|
|
2869
|
+
if self.sync == 1:
|
|
2870
|
+
self.sync = line
|
|
2871
|
+
continue
|
|
2872
|
+
else:
|
|
2873
|
+
if line == '':
|
|
2874
|
+
continue
|
|
2875
|
+
self.sync = None
|
|
2876
|
+
continue
|
|
2877
|
+
|
|
2878
|
+
if re.match(MagmaGBLogPrettyPrinter.app_inpt, line):
|
|
2879
|
+
continue
|
|
2880
|
+
|
|
2881
|
+
if re.match(MagmaGBLogPrettyPrinter.deg_curr, line):
|
|
2882
|
+
match = re.match(MagmaGBLogPrettyPrinter.deg_curr, line)
|
|
2883
|
+
|
|
2884
|
+
nbasis, npairs, deg, npairs_deg = map(int, match.groups())
|
|
2885
|
+
|
|
2886
|
+
self.curr_deg = deg
|
|
2887
|
+
self.curr_npairs = npairs
|
|
2888
|
+
|
|
2889
|
+
if re.match(MagmaGBLogPrettyPrinter.pol_curr, line):
|
|
2890
|
+
match = re.match(MagmaGBLogPrettyPrinter.pol_curr, line)
|
|
2891
|
+
pol_curr, col_curr = map(int, match.groups())
|
|
2892
|
+
|
|
2893
|
+
if pol_curr != 0:
|
|
2894
|
+
self.max_deg = max(self.max_deg, self.curr_deg)
|
|
2895
|
+
|
|
2896
|
+
if style == "sage" and verbosity >= 1:
|
|
2897
|
+
print("Leading term degree: %2d. Critical pairs: %d." %
|
|
2898
|
+
(self.curr_deg, self.curr_npairs))
|
|
2899
|
+
else:
|
|
2900
|
+
if style == "sage" and verbosity >= 1:
|
|
2901
|
+
print("Leading term degree: %2d. Critical pairs: %d (all pairs of current degree eliminated by criteria)." %
|
|
2902
|
+
(self.curr_deg, self.curr_npairs))
|
|
2903
|
+
|
|
2904
|
+
if style == "magma" and verbosity >= 1:
|
|
2905
|
+
print(line)
|
|
2906
|
+
|
|
2907
|
+
def flush(self):
|
|
2908
|
+
"""
|
|
2909
|
+
EXAMPLES::
|
|
2910
|
+
|
|
2911
|
+
sage: from sage.interfaces.magma import MagmaGBLogPrettyPrinter
|
|
2912
|
+
sage: logs = MagmaGBLogPrettyPrinter()
|
|
2913
|
+
sage: logs.flush()
|
|
2914
|
+
"""
|
|
2915
|
+
import sys
|
|
2916
|
+
sys.stdout.flush()
|
|
2917
|
+
|
|
2918
|
+
|
|
2919
|
+
class MagmaGBDefaultContext:
|
|
2920
|
+
"""
|
|
2921
|
+
Context to force preservation of verbosity options for Magma's
|
|
2922
|
+
Groebner basis computation.
|
|
2923
|
+
"""
|
|
2924
|
+
def __init__(self, magma=None):
|
|
2925
|
+
"""
|
|
2926
|
+
INPUT:
|
|
2927
|
+
|
|
2928
|
+
- ``magma`` -- (default: ``magma_default``)
|
|
2929
|
+
|
|
2930
|
+
EXAMPLES::
|
|
2931
|
+
|
|
2932
|
+
sage: from sage.interfaces.magma import MagmaGBDefaultContext
|
|
2933
|
+
sage: magma.SetVerbose('Groebner',1) # optional - magma
|
|
2934
|
+
sage: with MagmaGBDefaultContext(): magma.GetVerbose('Groebner') # optional - magma
|
|
2935
|
+
0
|
|
2936
|
+
"""
|
|
2937
|
+
if magma is None:
|
|
2938
|
+
from sage.interfaces.magma import magma as magma_default
|
|
2939
|
+
magma = magma_default
|
|
2940
|
+
|
|
2941
|
+
self.magma = magma
|
|
2942
|
+
|
|
2943
|
+
def __enter__(self):
|
|
2944
|
+
"""
|
|
2945
|
+
EXAMPLES::
|
|
2946
|
+
|
|
2947
|
+
sage: from sage.interfaces.magma import MagmaGBDefaultContext
|
|
2948
|
+
sage: magma.SetVerbose('Groebner',1) # optional - magma
|
|
2949
|
+
sage: with MagmaGBDefaultContext(): magma.GetVerbose('Groebner') # optional - magma
|
|
2950
|
+
0
|
|
2951
|
+
"""
|
|
2952
|
+
self.groebner_basis_verbose = self.magma.GetVerbose('Groebner')
|
|
2953
|
+
self.magma.SetVerbose('Groebner', 0)
|
|
2954
|
+
|
|
2955
|
+
def __exit__(self, typ, value, tb):
|
|
2956
|
+
"""
|
|
2957
|
+
EXAMPLES::
|
|
2958
|
+
|
|
2959
|
+
sage: from sage.interfaces.magma import MagmaGBDefaultContext
|
|
2960
|
+
sage: magma.SetVerbose('Groebner',1) # optional - magma
|
|
2961
|
+
sage: with MagmaGBDefaultContext(): magma.GetVerbose('Groebner') # optional - magma
|
|
2962
|
+
0
|
|
2963
|
+
sage: magma.GetVerbose('Groebner') # optional - magma
|
|
2964
|
+
1
|
|
2965
|
+
"""
|
|
2966
|
+
self.magma.SetVerbose('Groebner', self.groebner_basis_verbose)
|
|
2967
|
+
|
|
2968
|
+
|
|
2969
|
+
def magma_gb_standard_options(func):
|
|
2970
|
+
"""
|
|
2971
|
+
Decorator to force default options for Magma.
|
|
2972
|
+
|
|
2973
|
+
EXAMPLES::
|
|
2974
|
+
|
|
2975
|
+
sage: # needs sage.libs.singular
|
|
2976
|
+
sage: P.<a,b,c,d,e> = PolynomialRing(GF(127))
|
|
2977
|
+
sage: J = sage.rings.ideal.Cyclic(P).homogenize()
|
|
2978
|
+
sage: from sage.misc.sageinspect import sage_getsource
|
|
2979
|
+
sage: "mself" in sage_getsource(J._groebner_basis_magma)
|
|
2980
|
+
True
|
|
2981
|
+
"""
|
|
2982
|
+
from sage.misc.decorators import sage_wraps
|
|
2983
|
+
|
|
2984
|
+
@sage_wraps(func)
|
|
2985
|
+
def wrapper(*args, **kwds):
|
|
2986
|
+
"""
|
|
2987
|
+
Execute function in ``MagmaGBDefaultContext``.
|
|
2988
|
+
"""
|
|
2989
|
+
with MagmaGBDefaultContext():
|
|
2990
|
+
return func(*args, **kwds)
|
|
2991
|
+
return wrapper
|