Skip to content

Commit

Permalink
improve the qubit_states function
Browse files Browse the repository at this point in the history
Support a more general way of definition:
- use a list or a string for zero/one/plus/minus state
- use a list of integers for defining arbitrary disentangled quantum states (up to a global phase).
  • Loading branch information
BoxiLi committed Sep 14, 2022
1 parent 50b7fb2 commit ba3157f
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 61 deletions.
92 changes: 71 additions & 21 deletions src/qutip_qip/qubits.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,86 @@
from numpy import sqrt


def qubit_states(N=1, states=[0]):
def qubit_states(states):
"""
Function to define initial state of the qubits.
Shortcut to generate disentangled qubit states.
Parameters
----------
N : Integer
Number of qubits in the register.
states : List
Initial state of each qubit.
states : list or str
- If a list consisting of ``0``, ``1``, ``"0"``, ``"1"``, ``"+"``
and ``"-"``, return the corresponding zero/one/plus/minus state.
- If a string consisting of ``0``, ``1``, ``+``, ``-``,
same as above.
- If a list of float or complex numbers,
each number is mapped to a state of the form
:math:`\\sqrt{1 - |a|^2} \\left|0\\right\\rangle + a |1\\rangle`,
where :math:`a` is the given number.
Returns
-------
qstates : Qobj
List of qubits.
quantum_states : :obj:`qutip.Qobj`
The generated qubit states.
Examples
--------
>>> from qutip_qip.qubits import qubit_states
>>> qubit_states([0, 0]) # doctest: +NORMALIZE_WHITESPACE
Quantum object: dims = [[2, 2], [1, 1]], shape = (4, 1), type = ket
Qobj data =
[[1.]
[0.]
[0.]
[0.]]
>>> qubit_states([1, "+"]) # doctest: +NORMALIZE_WHITESPACE
Quantum object: dims = [[2, 2], [1, 1]], shape =
(4, 1), type = ket
Qobj data =
[[0. ]
[0. ]
[0.70710678]
[0.70710678]]
>>> qubit_states("-") # doctest: +NORMALIZE_WHITESPACE
Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[ 0.70710678]
[-0.70710678]]
>>> qubit_states("1-") # doctest: +NORMALIZE_WHITESPACE
Quantum object: dims = [[2, 2], [1, 1]], shape =
(4, 1), type = ket
Qobj data =
[[ 0. ]
[ 0. ]
[ 0.70710678]
[-0.70710678]]
>>> import numpy as np
>>> qubit_states([1.j/np.sqrt(2)]) # doctest: +NORMALIZE_WHITESPACE
Quantum object: dims = [[2], [1]], shape = (2, 1), type = ket
Qobj data =
[[0.70710678+0.j ]
[0. +0.70710678j]]
"""
state_list = []
for i in range(N):
if N > len(states) and i >= len(states):
state_list.append(0)
states_map = {
0: qutip.basis(2, 0),
1: qutip.basis(2, 1),
"0": qutip.basis(2, 0),
"1": qutip.basis(2, 1),
"+": (qutip.basis(2, 0) + qutip.basis(2, 1)).unit(),
"-": (qutip.basis(2, 0) - qutip.basis(2, 1)).unit(),
}

states_list = []
for s in states:
if s in states_map:
states_list.append(states_map[s])
elif np.isscalar(s) and abs(s) <= 1:
states_list.append(
s * qutip.basis(2, 1)
+ np.sqrt(1 - abs(s) ** 2) * qutip.basis(2, 0)
)
else:
state_list.append(states[i])

return tensor(
[
alpha * basis(2, 1) + sqrt(1 - alpha**2) * basis(2, 0)
for alpha in state_list
]
)
raise TypeError(f"Invalid input {s}.")
return qutip.tensor(states_list)


def _find_reduced_indices(dims):
Expand Down Expand Up @@ -174,5 +224,5 @@ def expand_qubit_state(state, dims):
output = np.zeros([np.product(d) for d in full_dims], dtype=complex)
reduced_indices1 = reduced_indices if not state.isbra else zero_slice
reduced_indices2 = reduced_indices if not state.isket else zero_slice
output[reduced_indices1[:, np.newaxis], reduced_indices2] = state
output[reduced_indices1[:, np.newaxis], reduced_indices2] = state.full()
return qutip.Qobj(output, dims=full_dims)
8 changes: 4 additions & 4 deletions tests/test_optpulseprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def test_simple_hadamard(self):
qc, num_tslots=num_tslots, evo_time=evo_time, verbose=True)

# test run_state
rho0 = qubit_states(1, [0])
plus = (qubit_states(1, [0]) + qubit_states(1, [1])).unit()
rho0 = qubit_states([0])
plus = (qubit_states([0]) + qubit_states([1])).unit()
result = test.run_state(rho0)
assert_allclose(fidelity(result.states[-1], plus), 1, rtol=1.0e-6)

