/
array.py
204 lines (165 loc) · 7.74 KB
/
array.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Tools to create LaTeX arrays.
"""
import numpy as np
from qiskit.exceptions import MissingOptionalLibraryError
def _num_to_latex(raw_value, decimals=15, first_term=True, coefficient=False):
"""Convert a complex number to latex code suitable for a ket expression
Args:
raw_value (complex): Value to convert.
decimals (int): Number of decimal places to round to (default 15).
coefficient (bool): Whether the number is to be used as a coefficient
of a ket.
first_term (bool): If a coefficient, whether this number is the first
coefficient in the expression.
Returns:
str: latex code
"""
import sympy # runtime import
raw_value = np.around(raw_value, decimals=decimals).item()
value = sympy.nsimplify(raw_value, rational=False)
if isinstance(value, sympy.core.numbers.Rational) and value.denominator > 50:
# Avoid showing ugly fractions (e.g. 50498971964399/62500000000000)
value = value.evalf() # Display as float
if isinstance(value, sympy.core.numbers.Float):
value = round(value, decimals)
element = sympy.latex(value, full_prec=False)
if not coefficient:
return element
if isinstance(value, sympy.core.Add):
# element has two terms
element = f"({element})"
if element == "1":
element = ""
if element == "-1":
element = "-"
if not first_term and not element.startswith("-"):
element = f"+{element}"
return element
def _matrix_to_latex(matrix, decimals=10, prefix="", max_size=(8, 8)):
"""Latex representation of a complex numpy array (with maximum dimension 2)
Args:
matrix (ndarray): The matrix to be converted to latex, must have dimension 2.
decimals (int): For numbers not close to integers, the number of decimal places
to round to.
prefix (str): Latex string to be prepended to the latex, intended for labels.
max_size (list(```int```)): Indexable containing two integers: Maximum width and maximum
height of output Latex matrix (including dots characters). If the
width and/or height of matrix exceeds the maximum, the centre values
will be replaced with dots. Maximum width or height must be greater
than 3.
Returns:
str: Latex representation of the matrix
Raises:
ValueError: If minimum value in max_size < 3
"""
if min(max_size) < 3:
raise ValueError("""Smallest value in max_size must be greater than or equal to 3""")
out_string = f"\n{prefix}\n"
out_string += "\\begin{bmatrix}\n"
def _elements_to_latex(elements):
# Takes a list of elements (a row) and creates a latex
# string from it; Each element separated by `&`
el_string = ""
for el in elements:
num_string = _num_to_latex(el, decimals=decimals)
el_string += num_string + " & "
el_string = el_string[:-2] # remove trailing ampersands
return el_string
def _rows_to_latex(rows, max_width):
# Takes a list of lists (list of 'rows') and creates a
# latex string from it
row_string = ""
for r in rows:
if len(r) <= max_width:
row_string += _elements_to_latex(r)
else:
row_string += _elements_to_latex(r[: max_width // 2])
row_string += "& \\cdots & "
row_string += _elements_to_latex(r[-max_width // 2 + 1 :])
row_string += " \\\\\n "
return row_string
max_width, max_height = max_size
if matrix.ndim == 1:
out_string += _rows_to_latex([matrix], max_width)
elif len(matrix) > max_height:
# We need to truncate vertically, so we process the rows at the beginning
# and end, and add a line of vertical elipse (dots) characters between them
out_string += _rows_to_latex(matrix[: max_height // 2], max_width)
if max_width >= matrix.shape[1]:
out_string += "\\vdots & " * matrix.shape[1]
else:
# In this case we need to add the diagonal dots in line with the column
# of horizontal dots
pre_vdots = max_width // 2
post_vdots = max_width // 2 + np.mod(max_width, 2) - 1
out_string += "\\vdots & " * pre_vdots
out_string += "\\ddots & "
out_string += "\\vdots & " * post_vdots
out_string = out_string[:-2] + "\\\\\n "
out_string += _rows_to_latex(matrix[-max_height // 2 + 1 :], max_width)
else:
out_string += _rows_to_latex(matrix, max_width)
out_string += "\\end{bmatrix}\n"
return out_string
def array_to_latex(array, precision=10, prefix="", source=False, max_size=8):
"""Latex representation of a complex numpy array (with dimension 1 or 2)
Args:
array (ndarray): The array to be converted to latex, must have dimension 1 or 2 and
contain only numerical data.
precision (int): For numbers not close to integers or common terms, the number of
decimal places to round to.
prefix (str): Latex string to be prepended to the latex, intended for labels.
source (bool): If ``False``, will return IPython.display.Latex object. If display is
``True``, will instead return the LaTeX source string.
max_size (list(int) or int): The maximum size of the output Latex array.
* If list(``int``), then the 0th element of the list specifies the maximum
width (including dots characters) and the 1st specifies the maximum height
(also inc. dots characters).
* If a single ``int`` then this value sets the maximum width _and_ maximum
height.
Returns:
str or IPython.display.Latex: If ``source`` is ``True``, a ``str`` of the LaTeX
representation of the array, else an ``IPython.display.Latex`` representation of
the array.
Raises:
TypeError: If array can not be interpreted as a numerical numpy array.
ValueError: If the dimension of array is not 1 or 2.
MissingOptionalLibraryError: If ``source`` is ``False`` and ``IPython.display.Latex`` cannot be
imported.
"""
try:
array = np.asarray(array)
_ = array[0] + 1 # Test first element contains numerical data
except TypeError as err:
raise TypeError(
"""array_to_latex can only convert numpy arrays containing numerical data,
or types that can be converted to such arrays"""
) from err
if array.ndim > 2:
raise ValueError("array_to_latex can only convert numpy ndarrays of dimension 1 or 2")
if isinstance(max_size, int):
max_size = (max_size, max_size)
outstr = _matrix_to_latex(array, decimals=precision, prefix=prefix, max_size=max_size)
if source is True:
return outstr
try:
from IPython.display import Latex
except ImportError as err:
raise MissingOptionalLibraryError(
libname="IPython",
name="array_to_latex",
pip_install="pip install ipython",
) from err
return Latex(f"$${outstr}$$")