basic¶
references:
- doi-link Pure-state tomography with the expectation value of Pauli operators
- arxiv-link A Variational Approach to Unique Determinedness in Pure-state Tomography
Measurement scheme $A$ for quantum state tomography is a collection of Hermitian operators
$$A=\{A_0=I,A_1,\cdots, A_{n-1}\}\subset\mathrm{Herm}^d$$
where $\mathrm{Herm}^d$ denotes the set of d-by-d Hermitian operators. We fix the first measurement operator $A_0=I$ to be the identity operator. The measurement vector for a density matrix $\rho$ is given by trace operation
$$M_A(\rho)=\left(\mathrm{Tr}(A_0\rho)=1, \mathrm{Tr}(A_1\rho),\cdots,\mathrm{Tr}(A_{n-1}\rho)\right)\in\mathbb{R}^n.$$
Two kinds of unique determinedness (UD) are considered in this tutorial:
UDA: A measurement scheme $A$ is said to be UDA if, for any pure state $|\psi\rangle$, the measurement vector $M_A(|\psi\rangle\langle\psi|)$ uniquely determines the state $|\psi\rangle$ among all density matrices.
UDP: A measurement scheme $A$ is said to be UDA if, for any pure state $|\psi\rangle$, the measurement vector $M_A(|\psi\rangle\langle\psi|)$ uniquely determines the state $|\psi\rangle$ among all pure states.
Apparently,
- if measurement scheme $A$ is UDA, then it's UDP.
- if measurement scheme $A$ is UD, then appending any Hermitian operator to $A$, e.g. $A\cup \{O\}$ , will still be UD.
- Pauli measurement $A=\{X,Y,Z\}$ is UDA for qubit state tomography.
In this tutorial, we will show how to check UDA and UDP for a given measurement scheme $A$.
import numpy as np
try:
import numqi
except ImportError:
%pip install numqi
import numqi
2-qubits UD with Pauli measurements¶
For 2-qubits, the following measurement scheme is UDA:
$$ A=\{II, IX, IY, IZ, XI, YX, YY, YZ, ZX, ZY, ZZ\}. $$
We can generate some random pure states and check if the measurement vector $M_A(|\psi\rangle\langle\psi|)$ uniquely determines the state $|\psi\rangle$ among all density matrices.
pauli_str = 'II IX IY IZ XI YX YY YZ ZX ZY ZZ'.split(' ')
pauli_matrix = np.stack([numqi.gate.PauliOperator.from_str(x).full_matrix for x in pauli_str])
random_psi = numqi.random.rand_haar_state(dim=4)
measurement_vector = ((pauli_matrix @ random_psi) @ random_psi.conj()).real
to recover the state from the measurement vector $b=M_A(|\psi\rangle\langle\psi|)$, we can use the following optimization problem:
$$\begin{align*}\min_{\rho}&\left\Vert M_{A}(\rho)-b\right\Vert _{2}^{2}\\s.t.&\begin{cases} \rho\succeq0\\ \mathrm{Tr}(\rho)=1 \end{cases}\end{align*}$$
The above cone optimization problem can be solved by cvxpy
package.
dm_restore,error = numqi.unique_determine.density_matrix_recovery_SDP(pauli_matrix, measurement_vector)
EVL,EVC = np.linalg.eigh(dm_restore)
psi_restore = EVC[:,-1]
fidelity = np.abs(np.vdot(psi_restore,random_psi))**2
print('recovery error:', error)
print('eigenvalue:', EVL) #should be [0,0,0,1], which means it's a pure state
print('Fidelity:', fidelity)
recovery error: 6.862631281164131e-07 eigenvalue: [-1.05749199e-07 1.63266808e-07 2.75591427e-07 9.99999666e-01] Fidelity: 0.9999999999998381
As we can see, the restored state is pure and close to the original state. As UDA implies UDP, we can also recover the state pretending it's a UDP measurement scheme by solving the following optimization problem.
$$\begin{align*}\min_{|\phi\rangle}&\left\Vert M_{A}(|\phi\rangle\langle\phi|)-b\right\Vert _{2}^{2}\\s.t.&\left\Vert |\phi\rangle\right\Vert _{2}=1\end{align*}$$
As it's not convex optimization, we use gradient descent from scipy.optimize.minimize
with 'L-BFGS-B'
optimizer.
model = numqi.unique_determine.FindStateWithOpModel(pauli_matrix, use_dm=False)
model.set_expectation(measurement_vector)
theta_optim = numqi.optimize.minimize(model, num_repeat=3, tol=1e-10)
psi_restore = model.state.numpy().copy()
fidelity = np.abs(np.vdot(psi_restore,random_psi))**2
print('error:', theta_optim.fun)
print('Fidelity:', fidelity)
[round=0] min(f)=1.3643656792160902e-17, current(f)=1.3643656792160902e-17 [round=1] min(f)=5.678691090700986e-18, current(f)=5.678691090700986e-18 [round=2] min(f)=5.678691090700986e-18, current(f)=3.506242821889506e-17 error: 5.678691090700986e-18 Fidelity: 1.0000000000000004
As we can see, the restored state has almost perfect fidelity with the original state.
Usually, the number of operators needed for full tomography is more than UDA, and more than UDP. UD measurement scheme with less operators is more efficient for state tomography.
n-qubits UD with Pauli measurements¶
The optimal number of UD with Pauli measurements for 3-qubits is 31. numqi
package stores some UD schemes for 2/3/4/5
-qubits.
num_qubit = 3
size_to_pauli_index = numqi.unique_determine.load_pauli_ud_example(num_qubit, tag_group_by_size=True)
size_list = list(size_to_pauli_index.keys())
print('size:', size_list)
pauli_str = ' '.join([numqi.gate.pauli_index_to_str(x, num_qubit) for x in size_to_pauli_index[size_list[0]][0]])
print(f'[size={size_list[0]}]:', pauli_str)
size: [31, 32, 34] [size=31]: III IIX IIY IIZ IXI IXX IXY IYX IYY IZX IZY XIZ XXX XXY XYI XYX XYY XZI YIZ YXI YYI YZX YZY YZZ ZIX ZIY ZIZ ZYX ZYY ZYZ ZZZ
Check UDA and UDP¶
(TODO)
eigenvalue structures for UDA: 1 negative eigenvalue, d-1
positive eigenvalues
eigenvalue structures for UDP: 1 negative eigenvalue, d-1
positive eigenvalues
Example: for optimal UDA measurements scheme, removing any operator will make it not UDA.
num_qubit = 2
pauli_index = numqi.unique_determine.load_pauli_ud_example(num_qubit, tag_group_by_size=True)[11][0]
UDA_matrix = np.stack([numqi.gate.PauliOperator.from_index(x, num_qubit).full_matrix for x in pauli_index])
np_rng = np.random.default_rng()
tmp0 = set(pauli_index)-{pauli_index[np_rng.integers(len(pauli_index))]}
non_UDA_matrix = np.stack([numqi.gate.PauliOperator.from_index(x, num_qubit).full_matrix for x in tmp0])
kwargs = dict(num_repeat=10, converge_tol=1e-10, early_stop_threshold=0.01, dtype='float64')
tag_is_uda0,loss0 = numqi.unique_determine.check_UD('uda', UDA_matrix, **kwargs)
tag_is_uda1,loss1 = numqi.unique_determine.check_UD('uda', non_UDA_matrix, **kwargs)
print('optimal UDA Pauli: ', tag_is_uda0, loss0)
print('optimal UDA Pauli drops one operator:', tag_is_uda1, loss1)
optimal UDA Pauli: True 1.3333333378056982 optimal UDA Pauli drops one operator: False 1.6814753205195241e-09
Example: 4PBs scheme is UDP, but 3PBs scheme is not UDP.
dim = 4
alpha = np.pi/dim
mat_3pb = numqi.unique_determine.get_chebshev_orthonormal(dim, alpha, with_computational_basis=False)[:(-dim)]
mat_4pb = numqi.unique_determine.get_chebshev_orthonormal(dim, alpha, with_computational_basis=False)
kwargs = dict(num_repeat=5, early_stop_threshold=1e-10, converge_tol=1e-12, dtype='float64')
tag_is_udp0,loss0 = numqi.unique_determine.check_UD('udp', mat_3pb, **kwargs)
tag_is_udp1,loss1 = numqi.unique_determine.check_UD('udp', mat_4pb, **kwargs)
print('3PB:', tag_is_udp0, loss0)
print('4PB:', tag_is_udp1, loss1)
3PB: False 2.3594680178610756e-14 4PB: True 0.00012655244630332548