-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug]: Partial trace of state vecrtor backend #73
Comments
Just to clarify: this indeed is something to be taken care of, but |
I've written a draft improvement plan for partial tracing a separable state below.
In my environment, the execution time has improved by 3 orders of magnitude when the number of qubits(nqubit) is equal to 10. |
The above method is not yet complete because the k-th qubit can be in the |0> state(and, not the |
This is completed by the follwoings. def truncate_one_qubit(self, qarg):
"""truncate one qubit
Args:
qarg (int): qubit index
"""
# extract |***0_{qarg}***> components if not zero else |***1_{qarg}***>
psi = self.psi.take(indices=0, axis=qarg)
self.psi = psi if psi[(0,) * psi.ndim] != 0.0 else self.psi.take(indices=1, axis=qarg)
self.normalize() The performance can be compared by the followings. # %%
import numpy as np
from graphix.sim.statevec import Statevec, meas_op
import time
def truncate_one_qubit_old(k, sv):
n = len(sv.dims())
taken = np.zeros(2**(n - 1), dtype=np.complex128)
state = sv.flatten()
for i in range(2**k):
for j in range(2**(n - k - 1)):
taken[i * 2**(n - k - 1) + j] = state[i * 2**(n - k) + j]
norm = taken.dot(taken.flatten().conjugate())
taken = taken / norm**0.5
return taken
def truncate_one_qubit(k, sv):
# extract |***0_{qarg}***> components if not zero else |***1_{qarg}***>
psi = sv.psi.take(indices=0, axis=k)
psi = psi if psi[(0,) * psi.ndim] != 0.0 else sv.take(indices=1, axis=k).psi
norm = psi.flatten().dot(psi.flatten().conjugate())
psi = psi / norm**0.5
return psi.flatten()
# %%
# prepare a non-separable state
k = 3
n = 10
statevec = Statevec(nqubit=n)
for i in range(n):
statevec.entangle((i, (i + 1) % n))
# print(statevec.flatten())
# %%
# measure qubit k
m_op = meas_op(np.pi / 5)
statevec.evolve(m_op, [k])
# print(statevec.flatten())
# %%
# discard qubit 0 (old)
start = time.perf_counter()
reduced = truncate_one_qubit_old(k, statevec)
end = time.perf_counter()
print("time(old method)", end - start)
# print(reduced)
# %%
# discard qubit 0 (new)
start = time.perf_counter()
reduced2 = truncate_one_qubit(k, statevec)
end = time.perf_counter()
print("time(new method)", end - start)
# print(reduced2)
# %%
# reference
start = time.perf_counter()
statevec.ptrace([k])
end = time.perf_counter()
print("time(ptrace)", end - start)
# print(statevec.flatten())
# %%
# check inner product
inner_product = reduced.dot(statevec.flatten().conjugate())
print(np.abs(inner_product))
inner_product = reduced2.dot(statevec.flatten().conjugate())
print(np.abs(inner_product)) In my environment, the new version sometimes slower than the old |
@nabe98 also what do you mean by below? is it sometimes slower somehow?
|
@nabe98 Thank you!
|
To make a comparison in the pattern simulator, we need to modify the 'measure' method which is tailored for the |
Describe the bug
The partial trace of a state vector is generally expected to return a density matrix, but
Statevec.ptrace
currently returns a state vector. This is an incorrect operation for non-separable quantum states, such as a Bell state.To Reproduce
You can check the above behavior with the following code.
Expected behavior
The
Statevec.ptrace
should return a density matrix. A code converting a reduced density matrix into a state vector should check its purity before conversion.Environment (please complete the following information):
Additional context
N/A
The text was updated successfully, but these errors were encountered: