Skip to content
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

Turns off QNode caching by default and makes variable values available during construction #209

Merged
merged 15 commits into from Jun 19, 2019

Conversation

Projects
None yet
3 participants
@josh146
Copy link
Member

commented May 22, 2019

Context:

Currently, the way the QNode works uses a method of 'JIT' circuit compilation:

  • On the first evaluation call, the QNode.construct method is called. This acts to:

    • inspect the function signature and the passed args/kwargs for array size/shape,
    • wraps them in the Variable class,
    • runs the custom quantum function,
    • extracts the queued operations and queued expectations,
    • builds up a dictionary of which operations depend on which arguments.

From this point on, the user's function is never called again --- the QNode has cached all the information about the arguments, operations, and expectations to directly query the device.

However, there is a potential (advanced) use case where the user might want to do classical processing on keyword arguments within the QNode. This is generally a safe procedure, as QNodes, by default, treat keyword arguments as non-differentiable data.

Description of the Change:

To allow the above code pattern, the following has been modified:

  • The QNode now by default re-evaluates the function/re-constructs the circuit on every execution. The QNode class and qnode decorator have a new keyword argument cache. By default this is set to False, the user may set this to True if they want to return to the old behaviour.

  • The QNode now makes variable values available during construction when the function is called. Previously, it only did this during evaluation, and just used dummy Variable classes during construction.

Benefits:

This PR makes the following code pattern legal:

@qml.qnode(dev)
def circuit(x, c=None):
    qml.RX(x, wires=0)

    for i in range(c):
        qml.RX(x, wires=i)

    return qml.expval.PauliZ(0)

circuit(0.5, c=2)

i.e., you can perform classical processing of keyword arguments inside the QNode --- data that is assumed to be non-differentiable, and programmatically change the circuit structure.

Possible Drawbacks:

  • There could be a small speed penalty in having to reconstruct the QNode with every evaluation. However, since the bottleneck would in almost all cases be the quantum simulation/optimization, this should be negligible.

Related GitHub Issues: https://discuss.pennylane.ai/t/passing-non-differentiable-arguments-to-qnode/135/14

@josh146 josh146 requested a review from co9olguy May 22, 2019

@codecov

This comment has been minimized.

Copy link

commented May 22, 2019

Codecov Report

Merging #209 into master will not change coverage.
The diff coverage is 100%.

@@          Coverage Diff          @@
##           master   #209   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          30     30           
  Lines        1851   1878   +27     
=====================================
+ Hits         1851   1878   +27
Impacted Files Coverage Δ
pennylane/qnode.py 100% <100%> (ø) ⬆️
pennylane/decorator.py 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a450af0...a556c57. Read the comment docs.

@josh146 josh146 requested a review from quantshah May 22, 2019

josh146 added some commits May 22, 2019

@josh146 josh146 closed this May 23, 2019

@josh146 josh146 removed the review-ready label May 23, 2019

@josh146 josh146 reopened this Jun 9, 2019

@josh146 josh146 changed the title Make variable values available during construction Adds eager mode and makes variable values available during construction Jun 9, 2019

@josh146

This comment has been minimized.

Copy link
Member Author

commented Jun 11, 2019

@co9olguy, @smite, @quantshah - the name eager used here doesn't really make sense, and it should be renamed before merging. Any suggestion for an alternative name?

Needs to express that turning on this experimental option changes PennyLane from only evaluating the users quantum function once -> evaluating it on every QNode call.

qml.no_caching()?

