Ansatz generation
qiskit_addon_aqc_tensor.ansatz_generation
Tools for generating ansatz circuits.
AnsatzBlock | Ansatz block. |
OneQubitAnsatzBlock | One-qubit ansatz block. |
TwoQubitAnsatzBlock | Two-qubit ansatz block. |
ZXZ | One-qubit ansatz block based on the ZXZ decomposition. |
KAK | Two-qubit ansatz block based on the KAK decomposition. |
generate_ansatz_from_circuit
generate_ansatz_from_circuit(qc, /, *, qubits_initially_zero=False, parameter_name='theta')
Generate an ansatz from the two-qubit connectivity structure of a circuit.
See the explanatatory material for motivation.
Parameters
- qc (
QuantumCircuit
) – A circuit, which is assumed to be unitary. Barriers are ignored. - qubits_initially_zero (
bool
) – IfTrue
, the first Z rotation on each qubit is fixed to zero because such a rotation has no effect on the state . - parameter_name (
str
) – Name for theParameterVector
representing the free parameters in the returned ansatz circuit.
Return type
tuple
[QuantumCircuit
, list
[float
]]
Returns
(ansatz, parameter_values)
such that ansatz.assign_parameters(parameter_values)
is equivalent to qc
up to a global phase.
Example:
Consider the following circuit as an example:
from qiskit import QuantumCircuit
qc = QuantumCircuit(6)
qc.rx(0.4, 0)
qc.ryy(0.2, 2, 3)
qc.h(2)
qc.rz(0.1, 2)
qc.rxx(0.3, 0, 1)
qc.rzz(0.3, 0, 1)
qc.cx(2, 1)
qc.s(1)
qc.h(4)
qc.draw("mpl")
If the above circuit is passed to generate_ansatz_from_circuit()
, it will return an ansatz with parametrized two-qubit KAK rotations in the same locations as the input:
from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit
ansatz, initial_params = generate_ansatz_from_circuit(
qc, qubits_initially_zero=True, parameter_name="x"
)
ansatz.draw("mpl")
Note that in the generated ansatz, all consecutive single-qubit gates are collapsed into the same ZXZ block, and all consecutive two-qubit gates are collapsed into a single KAK block, up to single-qubit rotations.
Further, the generate_ansatz_from_circuit()
function provides parameters which, when bound to the ansatz, will result in a circuit equivalent to the original one, up to a global phase:
ansatz.assign_parameters(initial_params).draw("mpl")
A 1D Trotter circuit leads to a similar result, with its characteristic brickwork structure:
from rustworkx.generators import path_graph
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit, generate_xyz_hamiltonian
hamiltonian = generate_xyz_hamiltonian(
path_graph(6),
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)
good_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=2),
time=1.0,
)
good_circuit.draw("mpl", initial_state=True)
from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit
ansatz, initial_params = generate_ansatz_from_circuit(
good_circuit, qubits_initially_zero=True, parameter_name="x"
)
ansatz.assign_parameters(initial_params).draw("mpl", initial_state=True)
parametrize_circuit
parametrize_circuit(qc, /, *, parameter_name='theta')
Create a parametrized version of a circuit.
Given a quantum circuit, constructs another quantum circuit which is identical except that any gates with numerical parameters are replaced by gates (of the same type) with free parameters. The new circuit is returned along with a list containing the original values of the parameters.
Parameters
- qc (QuantumCircuit) – The quantum circuit to parametrize.
- parameter_name (str) – Name for the
ParameterVector
representing the free parameters in the returned ansatz circuit.
Return type
tuple[QuantumCircuit, list[float | None]]
Returns
(ansatz, parameter_values)
such that ansatz.assign_parameters(parameter_values)
is identical to qc
as long as qc
did not already contain parameters. If qc
already had parameters, then parameter_values
will contain None
at the entries corresponding to those parameters.
Example:
Consider the following circuit as an example:
from qiskit import QuantumCircuit
qc = QuantumCircuit(6)
qc.rx(0.4, 0)
qc.ryy(0.2, 2, 3)
qc.h(2)
qc.rz(0.1, 2)
qc.rxx(0.3, 0, 1)
qc.rzz(0.3, 0, 1)
qc.cx(2, 1)
qc.s(1)
qc.h(4)
qc.draw("mpl")
If the above circuit is passed to parametrize_circuit()
, it will return an ansatz obtained from this circuit by replacing numerical parameters with free parameters:
from qiskit_addon_aqc_tensor import parametrize_circuit
ansatz, initial_params = parametrize_circuit(qc)
ansatz.draw("mpl")
Further, the parametrize_circuit()
function provides parameters which, when bound to the ansatz, will result in a circuit identical to the original one:
ansatz.assign_parameters(initial_params).draw("mpl")
If the original circuit already contained parameters, then the returned parameter values will contain None
at the entries corresponding to those parameters, and the preceding code will not work. The following example shows how to recover the original circuit in this case.
from qiskit.circuit import Parameter
qc = QuantumCircuit(3)
alpha1 = Parameter("alpha1")
alpha2 = Parameter("alpha2")
qc.ry(alpha1, [0])
qc.rz(0.1, [0])
qc.ry(alpha2, [1])
qc.rz(alpha1, [1])
qc.ry(0.2, [2])
qc.rz(0.3, [2])
ansatz, initial_params = parametrize_circuit(qc)
ansatz.assign_parameters(
{
param: val
for param, val in zip(ansatz.parameters, initial_params)
if val is not None
},
inplace=True,
)
ansatz.draw("mpl")