-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed bug in gradients of loaded qiskit circuits #71
Changes from 8 commits
81dd32a
8710bbc
3183a7e
5359ce3
ac64638
9917ea5
cad1a5c
2d7cac2
1bc6e5c
2ce3a9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,12 @@ | |
This module contains functions for converting Qiskit QuantumCircuit objects | ||
into PennyLane circuit templates. | ||
""" | ||
from typing import Dict, Any | ||
import warnings | ||
|
||
import numpy as np | ||
from qiskit import QuantumCircuit | ||
from qiskit.circuit import Parameter | ||
from qiskit.circuit import Parameter, ParameterExpression | ||
from qiskit.exceptions import QiskitError | ||
|
||
import pennylane as qml | ||
|
@@ -36,40 +37,59 @@ | |
inv_map = {v.__name__: k for k, v in QISKIT_OPERATION_MAP.items()} | ||
|
||
|
||
def _check_parameter_bound(param): | ||
def _check_parameter_bound(param: Parameter, var_ref_map: Dict[Parameter, qml.variable.VariableRef]): | ||
"""Utility function determining if a certain parameter in a QuantumCircuit has | ||
been bound. | ||
|
||
Args: | ||
param: the parameter to be checked | ||
param (qiskit.circuit.Parameter): the parameter to be checked | ||
var_ref_map (dict[qiskit.circuit.Parameter, pennylane.variable.VariableRef]): | ||
a dictionary mapping qiskit parameters to PennyLane variables | ||
""" | ||
if isinstance(param, Parameter) and param not in var_ref_map: | ||
raise ValueError("The parameter {} was not bound correctly.".format(param)) | ||
|
||
|
||
def _extract_variable_refs(params: Dict[Parameter, Any]) -> Dict[Parameter, qml.variable.VariableRef]: | ||
"""Iterate through the parameter mapping to be bound to the circuit, | ||
and return a dictionary containing the differentiable parameters. | ||
|
||
Args: | ||
params (dict): dictionary of the parameters in the circuit to their corresponding values | ||
|
||
Returns: | ||
param: the parameter after the check | ||
dict[qiskit.circuit.Parameter, pennylane.variable.VariableRef]: a dictionary mapping | ||
qiskit parameters to PennyLane variables | ||
""" | ||
if isinstance(param, Parameter): | ||
raise ValueError("The parameter {} was not bound correctly.".format(param)) | ||
return param | ||
# map qiskit parameters to PennyLane differentiable VariableRefs. | ||
if params is None: | ||
return {} | ||
|
||
return {k: v for k, v in params.items() if isinstance(v, qml.variable.VariableRef)} | ||
|
||
|
||
def _check_circuit_and_bind_parameters(quantum_circuit: QuantumCircuit, params: dict) -> QuantumCircuit: | ||
def _check_circuit_and_bind_parameters(quantum_circuit: QuantumCircuit, params: dict, diff_params: dict) -> QuantumCircuit: | ||
"""Utility function for checking for a valid quantum circuit and then binding parameters. | ||
|
||
Args: | ||
quantum_circuit (QuantumCircuit): the quantum circuit to check and bind the parameters for | ||
params (dict): dictionary of the parameters in the circuit | ||
params (dict): dictionary of the parameters in the circuit to their corresponding values | ||
diff_params (dict): dictionary mapping the differentiable parameters to PennyLane | ||
VariableRef instances | ||
|
||
Returns: | ||
qc (QuantumCircuit): quantum circuit with bound parameters | ||
QuantumCircuit: quantum circuit with bound parameters | ||
""" | ||
if not isinstance(quantum_circuit, QuantumCircuit): | ||
raise ValueError("The circuit {} is not a valid Qiskit QuantumCircuit.".format(quantum_circuit)) | ||
|
||
if params is None: | ||
return quantum_circuit | ||
|
||
for k, v in params.items(): | ||
if isinstance(v, qml.variable.Variable): | ||
params.update({k: v.val}) | ||
for k in diff_params: | ||
# Since we cannot bind VariableRefs to Qiskit circuits, | ||
# we must remove them from the binding dictionary before binding. | ||
del params[k] | ||
|
||
return quantum_circuit.bind_parameters(params) | ||
|
||
|
@@ -83,7 +103,7 @@ def map_wires(wires: list, qc_wires: list) -> dict: | |
qc_wires (list): wires from the converted quantum circuit | ||
|
||
Returns: | ||
wire_map (dict): map from quantum circuit wires to the user defined wires | ||
dict[int, int]: map from quantum circuit wires to the user defined wires | ||
""" | ||
if wires is None: | ||
return dict(zip(qc_wires, range(len(qc_wires)))) | ||
|
@@ -105,17 +125,12 @@ def execute_supported_operation(operation_name: str, parameters: list, wires: li | |
""" | ||
operation = getattr(pennylane_ops, operation_name) | ||
|
||
parameters = [_check_parameter_bound(param) for param in parameters] | ||
|
||
if not parameters: | ||
operation(wires=wires) | ||
elif operation_name == 'QubitStateVector': | ||
operation(np.array(parameters), wires=wires) | ||
elif operation_name == 'QubitUnitary': | ||
operation(*parameters, wires=wires) | ||
else: | ||
float_params = [float(param) for param in parameters] | ||
operation(*float_params, wires=wires) | ||
operation(*parameters, wires=wires) | ||
|
||
|
||
def load(quantum_circuit: QuantumCircuit): | ||
|
@@ -144,8 +159,8 @@ def _function(params: dict = None, wires: list = None): | |
Returns: | ||
function: the new PennyLane template | ||
""" | ||
|
||
qc = _check_circuit_and_bind_parameters(quantum_circuit, params) | ||
var_ref_map = _extract_variable_refs(params) | ||
qc = _check_circuit_and_bind_parameters(quantum_circuit, params, var_ref_map) | ||
|
||
# Wires from a qiskit circuit are unique w.r.t. a register name and a qubit index | ||
qc_wires = [(q.register.name, q.index) for q in quantum_circuit.qubits] | ||
|
@@ -160,8 +175,32 @@ def _function(params: dict = None, wires: list = None): | |
operation_wires = [wire_map[(qubit.register.name, qubit.index)] for qubit in op[1]] | ||
|
||
if instruction_name in inv_map and inv_map[instruction_name] in pennylane_ops.ops: | ||
|
||
execute_supported_operation(inv_map[instruction_name], op[0].params, operation_wires) | ||
# Extract the bound parameters from the operation. If the bound parameters are a | ||
# Qiskit ParameterExpression, then replace it with the corresponding PennyLane | ||
# variable from the var_ref_map dictionary. | ||
|
||
parameters = [] | ||
for p in op[0].params: | ||
|
||
_check_parameter_bound(p, var_ref_map) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't help but feel we can use the return value of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed the return statement on _check_parameter_bound, since it is never used |
||
|
||
if isinstance(p, ParameterExpression): | ||
if p.parameters: | ||
# p.parameters must be a single parameter, as PennyLane | ||
# does not support expressions of variables currently. | ||
if len(p.parameters) > 1: | ||
raise ValueError("Operation {} has invalid parameter {}. PennyLane does not support " | ||
"expressions containing differentiable parameters as operation " | ||
"arguments".format(instruction_name, p)) | ||
|
||
param = min(p.parameters) | ||
parameters.append(var_ref_map.get(param)) | ||
else: | ||
parameters.append(float(p)) | ||
else: | ||
parameters.append(p) | ||
|
||
execute_supported_operation(inv_map[instruction_name], parameters, operation_wires) | ||
|
||
elif instruction_name == 'SdgGate': | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
qiskit>=0.15 | ||
PennyLane>=0.7.0 | ||
git+git://github.com/XanaduAI/pennylane.git#egg=pennylane | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious why this is needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A recent PR in PennyLane master renamed the |
||
numpy | ||
networkx==2.3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, it might be worth considering the
typing
module:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I will add that in!