-
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
Conversation
Codecov Report
@@ Coverage Diff @@
## master #71 +/- ##
==========================================
+ Coverage 99.08% 99.11% +0.02%
==========================================
Files 7 7
Lines 327 338 +11
==========================================
+ Hits 324 335 +11
Misses 3 3 Continue to review full report at Codecov.
|
pennylane_qiskit/converter.py
Outdated
# iterate through the parameters | ||
if isinstance(v, qml.variable.VariableRef): | ||
# If a parameter is a Variable reference, then it represents | ||
# a differentiable parameter. |
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.
This could go as a dictionary comprehension as well (though might be too long of a one-liner):
var_ref_map = {k:v for k, v in params.items() if isinstance(v, qml.variable.VariableRef)}
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.
Will change, I left it as a for loop in an attempt to minimize line changes 😆
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.
Done!
pennylane_qiskit/converter.py
Outdated
|
||
return quantum_circuit.bind_parameters(params) | ||
return quantum_circuit.bind_parameters(params), var_ref_map |
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.
It might be slightly unintuitive that here there's a tuple that is returned (but this is also connected to the original implementation which was my doing 😅 ). Given the change there seem to be four parts of logic here inside of this single function:
- check_circuit
- extract
VariableRefs
- remove
VariableRefs
from params - bind parameters
While in theory, this means four separate functions it might be worth having a separate function that solely extracts the VariableRefs
and then later the bind_parameters
function could delete the needed refs. To me, this latter separation would make this part easier to parse :)
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.
Done!
pennylane_qiskit/converter.py
Outdated
|
||
if isinstance(p, ParameterExpression): | ||
if p.parameters: | ||
parameters.append(var_ref_map.get(min(p.parameters))) |
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.
Not too sure about this line here, in particular min(p.parameters)
. How come such a call is made? Perhaps naming a certain part of this line (e.g. min_param=min(p.parameters)
) and changing this line accordingly could help with providing further clues to the reader :)
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.
Does len(p.parameters) == 1
? If not, what happens to p
's other parameters?
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.
Oh, this is partly because
-
Python doesn't provide a way to index into a set, and
-
Due to a PennyLane restriction, the Qiskit ParameterExpression must always be a set containing a single parameter.
As a result, min(single_element_set)
is a useful way of extracting the element. Another approach is single_element_set.pop()
, but I tend not to favour pop
due to its side effects.
Side note: probably out of scope for this PR, but we should create a PR that raises an error if len(p.parameters) > 1
.
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.
Oh, got ya! So a ParameterExpression
contains a
Mapping of Parameter instances to the sympy.Symbol serving as their placeholder in expr.
Having seen that, I was also wondering why simply a Parameter
could not do it here, but this now allows for ParameterExpression
instances that contain only one Parameter
as well.
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.
Yep - the issue seems to be that even if you start off with a single Parameter
, after binding Qiskit converts everything into a ParameterExpression
. So Parameter
objects never appear at this point in the logic.
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.
This is amazing @josh146, thank you very much for getting that! I've left a couple of comments mainly for improving my own understanding 🙂
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.
Thanks for getting to this so quickly!
pennylane_qiskit/converter.py
Outdated
_check_parameter_bound(p, var_ref_map) | ||
|
||
if isinstance(p, ParameterExpression): | ||
if p.parameters: | ||
parameters.append(var_ref_map.get(min(p.parameters))) | ||
else: | ||
parameters.append(float(p)) | ||
else: | ||
parameters.append(p) |
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.
Don't feel particularly strongly about this, but this could be pulled into a helper method, so parameters
then gets constructed as a list comprehension.
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.
I had originally had this as a list comprehension, but changed it to a loop for readability (it became quite long due to the ternary expressions)
requirements.txt
Outdated
@@ -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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
A recent PR in PennyLane master renamed the Variable
class to VariableRef
--- I wanted to double check that this PR passes against the latest master of PennyLane. However, this renaming will likely be reverted due to side effects that occurred, so this change here is temporary and should be reverted before this PR is merged.
pennylane_qiskit/converter.py
Outdated
"""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]): |
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:
from typing import Dict
def _check_parameter_bound(param: ..., var_ref_map: Dict[Parameter, VariableRef]) -> Parameter:
"""
...
var_ref_map (Dict[Parameter, VariableRef]): ...
"""
...
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!
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Can't help but feel we can use the return value of _check_parameter_bound
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.
I've removed the return statement on _check_parameter_bound, since it is never used
pennylane_qiskit/converter.py
Outdated
|
||
if isinstance(p, ParameterExpression): | ||
if p.parameters: | ||
parameters.append(var_ref_map.get(min(p.parameters))) |
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.
Does len(p.parameters) == 1
? If not, what happens to p
's other parameters?
Co-Authored-By: antalszava <[email protected]>
…kit into bug/load-grad-fix
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.
Looks good to me @josh146! 😊
Ready to merge @josh146? |
I'm holding off, as I would like to revert |
Context: By expliciting calling
params.val
, theVariableRef
classes, which allow us to keep track of which operations are using which parameters so that the parameter shift rule can be applied, are being lost in the conversion.Description of the changes:
The binding logic in the conversion functions has been modified --- now, a mapping is kept of the differentiable Qiskit parameters to the PennyLane variables. The PennyLane variables are then used for operation execution where needed.
An integration test has been added to ensure that the gradient computation works correctly on converted qiskit circuits.