Trajectree 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.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.
- trajectree/__init__.py +0 -3
- trajectree/fock_optics/devices.py +1 -1
- trajectree/fock_optics/light_sources.py +2 -2
- trajectree/fock_optics/measurement.py +3 -3
- trajectree/fock_optics/utils.py +6 -6
- trajectree/trajectory.py +2 -2
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/METADATA +2 -3
- trajectree-0.0.2.dist-info/RECORD +16 -0
- trajectree/quimb/docs/_pygments/_pygments_dark.py +0 -118
- trajectree/quimb/docs/_pygments/_pygments_light.py +0 -118
- trajectree/quimb/docs/conf.py +0 -158
- trajectree/quimb/docs/examples/ex_mpi_expm_evo.py +0 -62
- trajectree/quimb/quimb/__init__.py +0 -507
- trajectree/quimb/quimb/calc.py +0 -1491
- trajectree/quimb/quimb/core.py +0 -2279
- trajectree/quimb/quimb/evo.py +0 -712
- trajectree/quimb/quimb/experimental/__init__.py +0 -0
- trajectree/quimb/quimb/experimental/autojittn.py +0 -129
- trajectree/quimb/quimb/experimental/belief_propagation/__init__.py +0 -109
- trajectree/quimb/quimb/experimental/belief_propagation/bp_common.py +0 -397
- trajectree/quimb/quimb/experimental/belief_propagation/d1bp.py +0 -316
- trajectree/quimb/quimb/experimental/belief_propagation/d2bp.py +0 -653
- trajectree/quimb/quimb/experimental/belief_propagation/hd1bp.py +0 -571
- trajectree/quimb/quimb/experimental/belief_propagation/hv1bp.py +0 -775
- trajectree/quimb/quimb/experimental/belief_propagation/l1bp.py +0 -316
- trajectree/quimb/quimb/experimental/belief_propagation/l2bp.py +0 -537
- trajectree/quimb/quimb/experimental/belief_propagation/regions.py +0 -194
- trajectree/quimb/quimb/experimental/cluster_update.py +0 -286
- trajectree/quimb/quimb/experimental/merabuilder.py +0 -865
- trajectree/quimb/quimb/experimental/operatorbuilder/__init__.py +0 -15
- trajectree/quimb/quimb/experimental/operatorbuilder/operatorbuilder.py +0 -1631
- trajectree/quimb/quimb/experimental/schematic.py +0 -7
- trajectree/quimb/quimb/experimental/tn_marginals.py +0 -130
- trajectree/quimb/quimb/experimental/tnvmc.py +0 -1483
- trajectree/quimb/quimb/gates.py +0 -36
- trajectree/quimb/quimb/gen/__init__.py +0 -2
- trajectree/quimb/quimb/gen/operators.py +0 -1167
- trajectree/quimb/quimb/gen/rand.py +0 -713
- trajectree/quimb/quimb/gen/states.py +0 -479
- trajectree/quimb/quimb/linalg/__init__.py +0 -6
- trajectree/quimb/quimb/linalg/approx_spectral.py +0 -1109
- trajectree/quimb/quimb/linalg/autoblock.py +0 -258
- trajectree/quimb/quimb/linalg/base_linalg.py +0 -719
- trajectree/quimb/quimb/linalg/mpi_launcher.py +0 -397
- trajectree/quimb/quimb/linalg/numpy_linalg.py +0 -244
- trajectree/quimb/quimb/linalg/rand_linalg.py +0 -514
- trajectree/quimb/quimb/linalg/scipy_linalg.py +0 -293
- trajectree/quimb/quimb/linalg/slepc_linalg.py +0 -892
- trajectree/quimb/quimb/schematic.py +0 -1518
- trajectree/quimb/quimb/tensor/__init__.py +0 -401
- trajectree/quimb/quimb/tensor/array_ops.py +0 -610
- trajectree/quimb/quimb/tensor/circuit.py +0 -4824
- trajectree/quimb/quimb/tensor/circuit_gen.py +0 -411
- trajectree/quimb/quimb/tensor/contraction.py +0 -336
- trajectree/quimb/quimb/tensor/decomp.py +0 -1255
- trajectree/quimb/quimb/tensor/drawing.py +0 -1646
- trajectree/quimb/quimb/tensor/fitting.py +0 -385
- trajectree/quimb/quimb/tensor/geometry.py +0 -583
- trajectree/quimb/quimb/tensor/interface.py +0 -114
- trajectree/quimb/quimb/tensor/networking.py +0 -1058
- trajectree/quimb/quimb/tensor/optimize.py +0 -1818
- trajectree/quimb/quimb/tensor/tensor_1d.py +0 -4778
- trajectree/quimb/quimb/tensor/tensor_1d_compress.py +0 -1854
- trajectree/quimb/quimb/tensor/tensor_1d_tebd.py +0 -662
- trajectree/quimb/quimb/tensor/tensor_2d.py +0 -5954
- trajectree/quimb/quimb/tensor/tensor_2d_compress.py +0 -96
- trajectree/quimb/quimb/tensor/tensor_2d_tebd.py +0 -1230
- trajectree/quimb/quimb/tensor/tensor_3d.py +0 -2869
- trajectree/quimb/quimb/tensor/tensor_3d_tebd.py +0 -46
- trajectree/quimb/quimb/tensor/tensor_approx_spectral.py +0 -60
- trajectree/quimb/quimb/tensor/tensor_arbgeom.py +0 -3237
- trajectree/quimb/quimb/tensor/tensor_arbgeom_compress.py +0 -565
- trajectree/quimb/quimb/tensor/tensor_arbgeom_tebd.py +0 -1138
- trajectree/quimb/quimb/tensor/tensor_builder.py +0 -5411
- trajectree/quimb/quimb/tensor/tensor_core.py +0 -11179
- trajectree/quimb/quimb/tensor/tensor_dmrg.py +0 -1472
- trajectree/quimb/quimb/tensor/tensor_mera.py +0 -204
- trajectree/quimb/quimb/utils.py +0 -892
- trajectree/quimb/tests/__init__.py +0 -0
- trajectree/quimb/tests/test_accel.py +0 -501
- trajectree/quimb/tests/test_calc.py +0 -788
- trajectree/quimb/tests/test_core.py +0 -847
- trajectree/quimb/tests/test_evo.py +0 -565
- trajectree/quimb/tests/test_gen/__init__.py +0 -0
- trajectree/quimb/tests/test_gen/test_operators.py +0 -361
- trajectree/quimb/tests/test_gen/test_rand.py +0 -296
- trajectree/quimb/tests/test_gen/test_states.py +0 -261
- trajectree/quimb/tests/test_linalg/__init__.py +0 -0
- trajectree/quimb/tests/test_linalg/test_approx_spectral.py +0 -368
- trajectree/quimb/tests/test_linalg/test_base_linalg.py +0 -351
- trajectree/quimb/tests/test_linalg/test_mpi_linalg.py +0 -127
- trajectree/quimb/tests/test_linalg/test_numpy_linalg.py +0 -84
- trajectree/quimb/tests/test_linalg/test_rand_linalg.py +0 -134
- trajectree/quimb/tests/test_linalg/test_slepc_linalg.py +0 -283
- trajectree/quimb/tests/test_tensor/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/__init__.py +0 -0
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d1bp.py +0 -39
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_d2bp.py +0 -67
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hd1bp.py +0 -64
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_hv1bp.py +0 -51
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l1bp.py +0 -142
- trajectree/quimb/tests/test_tensor/test_belief_propagation/test_l2bp.py +0 -101
- trajectree/quimb/tests/test_tensor/test_circuit.py +0 -816
- trajectree/quimb/tests/test_tensor/test_contract.py +0 -67
- trajectree/quimb/tests/test_tensor/test_decomp.py +0 -40
- trajectree/quimb/tests/test_tensor/test_mera.py +0 -52
- trajectree/quimb/tests/test_tensor/test_optimizers.py +0 -488
- trajectree/quimb/tests/test_tensor/test_tensor_1d.py +0 -1171
- trajectree/quimb/tests/test_tensor/test_tensor_2d.py +0 -606
- trajectree/quimb/tests/test_tensor/test_tensor_2d_tebd.py +0 -144
- trajectree/quimb/tests/test_tensor/test_tensor_3d.py +0 -123
- trajectree/quimb/tests/test_tensor/test_tensor_arbgeom.py +0 -226
- trajectree/quimb/tests/test_tensor/test_tensor_builder.py +0 -441
- trajectree/quimb/tests/test_tensor/test_tensor_core.py +0 -2066
- trajectree/quimb/tests/test_tensor/test_tensor_dmrg.py +0 -388
- trajectree/quimb/tests/test_tensor/test_tensor_spectral_approx.py +0 -63
- trajectree/quimb/tests/test_tensor/test_tensor_tebd.py +0 -270
- trajectree/quimb/tests/test_utils.py +0 -85
- trajectree-0.0.1.dist-info/RECORD +0 -126
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/WHEEL +0 -0
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {trajectree-0.0.1.dist-info → trajectree-0.0.2.dist-info}/top_level.txt +0 -0
trajectree/quimb/quimb/calc.py
DELETED
|
@@ -1,1491 +0,0 @@
|
|
|
1
|
-
"""Functions for more advanced calculations of quantities and properties of
|
|
2
|
-
quantum objects.
|
|
3
|
-
"""
|
|
4
|
-
import numbers
|
|
5
|
-
import itertools
|
|
6
|
-
import functools
|
|
7
|
-
import collections
|
|
8
|
-
from math import sin, cos, pi, log, log2, sqrt
|
|
9
|
-
|
|
10
|
-
import numpy as np
|
|
11
|
-
import numpy.linalg as nla
|
|
12
|
-
from scipy.optimize import minimize
|
|
13
|
-
|
|
14
|
-
from .core import (
|
|
15
|
-
dag,
|
|
16
|
-
dop,
|
|
17
|
-
dot,
|
|
18
|
-
ensure_qarray,
|
|
19
|
-
expec,
|
|
20
|
-
eye,
|
|
21
|
-
ikron,
|
|
22
|
-
infer_size,
|
|
23
|
-
isop,
|
|
24
|
-
issparse,
|
|
25
|
-
isvec,
|
|
26
|
-
kron,
|
|
27
|
-
njit,
|
|
28
|
-
prod,
|
|
29
|
-
ptr,
|
|
30
|
-
qu,
|
|
31
|
-
realify,
|
|
32
|
-
tr,
|
|
33
|
-
zeroify,
|
|
34
|
-
)
|
|
35
|
-
from .linalg.base_linalg import eigh, eigvalsh, norm, sqrtm, norm_trace_dense
|
|
36
|
-
from .linalg.approx_spectral import (
|
|
37
|
-
entropy_subsys_approx,
|
|
38
|
-
gen_bipartite_spectral_fn,
|
|
39
|
-
logneg_subsys_approx,
|
|
40
|
-
tr_sqrt_subsys_approx,
|
|
41
|
-
)
|
|
42
|
-
from .gen.operators import pauli
|
|
43
|
-
from .gen.states import basis_vec, bell_state, bloch_state
|
|
44
|
-
from .utils import (
|
|
45
|
-
frequencies,
|
|
46
|
-
int2tup,
|
|
47
|
-
keymap,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
from .tensor.contraction import array_contract
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def fidelity(p1, p2, squared=False):
|
|
54
|
-
"""Fidelity between two quantum states. By default, the unsquared fidelity
|
|
55
|
-
is used, equivalent in the pure state case to ``|<psi|phi>|``.
|
|
56
|
-
|
|
57
|
-
Parameters
|
|
58
|
-
----------
|
|
59
|
-
p1 : vector or operator
|
|
60
|
-
First state.
|
|
61
|
-
p2 : vector or operator
|
|
62
|
-
Second state.
|
|
63
|
-
squared : bool, optional
|
|
64
|
-
Whether to use the squared convention or not, by default False.
|
|
65
|
-
|
|
66
|
-
Returns
|
|
67
|
-
-------
|
|
68
|
-
float
|
|
69
|
-
"""
|
|
70
|
-
if isvec(p1) or isvec(p2):
|
|
71
|
-
F = expec(p1, p2).real
|
|
72
|
-
if not squared:
|
|
73
|
-
F = max(0.0, F) ** 0.5
|
|
74
|
-
return F
|
|
75
|
-
|
|
76
|
-
sqrho = sqrtm(p1)
|
|
77
|
-
F = tr(sqrtm(dot(sqrho, dot(p2, sqrho)))).real
|
|
78
|
-
|
|
79
|
-
if squared:
|
|
80
|
-
F = F**2
|
|
81
|
-
|
|
82
|
-
return F
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def purify(rho):
|
|
86
|
-
"""Take state rho and purify it into a wavefunction of squared
|
|
87
|
-
dimension.
|
|
88
|
-
|
|
89
|
-
Parameters
|
|
90
|
-
----------
|
|
91
|
-
rho : operator
|
|
92
|
-
Density operator to purify.
|
|
93
|
-
|
|
94
|
-
Returns
|
|
95
|
-
-------
|
|
96
|
-
vector :
|
|
97
|
-
The purified ket.
|
|
98
|
-
"""
|
|
99
|
-
d = rho.shape[0]
|
|
100
|
-
evals, vs = eigh(rho)
|
|
101
|
-
evals = np.sqrt(np.clip(evals, 0, 1))
|
|
102
|
-
psi = np.zeros(shape=(d**2, 1), dtype=complex)
|
|
103
|
-
for i, evals in enumerate(evals.flat):
|
|
104
|
-
psi += evals * kron(vs[:, [i]], basis_vec(i, d))
|
|
105
|
-
return qu(psi)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@ensure_qarray
|
|
109
|
-
def kraus_op(rho, Ek, dims=None, where=None, check=False):
|
|
110
|
-
r"""Operate on a mixed state with a set of kraus operators:
|
|
111
|
-
|
|
112
|
-
.. math::
|
|
113
|
-
|
|
114
|
-
\sigma = \sum_k E_k \rho E_k^{\dagger}
|
|
115
|
-
|
|
116
|
-
Parameters
|
|
117
|
-
----------
|
|
118
|
-
rho : (d, d) density matrix
|
|
119
|
-
The density operator to perform operation on.
|
|
120
|
-
Ek : (K, d, d) array or sequence of K (d, d) arrays
|
|
121
|
-
The Kraus operator(s).
|
|
122
|
-
dims : sequence of int, optional
|
|
123
|
-
The subdimensions of ``rho``.
|
|
124
|
-
where : int or sequence of int, optional
|
|
125
|
-
Which of the subsystems to apply the operation on.
|
|
126
|
-
check : bool, optional
|
|
127
|
-
Where to check ``sum_k(Ek.H @ Ek) == 1``.
|
|
128
|
-
|
|
129
|
-
Returns
|
|
130
|
-
-------
|
|
131
|
-
sigma : density matrix
|
|
132
|
-
The state after the kraus operation.
|
|
133
|
-
|
|
134
|
-
Examples
|
|
135
|
-
--------
|
|
136
|
-
The depolarising channel:
|
|
137
|
-
|
|
138
|
-
.. code-block:: python3
|
|
139
|
-
|
|
140
|
-
In [1]: import quimb as qu
|
|
141
|
-
|
|
142
|
-
In [2]: rho = qu.rand_rho(2)
|
|
143
|
-
|
|
144
|
-
In [3]: I, X, Y, Z = (qu.pauli(s) for s in 'IXYZ')
|
|
145
|
-
|
|
146
|
-
In [4]: [qu.expec(rho, A) for A in (X, Y, Z)]
|
|
147
|
-
Out[4]: [-0.652176185230622, -0.1301762132792484, 0.22362918368272583]
|
|
148
|
-
|
|
149
|
-
In [5]: p = 0.1
|
|
150
|
-
|
|
151
|
-
In [6]: Ek = [
|
|
152
|
-
...: (1 - p)**0.5 * I,
|
|
153
|
-
...: (p / 3)**0.5 * X,
|
|
154
|
-
...: (p / 3)**0.5 * Y,
|
|
155
|
-
...: (p / 3)**0.5 * Z
|
|
156
|
-
...: ]
|
|
157
|
-
|
|
158
|
-
In [7]: sigma = qu.kraus_op(rho, Ek)
|
|
159
|
-
|
|
160
|
-
In [8]: [qu.expec(sigma, A) for A in (X, Y, Z)]
|
|
161
|
-
Out[8]: [-0.5652193605332058, -0.11281938484201527, 0.1938119591916957]
|
|
162
|
-
"""
|
|
163
|
-
if not isinstance(Ek, np.ndarray):
|
|
164
|
-
Ek = np.stack(Ek, axis=0)
|
|
165
|
-
|
|
166
|
-
if check:
|
|
167
|
-
SEk = np.einsum("kij,kil", Ek.conj(), Ek)
|
|
168
|
-
if norm(SEk - eye(Ek.shape[-1]), "fro") > 1e-12:
|
|
169
|
-
raise ValueError("Did not find ``sum(E_k.H @ Ek) == 1``.")
|
|
170
|
-
|
|
171
|
-
if int(dims is None) + int(where is None) == 1:
|
|
172
|
-
raise ValueError("If `dims` is specified so should `where`.")
|
|
173
|
-
|
|
174
|
-
if isinstance(where, numbers.Integral):
|
|
175
|
-
where = (where,)
|
|
176
|
-
|
|
177
|
-
if dims:
|
|
178
|
-
# the full subdimensions of ``rho``
|
|
179
|
-
dims = tuple(dims)
|
|
180
|
-
rho = rho.reshape(dims + dims)
|
|
181
|
-
N = len(dims)
|
|
182
|
-
|
|
183
|
-
# the subdimensions of where the kraus operator should act
|
|
184
|
-
kdims = tuple(dims[i] for i in where)
|
|
185
|
-
Ek = Ek.reshape((-1,) + kdims + kdims)
|
|
186
|
-
|
|
187
|
-
rho_inds = (
|
|
188
|
-
*(f"i*{q}" if q in where else f"i{q}" for q in range(N)),
|
|
189
|
-
*(f"j*{q}" if q in where else f"j{q}" for q in range(N)),
|
|
190
|
-
)
|
|
191
|
-
Ei_inds = ("K", *(f"i{q}" for q in where), *(f"i*{q}" for q in where))
|
|
192
|
-
Ej_inds = ("K", *(f"j{q}" for q in where), *(f"j*{q}" for q in where))
|
|
193
|
-
out = (*(f"i{q}" for q in range(N)), *(f"j{q}" for q in range(N)))
|
|
194
|
-
else:
|
|
195
|
-
Ei_inds = ("K", "i", "i*")
|
|
196
|
-
rho_inds = ("i*", "j*")
|
|
197
|
-
Ej_inds = ("K", "j", "j*")
|
|
198
|
-
out = ("i", "j")
|
|
199
|
-
|
|
200
|
-
sigma = array_contract(
|
|
201
|
-
(Ek, rho, Ek.conj()),
|
|
202
|
-
(Ei_inds, rho_inds, Ej_inds),
|
|
203
|
-
out,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
if dims:
|
|
207
|
-
sigma = sigma.reshape(prod(dims), prod(dims))
|
|
208
|
-
|
|
209
|
-
return sigma
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
@ensure_qarray
|
|
213
|
-
def projector(A, eigenvalue=1.0, tol=1e-12, autoblock=False):
|
|
214
|
-
"""Compute the projector for the target ``eigenvalue`` of operator
|
|
215
|
-
``A``.
|
|
216
|
-
|
|
217
|
-
Parameters
|
|
218
|
-
----------
|
|
219
|
-
A : operator or tuple[array, operator].
|
|
220
|
-
The hermitian observable. If a tuple is supplied, assume this is the
|
|
221
|
-
eigendecomposition of the observable.
|
|
222
|
-
eigenvalue : float, optional
|
|
223
|
-
The target eigenvalue to construct the projector for, default: 1.
|
|
224
|
-
tol : float, optional
|
|
225
|
-
The tolerance with which to select all eigenvalues.
|
|
226
|
-
autoblock : bool, optional
|
|
227
|
-
Whether to use automatic symmetry exploitation.
|
|
228
|
-
|
|
229
|
-
Returns
|
|
230
|
-
-------
|
|
231
|
-
P : dense matrix
|
|
232
|
-
The projector onto the target eigenspace.
|
|
233
|
-
"""
|
|
234
|
-
if isinstance(A, (tuple, list)):
|
|
235
|
-
el, ev = A
|
|
236
|
-
else:
|
|
237
|
-
el, ev = eigh(A, autoblock=autoblock)
|
|
238
|
-
which = np.argwhere(abs(el - eigenvalue) < tol)
|
|
239
|
-
P = np.zeros_like(ev)
|
|
240
|
-
for i in which:
|
|
241
|
-
vi = ev[:, i]
|
|
242
|
-
P += vi @ vi.H
|
|
243
|
-
return P
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def measure(p, A, eigenvalue=None, tol=1e-12):
|
|
247
|
-
r"""Measure state ``p`` with observable ``A``, collapsing the state in the
|
|
248
|
-
process and returning the relevant eigenvalue.
|
|
249
|
-
|
|
250
|
-
.. math::
|
|
251
|
-
|
|
252
|
-
\left| \psi \right\rangle \rightarrow
|
|
253
|
-
\frac{
|
|
254
|
-
P_i \left| \psi \right\rangle
|
|
255
|
-
}{
|
|
256
|
-
\sqrt{\left\langle \psi \right| P_i \left| \psi \right\rangle}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
and
|
|
260
|
-
|
|
261
|
-
.. math::
|
|
262
|
-
|
|
263
|
-
\rho \rightarrow
|
|
264
|
-
\frac{
|
|
265
|
-
P_i \rho P_i^{\dagger}
|
|
266
|
-
}{
|
|
267
|
-
\text{Tr} \left[ P_i \rho \right]
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
along with the corresponding eigenvalue :math:`\lambda_i`.
|
|
271
|
-
|
|
272
|
-
Parameters
|
|
273
|
-
----------
|
|
274
|
-
p : vector or matrix
|
|
275
|
-
The quantum state to measure.
|
|
276
|
-
A : matrix or tuple[array, matrix]
|
|
277
|
-
The hermitian operator corresponding to an observable. You can supply
|
|
278
|
-
also a pre-diagonalized operator here as a tuple of eigenvalues and
|
|
279
|
-
eigenvectors.
|
|
280
|
-
tol : float, optional
|
|
281
|
-
The tolerance within which to group eigenspaces.
|
|
282
|
-
eigenvalue : float, optional
|
|
283
|
-
If specified, deterministically collapse to this result. Otherwise
|
|
284
|
-
randomly choose a result as in 'real-life'.
|
|
285
|
-
|
|
286
|
-
Returns
|
|
287
|
-
-------
|
|
288
|
-
result : float
|
|
289
|
-
The result of the measurement.
|
|
290
|
-
p_after : vector or matrix
|
|
291
|
-
The quantum state post measurement.
|
|
292
|
-
"""
|
|
293
|
-
if isinstance(A, (tuple, list)):
|
|
294
|
-
el, ev = A
|
|
295
|
-
else:
|
|
296
|
-
el, ev = eigh(A)
|
|
297
|
-
|
|
298
|
-
js = np.arange(el.size)
|
|
299
|
-
|
|
300
|
-
# compute prob of each eigenvector
|
|
301
|
-
if isvec(p):
|
|
302
|
-
pj = (abs(ev.H @ p) ** 2).flatten()
|
|
303
|
-
else:
|
|
304
|
-
pj = array_contract(
|
|
305
|
-
(ev.H, p, ev),
|
|
306
|
-
("jk", "kl", "lj"),
|
|
307
|
-
("j",),
|
|
308
|
-
).real
|
|
309
|
-
|
|
310
|
-
# then choose one
|
|
311
|
-
if eigenvalue is None:
|
|
312
|
-
j = np.random.choice(js, p=pj)
|
|
313
|
-
eigenvalue = el[j]
|
|
314
|
-
|
|
315
|
-
# now combine whole eigenspace
|
|
316
|
-
P = projector((el, ev), eigenvalue=eigenvalue, tol=tol)
|
|
317
|
-
total_prob = np.sum(pj[abs(el - eigenvalue) < tol])
|
|
318
|
-
|
|
319
|
-
# now collapse the state
|
|
320
|
-
if isvec(p):
|
|
321
|
-
p_after = P @ (p / total_prob**0.5)
|
|
322
|
-
else:
|
|
323
|
-
p_after = (P @ p @ P.H) / total_prob
|
|
324
|
-
|
|
325
|
-
return eigenvalue, p_after
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
def simulate_counts(p, C, phys_dim=2, seed=None):
|
|
329
|
-
"""Simulate measuring each qubit of ``p`` in the computational basis,
|
|
330
|
-
producing output like that of ``qiskit``.
|
|
331
|
-
|
|
332
|
-
Parameters
|
|
333
|
-
----------
|
|
334
|
-
p : vector or operator
|
|
335
|
-
The quantum state, assumed to be normalized, as either a ket or density
|
|
336
|
-
operator.
|
|
337
|
-
C : int
|
|
338
|
-
The number of counts to perform.
|
|
339
|
-
phys_dim : int, optional
|
|
340
|
-
The assumed size of the subsystems of ``p``, defaults to 2 for qubits.
|
|
341
|
-
|
|
342
|
-
Returns
|
|
343
|
-
-------
|
|
344
|
-
results : dict[str, int]
|
|
345
|
-
The counts for each bit string measured.
|
|
346
|
-
|
|
347
|
-
Examples
|
|
348
|
-
--------
|
|
349
|
-
|
|
350
|
-
Simulate measuring the state of each qubit in a GHZ-state:
|
|
351
|
-
|
|
352
|
-
.. code:: python3
|
|
353
|
-
|
|
354
|
-
>>> import quimb as qu
|
|
355
|
-
>>> psi = qu.ghz_state(3)
|
|
356
|
-
>>> qu.simulate_counts(psi, 1024)
|
|
357
|
-
{'000': 514, '111': 510}
|
|
358
|
-
|
|
359
|
-
"""
|
|
360
|
-
rng = np.random.default_rng(seed)
|
|
361
|
-
|
|
362
|
-
n = infer_size(p, phys_dim)
|
|
363
|
-
d = phys_dim**n
|
|
364
|
-
|
|
365
|
-
if isop(p):
|
|
366
|
-
pi = np.diag(p).real
|
|
367
|
-
else:
|
|
368
|
-
pi = np.multiply(np.conj(p), p).real
|
|
369
|
-
|
|
370
|
-
# probability of each basis state
|
|
371
|
-
pi = pi.reshape(-1)
|
|
372
|
-
|
|
373
|
-
# raw counts in terms of integers
|
|
374
|
-
raw_counts = rng.choice(d, size=C, p=pi)
|
|
375
|
-
|
|
376
|
-
# convert to frequencies of binary
|
|
377
|
-
bin_str = "{:0>" + str(n) + "b}"
|
|
378
|
-
results = keymap(bin_str.format, frequencies(raw_counts))
|
|
379
|
-
|
|
380
|
-
return results
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
def dephase(rho, p, rand_rank=None):
|
|
384
|
-
"""Dephase ``rho`` by amount ``p``, that is, mix it
|
|
385
|
-
with the maximally mixed state:
|
|
386
|
-
|
|
387
|
-
rho -> (1 - p) * rho + p * I / d
|
|
388
|
-
|
|
389
|
-
Parameters
|
|
390
|
-
----------
|
|
391
|
-
rho : operator
|
|
392
|
-
The state.
|
|
393
|
-
p : float
|
|
394
|
-
The final proportion of identity.
|
|
395
|
-
rand_rank : int or float, optional
|
|
396
|
-
If given, dephase with a random diagonal operator with this many
|
|
397
|
-
non-zero entries. If float, proportion of full size.
|
|
398
|
-
|
|
399
|
-
Returns
|
|
400
|
-
-------
|
|
401
|
-
rho_dephase : operator
|
|
402
|
-
The dephased density operator.
|
|
403
|
-
"""
|
|
404
|
-
d = rho.shape[0]
|
|
405
|
-
|
|
406
|
-
if (rand_rank is None) or (rand_rank == d) or (rand_rank == 1.0):
|
|
407
|
-
dephaser = eye(d) / d
|
|
408
|
-
|
|
409
|
-
else:
|
|
410
|
-
if not isinstance(rand_rank, numbers.Integral):
|
|
411
|
-
rand_rank = int(rand_rank * d)
|
|
412
|
-
rand_rank = min(max(1, rand_rank), d)
|
|
413
|
-
|
|
414
|
-
dephaser = np.zeros((d, d))
|
|
415
|
-
dephaser_diag = np.einsum("aa->a", dephaser)
|
|
416
|
-
nnz = np.random.choice(np.arange(d), size=rand_rank, replace=False)
|
|
417
|
-
dephaser_diag[nnz] = 1 / rand_rank
|
|
418
|
-
|
|
419
|
-
return (1 - p) * rho + p * dephaser
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
@zeroify
|
|
423
|
-
def entropy(a, rank=None):
|
|
424
|
-
"""Compute the (von Neumann) entropy.
|
|
425
|
-
|
|
426
|
-
Parameters
|
|
427
|
-
----------
|
|
428
|
-
a : operator or 1d array
|
|
429
|
-
Positive operator or list of positive eigenvalues.
|
|
430
|
-
rank : int (optional)
|
|
431
|
-
If operator has known rank, then a partial decomposition can be
|
|
432
|
-
used to accelerate the calculation.
|
|
433
|
-
|
|
434
|
-
Returns
|
|
435
|
-
-------
|
|
436
|
-
float
|
|
437
|
-
The von Neumann entropy.
|
|
438
|
-
|
|
439
|
-
See Also
|
|
440
|
-
--------
|
|
441
|
-
mutinf, entropy_subsys, entropy_subsys_approx
|
|
442
|
-
"""
|
|
443
|
-
a = np.asarray(a)
|
|
444
|
-
if np.ndim(a) == 1:
|
|
445
|
-
evals = a
|
|
446
|
-
else:
|
|
447
|
-
if rank is None:
|
|
448
|
-
evals = eigvalsh(a)
|
|
449
|
-
else: # know that not all eigenvalues needed
|
|
450
|
-
evals = eigvalsh(a, k=rank, which="LM", backend="AUTO")
|
|
451
|
-
|
|
452
|
-
evals = evals[evals > 0.0]
|
|
453
|
-
return np.sum(-evals * np.log2(evals))
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
entropy_subsys = gen_bipartite_spectral_fn(entropy, entropy_subsys_approx, 0.0)
|
|
457
|
-
"""Calculate the entropy of a pure states' subsystem, optionally switching
|
|
458
|
-
to an approximate lanczos method when the subsystem is very large.
|
|
459
|
-
|
|
460
|
-
Parameters
|
|
461
|
-
----------
|
|
462
|
-
psi_ab : vector
|
|
463
|
-
Bipartite state.
|
|
464
|
-
dims : sequence of int
|
|
465
|
-
The sub-dimensions of the state.
|
|
466
|
-
sysa : sequence of int
|
|
467
|
-
The indices of which dimensions to calculate the entropy for.
|
|
468
|
-
approx_thresh : int, optional
|
|
469
|
-
The size of sysa at which to switch to the approx method. Set to
|
|
470
|
-
``None`` to never use the approximation.
|
|
471
|
-
**approx_opts
|
|
472
|
-
Supplied to :func:`entropy_subsys_approx`, if used.
|
|
473
|
-
|
|
474
|
-
Returns
|
|
475
|
-
-------
|
|
476
|
-
float
|
|
477
|
-
The subsytem entropy.
|
|
478
|
-
|
|
479
|
-
See Also
|
|
480
|
-
--------
|
|
481
|
-
entropy, entropy_subsys_approx, mutinf_subsys
|
|
482
|
-
"""
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
@zeroify
|
|
486
|
-
def mutinf(p, dims=(2, 2), sysa=0, rank=None):
|
|
487
|
-
"""Find the mutual information for a bipartition of a state.
|
|
488
|
-
|
|
489
|
-
That is, ``H(A) + H(B) - H(AB)``, for von Neumann entropy ``H``, and two
|
|
490
|
-
subsystems A and B.
|
|
491
|
-
|
|
492
|
-
Parameters
|
|
493
|
-
----------
|
|
494
|
-
p : vector or operator
|
|
495
|
-
State, can be vector or operator.
|
|
496
|
-
dims : tuple(int), optional
|
|
497
|
-
Internal dimensions of state.
|
|
498
|
-
sysa : int, optional
|
|
499
|
-
Index of first subsystem, A.
|
|
500
|
-
sysb : int, optional
|
|
501
|
-
Index of second subsystem, B.
|
|
502
|
-
rank : int, optional
|
|
503
|
-
If known, the rank of rho_ab, to speed calculation of ``H(AB)`` up.
|
|
504
|
-
For example, if ``p`` comes from tracing out three qubits from a
|
|
505
|
-
system, then its rank is 2^3 = 8 etc.
|
|
506
|
-
|
|
507
|
-
Returns
|
|
508
|
-
-------
|
|
509
|
-
float
|
|
510
|
-
|
|
511
|
-
See Also
|
|
512
|
-
--------
|
|
513
|
-
entropy, mutinf_subsys, entropy_subsys_approx
|
|
514
|
-
"""
|
|
515
|
-
sysa = int2tup(sysa)
|
|
516
|
-
|
|
517
|
-
# mixed combined system
|
|
518
|
-
if isop(p):
|
|
519
|
-
# total
|
|
520
|
-
hab = entropy(p, rank=rank)
|
|
521
|
-
|
|
522
|
-
# subsystem a
|
|
523
|
-
rhoa = ptr(p, dims, sysa)
|
|
524
|
-
ha = entropy(rhoa)
|
|
525
|
-
|
|
526
|
-
# need subsystem b as well
|
|
527
|
-
sysb = tuple(i for i in range(len(dims)) if i not in sysa)
|
|
528
|
-
rhob = ptr(p, dims, sysb)
|
|
529
|
-
hb = entropy(rhob)
|
|
530
|
-
|
|
531
|
-
# pure combined system
|
|
532
|
-
else:
|
|
533
|
-
hab = 0.0
|
|
534
|
-
ha = hb = entropy_subsys(p, dims, sysa)
|
|
535
|
-
|
|
536
|
-
return ha + hb - hab
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
mutual_information = mutinf
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
def check_dims_and_indices(dims, *syss):
|
|
543
|
-
"""Make sure all indices found in the tuples ``syss`` are in
|
|
544
|
-
``range(len(dims))``.
|
|
545
|
-
"""
|
|
546
|
-
nsys = len(dims)
|
|
547
|
-
all_sys = sum(syss, ())
|
|
548
|
-
|
|
549
|
-
if not all(0 <= i < nsys for i in all_sys):
|
|
550
|
-
raise ValueError(
|
|
551
|
-
"Indices specified in `sysa` and `sysb` must be "
|
|
552
|
-
f"in range({nsys}) for dims {dims}."
|
|
553
|
-
)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
def mutinf_subsys(
|
|
557
|
-
psi_abc, dims, sysa, sysb, approx_thresh=2**13, **approx_opts
|
|
558
|
-
):
|
|
559
|
-
"""Calculate the mutual information of two subsystems of a pure state,
|
|
560
|
-
possibly using an approximate lanczos method for large subsytems.
|
|
561
|
-
|
|
562
|
-
Parameters
|
|
563
|
-
----------
|
|
564
|
-
psi_abc : vector
|
|
565
|
-
Tri-partite pure state.
|
|
566
|
-
dims : sequence of int
|
|
567
|
-
The sub dimensions of the state.
|
|
568
|
-
sysa : sequence of int
|
|
569
|
-
The index(es) of the subsystem(s) to consider part of 'A'.
|
|
570
|
-
sysb : sequence of int
|
|
571
|
-
The index(es) of the subsystem(s) to consider part of 'B'.
|
|
572
|
-
approx_thresh : int, optional
|
|
573
|
-
The size of subsystem at which to switch to the approximate lanczos
|
|
574
|
-
method. Set to ``None`` to never use the approximation.
|
|
575
|
-
approx_opts
|
|
576
|
-
Supplied to :func:`entropy_subsys_approx`, if used.
|
|
577
|
-
|
|
578
|
-
Returns
|
|
579
|
-
-------
|
|
580
|
-
float
|
|
581
|
-
The mutual information.
|
|
582
|
-
|
|
583
|
-
See Also
|
|
584
|
-
--------
|
|
585
|
-
mutinf, entropy_subsys, entropy_subsys_approx, logneg_subsys
|
|
586
|
-
"""
|
|
587
|
-
sysa, sysb = int2tup(sysa), int2tup(sysb)
|
|
588
|
-
|
|
589
|
-
check_dims_and_indices(dims, sysa, sysb)
|
|
590
|
-
|
|
591
|
-
sz_a = prod(d for i, d in enumerate(dims) if i in sysa)
|
|
592
|
-
sz_b = prod(d for i, d in enumerate(dims) if i in sysb)
|
|
593
|
-
sz_c = prod(dims) // (sz_a * sz_b)
|
|
594
|
-
|
|
595
|
-
kws = {"approx_thresh": approx_thresh, **approx_opts}
|
|
596
|
-
|
|
597
|
-
if sz_c == 1:
|
|
598
|
-
hab = 0.0
|
|
599
|
-
ha = hb = entropy_subsys(psi_abc, dims, sysa, **kws)
|
|
600
|
-
else:
|
|
601
|
-
hab = entropy_subsys(psi_abc, dims, sysa + sysb, **kws)
|
|
602
|
-
ha = entropy_subsys(psi_abc, dims, sysa, **kws)
|
|
603
|
-
hb = entropy_subsys(psi_abc, dims, sysb, **kws)
|
|
604
|
-
|
|
605
|
-
return hb + ha - hab
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
def schmidt_gap(psi_ab, dims, sysa):
|
|
609
|
-
"""Find the schmidt gap of the bipartition of ``psi_ab``. That is, the
|
|
610
|
-
difference between the two largest eigenvalues of the reduced density
|
|
611
|
-
operator.
|
|
612
|
-
|
|
613
|
-
Parameters
|
|
614
|
-
----------
|
|
615
|
-
psi_ab : vector
|
|
616
|
-
Bipartite state.
|
|
617
|
-
dims : sequence of int
|
|
618
|
-
The sub-dimensions of the state.
|
|
619
|
-
sysa : sequence of int
|
|
620
|
-
The indices of which dimensions to calculate the entropy for.
|
|
621
|
-
|
|
622
|
-
Returns
|
|
623
|
-
-------
|
|
624
|
-
float
|
|
625
|
-
"""
|
|
626
|
-
sysa = int2tup(sysa)
|
|
627
|
-
sz_a = prod(d for i, d in enumerate(dims) if i in sysa)
|
|
628
|
-
sz_b = prod(dims) // sz_a
|
|
629
|
-
|
|
630
|
-
# pure state
|
|
631
|
-
if sz_b == 1:
|
|
632
|
-
return 1.0
|
|
633
|
-
|
|
634
|
-
# also check if system b is smaller, since spectrum is same for both
|
|
635
|
-
if sz_b < sz_a:
|
|
636
|
-
# if so swap things around
|
|
637
|
-
sysb = [i for i in range(len(dims)) if i not in sysa]
|
|
638
|
-
sysa = sysb
|
|
639
|
-
|
|
640
|
-
rho_a = ptr(psi_ab, dims, sysa)
|
|
641
|
-
el = eigvalsh(rho_a, k=2, which="LM")
|
|
642
|
-
return abs(el[0] - el[1])
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
def tr_sqrt(A, rank=None):
|
|
646
|
-
"""Return the trace of the sqrt of a positive semidefinite operator."""
|
|
647
|
-
if rank is None:
|
|
648
|
-
el = eigvalsh(A, sort=False)
|
|
649
|
-
else:
|
|
650
|
-
el = eigvalsh(A, k=rank, which="LM", backend="AUTO")
|
|
651
|
-
return np.sum(np.sqrt(el[el > 0.0]))
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
tr_sqrt_subsys = gen_bipartite_spectral_fn(tr_sqrt, tr_sqrt_subsys_approx, 1.0)
|
|
655
|
-
"""Compute the trace sqrt of a subsystem, possibly using an approximate
|
|
656
|
-
lanczos method when the subsytem is big.
|
|
657
|
-
|
|
658
|
-
Parameters
|
|
659
|
-
----------
|
|
660
|
-
psi_ab : vector
|
|
661
|
-
Bipartite state.
|
|
662
|
-
dims : sequence of int
|
|
663
|
-
The sub-dimensions of the state.
|
|
664
|
-
sysa : sequence of int
|
|
665
|
-
The indices of which dimensions to calculate the trace sqrt for.
|
|
666
|
-
approx_thresh : int, optional
|
|
667
|
-
The size of sysa at which to switch to the approx method. Set to
|
|
668
|
-
``None`` to never use the approximation.
|
|
669
|
-
**approx_opts
|
|
670
|
-
Supplied to :func:`tr_sqrt_subsys_approx`, if used.
|
|
671
|
-
|
|
672
|
-
Returns
|
|
673
|
-
-------
|
|
674
|
-
float
|
|
675
|
-
The subsytem entropy.
|
|
676
|
-
|
|
677
|
-
See Also
|
|
678
|
-
--------
|
|
679
|
-
tr_sqrt, tr_sqrt_subsys_approx, partial_transpose_norm
|
|
680
|
-
"""
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
@ensure_qarray
|
|
684
|
-
def partial_transpose(p, dims=(2, 2), sysa=0):
|
|
685
|
-
"""Partial transpose of a density operator.
|
|
686
|
-
|
|
687
|
-
Parameters
|
|
688
|
-
----------
|
|
689
|
-
p : operator or vector
|
|
690
|
-
The state to partially transpose.
|
|
691
|
-
dims : tuple(int), optional
|
|
692
|
-
The internal dimensions of the state.
|
|
693
|
-
sysa : sequence of int
|
|
694
|
-
The indices of 'system A', everything else assumed to be 'system B'.
|
|
695
|
-
|
|
696
|
-
Returns
|
|
697
|
-
-------
|
|
698
|
-
operator
|
|
699
|
-
|
|
700
|
-
See Also
|
|
701
|
-
--------
|
|
702
|
-
logneg, negativity
|
|
703
|
-
"""
|
|
704
|
-
sysa = int2tup(sysa)
|
|
705
|
-
|
|
706
|
-
ndims = len(dims)
|
|
707
|
-
perm_ket_inds = []
|
|
708
|
-
perm_bra_inds = []
|
|
709
|
-
|
|
710
|
-
for i in range(ndims):
|
|
711
|
-
if i in sysa:
|
|
712
|
-
perm_ket_inds.append(i + ndims)
|
|
713
|
-
perm_bra_inds.append(i)
|
|
714
|
-
else:
|
|
715
|
-
perm_ket_inds.append(i)
|
|
716
|
-
perm_bra_inds.append(i + ndims)
|
|
717
|
-
|
|
718
|
-
return (
|
|
719
|
-
np.asarray(qu(p, "dop"))
|
|
720
|
-
.reshape((*dims, *dims))
|
|
721
|
-
.transpose((*perm_ket_inds, *perm_bra_inds))
|
|
722
|
-
.reshape((prod(dims), prod(dims)))
|
|
723
|
-
)
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
def partial_transpose_norm(p, dims, sysa):
|
|
727
|
-
"""Compute the norm of the partial transpose for (log)-negativity,
|
|
728
|
-
taking a shortcut (trace sqrt of reduced subsytem), when system is a
|
|
729
|
-
vector.
|
|
730
|
-
"""
|
|
731
|
-
sysa = int2tup(sysa)
|
|
732
|
-
|
|
733
|
-
# check for pure bipartition -> easier to calc
|
|
734
|
-
if isvec(p):
|
|
735
|
-
sz_a = prod(d for i, d in enumerate(dims) if i in sysa)
|
|
736
|
-
sz_b = prod(dims) // sz_a
|
|
737
|
-
|
|
738
|
-
# check if system b is smaller, since entropy is same for both a & b.
|
|
739
|
-
if sz_b < sz_a:
|
|
740
|
-
# if so swap things around
|
|
741
|
-
sysb = [i for i in range(len(dims)) if i not in sysa]
|
|
742
|
-
sysa = sysb
|
|
743
|
-
|
|
744
|
-
rhoa = ptr(p, dims, sysa)
|
|
745
|
-
return tr_sqrt(rhoa) ** 2
|
|
746
|
-
|
|
747
|
-
return norm_trace_dense(partial_transpose(p, dims, sysa), isherm=True)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
@zeroify
|
|
751
|
-
def logneg(p, dims=(2, 2), sysa=0):
|
|
752
|
-
"""Compute logarithmic negativity between two subsytems.
|
|
753
|
-
This is defined as log_2( | rho_{AB}^{T_B} | ). This only handles
|
|
754
|
-
bipartitions (including pure states efficiently), and will not trace
|
|
755
|
-
anything out.
|
|
756
|
-
|
|
757
|
-
Parameters
|
|
758
|
-
----------
|
|
759
|
-
p : ket vector or density operator
|
|
760
|
-
State to compute logarithmic negativity for.
|
|
761
|
-
dims : tuple(int), optional
|
|
762
|
-
The internal dimensions of ``p``.
|
|
763
|
-
sysa : int, optional
|
|
764
|
-
Index of the first subsystem, A, relative to ``dims``.
|
|
765
|
-
|
|
766
|
-
Returns
|
|
767
|
-
-------
|
|
768
|
-
float
|
|
769
|
-
|
|
770
|
-
See Also
|
|
771
|
-
--------
|
|
772
|
-
negativity, partial_transpose, logneg_subsys_approx
|
|
773
|
-
"""
|
|
774
|
-
return max(0.0, log2(partial_transpose_norm(p, dims, sysa)))
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
logarithmic_negativity = logneg
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
def logneg_subsys(
|
|
781
|
-
psi_abc, dims, sysa, sysb, approx_thresh=2**13, **approx_opts
|
|
782
|
-
):
|
|
783
|
-
"""Compute the logarithmic negativity between two subsystems of a pure
|
|
784
|
-
state, possibly using an approximate lanczos for large subsystems. Uses
|
|
785
|
-
a special method if the two subsystems form a bipartition of the state.
|
|
786
|
-
|
|
787
|
-
Parameters
|
|
788
|
-
----------
|
|
789
|
-
psi_abc : vector
|
|
790
|
-
Tri-partite pure state.
|
|
791
|
-
dims : sequence of int
|
|
792
|
-
The sub dimensions of the state.
|
|
793
|
-
sysa : sequence of int
|
|
794
|
-
The index(es) of the subsystem(s) to consider part of 'A'.
|
|
795
|
-
sysb : sequence of int
|
|
796
|
-
The index(es) of the subsystem(s) to consider part of 'B'.
|
|
797
|
-
approx_thresh : int, optional
|
|
798
|
-
The size of subsystem at which to switch to the approximate lanczos
|
|
799
|
-
method. Set to ``None`` to never use the approximation.
|
|
800
|
-
approx_opts
|
|
801
|
-
Supplied to :func:`~quimb.logneg_subsys_approx`, if used.
|
|
802
|
-
|
|
803
|
-
Returns
|
|
804
|
-
-------
|
|
805
|
-
float
|
|
806
|
-
The logarithmic negativity.
|
|
807
|
-
|
|
808
|
-
See Also
|
|
809
|
-
--------
|
|
810
|
-
logneg, mutinf_subsys, logneg_subsys_approx
|
|
811
|
-
"""
|
|
812
|
-
sysa, sysb = int2tup(sysa), int2tup(sysb)
|
|
813
|
-
|
|
814
|
-
check_dims_and_indices(dims, sysa, sysb)
|
|
815
|
-
|
|
816
|
-
sz_a = prod(d for i, d in enumerate(dims) if i in sysa)
|
|
817
|
-
sz_b = prod(d for i, d in enumerate(dims) if i in sysb)
|
|
818
|
-
sz_ab = sz_a * sz_b
|
|
819
|
-
sz_c = prod(dims) // sz_ab
|
|
820
|
-
|
|
821
|
-
# check for pure bipartition
|
|
822
|
-
if sz_c == 1:
|
|
823
|
-
psi_ab_ppt_norm = (
|
|
824
|
-
tr_sqrt_subsys(
|
|
825
|
-
psi_abc, dims, sysa, approx_thresh=approx_thresh, **approx_opts
|
|
826
|
-
)
|
|
827
|
-
** 2
|
|
828
|
-
)
|
|
829
|
-
return max(log2(psi_ab_ppt_norm), 0.0)
|
|
830
|
-
|
|
831
|
-
# check whether to use approx lanczos method
|
|
832
|
-
if (approx_thresh is not None) and (sz_ab >= approx_thresh):
|
|
833
|
-
return logneg_subsys_approx(psi_abc, dims, sysa, sysb, **approx_opts)
|
|
834
|
-
|
|
835
|
-
rho_ab = ptr(psi_abc, dims, sysa + sysb)
|
|
836
|
-
|
|
837
|
-
# need to adjust for new dimensions and indices
|
|
838
|
-
new_dims, new_sysa = [], []
|
|
839
|
-
new_inds = iter(range(len(dims)))
|
|
840
|
-
|
|
841
|
-
for i, d in enumerate(dims):
|
|
842
|
-
if i in sysa:
|
|
843
|
-
new_dims.append(d)
|
|
844
|
-
new_sysa.append(next(new_inds))
|
|
845
|
-
elif i in sysb:
|
|
846
|
-
new_dims.append(d)
|
|
847
|
-
next(new_inds) # don't need sysb
|
|
848
|
-
|
|
849
|
-
return logneg(rho_ab, new_dims, new_sysa)
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
def negativity(p, dims=(2, 2), sysa=0):
|
|
853
|
-
"""Compute negativity between two subsytems.
|
|
854
|
-
|
|
855
|
-
This is defined as (| rho_{AB}^{T_B} | - 1) / 2. If ``len(dims) > 2``,
|
|
856
|
-
then the non-target dimensions will be traced out first.
|
|
857
|
-
|
|
858
|
-
Parameters
|
|
859
|
-
----------
|
|
860
|
-
p : ket vector or density operator
|
|
861
|
-
State to compute logarithmic negativity for.
|
|
862
|
-
dims : tuple(int), optional
|
|
863
|
-
The internal dimensions of ``p``.
|
|
864
|
-
sysa : int, optional
|
|
865
|
-
Index of the first subsystem, A, relative to ``dims``.
|
|
866
|
-
|
|
867
|
-
Returns
|
|
868
|
-
-------
|
|
869
|
-
float
|
|
870
|
-
|
|
871
|
-
See Also
|
|
872
|
-
--------
|
|
873
|
-
logneg, partial_transpose, negativity_subsys_approx
|
|
874
|
-
"""
|
|
875
|
-
return max(0.0, (partial_transpose_norm(p, dims, sysa) - 1) / 2)
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
@zeroify
|
|
879
|
-
def concurrence(p, dims=(2, 2), sysa=0, sysb=1):
|
|
880
|
-
"""Concurrence of two-qubit state.
|
|
881
|
-
|
|
882
|
-
If ``len(dims) > 2``, then the non-target dimensions will be traced out
|
|
883
|
-
first.
|
|
884
|
-
|
|
885
|
-
Parameters
|
|
886
|
-
----------
|
|
887
|
-
p : ket vector or density operator
|
|
888
|
-
State to compute concurrence for.
|
|
889
|
-
dims : tuple(int), optional
|
|
890
|
-
The internal dimensions of ``p``.
|
|
891
|
-
sysa : int, optional
|
|
892
|
-
Index of the first subsystem, A, relative to ``dims``.
|
|
893
|
-
sysb : int, optional
|
|
894
|
-
Index of the first subsystem, B, relative to ``dims``.
|
|
895
|
-
|
|
896
|
-
Returns
|
|
897
|
-
-------
|
|
898
|
-
float
|
|
899
|
-
"""
|
|
900
|
-
if len(dims) > 2:
|
|
901
|
-
p = ptr(p, dims, (sysa, sysb))
|
|
902
|
-
|
|
903
|
-
Y = pauli("Y")
|
|
904
|
-
|
|
905
|
-
if isop(p):
|
|
906
|
-
pt = dot(kron(Y, Y), dot(p.conj(), kron(Y, Y)))
|
|
907
|
-
evals = (nla.eigvals(dot(p, pt)).real ** 2) ** 0.25
|
|
908
|
-
return max(0, 2 * np.max(evals) - np.sum(evals))
|
|
909
|
-
else:
|
|
910
|
-
pt = dot(kron(Y, Y), p.conj())
|
|
911
|
-
c = np.real(abs(dot(dag(p), pt))).item(0)
|
|
912
|
-
return max(0, c)
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
def one_way_classical_information(p_ab, prjs, precomp_func=False):
|
|
916
|
-
"""One way classical information for two qubit density operator.
|
|
917
|
-
|
|
918
|
-
Parameters
|
|
919
|
-
----------
|
|
920
|
-
p_ab : operator
|
|
921
|
-
State of two qubits
|
|
922
|
-
prjs : sequence of matrices
|
|
923
|
-
The POVMs.
|
|
924
|
-
precomp_func : bool, optional
|
|
925
|
-
Whether to return a pre-computed function, closed over the actual
|
|
926
|
-
state.
|
|
927
|
-
|
|
928
|
-
Returns
|
|
929
|
-
-------
|
|
930
|
-
float or callable
|
|
931
|
-
The one-way classical information or the function to compute it for
|
|
932
|
-
the given state which takes a set of POVMs as its single argument.
|
|
933
|
-
"""
|
|
934
|
-
p_a = ptr(p_ab, (2, 2), 0)
|
|
935
|
-
s_a = entropy(p_a)
|
|
936
|
-
|
|
937
|
-
def owci(prjs):
|
|
938
|
-
def gen_paj():
|
|
939
|
-
for prj in prjs:
|
|
940
|
-
p_ab_j = dot((eye(2) & prj), p_ab)
|
|
941
|
-
prob = tr(p_ab_j)
|
|
942
|
-
p_a_j = ptr(p_ab_j, (2, 2), 0) / prob
|
|
943
|
-
yield prob, p_a_j
|
|
944
|
-
|
|
945
|
-
return s_a - sum(p * entropy(rho) for p, rho in gen_paj())
|
|
946
|
-
|
|
947
|
-
return owci if precomp_func else owci(prjs)
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
@zeroify
|
|
951
|
-
def quantum_discord(
|
|
952
|
-
p,
|
|
953
|
-
dims=(2, 2),
|
|
954
|
-
sysa=0,
|
|
955
|
-
sysb=1,
|
|
956
|
-
method="COBYLA",
|
|
957
|
-
tol=1e-12,
|
|
958
|
-
maxiter=2**14,
|
|
959
|
-
):
|
|
960
|
-
"""Quantum Discord for two qubit density operator.
|
|
961
|
-
|
|
962
|
-
If ``len(dims) > 2``, then the non-target dimensions will be traced out
|
|
963
|
-
first.
|
|
964
|
-
|
|
965
|
-
Parameters
|
|
966
|
-
----------
|
|
967
|
-
p : ket vector or density operator
|
|
968
|
-
State to compute quantum discord for.
|
|
969
|
-
dims : tuple(int), optional
|
|
970
|
-
The internal dimensions of ``p``.
|
|
971
|
-
sysa : int, optional
|
|
972
|
-
Index of the first subsystem, A, relative to ``dims``.
|
|
973
|
-
sysb : int, optional
|
|
974
|
-
Index of the first subsystem, B, relative to ``dims``.
|
|
975
|
-
|
|
976
|
-
Returns
|
|
977
|
-
-------
|
|
978
|
-
float
|
|
979
|
-
"""
|
|
980
|
-
if len(dims) > 2:
|
|
981
|
-
p = ptr(p, dims, (sysa, sysb))
|
|
982
|
-
else:
|
|
983
|
-
p = qu(p, "dop")
|
|
984
|
-
iab = mutual_information(p)
|
|
985
|
-
owci = one_way_classical_information(p, None, precomp_func=True)
|
|
986
|
-
|
|
987
|
-
def trial_qd(a):
|
|
988
|
-
ax, ay, az = sin(a[0]) * cos(a[1]), sin(a[0]) * sin(a[1]), cos(a[0])
|
|
989
|
-
prja = bloch_state(ax, ay, az)
|
|
990
|
-
prjb = eye(2) - prja
|
|
991
|
-
return iab - owci((prja, prjb))
|
|
992
|
-
|
|
993
|
-
opt = minimize(
|
|
994
|
-
trial_qd,
|
|
995
|
-
(pi / 2, pi),
|
|
996
|
-
method=method,
|
|
997
|
-
bounds=((0, pi), (0, 2 * pi)),
|
|
998
|
-
tol=tol,
|
|
999
|
-
options=dict(
|
|
1000
|
-
maxiter=maxiter,
|
|
1001
|
-
),
|
|
1002
|
-
)
|
|
1003
|
-
if opt.success:
|
|
1004
|
-
return opt.fun
|
|
1005
|
-
else: # pragma: no cover
|
|
1006
|
-
raise ValueError(opt.message)
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
@zeroify
|
|
1010
|
-
def trace_distance(p1, p2):
|
|
1011
|
-
r"""Trace distance between two states:
|
|
1012
|
-
|
|
1013
|
-
.. math::
|
|
1014
|
-
|
|
1015
|
-
\delta(\rho, \sigma)
|
|
1016
|
-
=
|
|
1017
|
-
\frac{1}{2} \left| \rho - \sigma \right|_\mathrm{tr}
|
|
1018
|
-
|
|
1019
|
-
If two wavefunctions are supplied the trace distance will be computed via
|
|
1020
|
-
the more efficient expression:
|
|
1021
|
-
|
|
1022
|
-
.. math::
|
|
1023
|
-
|
|
1024
|
-
\delta(|\psi\rangle\langle\psi|, |\phi\rangle\langle\phi|)
|
|
1025
|
-
=
|
|
1026
|
-
\sqrt{1 - \langle \psi | \phi \rangle^2}
|
|
1027
|
-
|
|
1028
|
-
Parameters
|
|
1029
|
-
----------
|
|
1030
|
-
p1 : ket or density operator
|
|
1031
|
-
The first state.
|
|
1032
|
-
p2 : ket or density operator
|
|
1033
|
-
The second state.
|
|
1034
|
-
|
|
1035
|
-
Returns
|
|
1036
|
-
-------
|
|
1037
|
-
float
|
|
1038
|
-
"""
|
|
1039
|
-
p1_is_op, p2_is_op = isop(p1), isop(p2)
|
|
1040
|
-
|
|
1041
|
-
# If both are pure kets then special case
|
|
1042
|
-
if (not p1_is_op) and (not p2_is_op):
|
|
1043
|
-
return sqrt(1 - expec(p1, p2))
|
|
1044
|
-
|
|
1045
|
-
# Otherwise do full calculation
|
|
1046
|
-
return 0.5 * norm(
|
|
1047
|
-
(p1 if p1_is_op else dop(p1)) - (p2 if p2_is_op else dop(p2)), "tr"
|
|
1048
|
-
)
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
def cprint(psi, prec=6):
|
|
1052
|
-
"""Print a state in the computational basis.
|
|
1053
|
-
|
|
1054
|
-
Parameters
|
|
1055
|
-
----------
|
|
1056
|
-
psi : vector
|
|
1057
|
-
The pure state.
|
|
1058
|
-
prec : int, optional
|
|
1059
|
-
How many significant figures to print.
|
|
1060
|
-
|
|
1061
|
-
Examples
|
|
1062
|
-
--------
|
|
1063
|
-
|
|
1064
|
-
>>> cprint(rand_ket(2**2))
|
|
1065
|
-
(-0.0751069-0.192635j) |00>
|
|
1066
|
-
(0.156837-0.562213j) |01>
|
|
1067
|
-
(-0.307381+0.0291168j) |10>
|
|
1068
|
-
(-0.14302+0.707661j) |11>
|
|
1069
|
-
|
|
1070
|
-
>>> cprint(w_state(4))
|
|
1071
|
-
(0.5+0j) |0001>
|
|
1072
|
-
(0.5+0j) |0010>
|
|
1073
|
-
(0.5+0j) |0100>
|
|
1074
|
-
(0.5+0j) |1000>
|
|
1075
|
-
"""
|
|
1076
|
-
d = psi.shape[0]
|
|
1077
|
-
n = int(log2(d))
|
|
1078
|
-
if 2**n != d:
|
|
1079
|
-
raise ValueError("State is not factorizable into computational basis.")
|
|
1080
|
-
|
|
1081
|
-
coeff_str = "{:." + str(prec) + "}"
|
|
1082
|
-
|
|
1083
|
-
coeffs, cs = [], []
|
|
1084
|
-
for c in itertools.product("01", repeat=n):
|
|
1085
|
-
c = "".join(c)
|
|
1086
|
-
ix = int(c, 2)
|
|
1087
|
-
coeff = psi.item(ix)
|
|
1088
|
-
if coeff != 0.0:
|
|
1089
|
-
cs.append(c)
|
|
1090
|
-
coeffs.append(coeff_str.format(coeff))
|
|
1091
|
-
|
|
1092
|
-
max_str_len = max(map(len, coeffs))
|
|
1093
|
-
full_str = "{:>" + str(max_str_len) + "} |{}>"
|
|
1094
|
-
for coeff, c in zip(coeffs, cs):
|
|
1095
|
-
print(full_str.format(coeff, c))
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
def decomp(a, fn, fn_args, fn_d, nmlz_func, mode="p", tol=1e-3):
|
|
1099
|
-
"""Decomposes an operator via the Hilbert-schmidt inner product.
|
|
1100
|
-
|
|
1101
|
-
Can both print the decomposition or return it.
|
|
1102
|
-
|
|
1103
|
-
Parameters
|
|
1104
|
-
----------
|
|
1105
|
-
a : ket or density operator
|
|
1106
|
-
Operator to decompose.
|
|
1107
|
-
fn : callable
|
|
1108
|
-
Function to generate operator/state to decompose with.
|
|
1109
|
-
fn_args :
|
|
1110
|
-
Sequence of args whose permutations will be supplied to ``fn``.
|
|
1111
|
-
fn_d : int
|
|
1112
|
-
The dimension of the operators that `fn` produces.
|
|
1113
|
-
nmlz_func : callable
|
|
1114
|
-
Function to produce a normlization coefficient given the ``n``
|
|
1115
|
-
permutations of operators that will be produced.
|
|
1116
|
-
mode :
|
|
1117
|
-
String, include ``'p'`` to print the decomp and/or ``'c'`` to
|
|
1118
|
-
return OrderedDict, sorted by size of contribution.
|
|
1119
|
-
tol :
|
|
1120
|
-
Print operators with contirbution above ``tol`` only.
|
|
1121
|
-
|
|
1122
|
-
Returns
|
|
1123
|
-
-------
|
|
1124
|
-
None or OrderedDict:
|
|
1125
|
-
Pauli operator name and expec with ``a``.
|
|
1126
|
-
|
|
1127
|
-
See Also
|
|
1128
|
-
--------
|
|
1129
|
-
pauli_decomp, bell_decomp
|
|
1130
|
-
"""
|
|
1131
|
-
if isvec(a):
|
|
1132
|
-
a = qu(a, "dop") # make sure operator
|
|
1133
|
-
n = infer_size(a, base=fn_d)
|
|
1134
|
-
|
|
1135
|
-
# define generator for inner product to iterate over efficiently
|
|
1136
|
-
def calc_name_and_overlap():
|
|
1137
|
-
for perm in itertools.product(fn_args, repeat=n):
|
|
1138
|
-
op = kron(*(fn(x, sparse=True) for x in perm)) * nmlz_func(n)
|
|
1139
|
-
cff = expec(a, op)
|
|
1140
|
-
yield "".join(str(x) for x in perm), cff
|
|
1141
|
-
|
|
1142
|
-
names_cffs = list(calc_name_and_overlap())
|
|
1143
|
-
# sort by descending expec and turn into OrderedDict
|
|
1144
|
-
names_cffs.sort(key=lambda pair: -abs(pair[1]))
|
|
1145
|
-
names_cffs = collections.OrderedDict(names_cffs)
|
|
1146
|
-
# Print decomposition
|
|
1147
|
-
if "p" in mode:
|
|
1148
|
-
for x, cff in names_cffs.items():
|
|
1149
|
-
if abs(cff) < 0.01:
|
|
1150
|
-
break
|
|
1151
|
-
dps = int(round(0.5 - np.log10(1.001 * tol))) # decimal places
|
|
1152
|
-
print(x, "{: .{prec}f}".format(cff, prec=dps))
|
|
1153
|
-
# Return full calculation
|
|
1154
|
-
if "c" in mode:
|
|
1155
|
-
return names_cffs
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
pauli_decomp = functools.partial(
|
|
1159
|
-
decomp, fn=pauli, fn_args="IXYZ", fn_d=2, nmlz_func=lambda n: 2**-n
|
|
1160
|
-
)
|
|
1161
|
-
"""Decompose an operator into Paulis."""
|
|
1162
|
-
|
|
1163
|
-
bell_decomp = functools.partial(
|
|
1164
|
-
decomp, fn=bell_state, fn_args=(0, 1, 2, 3), fn_d=4, nmlz_func=lambda x: 1
|
|
1165
|
-
)
|
|
1166
|
-
"""Decompose an operator into bell-states."""
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
def correlation(
|
|
1170
|
-
p, A, B, sysa, sysb, dims=None, sparse=None, precomp_func=False
|
|
1171
|
-
):
|
|
1172
|
-
"""Calculate the correlation between two sites given two operators.
|
|
1173
|
-
|
|
1174
|
-
Parameters
|
|
1175
|
-
----------
|
|
1176
|
-
p : ket or density operator
|
|
1177
|
-
State to compute correlations for, ignored if ``precomp_func=True``.
|
|
1178
|
-
A : operator
|
|
1179
|
-
Operator to act on first subsystem.
|
|
1180
|
-
B : operator
|
|
1181
|
-
Operator to act on second subsystem.
|
|
1182
|
-
sysa : int
|
|
1183
|
-
Index of first subsystem.
|
|
1184
|
-
sysb : int
|
|
1185
|
-
Index of second subsystem.
|
|
1186
|
-
dims : tuple of int, optional
|
|
1187
|
-
Internal dimensions of ``p``, will be assumed to be qubits if not
|
|
1188
|
-
given.
|
|
1189
|
-
sparse : bool, optional
|
|
1190
|
-
Whether to compute with sparse operators.
|
|
1191
|
-
precomp_func : bool, optional
|
|
1192
|
-
Whether to return result or single arg function closed
|
|
1193
|
-
over precomputed operator.
|
|
1194
|
-
|
|
1195
|
-
Returns
|
|
1196
|
-
-------
|
|
1197
|
-
float or callable
|
|
1198
|
-
The correlation, <ab> - <a><b>, or a function to compute for an
|
|
1199
|
-
arbitrary state.
|
|
1200
|
-
"""
|
|
1201
|
-
if dims is None:
|
|
1202
|
-
sz_p = infer_size(p)
|
|
1203
|
-
dims = (2,) * sz_p
|
|
1204
|
-
if sparse is None:
|
|
1205
|
-
sparse = issparse(A) or issparse(B)
|
|
1206
|
-
|
|
1207
|
-
opts = {
|
|
1208
|
-
"sparse": sparse,
|
|
1209
|
-
"coo_build": sparse,
|
|
1210
|
-
"stype": "csr" if sparse else None,
|
|
1211
|
-
}
|
|
1212
|
-
opab = ikron((A, B), dims, (sysa, sysb), **opts)
|
|
1213
|
-
A = ikron((A,), dims, sysa, **opts)
|
|
1214
|
-
B = ikron((B,), dims, sysb, **opts)
|
|
1215
|
-
|
|
1216
|
-
@realify
|
|
1217
|
-
def corr(state):
|
|
1218
|
-
return expec(opab, state) - expec(A, state) * expec(B, state)
|
|
1219
|
-
|
|
1220
|
-
return corr if precomp_func else corr(p)
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
def pauli_correlations(
|
|
1224
|
-
p, ss=("xx", "yy", "zz"), sysa=0, sysb=1, sum_abs=False, precomp_func=False
|
|
1225
|
-
):
|
|
1226
|
-
"""Calculate the correlation between sites for a list of operator pairs
|
|
1227
|
-
choisen from the pauli matrices.
|
|
1228
|
-
|
|
1229
|
-
Parameters
|
|
1230
|
-
----------
|
|
1231
|
-
p : ket or density operator
|
|
1232
|
-
State to compute correlations for. Ignored if ``precomp_func=True``.
|
|
1233
|
-
ss : tuple or str
|
|
1234
|
-
List of pairs specifiying pauli matrices.
|
|
1235
|
-
sysa : int, optional
|
|
1236
|
-
Index of first site.
|
|
1237
|
-
sysb : int, optional
|
|
1238
|
-
Index of second site.
|
|
1239
|
-
sum_abs : bool, optional
|
|
1240
|
-
Whether to sum over the absolute values of each correlation
|
|
1241
|
-
precomp_func : bool, optional
|
|
1242
|
-
whether to return the values or a single argument
|
|
1243
|
-
function closed over precomputed operators etc.
|
|
1244
|
-
|
|
1245
|
-
Returns
|
|
1246
|
-
-------
|
|
1247
|
-
list of float, list of callable, float or callable
|
|
1248
|
-
Either the value(s) of each correlation or the function(s) to compute
|
|
1249
|
-
the correlations for an arbitrary state, depending on ``sum_abs`` and
|
|
1250
|
-
``precomp_func``.
|
|
1251
|
-
"""
|
|
1252
|
-
|
|
1253
|
-
def gen_corr_list():
|
|
1254
|
-
for s1, s2 in ss:
|
|
1255
|
-
yield correlation(
|
|
1256
|
-
p, pauli(s1), pauli(s2), sysa, sysb, precomp_func=precomp_func
|
|
1257
|
-
)
|
|
1258
|
-
|
|
1259
|
-
if sum_abs:
|
|
1260
|
-
if precomp_func:
|
|
1261
|
-
return lambda p: sum((abs(corr(p)) for corr in gen_corr_list()))
|
|
1262
|
-
|
|
1263
|
-
return sum((abs(corr) for corr in gen_corr_list()))
|
|
1264
|
-
|
|
1265
|
-
return tuple(gen_corr_list())
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
def ent_cross_matrix(
|
|
1269
|
-
p, sz_blc=1, ent_fn=logneg, calc_self_ent=True, upscale=False
|
|
1270
|
-
):
|
|
1271
|
-
"""Calculate the pair-wise function ent_fn between all sites or blocks
|
|
1272
|
-
of a state.
|
|
1273
|
-
|
|
1274
|
-
Parameters
|
|
1275
|
-
----------
|
|
1276
|
-
p : ket or density operator
|
|
1277
|
-
State.
|
|
1278
|
-
sz_blc : int
|
|
1279
|
-
Size of the blocks to partition the state into. If the number of
|
|
1280
|
-
individual sites is not a multiple of this then the final (smaller)
|
|
1281
|
-
block will be ignored.
|
|
1282
|
-
ent_fn : callable
|
|
1283
|
-
Bipartite function, notionally entanglement
|
|
1284
|
-
calc_self_ent : bool
|
|
1285
|
-
Whether to calculate the function for each site
|
|
1286
|
-
alone, purified. If the whole state is pure then this is the
|
|
1287
|
-
entanglement with the whole remaining system.
|
|
1288
|
-
upscale : bool, optional
|
|
1289
|
-
Whether, if sz_blc != 1, to upscale the results so that the output
|
|
1290
|
-
array is the same size as if it was.
|
|
1291
|
-
|
|
1292
|
-
Returns
|
|
1293
|
-
-------
|
|
1294
|
-
2D-array
|
|
1295
|
-
array of pairwise ent_fn results.
|
|
1296
|
-
"""
|
|
1297
|
-
|
|
1298
|
-
sz_p = infer_size(p)
|
|
1299
|
-
dims = (2,) * sz_p
|
|
1300
|
-
n = sz_p // sz_blc
|
|
1301
|
-
ents = np.empty((n, n))
|
|
1302
|
-
|
|
1303
|
-
ispure = isvec(p)
|
|
1304
|
-
if ispure and sz_blc * 2 == sz_p: # pure bipartition
|
|
1305
|
-
ent = ent_fn(p, dims=(2**sz_blc, 2**sz_blc)) / sz_blc
|
|
1306
|
-
ents[:, :] = ent
|
|
1307
|
-
if not calc_self_ent:
|
|
1308
|
-
for i in range(n):
|
|
1309
|
-
ents[i, i] = np.nan
|
|
1310
|
-
|
|
1311
|
-
else:
|
|
1312
|
-
# Range over pairwise blocks
|
|
1313
|
-
for i in range(0, sz_p - sz_blc + 1, sz_blc):
|
|
1314
|
-
for j in range(i, sz_p - sz_blc + 1, sz_blc):
|
|
1315
|
-
if i == j:
|
|
1316
|
-
if calc_self_ent:
|
|
1317
|
-
rhoa = ptr(p, dims, [i + b for b in range(sz_blc)])
|
|
1318
|
-
psiap = purify(rhoa)
|
|
1319
|
-
ent = (
|
|
1320
|
-
ent_fn(psiap, dims=(2**sz_blc, 2**sz_blc)) / sz_blc
|
|
1321
|
-
)
|
|
1322
|
-
else:
|
|
1323
|
-
ent = np.nan
|
|
1324
|
-
else:
|
|
1325
|
-
rhoab = ptr(
|
|
1326
|
-
p,
|
|
1327
|
-
dims,
|
|
1328
|
-
[i + b for b in range(sz_blc)]
|
|
1329
|
-
+ [j + b for b in range(sz_blc)],
|
|
1330
|
-
)
|
|
1331
|
-
ent = ent_fn(rhoab, dims=(2**sz_blc, 2**sz_blc)) / sz_blc
|
|
1332
|
-
ents[i // sz_blc, j // sz_blc] = ent
|
|
1333
|
-
ents[j // sz_blc, i // sz_blc] = ent
|
|
1334
|
-
|
|
1335
|
-
if upscale:
|
|
1336
|
-
up_ents = np.tile(np.nan, (sz_p, sz_p))
|
|
1337
|
-
|
|
1338
|
-
for i in range(n):
|
|
1339
|
-
for j in range(i, n):
|
|
1340
|
-
up_ents[
|
|
1341
|
-
i * sz_blc : (i + 1) * sz_blc,
|
|
1342
|
-
j * sz_blc : (j + 1) * sz_blc,
|
|
1343
|
-
] = ents[i, j]
|
|
1344
|
-
up_ents[
|
|
1345
|
-
j * sz_blc : (j + 1) * sz_blc,
|
|
1346
|
-
i * sz_blc : (i + 1) * sz_blc,
|
|
1347
|
-
] = ents[j, i]
|
|
1348
|
-
|
|
1349
|
-
ents = up_ents
|
|
1350
|
-
|
|
1351
|
-
return ents
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
def qid(
|
|
1355
|
-
p,
|
|
1356
|
-
dims,
|
|
1357
|
-
inds,
|
|
1358
|
-
precomp_func=False,
|
|
1359
|
-
sparse_comp=True,
|
|
1360
|
-
norm_func=norm,
|
|
1361
|
-
power=2,
|
|
1362
|
-
coeff=1,
|
|
1363
|
-
):
|
|
1364
|
-
# Check inputs
|
|
1365
|
-
inds = (inds,) if isinstance(inds, numbers.Number) else inds
|
|
1366
|
-
|
|
1367
|
-
# Construct operators
|
|
1368
|
-
ops_i = tuple(
|
|
1369
|
-
tuple(ikron(pauli(s), dims, ind, sparse=sparse_comp) for s in "xyz")
|
|
1370
|
-
for ind in inds
|
|
1371
|
-
)
|
|
1372
|
-
|
|
1373
|
-
# Define function closed over precomputed operators
|
|
1374
|
-
def qid_func(x):
|
|
1375
|
-
if isvec(x):
|
|
1376
|
-
x = dop(x)
|
|
1377
|
-
return tuple(
|
|
1378
|
-
sum(
|
|
1379
|
-
coeff * norm_func(dot(x, op) - dot(op, x)) ** power
|
|
1380
|
-
for op in ops
|
|
1381
|
-
)
|
|
1382
|
-
for ops in ops_i
|
|
1383
|
-
)
|
|
1384
|
-
|
|
1385
|
-
return qid_func if precomp_func else qid_func(p)
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
def is_degenerate(op, tol=1e-12):
|
|
1389
|
-
"""Check if operator has any degenerate eigenvalues, determined relative
|
|
1390
|
-
to mean spacing of all eigenvalues.
|
|
1391
|
-
|
|
1392
|
-
Parameters
|
|
1393
|
-
----------
|
|
1394
|
-
op : operator or 1d-array
|
|
1395
|
-
Operator or assumed eigenvalues to check degeneracy for.
|
|
1396
|
-
tol : float
|
|
1397
|
-
How much closer than evenly spaced the eigenvalue gap has to be
|
|
1398
|
-
to count as degenerate.
|
|
1399
|
-
|
|
1400
|
-
Returns
|
|
1401
|
-
-------
|
|
1402
|
-
n_dgen : int
|
|
1403
|
-
Number of degenerate eigenvalues.
|
|
1404
|
-
"""
|
|
1405
|
-
op = np.asarray(op)
|
|
1406
|
-
if op.ndim != 1:
|
|
1407
|
-
evals = eigvalsh(op)
|
|
1408
|
-
else:
|
|
1409
|
-
evals = op
|
|
1410
|
-
l_gaps = evals[1:] - evals[:-1]
|
|
1411
|
-
l_tol = tol * (evals[-1] - evals[0]) / op.shape[0]
|
|
1412
|
-
return np.count_nonzero(abs(l_gaps) < l_tol)
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
def is_eigenvector(x, A, tol=1e-14):
|
|
1416
|
-
"""Determines whether a vector is an eigenvector of an operator.
|
|
1417
|
-
|
|
1418
|
-
Parameters
|
|
1419
|
-
----------
|
|
1420
|
-
x : vector
|
|
1421
|
-
Vector to check.
|
|
1422
|
-
A : operator
|
|
1423
|
-
Matrix to check.
|
|
1424
|
-
tol : float, optional
|
|
1425
|
-
The variance must be smaller than this value.
|
|
1426
|
-
|
|
1427
|
-
Returns
|
|
1428
|
-
-------
|
|
1429
|
-
bool
|
|
1430
|
-
Whether ``A @ x = l * x`` for some scalar ``l``.
|
|
1431
|
-
"""
|
|
1432
|
-
mat_vec = dot(A, x)
|
|
1433
|
-
E = expec(x, mat_vec)
|
|
1434
|
-
E2 = expec(x, dot(A, mat_vec))
|
|
1435
|
-
return abs(E**2 - E2) < tol
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
@njit
|
|
1439
|
-
def page_entropy(sz_subsys, sz_total): # pragma: no cover
|
|
1440
|
-
"""Calculate the page entropy, i.e. expected entropy for a subsytem
|
|
1441
|
-
of a random state in Hilbert space.
|
|
1442
|
-
|
|
1443
|
-
Parameters
|
|
1444
|
-
----------
|
|
1445
|
-
sz_subsys : int
|
|
1446
|
-
Dimension of subsystem.
|
|
1447
|
-
sz_total : int
|
|
1448
|
-
Dimension of total system.
|
|
1449
|
-
|
|
1450
|
-
Returns
|
|
1451
|
-
-------
|
|
1452
|
-
s : float
|
|
1453
|
-
Entropy in bits.
|
|
1454
|
-
"""
|
|
1455
|
-
if sz_subsys > sz_total**0.5:
|
|
1456
|
-
sz_subsys = sz_total // sz_subsys
|
|
1457
|
-
|
|
1458
|
-
n = sz_total // sz_subsys
|
|
1459
|
-
|
|
1460
|
-
s = 0
|
|
1461
|
-
for k in range(n + 1, sz_total + 1):
|
|
1462
|
-
s += 1 / k
|
|
1463
|
-
s -= (sz_subsys - 1) / (2 * n)
|
|
1464
|
-
|
|
1465
|
-
# Normalize into bits of entropy
|
|
1466
|
-
return s / log(2)
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
def heisenberg_energy(L):
|
|
1470
|
-
"""Get the analytic isotropic heisenberg chain ground energy for length L.
|
|
1471
|
-
Useful for testing. Assumes the heisenberg model is defined with spin
|
|
1472
|
-
operators not pauli matrices (overall factor of 2 smaller). Taken from [1].
|
|
1473
|
-
|
|
1474
|
-
[1] Nickel, Bernie. "Scaling corrections to the ground state energy
|
|
1475
|
-
of the spin-½ isotropic anti-ferromagnetic Heisenberg chain." Journal of
|
|
1476
|
-
Physics Communications 1.5 (2017): 055021
|
|
1477
|
-
|
|
1478
|
-
Parameters
|
|
1479
|
-
----------
|
|
1480
|
-
L : int
|
|
1481
|
-
The length of the chain.
|
|
1482
|
-
|
|
1483
|
-
Returns
|
|
1484
|
-
-------
|
|
1485
|
-
energy : float
|
|
1486
|
-
The ground state energy.
|
|
1487
|
-
"""
|
|
1488
|
-
Einf = (0.5 - 2 * log(2)) * L
|
|
1489
|
-
Efinite = pi**2 / (6 * L)
|
|
1490
|
-
correction = 1 + 0.375 / log(L) ** 3
|
|
1491
|
-
return (Einf - Efinite * correction) / 2
|