Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/doc/en/developer/doctesting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,19 @@ to save any changes made in the file.
After running the doctest fixer, it is a good idea to use ``git diff`` to check
all edits that the automated tool made.

Note that in some cases the output in doctest may be slightly different from
the output in the actual Sage command-line (see :func:`sage.doctest.forker.init_sage`)::

sage: set_random_seed(1)
sage: randint(1, 100)
41 # actual
83 # in doctest
sage: {3: 4, 1: 2}
{3: 4, 1: 2} # actual
{1: 2, 3: 4} # in doctest

.. this whole file is marked nodoctest, so the example above is not tested

An alternative to this workflow is to use the option ``--keep-both``. When expected and
actual output of an example differ, it duplicates the example, marking the two copies
``# optional - EXPECTED`` and ``# optional - GOT``. (Thus, when re-running the doctester,
Expand Down
44 changes: 35 additions & 9 deletions src/sage/doctest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ def reproducible_repr(val):
r"""
String representation of an object in a reproducible way.

.. NOTE::

This function is deprecated, in most cases it suffices to use
the automatic sorting of dictionary keys and set items by a displayhook.
See :func:`sage.doctest.forker.init_sage`.
If used in a format string, use :func:`IPython.lib.pretty.pretty`.
In the rare cases where the ordering of the elements is not reliable
or transitive, ``sorted`` with a sane key can be used instead.

This tries to ensure that the returned string does not depend on
factors outside the control of the doctest.
One example is the order of elements in a hash-based structure.
Expand All @@ -74,18 +83,32 @@ def reproducible_repr(val):

EXAMPLES::

sage: # not tested (test fails because of deprecation warning)
sage: from sage.doctest.fixtures import reproducible_repr
sage: print(reproducible_repr(set(["a", "c", "b", "d"])))
set(['a', 'b', 'c', 'd'])
sage: print(reproducible_repr(frozenset(["a", "c", "b", "d"])))
frozenset(['a', 'b', 'c', 'd'])
sage: print(reproducible_repr([1, frozenset("cab"), set("bar"), 0]))
[1, frozenset(['a', 'b', 'c']), set(['a', 'b', 'r']), 0]
sage: print(reproducible_repr({3.0: "three", "2": "two", 1: "one"})) # optional - sage.rings.real_mpfr
sage: print(reproducible_repr({3.0: "three", "2": "two", 1: "one"})) # needs sage.rings.real_mpfr
{'2': 'two', 1: 'one', 3.00000000000000: 'three'}
sage: print(reproducible_repr("foo\nbar")) # demonstrate default case
'foo\nbar'

TESTS:

Ensures deprecation warning is printed out::

sage: from sage.doctest.fixtures import reproducible_repr
sage: print(reproducible_repr(set(["a", "c", "b", "d"])))
doctest:warning...
DeprecationWarning: reproducible_repr is deprecated, see its documentation for details
See https://github.com/sagemath/sage/issues/39420 for details.
set(['a', 'b', 'c', 'd'])
"""
from sage.misc.superseded import deprecation
deprecation(39420, 'reproducible_repr is deprecated, see its documentation for details')

def sorted_pairs(iterable, pairs=False):
# We don't know whether container data structures will have
Expand Down Expand Up @@ -181,22 +204,23 @@ def get(self, name):
4
"""
val = getattr(self.delegate, name)
from IPython.lib.pretty import pretty
if callable(val) and name not in self.delegate.__dict__:
@wraps(val)
def wrapper(*args, **kwds):
arglst = [reproducible_repr(arg) for arg in args]
arglst.extend("{}={}".format(k, reproducible_repr(v))
arglst = [pretty(arg) for arg in args]
arglst.extend("{}={}".format(k, pretty(v))
for k, v in sorted(kwds.items()))
res = val(*args, **kwds)
print("{}call {}({}) -> {}"
.format(self.prefix, name, ", ".join(arglst),
reproducible_repr(res)))
pretty(res)))
return res
return wrapper
else:
if self.reads:
print("{}read {} = {}".format(self.prefix, name,
reproducible_repr(val)))
pretty(val)))
return val

def set(self, name, val):
Expand All @@ -218,8 +242,9 @@ def set(self, name, val):
sage: foo.x
2
"""
from IPython.lib.pretty import pretty
print("{}write {} = {}".format(self.prefix, name,
reproducible_repr(val)))
pretty(val)))
setattr(self.delegate, name, val)


