Skip to content

Commit

Permalink
MAHOUT-2202 Testing Suite Template (plus MAHOUT-2207) (#442)
Browse files Browse the repository at this point in the history
* added testing folder

* successful pytest

* comparison test working

* added testing folder

* successful pytest

* comparison test working

* conform to cirq 1.3.0 python reqs

* MAHOUT-2202 add license headers

* Stable implementation of prototype method

* support for arbitrary backends

* fixes and tidying

* tidying readme

* another header

* another header fix
  • Loading branch information
tdnaugle authored Apr 29, 2024
1 parent 6e4b203 commit b5b76c0
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ qiskit = "^0.45.1"
qiskit-aer = "^0.13.2"
cirq = "^1.3.0"

[tool.poetry.dev-dependencies]
pytest = "^8.1.1"

[build-system]
requires = ["poetry-core"]
Expand Down
9 changes: 9 additions & 0 deletions qumat/qiskit_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,12 @@ def execute_circuit(circuit, backend, backend_config):
result = job.result()
return result.get_counts(transpiled_circuit)

# placeholder method for use in the testing suite
def get_final_state_vector(circuit, backend, backend_config):
simulator = qiskit.Aer.get_backend('statevector_simulator')

# Simulate the circuit
job = qiskit.execute(circuit, simulator)
result = job.result()

return result.get_statevector()
4 changes: 4 additions & 0 deletions qumat/qumat.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ def apply_pauli_z_gate(self, qubit_index):

def execute_circuit(self):
return self.backend_module.execute_circuit(self.circuit, self.backend, self.backend_config)

# placeholder method for use in the testing suite
def get_final_state_vector(self):
return self.backend_module.get_final_state_vector(self.circuit, self.backend, self.backend_config)
33 changes: 33 additions & 0 deletions testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
Apache Mahout Testing Suite
===========
For each backend supported in Apache Mahout, the testing suite executes an example circuit using the qumat implementation of the backend, and then executes the same example circuit using the backend's native implementation. The test then checks that the resulting final state vectors are the same.

The testing suite is run using pytest, which is installed by default using poetry. To run the tests, simply run
```
pytest
```

### How to add a test for a new backend
In order to add *my-new-backend* to the testing suite:
1. Create a file `testing/my-new-backend_helpers.py`
2. In `testing/my-new-backend_helpers.py`, create a function `get_qumat_backend_config` which returns the qumat backend config needed for the qumat implementation of my-new-backend
3. In `testing/my-new-backend_helpers.py`, create a function `get_native_example_final_state_vector` which builds and executes the example circuit using the native implementation of my-new-backend
4. In `testing/test_final_quantum_states.py`, add `"my-new-backend"` to `backends_to_test`

68 changes: 68 additions & 0 deletions testing/qiskit_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Import necessary Qiskit libraries
from qiskit import Aer, QuantumCircuit, execute
from qiskit.quantum_info import Statevector

def get_qumat_backend_config(test_type: str = "get_final_state_vector"):
if test_type == "get_final_state_vector":
print("success")
qumat_backend_config = {
'backend_name': 'qiskit',
'backend_options': {
'simulator_type': 'statevector_simulator',
'shots': 1
}
}
else:
pass

return qumat_backend_config


def get_native_example_final_state_vector(initial_state_ket_str: str = "000") -> Statevector:
n_qubits = len(initial_state_ket_str)
assert n_qubits == 3, print("The current qiskit native testing example is strictly 3 qubits")

simulator = Aer.get_backend('statevector_simulator')

qc = QuantumCircuit(n_qubits)

initial_state = Statevector.from_label(initial_state_ket_str)
qc.initialize(initial_state, range(n_qubits))

# Create entanglement between qubits 1 and 2
qc.h(1) # Apply Hadamard gate on qubit 1
qc.cx(1, 2) # Apply CNOT gate with qubit 1 as control and qubit 2 as target

# Prepare the state to be teleported on qubit 0
qc.h(0) # Apply Hadamard gate on qubit 0
qc.z(0) # Apply Pauli-Z gate on qubit 0

# Perform Bell measurement on qubits 0 and 1
qc.cx(0, 1) # Apply CNOT gate with qubit 0 as control and qubit 1 as target
qc.h(0) # Apply Hadamard gate on qubit 0

# Simulate the circuit
job = execute(qc, simulator)
result = job.result()

# Get the state vector
state_vector = result.get_statevector()

return state_vector
60 changes: 60 additions & 0 deletions testing/qumat_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import numpy as np
from functools import reduce
from qumat.qumat import QuMat

class BinaryString(str):
def __new__(cls, value):
if not all(char in ['0', '1'] for char in value):
raise ValueError("String contains characters other than '0' and '1'")
return str.__new__(cls, value)


def create_np_computational_basis_state(ket_str: BinaryString,
np_dtype: str = "complex128") -> np.array:
single_qubit_state_dict = {
"0": np.array([1, 0], dtype=np_dtype),
"1": np.array([0, 1], dtype=np_dtype)
}

single_qubit_vectors = map(single_qubit_state_dict.get, ket_str)
computational_basis_vector = reduce(np.kron, single_qubit_vectors)

return computational_basis_vector


def get_qumat_example_final_state_vector(backend_config: dict, initial_state_ket_str: BinaryString = "000"):
n_qubits = len(initial_state_ket_str)
assert n_qubits == 3, print("The current qumat testing example is strictly 3 qubits")

qumat_instance = QuMat(backend_config)

qumat_instance.create_empty_circuit(num_qubits=3)
initial_state = create_np_computational_basis_state(initial_state_ket_str)
qumat_instance.circuit.initialize(initial_state, range(n_qubits))

qumat_instance.apply_hadamard_gate(qubit_index=1)
qumat_instance.apply_cnot_gate(control_qubit_index=1, target_qubit_index=2)
qumat_instance.apply_hadamard_gate(qubit_index=0)
qumat_instance.apply_pauli_z_gate(qubit_index=0)
qumat_instance.apply_cnot_gate(control_qubit_index=0, target_qubit_index=1)
qumat_instance.apply_hadamard_gate(qubit_index=0)

state_vector = qumat_instance.get_final_state_vector()

return state_vector
37 changes: 37 additions & 0 deletions testing/test_final_quantum_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from qumat_helpers import get_qumat_example_final_state_vector
import numpy as np
from importlib import import_module

def test_final_state_vector():
# Specify initial computational basis state vector
initial_ket_str = "001"

backends_to_test = ["qiskit"]
for backend_name in backends_to_test:
backend_module = import_module(f"{backend_name}_helpers", package="qumat")
# use native implementation
native_example_vector = backend_module.get_native_example_final_state_vector(initial_ket_str)

# use qumat implementation
qumat_backend_config = backend_module.get_qumat_backend_config("get_final_state_vector")
qumat_example_vector = get_qumat_example_final_state_vector(qumat_backend_config, initial_ket_str)

# Compare final state vectors from qumat vs. native implementation
np.testing.assert_array_equal(qumat_example_vector, native_example_vector)

0 comments on commit b5b76c0

Please sign in to comment.