Expand Down Expand Up @@ -72,8 +72,8 @@ def test_multi_qubits(self):
qc = [tensor([identity(2), cnot()])]
test.load_circuit(qc, num_tslots=num_tslots,
evo_time=evo_time, min_fid_err=1.0e-6)
rho0 = qubit_states(3, [1, 1, 1])
rho1 = qubit_states(3, [1, 1, 0])
rho0 = qubit_states([1, 1, 1])
rho1 = qubit_states([1, 1, 0])
result = test.run_state(
rho0, options=SolverOptions(store_states=True))
assert_(fidelity(result.states[-1], rho1) > 1-1.0e-6)
Expand Down
18 changes: 5 additions & 13 deletions tests/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def testNoise(self):
Test for Processor with noise
"""
# setup and fidelity without noise
init_state = qubit_states(2, [0, 0, 0, 0])
init_state = qubit_states([0, 0])
tlist = np.array([0., np.pi/2.])
a = destroy(2)
proc = Processor(N=2)
Expand All @@ -285,24 +285,16 @@ def testNoise(self):
proc.set_all_tlist(tlist)
result = proc.run_state(init_state=init_state)
assert_allclose(
fidelity(
result.states[-1],
qubit_states(2, [0, 1, 0, 0])
),
1, rtol=1.e-7
)
fidelity(result.states[-1], qubit_states([0, 1])),
1, rtol=1.e-7)

# decoherence noise
dec_noise = DecoherenceNoise([0.25*a], targets=1)
proc.add_noise(dec_noise)
result = proc.run_state(init_state=init_state)
assert_allclose(
fidelity(
result.states[-1],
qubit_states(2, [0, 1, 0, 0])
),
0.981852, rtol=1.e-3
)
fidelity(result.states[-1], qubit_states([0, 1])),
0.981852, rtol=1.e-3)

# white random noise
proc.model._noise = []
Expand Down
41 changes: 18 additions & 23 deletions tests/test_qubits.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,26 @@ def testQubitStates(self):
"""
Tests the qubit_states function.
"""
psi0_a = basis(2, 0)
psi0_b = qubit_states()
assert_(psi0_a == psi0_b)

psi1_a = basis(2, 1)
psi1_b = qubit_states(states=[1])
assert_(psi1_a == psi1_b)

psi01_a = tensor(psi0_a, psi1_a)
psi01_b = qubit_states(N=2, states=[0, 1])
assert_(psi01_a == psi01_b)
assert(qubit_states([0]) == basis(2, 0))
assert(qubit_states([1]) == basis(2, 1))
assert(qubit_states([0, 1]) == tensor(basis(2, 0), basis(2, 1)))
plus = (basis(2, 0) + basis(2, 1)).unit()
minus = (basis(2, 0) - basis(2, 1)).unit()
assert(qubit_states("-+") == tensor(minus, plus))
assert(qubit_states("0+") == tensor(basis(2, 0), plus))
assert(qubit_states("+11") == tensor(plus, basis(2, 1), basis(2, 1)))
assert(
qubit_states([1.j/np.sqrt(2), 1.]) ==
tensor(qutip.Qobj([[1/np.sqrt(2)], [1.j/np.sqrt(2)]]), basis(2, 1))
)

@pytest.mark.parametrize(
"state, full_dims",
[
(qutip.rand_dm(18, dims=[[3, 2, 3], [3, 2, 3]]), [3, 2, 3]),
(qutip.rand_ket(18, dims=[[2, 3, 3], [1, 1, 1]]), [2, 3, 3]),
(qutip.rand_dm([3, 2, 3]), [3, 2, 3]),
(qutip.rand_ket([2, 3, 3]), [2, 3, 3]),
(
qutip.Qobj(
qutip.rand_ket(18).full().transpose(),
dims=[[1, 1, 1], [3, 2, 3]],
),
qutip.rand_ket([3, 2, 3]).dag(),
[3, 2, 3],
),
],
Expand Down Expand Up @@ -68,13 +66,10 @@ def test_state_truncation(self, state, full_dims):
@pytest.mark.parametrize(
"state, full_dims",
[
(qutip.rand_dm(8, dims=[[2, 2, 2], [2, 2, 2]]), [3, 2, 3]),
(qutip.rand_ket(8, dims=[[2, 2, 2], [1, 1, 1]]), [2, 3, 3]),
(qutip.rand_dm([2, 2, 2]), [3, 2, 3]),
(qutip.rand_ket([2, 2, 2]), [2, 3, 3]),
(
qutip.Qobj(
qutip.rand_ket(8).full().transpose(),
dims=[[1, 1, 1], [2, 2, 2]],
),
qutip.rand_ket([2, 2, 2]).dag(),
[3, 2, 3],
),
],
Expand Down

0 comments on commit ba3157f

Please sign in to comment.