-
Notifications
You must be signed in to change notification settings - Fork 603
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
Fix how trainable args are counted for gradients in GradientDescentOptimizer
and NesterovMomentumOptimizer
#1495
Changes from 23 commits
caed76a
d586c24
c79f5c0
b07357f
08fa4eb
c57a3fb
4751c69
9d2af7c
cbe3457
eba42af
0ea45d7
c45226e
d8ca734
a0d468c
386eb77
e29725f
f448a4d
210a32c
0b58d3e
5e400fe
7a02ff8
2d85634
9e5526e
675b7fc
ad0eb38
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 |
---|---|---|
|
@@ -747,16 +747,41 @@ def reset(opt): | |
opt.reset() | ||
|
||
|
||
@pytest.fixture | ||
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. Changed to this structure, as it seems that the previous code left state in between test cases. This made 2 tests fail: Before: https://github.com/PennyLaneAI/pennylane/runs/3244508688 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. Nice! 💯 |
||
def opt(opt_name): | ||
if opt_name == "gd": | ||
return GradientDescentOptimizer(stepsize) | ||
|
||
if opt_name == "nest": | ||
return NesterovMomentumOptimizer(stepsize, momentum=gamma) | ||
|
||
if opt_name == "moment": | ||
return MomentumOptimizer(stepsize, momentum=gamma) | ||
|
||
if opt_name == "ada": | ||
return AdagradOptimizer(stepsize) | ||
|
||
if opt_name == "rms": | ||
return RMSPropOptimizer(stepsize, decay=gamma) | ||
|
||
if opt_name == "adam": | ||
return AdamOptimizer(stepsize, beta1=gamma, beta2=delta) | ||
|
||
if opt_name == "roto": | ||
return RotosolveOptimizer() | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"opt, opt_name", | ||
"opt_name", | ||
[ | ||
(GradientDescentOptimizer(stepsize), "gd"), | ||
(MomentumOptimizer(stepsize, momentum=gamma), "moment"), | ||
(NesterovMomentumOptimizer(stepsize, momentum=gamma), "nest"), | ||
(AdagradOptimizer(stepsize), "ada"), | ||
(RMSPropOptimizer(stepsize, decay=gamma), "rms"), | ||
(AdamOptimizer(stepsize, beta1=gamma, beta2=delta), "adam"), | ||
(RotosolveOptimizer(), "roto"), | ||
"gd", | ||
"nest", | ||
antalszava marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"moment", | ||
"nest", | ||
"ada", | ||
"rms", | ||
"adam", | ||
"roto", | ||
], | ||
) | ||
class TestOverOpts: | ||
|
@@ -877,3 +902,76 @@ def func2(args): | |
assert x_seperate == pytest.approx(args_new[0], abs=tol) | ||
assert y_seperate == pytest.approx(args_new[1], abs=tol) | ||
assert z_seperate == pytest.approx(args_new[2], abs=tol) | ||
|
||
def test_one_trainable_one_non_trainable(self, opt, opt_name, tol): | ||
"""Tests that a cost function that takes one trainable and one | ||
non-trainable parameter executes well.""" | ||
dev = qml.device("default.qubit", wires=2) | ||
|
||
@qml.qnode(dev) | ||
def circuit(x): | ||
qml.RX(x, wires=0) | ||
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) | ||
|
||
def cost(x, target): | ||
return (circuit(x) - target) ** 2 | ||
|
||
ev = np.tensor(0.7781, requires_grad=False) | ||
x = np.tensor(0.0, requires_grad=True) | ||
|
||
original_ev = ev | ||
|
||
(x, ev), cost = opt.step_and_cost(cost, x, ev) | ||
antalszava marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# check that the argument to RX doesn't change, as the X rotation doesn't influence <Z> | ||
assert x == 0 | ||
assert ev == original_ev | ||
|
||
def test_one_non_trainable_one_trainable(self, opt, opt_name, tol): | ||
"""Tests that a cost function that takes one non-trainable and one | ||
trainable parameter executes well.""" | ||
dev = qml.device("default.qubit", wires=2) | ||
|
||
@qml.qnode(dev) | ||
def circuit(x): | ||
qml.RX(x, wires=0) | ||
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) | ||
|
||
def cost(target, x): # Note: the order of the arguments has been swapped | ||
return (circuit(x) - target) ** 2 | ||
|
||
ev = np.tensor(0.7781, requires_grad=False) | ||
x = np.tensor(0.0, requires_grad=True) | ||
|
||
original_ev = ev | ||
|
||
(ev, x), cost = opt.step_and_cost(cost, ev, x) | ||
# check that the argument to RX doesn't change, as the X rotation doesn't influence <Z> | ||
assert x == 0 | ||
assert ev == original_ev | ||
|
||
def test_two_trainable_args(self, opt, opt_name, tol): | ||
"""Tests that a cost function that takes at least two trainable | ||
arguments executes well.""" | ||
dev = qml.device("default.qubit", wires=2) | ||
|
||
@qml.qnode(dev) | ||
def circuit(x, y): | ||
qml.RX(x, wires=0) | ||
qml.RX(y, wires=0) | ||
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) | ||
|
||
def cost(x, y, target): | ||
return (circuit(x, y) - target) ** 2 | ||
|
||
ev = np.tensor(0.7781, requires_grad=False) | ||
x = np.tensor(0.0, requires_grad=True) | ||
y = np.tensor(0.0, requires_grad=True) | ||
|
||
original_ev = ev | ||
|
||
(x, y, ev), cost = opt.step_and_cost(cost, x, y, ev) | ||
|
||
# check that the argument to RX doesn't change, as the X rotation doesn't influence <Z> | ||
assert x == 0 | ||
assert ev == original_ev |
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.
@antalszava you might be able to use the new
functionality I just merged into the math 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.
(optional, though)
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 didn't seem to have worked for some tests 🤔
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.
No worries! We'll need to remember to update this in the future, if
requires_grad=False
by defaultThere 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.
The issue seems to be that if the argument is not a PennyLane NumPy tensor, then it'll be considered to belong to the "numpy" interface (instead of the "autgrad" interface), and all "numpy" arguments automatically evaluate as
False
when callingutils.requires_grad(arg)
. So, whenever anarg
is simply a floatget_trainable_indices
would consider it non-trainiable, while that seems to not be what's wanted here.