Hey, awesome this is great thanks. I think there is a bug here also. Would you like me to submit as bug in github?
The issue is indexing of the keyword arguments and there is a workaround that currently doesn’t make sense (to me). The function seems to think that both variables are stored in the first index of the term, and that the second index doesn’t exist.
Issue Code
%reset -f
import pennylane as qml
from pennylane import numpy as np
N = 2
dev = qml.device('forest.numpy_wavefunction', wires=N)
def sign_qubit(term): # s denotes the sign and a and b refer to the idx. Minus 1 from idx to return to standard indexing
print(term)
print(term[0].val)
print(term[1].val)
a = np.abs(term[0].val) - 1
b = np.abs(term[1].val) - 1
sa = np.sign(term[0].val)
sb = np.sign(term[1].val)
return sa, a, sb, b
def function(param, problem=None):
for term in problem:
sa, a ,sb, b = sign_qubit(term)
qml.RZ(sa*param[0], wires=a)
qml.RZ(sb*param[0], wires=b)
@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
function(param, problem=problem)
return qml.expval.PauliZ(i)
problem_val = [[1,2]] # renaming to val is ok
def cost(param):
exp = qaoa_one_qubit(param, problem=problem_val)
return exp
opt = qml.GradientDescentOptimizer(stepsize=0.01)
steps = 10
param = [0.01]
for i in range(steps):
param = opt.step(cost, param)
print(i)
Output
[<pennylane.variable.Variable object at 0x7f8ca38cde80>, <pennylane.variable.Variable object at 0x7f8ca3850630>]
[1, 2]
IndexError Traceback (most recent call last)
in
37
38 for i in range(steps):
—> 39 param = opt.step(cost, param)
40 print(i)
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/optimize/gradient_descent.py in step(self, objective_fn, x, grad_fn)
61 “”"
62
—> 63 g = self.compute_grad(objective_fn, x, grad_fn=grad_fn)
64
65 x_out = self.apply_grad(g, x)
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/optimize/gradient_descent.py in compute_grad(objective_fn, x, grad_fn)
85 else:
86 # default is autograd
—> 87 g = autograd.grad(objective_fn)(x) # pylint: disable=no-value-for-parameter
88 return g
89
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/wrap_util.py in nary_f(*args, **kwargs)
18 else:
19 x = tuple(args[i] for i in argnum)
—> 20 return unary_operator(unary_f, x, *nary_op_args, **nary_op_kwargs)
21 return nary_f
22 return nary_operator
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/differential_operators.py in grad(fun, x)
22 arguments as fun
, but returns the gradient instead. The function fun
23 should be scalar-valued. The gradient has the same type as the argument."""
—> 24 vjp, ans = _make_vjp(fun, x)
25 if not vspace(ans).size == 1:
26 raise TypeError("Grad only applies to real scalar-output functions. "
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/core.py in make_vjp(fun, x)
8 def make_vjp(fun, x):
9 start_node = VJPNode.new_root(x)
—> 10 end_value, end_node = trace(start_node, fun, x)
11 if end_node is None:
12 def vjp(g): return vspace(x).zeros()
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/tracer.py in trace(start_node, fun, x)
8 with trace_stack.new_trace() as t:
9 start_box = new_box(x, t, start_node)
—> 10 end_box = fun(start_box)
11 if isbox(end_box) and end_box._trace == start_box._trace:
12 return end_box._value, end_box._node
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/wrap_util.py in unary_f(x)
13 else:
14 subargs = subvals(args, zip(argnum, x))
—> 15 return fun(*subargs, **kwargs)
16 if isinstance(argnum, int):
17 x = args[argnum]
in cost(param)
29
30 def cost(param):
—> 31 exp = qaoa_one_qubit(param, problem=problem_val)
32 return exp
33
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/decorator.py in wrapper(*args, **kwargs)
151 def wrapper(*args, **kwargs):
152 “”“Wrapper function”""
–> 153 return qnode(*args, **kwargs)
154
155 # bind the jacobian method to the wrapped function
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/qnode.py in call(self, *args, **kwargs)
459 # pylint: disable=no-member
460 args = autograd.builtins.tuple(args) # prevents autograd boxed arguments from going through to evaluate
–> 461 return self.evaluate(args, **kwargs) # args as one tuple
462
463 @ae.primitive
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/tracer.py in f_wrapped(*args, **kwargs)
42 parents = tuple(box._node for _ , box in boxed_args)
43 argnums = tuple(argnum for argnum, _ in boxed_args)
—> 44 ans = f_wrapped(*argvals, **kwargs)
45 node = node_constructor(ans, f_wrapped, argvals, kwargs, argnums, parents)
46 return new_box(ans, trace, node)
~/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/tracer.py in f_wrapped(*args, **kwargs)
46 return new_box(ans, trace, node)
47 else:
—> 48 return f_raw(*args, **kwargs)
49 f_wrapped.fun = f_raw
50 f_wrapped._is_autograd_primitive = True
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/qnode.py in evaluate(self, args, **kwargs)
473 if not self.ops:
474 # construct the circuit
–> 475 self.construct(args, **kwargs)
476
477 # temporarily store keyword arguments
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/qnode.py in construct(self, args, **kwargs)
300 # generate the program queue by executing the quantum circuit function
301 try:
–> 302 res = self.func(*variables, **kwarg_variables)
303 finally:
304 # remove the context
in qaoa_one_qubit(param, problem)
23 @qml.qnode(dev)
24 def qaoa_one_qubit(param, problem=None):
—> 25 function(param, problem=problem)
26 return qml.expval.PauliZ(i)
27
in function(param, problem)
17 def function(param, problem=None):
18 for term in problem:
—> 19 sa, a ,sb, b = sign_qubit(term)
20 qml.RZ(saparam[0], wires=a)
21 qml.RZ(sbparam[0], wires=b)
in sign_qubit(term)
8 print(term)
9 print(term[0].val)
—> 10 print(term[1].val)
11 a = np.abs(term[0].val) - 1
12 b = np.abs(term[1].val) - 1
~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/variable.py in val(self)
145 # The variable is a placeholder for a keyword argument
146 if isinstance(self.kwarg_values[self.name], (Sequence, np.ndarray)):
–> 147 return self.kwarg_values[self.name][self.idx] * self.mult
148
149 return self.kwarg_values[self.name] * self.mult
IndexError: list index out of range
Notes
Seems to be that the it thinks both variables are stored in the first part of the list, so the workaround would be to index after .val call like this
Ex 2
%reset -f
import pennylane as qml
from pennylane import numpy as np
N = 2
dev = qml.device(‘forest.numpy_wavefunction’, wires=N)
def sign_qubit(term): # s denotes the sign and a and b refer to the idx. Minus 1 from idx to return to standard indexing
print(term)
print(term[0].val)
print('term_1: ', term[0].val[0])
print('term_2: ', term[0].val[1])
t1 = term[0].val[0]
t2 = term[0].val[1]
a = np.abs(t1) - 1
b = np.abs(t2) - 1
sa = np.sign(t1)
sb = np.sign(t2)
return sa, a, sb, b
def function(param, problem=None):
for term in problem:
sa, a ,sb, b = sign_qubit(term)
qml.RZ(saparam[0], wires=a)
qml.RZ(sbparam[0], wires=b)
@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
function(param, problem=problem)
return qml.expval.PauliZ(i)
problem_val = [[1,2]] # renaming to val is ok
def cost(param):
exp = qaoa_one_qubit(param, problem=problem_val)
return exp
opt = qml.GradientDescentOptimizer(stepsize=0.01)
steps = 10
param = [0.01]
for i in range(steps):
param = opt.step(cost, param)
print(i)
Output (which works)
[<pennylane.variable.Variable object at 0x7f8ca38cdef0>, <pennylane.variable.Variable object at 0x7f8ca38cd518>]
[1, 2]
term_1: 1
term_2: 2
0 1 2 3 4 5 6 7 8 9
Sidenote: Thanks for the great support & work on pennylane. If we meet I owe you one v. big beer.
Max