Passing non-differentiable arguments to qnode


#1

Hey,

Is there a way to pass iterable arguments to the qnode, where the cost and/or ansatz is redefined for each instance of the problem?

Ex 1:

import pennylane as qml
from pennylane import numpy as np
N = 2
dev = qml.device(‘forest.numpy_wavefunction’, wires=N)

@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
print(problem)
for i in problem:
qml.RX(param[0], wires=i)
return qml.expval.PauliZ(i)

problem = [0]
dcircuit = qml.grad(qaoa_one_qubit, argnum=0)
dcircuit([0.5], problem=problem)

The returned error here is that the qubit should be an int or qubit instance. The problem argument is converted to a pennylane variable.

Q: is any argument passed to a qnode classified as a Variable?

There is a semi-related problem in the docs https://pennylane.readthedocs.io/en/latest/tutorials/qubit_rotation.html where it states

“Keyword arguments may also be used in your custom quantum function. PennyLane does differentiate QNodes with respect to keyword arguments, so they are useful for passing external data to your QNode.”

Q: What if the keyword argument is not something you want to differentiate but is some external data like a list?

I have been working with global variables so far and they have some side effects I can’t get my head around. It would be super useful to be able to pass non-differentiable params to the qnode.


Extending this I attempted to put cost in as a class function which calls class variables (and here when using the GradientDescentOptimiser)

import pennylane as qml
from pennylane import numpy as np
N = 2
dev = qml.device(‘forest.numpy_wavefunction’, wires=N)

@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
print(int(problem[0]))
for i in problem:
qml.RX(param[0], wires=i)
return qml.expval.PauliZ(i)

class cost():
def init(self):
self.problem = [0]

def evaluate(param):
    exp = qaoa_one_qubit(param, problem=self.problem)
    return exp

opt = qml.GradientDescentOptimizer(stepsize=0.01)
steps = 10000
param = [0.01]
for i in range(steps):

param = opt.step(cost.evaluate, param)

which returns

NameError: name ‘self’ is not defined

Q: Will the gradientdescentoptimiser and the grad functions have the same usage (if this will work at all)


Overall just looking for a way to make the ansatz instance dependent for a problem.

Thanks for any help!

Max


#2

if you allow me
this example demonstrates how to use pytorch and also defines which variable needs to be differentiated

I think if i got your question right this answers your problem


#3

Hi Kareem! Thanks for the response.

Unfortunately I don’t think so. In the example you posted only differentiable parameters are passed to the qnode. There is a non-differentiable parameter passed to the cost function (step), but this isn’t used in / passed to the qnode.

What I would like to do is change the function inside the qnode dependent on some external data, but this external data be non-differentiable / not converted to a PennyLane Variable.

Thanks again though, and apologies if I misunderstood!

Max


#4

you’re most welcome

but I think if you actually set:

required_grad=False

then the torch backend won’t calculate its gradients it would be like a constant inside your quantum circuit


#5

There is a semi-related problem in the docs https://pennylane.readthedocs.io/en/latest/tutorials/qubit_rotation.html where it states

“Keyword arguments may also be used in your custom quantum function. PennyLane does differentiate QNodes with respect to keyword arguments, so they are useful for passing external data to your QNode.”

Thanks for pointing this out, this is actually a very unfortunate typo!

This should instead say: “PennyLane does not differentiate QNodes with respect to keyword arguments, so they are useful for passing external data to your QNode.”

With respect to the error you are getting, it looks like you have uncovered a very obscure bug in the PennyLane code base. This can be fixed by applying the following patch to your PennyLane installation:

diff --git a/pennylane/operation.py b/pennylane/operation.py
index 43db731..355c36c 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -373,7 +373,7 @@ class Operation(abc.ABC):
         """
         w = [i.val if isinstance(i, Variable) else i for i in self._wires]
         self.check_wires(w)
-        return w
+        return [int(i) for i in w]
 
     @property
     def parameters(self):

Once this is done, your example works as expected:

import pennylane as qml
from pennylane import numpy as np

N = 2
dev = qml.device('forest.numpy_wavefunction', wires=N)

@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
    print(problem)
    print('problem = ', [i.val for i in problem])
    for i in problem:
        qml.RX(param[0], wires=i)
    return qml.expval.PauliZ(i)

problem = [0]
dcircuit = qml.grad(qaoa_one_qubit, argnum=0)
dcircuit([0.5], problem=problem)

This gives the following output:

[<pennylane.variable.Variable object at 0x7f55333c2940>]
problem = [0]
[-0.479425538604203]

Note: In order to print out QNode parameters from inside a QNode, you will need to use the .val property.


Thanks for letting us know about this issue @mxn.wls, I will make a PR in the official PennyLane github repo ASAP!

Update: The PR is now available here: https://github.com/XanaduAI/pennylane/pull/206


#6

With the above bugfix, your optimization example now works:

@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
    for i in problem:
        qml.RX(param[0], wires=i)
    return qml.expval.PauliZ(i)

problem = [0]

def cost(param):
    exp = qaoa_one_qubit(param, problem=problem)
    return exp

opt = qml.GradientDescentOptimizer(stepsize=0.01)
steps = 10000
param = [0.01]

for i in range(steps):
    param = opt.step(cost, param)

#7

Ah thanks! Sorry, didn’t understand first time. I’ll try this out as well.


#8

Unfortunately, I’m still getting an error @josh.

Code

%reset -f
import pennylane as qml
from pennylane import numpy as np

import os
x = os.path.dirname(qml.file)
print(x)

N = 2
dev = qml.device(‘forest.numpy_wavefunction’, wires=N)

@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
print(problem)
print('problem = ', [i.val for i in problem])
for i in problem:
qml.RX(param[0], wires=i)
return qml.expval.PauliZ(i)

problem = [0]
dcircuit = qml.grad(qaoa_one_qubit, argnum=0)
dcircuit([0.5], problem=problem)

Out

/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane
[<pennylane.variable.Variable object at 0x7fc90c519f60>]

TypeError Traceback (most recent call last)
in
21 problem = [0]
22 dcircuit = qml.grad(qaoa_one_qubit, argnum=0)
—> 23 dcircuit([0.5], problem=problem)

~/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]

~/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)
455 # pylint: disable=no-member
456 args = autograd.builtins.tuple(args) # prevents autograd boxed arguments from going through to evaluate
–> 457 return self.evaluate(args, **kwargs) # args as one tuple
458
459 @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)
469 if not self.ops:
470 # construct the circuit
–> 471 self.construct(args, **kwargs)
472
473 # 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)
296 # generate the program queue by executing the quantum circuit function
297 try:
–> 298 res = self.func(*variables, **kwarg_variables)
299 finally:
300 # remove the context

in qaoa_one_qubit(param, problem)
14 def qaoa_one_qubit(param, problem=None):
15 print(problem)
—> 16 print('problem = ', [i.val for i in problem])
17 for i in problem:
18 qml.RX(param[0], wires=i)

in (.0)
14 def qaoa_one_qubit(param, problem=None):
15 print(problem)
—> 16 print('problem = ', [i.val for i in problem])
17 for i in problem:
18 qml.RX(param[0], wires=i)

~/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/variable.py in val(self)
144
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

TypeError: ‘NoneType’ object is not subscriptable

func @ line 373

@property
def wires(self):
    """Current wire values.

    Fixed wires are returned as is, free wires represented by
    :class:`~.variable.Variable` instances are replaced by their
    current numerical value.

    Returns:
        list[int]: wire values
    """
    w = [i.val if isinstance(i, Variable) else i for i in self._wires]
    self.check_wires(w)
    return [int(i) for i in w]

Uninstall & reinstall pennylane

pip uninstall pennylane
python setup.py install

Notes

I created a new conda environment installed pennylane by python setup.py install from pennylane folder (with new operations file). Same error as above


#9

Hey @kareem is this what you had in mind?

Thought we had it working for a second there… I’ve done a bunch of tests, its a Variable, and when I try and access it with .val it thinks it is None.

I want to iterate over ‘terms’ in a list of list, perform some operations on the elements of those terms, and adapt the gates of the circuit based on that. Seems like it should be able to be done but proving more difficult than anticipated.

Code

import pennylane as qml
import torch
from torch.autograd import Variable

dev = qml.device(‘forest.numpy_wavefunction’, wires=2)

@qml.qnode(dev, interface=‘torch’)
def circuit(phi, theta, problem):
x = [i.val for i in problem]
qml.RX(theta, wires=0)
qml.RZ(phi, wires=0)
return qml.expval.PauliZ(0)

def cost(phi, theta, problem, step):
target = -(-1)**(step // 100)
return torch.abs(circuit(phi, theta, problem) - target)**2

phi = Variable(torch.tensor(1., device=‘cuda’), requires_grad=True)
theta = Variable(torch.tensor(0.05, device=‘cuda’), requires_grad=True)
problem_var = Variable(torch.tensor([1,2], device=‘cuda’), requires_grad=False)

opt = torch.optim.Adam([phi, theta], lr = 0.1)

for i in range(10):
opt.zero_grad()
loss = cost(phi, theta, problem_var, i)
loss.backward()
opt.step()
print(‘fin’)

Error

Traceback (most recent call last):
File “/home/maxiwelian/projects/vqa_optimisation/qiskit_pennylane/test/pytorch_iterable_test/1.py”, line 27, in
loss = cost(phi, theta, problem_var, i)
File “/home/maxiwelian/projects/vqa_optimisation/qiskit_pennylane/test/pytorch_iterable_test/1.py”, line 17, in cost
return torch.abs(circuit(phi, theta, problem) - target)**2
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/interfaces/torch.py”, line 390, in custom_apply
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/interfaces/torch.py”, line 307, in forward
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/qnode.py”, line 457, in call
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/autograd-1.2-py3.6.egg/autograd/tracer.py”, line 48, in f_wrapped
return f_raw(*args, **kwargs)
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/qnode.py”, line 471, in evaluate
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/qnode.py”, line 298, in construct
File “/home/maxiwelian/projects/vqa_optimisation/qiskit_pennylane/test/pytorch_iterable_test/1.py”, line 10, in circuit
x = [i.val for i in problem]
File “/home/maxiwelian/projects/vqa_optimisation/qiskit_pennylane/test/pytorch_iterable_test/1.py”, line 10, in
x = [i.val for i in problem]
File “/home/maxiwelian/anaconda3/envs/vqa/lib/python3.6/site-packages/PennyLane-0.3.1-py3.6.egg/pennylane/variable.py”, line 143, in val
TypeError: ‘NoneType’ object is not subscriptable


#10

Hey @mxn.wls, I played around with your example, and I get the same error message. However, it works if I remove the print statements inside the QNode:

import pennylane as qml
from pennylane import numpy as np

N = 2
dev = qml.device('forest.numpy_wavefunction', wires=N)

@qml.qnode(dev)
def qaoa_one_qubit(param, problem=None):
    for i in problem:
        qml.RX(param[0], wires=i)
    return qml.expval.PauliZ(i)

problem = [0]
dcircuit = qml.grad(qaoa_one_qubit, argnum=0)
print(dcircuit([0.5], problem=problem))

I’ll look into why the print statements cause this error, in the meantime, can you verify that the above (without the print statements) works?


#11

The following bug fix allows the print statement inside the QNode to work :slight_smile:

But, there is a caveat. The way PennyLane is coded, the original function is only ever executed once. The QNode compiles the quantum functions after the first execution, and uses the compiled result from then on.

So while the below will allow the print function to work, it will only ever print output the first time the QNode is called.

diff --git a/pennylane/qnode.py b/pennylane/qnode.py
index 6160e8e..2929121 100644
--- a/pennylane/qnode.py
+++ b/pennylane/qnode.py
@@ -288,6 +288,10 @@ class QNode:
             temp = [Variable(idx, name=key) for idx, _ in enumerate(_flatten(val))]
             kwarg_variables[key] = unflatten(temp, val)
 
+        # temporarily store the free parameter values in the Variable class
+        Variable.free_param_values = np.array(list(_flatten(args)))
+        Variable.kwarg_values = keyword_values
+
         # set up the context for Operation entry
         if QNode._current_context is None:
             QNode._current_context = self

#12

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(sb
param[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(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 (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


#13

Ah, you’ve uncovered a bug/edge case in my previous bug fix! I played around with it, and it looks like my previous bugfix only worked for flat lists (not nested lists).

The following change to my previous bugfix fixes this:

diff --git a/pennylane/qnode.py b/pennylane/qnode.py
index 6160e8e..7facaab 100644
--- a/pennylane/qnode.py
+++ b/pennylane/qnode.py
@@ -288,6 +288,9 @@ class QNode:
             temp = [Variable(idx, name=key) for idx, _ in enumerate(_flatten(val))]
             kwarg_variables[key] = unflatten(temp, val)
 
         Variable.free_param_values = np.array(list(_flatten(args)))
-        Variable.kwarg_values = keyword_values
+        Variable.kwarg_values = {k: np.array(list(_flatten(v))) for k, v in keyword_values.items()}

         # set up the context for Operation entry
         if QNode._current_context is None:
             QNode._current_context = self

Thanks for following up on this @mxn.wls, this is a massive help to ensure we fix/remove as many bugs as we can in PennyLane! :beers:


The following script now runs for me:

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('first term:', term[0].val)
    print('second term:', 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)

And gives the following output:

[<pennylane.variable.Variable object at 0x7fcc6d0b9860>, <pennylane.variable.Variable object at 0x7fcc6d0b98d0>]
first term: 1
second term: 2
0
1
2
3
4
5
6
7
8
9


Update: PR available here: https://github.com/XanaduAI/pennylane/pull/209


#14

How do I install this on my command line?


Gradient Not Working Because of Keyword Arguments