Expand Down Expand Up @@ -373,11 +398,12 @@ def trace_method(obj, meth, **kwds):

@wraps(f)
def g(*args, **kwds):
arglst = [reproducible_repr(arg) for arg in args]
arglst.extend("{}={}".format(k, reproducible_repr(v))
from IPython.lib.pretty import pretty
arglst = [pretty(arg) for arg in args]
arglst.extend("{}={}".format(k, pretty(v))
for k, v in sorted(kwds.items()))
print("enter {}({})".format(meth, ", ".join(arglst)))
res = f(t, *args, **kwds)
print("exit {} -> {}".format(meth, reproducible_repr(res)))
print("exit {} -> {}".format(meth, pretty(res)))
return res
setattr(obj, meth, g)
24 changes: 24 additions & 0 deletions src/sage/misc/converting_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,27 @@ def update(self, *args, **kwds):
if kwds:
seq = ((f(k), v) for k, v in kwds.items())
u(seq)

def _repr_pretty_(self, p, cycle):
"""
For pretty printing in the Sage command prompt.

Since ``KeyConvertingDict`` inherits from ``dict``, we just use IPython's
built-in ``dict`` pretty printer.
When :issue:`36801` is fixed, this function will be redundant.

EXAMPLES::

sage: from sage.misc.converting_dict import KeyConvertingDict
sage: d = KeyConvertingDict(int)
sage: d["3"] = 4
sage: d["1"] = 2
sage: repr(d) # dictionaries are insertion ordered since Python 3.6
'{3: 4, 1: 2}'
sage: d # indirect doctest
{1: 2, 3: 4}

The last example output will be ``{3: 4, 1: 2}`` outside of doctesting,
see :func:`sage.doctest.forker.init_sage`.
"""
p.pretty(dict(self))
36 changes: 18 additions & 18 deletions src/sage/plot/animate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1412,25 +1412,25 @@ def _add_png(self, pngfile):
sage: from sage.plot.animate import APngAssembler
sage: APngAssembler._testCase1("_add_png", reads=False)
enter _add_png('...png')
write _current_chunk = (...'\x00\x00\x00\r', ...'IHDR', ...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', ...'\xb8\x1f9\xc6')
write _current_chunk = (...'\x00\x00\x00\r',...'IHDR',...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00',...'\xb8\x1f9\xc6')
call _copy() -> None
call _first_IHDR(...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00') -> None
write _current_chunk = (...'\x00\x00\x00\x04', ...'gAMA', ...'\x00\x01\x86\xa0', ...'1\xe8\x96_')
write _current_chunk = (...'\x00\x00\x00\x04',...'gAMA',...'\x00\x01\x86\xa0',...'1\xe8\x96_')
call _copy() -> None
write _current_chunk = (...'\x00\x00\x00\x07', ...'tIME', ...'\x07\xde\x06\x1b\x0b&$', ...'\x1f0z\xd5')
write _current_chunk = (...'\x00\x00\x00\x08', ...'IDAT', ...'img1data', ...'\xce\x8aI\x99')
write _current_chunk = (...'\x00\x00\x00\x07',...'tIME',...'\x07\xde\x06\x1b\x0b&$',...'\x1f0z\xd5')
write _current_chunk = (...'\x00\x00\x00\x08',...'IDAT',...'img1data',...'\xce\x8aI\x99')
call _first_IDAT(...'img1data') -> None
write _current_chunk = (...'\x00\x00\x00\x00', ...'IEND', ...'', ...'\xaeB`\x82')
write _current_chunk = (...'\x00\x00\x00\x00',...'IEND',...'',...'\xaeB`\x82')
write _first = False
exit _add_png -> None
enter _add_png('...png')
write _current_chunk = (...'\x00\x00\x00\r', ...'IHDR', ...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', ...'\xb8\x1f9\xc6')
write _current_chunk = (...'\x00\x00\x00\x04', ...'gAMA', ...'\x00\x01\x86\xa0', ...'1\xe8\x96_')
write _current_chunk = (...'\x00\x00\x00\x04', ...'IDAT', ...'img2', ...'\x0ei\xab\x1d')
write _current_chunk = (...'\x00\x00\x00\r',...'IHDR',...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00',...'\xb8\x1f9\xc6')
write _current_chunk = (...'\x00\x00\x00\x04',...'gAMA',...'\x00\x01\x86\xa0',...'1\xe8\x96_')
write _current_chunk = (...'\x00\x00\x00\x04',...'IDAT',...'img2',...'\x0ei\xab\x1d')
call _next_IDAT(...'img2') -> None
write _current_chunk = (...'\x00\x00\x00\x04', ...'IDAT', ...'data', ...'f\x94\xcbx')
write _current_chunk = (...'\x00\x00\x00\x04',...'IDAT',...'data',...'f\x94\xcbx')
call _next_IDAT(...'data') -> None
write _current_chunk = (...'\x00\x00\x00\x00', ...'IEND', ...'', ...'\xaeB`\x82')
write _current_chunk = (...'\x00\x00\x00\x00',...'IEND',...'',...'\xaeB`\x82')
write _first = False
exit _add_png -> None
"""
Expand Down Expand Up @@ -1553,16 +1553,16 @@ def _copy(self):
sage: from sage.plot.animate import APngAssembler
sage: APngAssembler._testCase1("_copy")
enter _copy()
read _current_chunk = (...'\x00\x00\x00\r', ...'IHDR', ...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', ...'\xb8\x1f9\xc6')
read out = <_io.BytesIO object at ...
read out = <_io.BytesIO object at ...
read out = <_io.BytesIO object at ...
read out = <_io.BytesIO object at ...
read _current_chunk = (...'\x00\x00\x00\r',...'IHDR',...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00',...'\xb8\x1f9\xc6')
read out = <_io.BytesIO... at ...
read out = <_io.BytesIO... at ...
read out = <_io.BytesIO... at ...
read out = <_io.BytesIO... at ...
exit _copy -> None
enter _copy()
read _current_chunk = (...'\x00\x00\x00\x04', ...'gAMA', ...'\x00\x01\x86\xa0', ...'1\xe8\x96_')
read _current_chunk = (...'\x00\x00\x00\x04',...'gAMA',...'\x00\x01\x86\xa0',...'1\xe8\x96_')
...
read _current_chunk = (...'\x00\x00\x00\x08', ...'IDAT', ...'img1data', ...'\xce\x8aI\x99')
read _current_chunk = (...'\x00\x00\x00\x08',...'IDAT',...'img1data',...'\xce\x8aI\x99')
...
exit _copy -> None
"""
Expand All @@ -1581,7 +1581,7 @@ def _actl(self):
read _actl_written = False
read num_frames = 2
read num_plays = 0
call _chunk(...'acTL', ...'\x00\x00\x00\x02\x00\x00\x00\x00') -> None
call _chunk(...'acTL',...'\x00\x00\x00\x02\x00\x00\x00\x00') -> None
write _actl_written = True
exit _actl -> None
"""
Expand Down
35 changes: 23 additions & 12 deletions src/sage/rings/polynomial/multi_polynomial_ideal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2617,8 +2617,8 @@ def variety(self, ring=None, *, algorithm='triangular_decomposition', proof=True
See :mod:`~sage.rings.polynomial.msolve` for more information. ::

sage: I.variety(RBF, algorithm='msolve', proof=False) # optional - msolve
[{x: [2.76929235423863 +/- 2.08e-15], y: [0.361103080528647 +/- 4.53e-16]},
{x: 1.000000000000000, y: 1.000000000000000}]
[{y: [0.361103080528647 +/- 4.53e-16], x: [2.76929235423863 +/- 2.08e-15]},
{y: 1.000000000000000, x: 1.000000000000000}]

Computation over floating point numbers may compute only a partial solution,
or even none at all. Notice that x values are missing from the following variety::
Expand Down Expand Up @@ -2672,10 +2672,10 @@ def variety(self, ring=None, *, algorithm='triangular_decomposition', proof=True
sage: sorted(I.variety(algorithm='msolve', # optional - msolve, needs sage.rings.finite_rings
....: proof=False),
....: key=str)
[{x: 1, y: 1},
{x: 1, y: 536870908},
{x: 536870908, y: 1},
{x: 536870908, y: 536870908}]
[{y: 1, x: 1},
{y: 1, x: 536870908},
{y: 536870908, x: 1},
{y: 536870908, x: 536870908}]

but may fail in small characteristic, especially with ideals of high
degree with respect to the characteristic::
Expand Down Expand Up @@ -3482,7 +3482,7 @@ def _reduce_using_macaulay2(self, f):

sage: R.<x,y,z,w> = PolynomialRing(ZZ, 4)
sage: I = ideal(x*y-z^2, y^2-w^2)
sage: I._reduce_using_macaulay2(x*y-z^2 + y^2) # optional - macaulay2
sage: I._reduce_using_macaulay2(x*y-z^2 + y^2) # optional - macaulay2
w^2
"""
I = self._macaulay2_()
Expand Down Expand Up @@ -5500,11 +5500,22 @@ def weil_restriction(self):
of Multivariate Polynomial Ring in x0, x1, x2, x3, x4, y0, y1, y2, y3, y4,
z0, z1, z2, z3, z4 over Finite Field of size 3
sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal
sage: from sage.doctest.fixtures import reproducible_repr
sage: print(reproducible_repr(J.variety()))
[{x0: 1, x1: 0, x2: 0, x3: 0, x4: 0,
y0: 0, y1: 0, y2: 0, y3: 0, y4: 0,
z0: 0, z1: 0, z2: 0, z3: 0, z4: 0}]
sage: J.variety()
[{z4: 0,
z3: 0,
z2: 0,
z1: 0,
z0: 0,
y4: 0,
y3: 0,
y2: 0,
y1: 0,
y0: 0,
x4: 0,
x3: 0,
x2: 0,
x1: 0,
x0: 1}]


Weil restrictions are often used to study elliptic curves over
Expand Down
21 changes: 10 additions & 11 deletions src/sage/rings/polynomial/multi_polynomial_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
pair and study it::

sage: set_random_seed(1)
sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori
sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori
....: try:
....: F, s = sr.polynomial_system()
....: break
Expand Down Expand Up @@ -122,7 +122,7 @@
easily::

sage: sr = mq.SR(1,1,1,4, gf2=True, polybori=True, order='lex') # needs sage.rings.polynomial.pbori
sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori
sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori
....: try:
....: F, s = sr.polynomial_system()
....: break
Expand Down Expand Up @@ -1552,12 +1552,11 @@ def solve(self, algorithm='polybori', n=1,
Without argument, a single arbitrary solution is returned::

sage: # needs sage.rings.polynomial.pbori
sage: from sage.doctest.fixtures import reproducible_repr
sage: R.<x,y,z> = BooleanPolynomialRing()
sage: S = Sequence([x*y + z, y*z + x, x + y + z + 1])
sage: sol = S.solve()
sage: print(reproducible_repr(sol))
[{x: 0, y: 1, z: 0}]
sage: sol
[{z: 0, y: 1, x: 0}]

We check that it is actually a solution::

Expand All @@ -1567,25 +1566,25 @@ def solve(self, algorithm='polybori', n=1,
We obtain all solutions::

sage: sols = S.solve(n=Infinity) # needs sage.rings.polynomial.pbori
sage: print(reproducible_repr(sols)) # needs sage.rings.polynomial.pbori
[{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}]
sage: sols # needs sage.rings.polynomial.pbori
[{z: 0, y: 1, x: 0}, {z: 1, y: 1, x: 1}]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this here z < y < x while in the other examples a bit down it is still x < y < z?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, must be because I don't have the optional packages.

By the way, how can optional packages be installed in meson again? (sage -i doesn't work if I recalled correctly?) Find the corresponding conda package, but how do you know which conda package corresponds to which sage package?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Find the corresponding conda package, but how do you know which conda package corresponds to which sage package?

There are conda.txt files in build/pkgs that tell you the mapping from sage pkg to conda pkg. (But they are not actively maintained and thus may be incorrect.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End up most of the packages needed seems not available on conda anyway (fes ginv macaulay2 magma msolve pycryptosat) so I just try to fix it by best effort.

sage: [S.subs(x) for x in sols] # needs sage.rings.polynomial.pbori
[[0, 0, 0], [0, 0, 0]]

We can force the use of exhaustive search if the optional
package ``FES`` is present::

sage: sol = S.solve(algorithm='exhaustive_search') # optional - fes # needs sage.rings.polynomial.pbori
sage: print(reproducible_repr(sol)) # optional - fes # needs sage.rings.polynomial.pbori
[{x: 1, y: 1, z: 1}]
sage: sol # optional - fes # needs sage.rings.polynomial.pbori
[{z: 1, y: 1, x: 1}]
sage: S.subs(sol[0]) # optional - fes # needs sage.rings.polynomial.pbori
[0, 0, 0]

And we may use SAT-solvers if they are available::

sage: sol = S.solve(algorithm='sat') # optional - pycryptosat # needs sage.rings.polynomial.pbori
sage: print(reproducible_repr(sol)) # optional - pycryptosat # needs sage.rings.polynomial.pbori
[{x: 0, y: 1, z: 0}]
sage: sol # optional - pycryptosat # needs sage.rings.polynomial.pbori
[{z: 0, y: 1, x: 0}]
sage: S.subs(sol[0]) # needs sage.rings.polynomial.pbori
[0, 0, 0]

Expand Down
15 changes: 8 additions & 7 deletions src/sage/rings/polynomial/pbori/pbori.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5026,11 +5026,10 @@ class BooleanPolynomialIdeal(MPolynomialIdeal):

A simple example::

sage: from sage.doctest.fixtures import reproducible_repr
sage: R.<x,y,z> = BooleanPolynomialRing()
sage: I = ideal( [ x*y*z + x*z + y + 1, x+y+z+1 ] )
sage: print(reproducible_repr(I.variety()))
[{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}]
sage: I.variety()
[{z: 0, y: 1, x: 0}, {z: 1, y: 1, x: 1}]

TESTS:

Expand All @@ -5047,14 +5046,16 @@ class BooleanPolynomialIdeal(MPolynomialIdeal):
....: x1*x2 + x1*x4 + x1*x5 + x1*x6 + x2*x3 + x2*x4 + x2*x5 + x3*x5 + x5*x6 + x5 + x6,
....: x1*x2 + x1*x6 + x2*x4 + x2*x5 + x2*x6 + x3*x6 + x4*x6 + x5*x6 + x5]
sage: I = R.ideal( polys )
sage: print(reproducible_repr(I.variety()))
[{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0}, {x1: 1, x2: 1, x3: 1, x4: 0, x5: 0, x6: 1}]
sage: I.variety()
[{x6: 0, x5: 0, x4: 0, x3: 0, x2: 0, x1: 0},
{x6: 1, x5: 0, x4: 0, x3: 1, x2: 1, x1: 1}]

sage: R = PolynomialRing(GF(2), 6, ['x%d'%(i+1) for i in range(6)], order='lex')
sage: I = R.ideal( polys )
sage: v = (I + sage.rings.ideal.FieldIdeal(R)).variety()
sage: print(reproducible_repr(v))
[{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0}, {x1: 1, x2: 1, x3: 1, x4: 0, x5: 0, x6: 1}]
sage: v
[{x6: 0, x5: 0, x4: 0, x3: 0, x2: 0, x1: 0},
{x6: 1, x5: 0, x4: 0, x3: 1, x2: 1, x1: 1}]


Check that :issue:`13976` is fixed::
Expand Down