@@ -198,11 +198,13 @@ class QNode:
"""
# pylint: disable=too-many-instance-attributes
_current_context = None #: QNode: for building Operation sequences by executing quantum circuit functions
eager = False

This comment has been minimized.

Copy link
@smite

smite Jun 11, 2019

Collaborator
Suggested change
eager = False
eager = False #: bool: if True, QNode.func will be called to reconstruct the quantum circuit on every execution of the QNode
@smite

This comment has been minimized.

Copy link
Collaborator

commented Jun 11, 2019

qml.mutable_qnodes?
qml.mutable_circuits?

I'm not sure mutable is the proper word here, since it's not mutability in the exact Python sense. The idea is that the if the setting is True, the circuit structure of the QNodes can change on every execution.

Show resolved Hide resolved pennylane/qnode.py Outdated
circuit_kwargs = {}
circuit_kwargs.update(kwargs)
for k in ('h', 'order', 'shots', 'force_order2'):
circuit_kwargs.pop(k, None)

This comment has been minimized.

Copy link
@smite

smite Jun 11, 2019

Collaborator

I think there should be a better way of separating the kwargs meant for QNode.func, and the ones meant for QNode methods. Maybe jacobian should receive the QNode.func kwargs as a separate dictionary argument?

This comment has been minimized.

Copy link
@josh146

josh146 Jun 11, 2019

Author Member

I don't think this is possible, purely because autograd will always send the function keyword arguments as keyword arguments. What would work is instead to collect the Jacobian keyword arguments within a grad_options dictionary.

This comment has been minimized.

Copy link
@josh146

josh146 Jun 11, 2019

Author Member

I thought about changing this here, but it seems out of scope. Should we mark this for a new PR?

@josh146 josh146 changed the title Adds eager mode and makes variable values available during construction Turns off QNode caching by default and makes variable values available during construction Jun 11, 2019

josh146 added some commits Jun 11, 2019

@co9olguy

This comment has been minimized.

Copy link
Member

commented Jun 12, 2019

* Since this requires knowledge of the `.val` property, this should be considered an **advanced feature**, available for those who require this code pattern.

Does this actually require knowledge of the .val property?

@josh146

This comment has been minimized.

Copy link
Member Author

commented Jun 12, 2019

Does this actually require knowledge of the .val property?

Good point. It might not... perhaps I can perform the 'variable unpacking' in the construct method, and pass the direct values to the users quantum function. This would be a one line change --- I'll explore this tomorrow and see if this works/make sure it doesn't break anything.

josh146 added some commits Jun 12, 2019

@josh146

This comment has been minimized.

Copy link
Member Author

commented Jun 12, 2019

I've added the following:

if self.cache:
    # caching mode, must use variables for kwargs
    # so they can be updated without reconstructing
    res = self.func(*variables, **kwarg_variables)
else:
    # no caching, fine to directly pass kwarg values
    res = self.func(*variables, **keyword_values)

This works really nicely, removes the need to use .val at all, and leads to a much nicer user experience. I've updated the top comment to reflect this.

@co9olguy
Copy link
Member

left a comment

Looks good. Most comments are questions, but 1-2 suggestions also

Show resolved Hide resolved pennylane/decorator.py Outdated
Show resolved Hide resolved pennylane/qnode.py Outdated
Show resolved Hide resolved pennylane/qnode.py Outdated
Show resolved Hide resolved pennylane/qnode.py
Show resolved Hide resolved pennylane/qnode.py Outdated
Show resolved Hide resolved pennylane/qnode.py
Show resolved Hide resolved tests/test_qnode.py
Show resolved Hide resolved tests/test_quantum_gradients.py
Show resolved Hide resolved tests/test_quantum_gradients.py
Show resolved Hide resolved tests/test_quantum_gradients.py
@co9olguy

This comment has been minimized.

Copy link
Member

commented Jun 18, 2019

Also, any new edge cases that we want to check for in new tests?

Show resolved Hide resolved pennylane/qnode.py Outdated
Apply suggestions from code review
Co-Authored-By: Nathan Killoran <co9olguy@users.noreply.github.com>
@josh146

This comment has been minimized.

Copy link
Member Author

commented Jun 18, 2019

I've placed a couple of new tests in test_qnode.py, in class TestQNodeCacheing. No other edge cases came to mind?

josh146 added some commits Jun 18, 2019

@josh146 josh146 merged commit 22efe4c into master Jun 19, 2019

3 checks passed

CodeFactor No issues found.
Details
Travis CI - Branch Build Passed
Details
Travis CI - Pull Request Build Passed
Details

@josh146 josh146 deleted the variables_during_construction branch Jun 19, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.