Skip to content

Commit 265cd13

Browse files
author
Release Manager
committed
Trac #30755: control the number of digits to display in matrices via IPython's %precision magic
Based on #30552, this ticket adds options for customizing the number of floating point digits to display in matrices. This is particularly useful for numeric computations involving large matrices. The option is linked to the IPython magic [https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic- precision %precision]. (The IPython magic also sets the corresponding option in !NumPy.) {{{ sage: %precision 4 '%.4f' sage: A = matrix.random(RDF, 5); A [-0.01337 -0.9269 -0.0893 0.1784 0.975] [ 0.2152 0.6165 0.4195 0.7558 -0.9292] [ 0.8234 -0.2305 0.83 -0.9375 0.4938] [ -0.6378 0.7058 0.1209 -0.9081 -0.9551] [ 0.9217 -0.3054 -0.2997 0.5871 -0.5642] sage: %precision '%r' sage: A [-0.013365510514271595 -0.9268868721196306 -0.08930150422327143 0.17839999989498323 0.9750196839071508] [ 0.21517169403284941 0.6165223907365385 0.4195049240955384 0.7557760012777428 -0.9291629307331721] [ 0.8233703740645537 -0.23053733934301857 0.8299695817296515 -0.9375095363818953 0.4937640499860838] [ -0.6378186095056861 0.7057551432596336 0.12087801033553447 -0.9080993678019631 -0.9551440194578318] [ 0.9216765622236254 -0.30541498083145413 -0.29967291645930083 0.5871367386637378 -0.5642041854379536] }}} URL: https://trac.sagemath.org/30755 Reported by: gh-mwageringel Ticket author(s): Markus Wageringel Reviewer(s): David Ayotte
2 parents c920272 + f7cc8c9 commit 265cd13

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

src/sage/matrix/constructor.pyx

+38
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,30 @@ class options(GlobalOptions):
669669
sage: matrix(ZZ, 4, 6)
670670
4 x 6 dense matrix over Integer Ring...
671671
sage: matrix.options._reset()
672+
673+
The precision can also be set via the IPython magic::
674+
675+
sage: from sage.repl.interpreter import get_test_shell
676+
sage: shell = get_test_shell()
677+
sage: shell.run_cell('%precision 5')
678+
'%.5f'
679+
sage: matrix.options.precision
680+
5
681+
sage: A = matrix(RR, [[200/3]]); A
682+
[66.667]
683+
684+
The number format can be specified as well::
685+
686+
sage: matrix.options.format_numeric = '{:.{prec}e}'
687+
sage: A
688+
[6.66667e+1]
689+
sage: matrix.options.format_numeric = '{:.{prec}f}'
690+
sage: A
691+
[66.66667]
692+
sage: matrix.options.format_numeric = '{:+.{prec}g}'
693+
sage: A
694+
[+66.667]
695+
sage: matrix.options._reset()
672696
"""
673697
NAME = 'Matrix'
674698
max_cols = dict(default=49,
@@ -677,3 +701,17 @@ class options(GlobalOptions):
677701
max_rows = dict(default=19,
678702
description='maximum number of rows to display',
679703
checker=lambda val: val >= 0)
704+
precision = \
705+
dict(default=None,
706+
description='number of digits to display for floating point '
707+
'entries; if ``None``, the exact representation is '
708+
'used instead. This option is also set by the '
709+
'`IPython magic <https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-precision>`_ '
710+
'``%precision``.',
711+
checker=lambda val: val is None or val >= 0)
712+
format_numeric = \
713+
dict(default='{:.{prec}}',
714+
description='string used for formatting floating point numbers of'
715+
' an (optional) precision ``prec``; only supported '
716+
'for entry types implementing ``__format__``',
717+
checker=lambda val: isinstance(val.format(3.1415, prec=3), str))

src/sage/matrix/matrix0.pyx

+48-1
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,25 @@ cdef class Matrix(sage.structure.element.Matrix):
18751875
-0.35104242112828943 0.5084492941557279⎟
18761876
-0.9541798283979341 -0.8948790563276592⎠
18771877
1878+
The number of floating point digits to display is controlled by
1879+
:obj:`matrix.options.precision <.constructor.options>` and can also be
1880+
set by the `IPython magic
1881+
<https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-precision>`_
1882+
``%precision``. This does not affect the internal precision of the
1883+
represented data, but only the textual display of matrices::
1884+
1885+
sage: matrix.options.precision = 4
1886+
sage: A = matrix(RR, [[1/3, 200/3], [-3, 1e6]]); A
1887+
[ 0.3333 66.67]
1888+
[ -3.000 1.000E+6]
1889+
sage: unicode_art(A)
1890+
⎛ 0.3333 66.67⎞
1891+
⎝ -3.000 1.000E+6⎠
1892+
sage: matrix.options.precision = None
1893+
sage: A
1894+
[ 0.333333333333333 66.6666666666667]
1895+
[ -3.00000000000000 1.00000000000000e6]
1896+
18781897
TESTS:
18791898
18801899
Prior to :trac:`11544` this could take a full minute to run (2011). ::
@@ -1898,6 +1917,19 @@ cdef class Matrix(sage.structure.element.Matrix):
18981917
⎛─⎞
18991918
⎜1⎟
19001919
⎝─⎠
1920+
1921+
Check that exact number types are not affected by the precision
1922+
option::
1923+
1924+
sage: matrix.options.precision = 4
1925+
sage: matrix(ZZ, [[10^10]])
1926+
[10000000000]
1927+
sage: matrix(QQ, [[2/3, 10^6]])
1928+
[ 2/3 1000000]
1929+
sage: R.<x,y> = QQ[[]]
1930+
sage: matrix(R, [[2/3 - 10^6 * x^3 + 3 * y + O(x, y)^4]])
1931+
[2/3 + 3*y - 1000000*x^3 + O(x, y)^4]
1932+
sage: matrix.options._reset()
19011933
"""
19021934
cdef Py_ssize_t nr, nc, r, c
19031935
nr = self._nrows
@@ -1988,15 +2020,30 @@ cdef class Matrix(sage.structure.element.Matrix):
19882020
if minus_one is not None:
19892021
rep_mapping[-self.base_ring().one()] = minus_one
19902022

