For the moment we only test single-qubit and controlled single-qubit
gates, as these gates can be easily expressed on the full Hilbert
space. A single qubit gate is just a tensor product, and a
controlled single-qubit gate is the sum of 2 tensor products.
The only real room for error when implementing gate application
will be in the ordering of the qubits, and these two tests are
already sensitive enough to catch this. Nevertheless it will
be useful later to add more tests, probably by building larger
gates by combining single-qubit and controlled single-qubit gates.
... | ... |
@@ -144,3 +144,42 @@ def test_deutch(): |
144 | 144 |
|
145 | 145 |
def test_swap(): |
146 | 146 |
assert np.all(qsim.gate.swap @ qsim.gate.swap == np.identity(4)) |
147 |
+ |
|
148 |
+ |
|
149 |
+@given(single_qubit_gates, n_qubits.flatmap(ket), n_qubits.flatmap(select_n_qubits(1))) |
|
150 |
+def test_applying_single_gates(gate, state, selected): |
|
151 |
+ qubit, = selected |
|
152 |
+ n_qubits = state.shape[0].bit_length() - 1 |
|
153 |
+ parts = [np.identity(2)] * n_qubits |
|
154 |
+ parts[qubit] = gate |
|
155 |
+ big_gate = product_gate(parts) |
|
156 |
+ |
|
157 |
+ should_be = big_gate @ state |
|
158 |
+ state = qsim.gate.apply(gate, [qubit], state) |
|
159 |
+ |
|
160 |
+ assert np.allclose(state, should_be) |
|
161 |
+ |
|
162 |
+ |
|
163 |
+@given( |
|
164 |
+ single_qubit_gates, |
|
165 |
+ n_qubits.filter(lambda n: n > 1).flatmap(ket), |
|
166 |
+ n_qubits.filter(lambda n: n > 1).flatmap(select_n_qubits(2)), |
|
167 |
+) |
|
168 |
+def test_applying_controlled_single_qubit_gates(gate, state, selected): |
|
169 |
+ control, qubit = selected |
|
170 |
+ n_qubits = state.shape[0].bit_length() - 1 |
|
171 |
+ # When control qubit is |0⟩ the controlled gate acts like the identity on the other qubit |
|
172 |
+ parts_zero = [np.identity(2)] * n_qubits |
|
173 |
+ parts_zero[control] = project_zero |
|
174 |
+ parts_zero[qubit] = np.identity(2) |
|
175 |
+ # When control qubit is |1⟩ the controlled gate acts like the original gate on the other qubit |
|
176 |
+ parts_one = [np.identity(2)] * n_qubits |
|
177 |
+ parts_one[control] = project_one |
|
178 |
+ parts_one[qubit] = gate |
|
179 |
+ # The total controlled gate is then the sum of these 2 product gates |
|
180 |
+ big_gate = product_gate(parts_zero) + product_gate(parts_one) |
|
181 |
+ |
|
182 |
+ should_be = big_gate @ state |
|
183 |
+ state = qsim.gate.apply(qsim.gate.controlled(gate), [control, qubit], state) |
|
184 |
+ |
|
185 |
+ assert np.allclose(state, should_be) |