Skip to content

Commit 5139dde

Browse files
authored
Allow Pauli propagation to accept circuit ansatz (#60)
1 parent b425753 commit 5139dde

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ This repository is still in development: new functionality is being added and th
190190
| 30 | Convert a graph with partial assignment to a hamiltonian | #53 |
191191
| 31 | Refactor linear angle interpolation (increase QAOA depth) | #55 |
192192
| 32 | Standardize the inheritance of evaluators and trainers | #57 |
193+
| 33 | Allow Pauli propagation to accept a custom circuit ansatz | #60 |
193194

194195
## IBM Public Repository Disclosure
195196

qaoa_training_pipeline/evaluation/pauli_propagation.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,16 @@ def evaluate(
139139
) -> float:
140140
"""Evaluate the QAOA circuit parameters."""
141141

142-
if ansatz_circuit is not None:
143-
raise NotImplementedError(
144-
f"Custom Ansatz circuits are currently not supported in {self.__class__.__name__}."
145-
)
142+
if ansatz_circuit is None:
143+
ansatz_circuit = cost_op
144+
else:
145+
if not isinstance(ansatz_circuit, SparsePauliOp):
146+
raise NotImplementedError(
147+
"Only ansatz_circuit specified by a sparse Pauli operator is supported."
148+
)
146149

147150
circuit = qaoa_ansatz(
148-
cost_op,
151+
ansatz_circuit,
149152
reps=len(params) // 2,
150153
initial_state=initial_state,
151154
mixer_operator=mixer, # type: ignore

qaoa_training_pipeline/training/param_result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __init__(
4646
"system": platform.system(),
4747
"processor": platform.processor(),
4848
"platform": platform.platform(),
49-
"qaoa_training_pipeline_version": 32,
49+
"qaoa_training_pipeline_version": 33,
5050
}
5151

5252
# Convert, e.g., np.float to float

test/evaluation/test_paulipropagation.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99
"""Statevector-based QAOA evaluator tests."""
1010

1111
from unittest import TestCase
12+
import numpy as np
1213

14+
from qiskit import transpile
15+
from qiskit.circuit.library import qaoa_ansatz
1316
from qiskit.quantum_info import SparsePauliOp
1417
from ddt import data, ddt, unpack
1518

19+
from qiskit_aer import Aer
20+
1621
from qaoa_training_pipeline.evaluation.statevector_evaluator import StatevectorEvaluator
1722
from qaoa_training_pipeline.evaluation.pauli_propagation import PPEvaluator
1823
from qaoa_training_pipeline.training.scipy_trainer import ScipyTrainer
@@ -35,6 +40,29 @@ def setUp(self):
3540
pp_kwargs = dict(max_weight=9, min_abs_coeff=1e-5)
3641
self.evaluator = PPEvaluator(pp_kwargs)
3742
self.sv_evaluator = StatevectorEvaluator()
43+
self.simulator = Aer.get_backend("aer_simulator_statevector")
44+
45+
def qiskit_circuit_simulation(self, cost_op, beta, gamma, circ_op=None):
46+
"""This is the baseline simulation based on Qiskit."""
47+
circ_op = circ_op or cost_op
48+
49+
depth_one_qaoa = qaoa_ansatz(
50+
circ_op,
51+
reps=1,
52+
).decompose()
53+
54+
# Printing parameters gives [ParameterVectorElement(β[0]), ParameterVectorElement(γ[0])]
55+
depth_one_qaoa.assign_parameters([beta, gamma], inplace=True)
56+
depth_one_qaoa = transpile(depth_one_qaoa, basis_gates=["cx", "sx", "x", "rz"])
57+
depth_one_qaoa.save_statevector()
58+
59+
res = self.simulator.run(depth_one_qaoa).result()
60+
state = res.get_statevector()
61+
62+
cost_mat = np.diag(cost_op.to_matrix())
63+
64+
nqubits = cost_op.num_qubits
65+
return np.real(sum(state[i].conj() * cost_mat[i] * state[i] for i in range(2**nqubits)))
3866

3967
@data(*TEST_CASES)
4068
@unpack
@@ -72,3 +100,22 @@ def test_from_none_config(self):
72100
evaluator = PPEvaluator.from_config(init_kwargs)
73101

74102
self.assertTrue(isinstance(evaluator, PPEvaluator))
103+
104+
@data((1, 1), (0.1234, -0.56), (0.25, 0.5), (0.5, 0.25))
105+
@unpack
106+
def test_sparse_ansatz(self, beta, gamma):
107+
"""Test the case where the ansatz structure does not match the graph."""
108+
109+
cost_op = SparsePauliOp.from_list([("IIZZ", 1.0), ("ZZII", 1.0), ("ZIIZ", 1.0)])
110+
circ_op = SparsePauliOp.from_list([("IIZZ", 1.0), ("ZZII", 1.0)])
111+
112+
baseline = self.qiskit_circuit_simulation(cost_op, beta, gamma, circ_op)
113+
114+
energy = self.evaluator.evaluate(cost_op, [beta, gamma], ansatz_circuit=circ_op)
115+
energy2 = self.evaluator.evaluate(cost_op, [beta, gamma])
116+
117+
# Ensure circuit simulation and efficient depth-one match.
118+
self.assertAlmostEqual(energy, baseline, places=6)
119+
120+
# Ensure efficient depth-one gives different results if the circuit structure is omitted.
121+
self.assertTrue(abs(energy2 - energy) > 0.02)

0 commit comments

Comments
 (0)