2023+
entries = self.list()
2024+
2025+
# only use floating point formatting for inexact types that have
2026+
# custom implementation of __format__
2027+
from .constructor import options
2028+
prec = options.precision()
2029+
if prec is None or callable(rep_mapping) or not entries \
2030+
or type(entries[0]).__format__ is Element.__format__ \
2031+
or self._base_ring.is_exact():
2032+
fmt_numeric = None
2033+
else:
2034+
fmt_numeric = options.format_numeric()
2035+
19912036
# compute column widths
19922037
S = []
1993-
for x in self.list():
2038+
for x in entries:
19942039
# Override the usual representations with those specified
19952040
if callable(rep_mapping):
19962041
rep = rep_mapping(x)
19972042
# avoid hashing entries, especially algebraic numbers
19982043
elif rep_mapping and x in rep_mapping:
19992044
rep = rep_mapping.get(x)
2045+
elif fmt_numeric is not None:
2046+
rep = fmt_numeric.format(x, prec=prec)
20002047
else:
20012048
rep = repr(x)
20022049
S.append(rep)

src/sage/repl/display/formatter.py

+39
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def __init__(self, *args, **kwds):
104104
from sage.repl.rich_output.backend_ipython import BackendIPython
105105
self.dm.check_backend_class(BackendIPython)
106106

107+
pt_formatter = self.formatters[PLAIN_TEXT]
108+
pt_formatter.observe(self._ipython_float_precision_changed,
109+
names=['float_precision'])
110+
107111
def format(self, obj, include=None, exclude=None):
108112
r"""
109113
Use the Sage rich output instead of IPython
@@ -195,6 +199,41 @@ def format(self, obj, include=None, exclude=None):
195199
ipy_metadata[PLAIN_TEXT] = sage_metadata[PLAIN_TEXT]
196200
return ipy_format, ipy_metadata
197201

202+
@staticmethod
203+
def _ipython_float_precision_changed(change):
204+
"""
205+
Update the current float precision for the display of matrices in Sage.
206+
207+
This function is called when the IPython ``%precision`` magic is
208+
invoked.
209+
210+
TESTS::
211+
212+
sage: from sage.repl.interpreter import get_test_shell
213+
sage: shell = get_test_shell()
214+
sage: shell.run_cell('%precision 4')
215+
'%.4f'
216+
sage: shell.run_cell('matrix.options.precision') # indirect doctest
217+
4
218+
sage: shell.run_cell('%precision')
219+
'%r'
220+
sage: shell.run_cell('matrix.options.precision') # indirect doctest
221+
None
222+
"""
223+
from sage.matrix.constructor import options
224+
s = change.new
225+
if not s:
226+
# unset the precision
227+
options.precision = None
228+
else:
229+
try:
230+
prec = int(s)
231+
if prec >= 0:
232+
options.precision = prec
233+
# otherwise ignore the change
234+
except ValueError:
235+
pass
236+
198237

199238
class SagePlainTextFormatter(PlainTextFormatter):
200239

0 commit comments

Comments
 (0)