Optimizing the parameters of MottonenStatePreparation produces the following error

def layer(W):
    for i in range(6):
        qml.Rot(W[i, 0], W[i, 1], W[i, 2], wires=i)
    for i in range(4):
        qml.CNOT(wires=[i, i + 1])
    qml.CNOT(wires=[5, 0])
    
def statepreparation(x):
    qml.MottonenStatePreparation(Norm1DArray(x), wires=[i for i in range(6)])
    
@qml.qnode(dev1)   
def conv_net(weights, x):

    statepreparation(x)

    
    for W in weights:
        layer(W)

    return qml.expval(qml.PauliZ(0))

opt = qml.NesterovMomentumOptimizer(0.05)
batch_size = 5

# train the variational classifier
weights = para_init
bias = bias_init

for it in range(60):

    # Update the weights by one optimizer step
    batch_index = np.random.randint(0, num_train, (batch_size,))
    x_train_batch = x_train[batch_index]
    y_train_batch = y_train[batch_index]
    weights, bias, _, _ = opt.step(cost, weights, bias, x_train_batch, y_train_batch)
    print(weights)

    # Compute predictions on train and validation set
    predictions_train = [np.sign(variational_classifier(weights, bias, f)) for f in x_train]
    predictions_val = [np.sign(variational_classifier(weights, bias, f)) for f in x_test]

    # Compute accuracy on train and validation set
    acc_train = accuracy(y_train, predictions_train)
    acc_val = accuracy(y_test, predictions_val)

    print(
        "Iter: {:5d} | Cost: {:0.7f} | Acc train: {:0.7f} | Acc validation: {:0.7f} "
        "".format(it + 1, cost(weights, bias, x_train, y_train), acc_train, acc_val)
    )# cost(para, bias, features, labels):

ValueError Traceback (most recent call last)
Input In [28], in <cell line: 8>()
12 x_train_batch = x_train[batch_index]
13 y_train_batch = y_train[batch_index]
β€”> 14 weights, bias, _, _ = opt.step(cost, weights, bias, x_train_batch, y_train_batch)
15 print(weights)
17 # Compute predictions on train and validation set

File d:\miniconda3\lib\site-packages\pennylane\optimize\gradient_descent.py:88, in GradientDescentOptimizer.step(self, objective_fn, grad_fn, *args, **kwargs)
70 def step(self, objective_fn, *args, grad_fn=None, **kwargs):
71 β€œβ€β€œUpdate trainable arguments with one step of the optimizer.
72
73 Args:
(…)
85 If single arg is provided, list [array] is replaced by array.
86 β€œβ€β€
β€”> 88 g, _ = self.compute_grad(objective_fn, args, kwargs, grad_fn=grad_fn)
89 new_args = self.apply_grad(g, args)
91 # unwrap from list if one argument, cleaner return

File d:\miniconda3\lib\site-packages\pennylane\optimize\nesterov_momentum.py:71, in NesterovMomentumOptimizer.compute_grad(self, objective_fn, args, kwargs, grad_fn)
68 shifted_args[index] = args[index] - self.momentum * self.accumulation[index]
70 g = get_gradient(objective_fn) if grad_fn is None else grad_fn
β€”> 71 grad = g(*shifted_args, **kwargs)
72 forward = getattr(g, β€œforward”, None)
74 grad = (grad,) if len(trainable_indices) == 1 else grad

File d:\miniconda3\lib\site-packages\pennylane_grad.py:115, in grad.call(self, *args, **kwargs)
112 self._forward = self._fun(*args, **kwargs)
113 return ()
β†’ 115 grad_value, ans = grad_fn(*args, **kwargs)
116 self._forward = ans
118 return grad_value

File d:\miniconda3\lib\site-packages\autograd\wrap_util.py:20, in unary_to_nary..nary_operator..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)

File d:\miniconda3\lib\site-packages\pennylane_grad.py:133, in grad._grad_with_forward(fun, x)
127 @staticmethod
128 @unary_to_nary
129 def _grad_with_forward(fun, x):
130 β€œβ€β€œThis function is a replica of autograd.grad, with the only
131 difference being that it returns both the gradient and the forward pass
132 value.β€β€œβ€
β†’ 133 vjp, ans = _make_vjp(fun, x)
135 if not vspace(ans).size == 1:
136 raise TypeError(
137 "Grad only applies to real scalar-output functions. "
138 β€œTry jacobian, elementwise_grad or holomorphic_grad.”
139 )

File d:\miniconda3\lib\site-packages\autograd\core.py:10, in make_vjp(fun, x)
8 def make_vjp(fun, x):
9 start_node = VJPNode.new_root()
β€”> 10 end_value, end_node = trace(start_node, fun, x)
11 if end_node is None:
12 def vjp(g): return vspace(x).zeros()

File d:\miniconda3\lib\site-packages\autograd\tracer.py:10, 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

File d:\miniconda3\lib\site-packages\autograd\wrap_util.py:15, in unary_to_nary..nary_operator..nary_f..unary_f(x)
13 else:
14 subargs = subvals(args, zip(argnum, x))
β€”> 15 return fun(*subargs, **kwargs)

Input In [24], in cost(para, bias, features, labels)
202 def cost(para, bias, features, labels):
β†’ 203 predictions = [variational_classifier(para, bias, f) for f in features]
204 return square_loss(labels, predictions)

Input In [24], in (.0)
202 def cost(para, bias, features, labels):
β†’ 203 predictions = [variational_classifier(para, bias, f) for f in features]
204 return square_loss(labels, predictions)

Input In [24], in variational_classifier(para, bias, features)
199 def variational_classifier(para, bias, features):
β†’ 200 return conv_net(para, features) + bias

File d:\miniconda3\lib\site-packages\pennylane\qnode.py:611, in QNode.call(self, *args, **kwargs)
608 set_shots(self._original_device, override_shots)(self._update_gradient_fn)()
610 # construct the tape
β†’ 611 self.construct(args, kwargs)
613 cache = self.execute_kwargs.get(β€œcache”, False)
614 using_custom_cache = (
615 hasattr(cache, β€œgetitem”)
616 and hasattr(cache, β€œsetitem”)
617 and hasattr(cache, β€œdelitem”)
618 )

File d:\miniconda3\lib\site-packages\pennylane\qnode.py:526, in QNode.construct(self, args, kwargs)
523 self._tape = qml.tape.QuantumTape()
525 with self.tape:
β†’ 526 self._qfunc_output = self.func(*args, **kwargs)
527 self._tape._qfunc_output = self._qfunc_output
529 params = self.tape.get_parameters(trainable_only=False)

Input In [24], in conv_net(weights, x)
172 @qml.qnode(dev1)
173 def conv_net(weights, x):
β†’ 175 statepreparation(x)
178 for W in weights:
179 layer(W)

Input In [24], in statepreparation(x)
169 def statepreparation(x):
β†’ 170 qml.MottonenStatePreparation(Norm1DArray(x), wires=[i for i in range(6)])

File d:\miniconda3\lib\site-packages\pennylane\templates\state_preparations\mottonen.py:314, in MottonenStatePreparation.init(self, state_vector, wires, do_queue, id)
312 norm = qml.math.sum(qml.math.abs(state) ** 2)
313 if not qml.math.allclose(norm, 1.0, atol=1e-3):
β†’ 314 raise ValueError(
315 f"State vectors have to be of norm 1.0, vector {i} has norm {norm}"
316 )
318 super().init(state_vector, wires=wires, do_queue=do_queue, id=id)

ValueError: State vectors have to be of norm 1.0, vector 0 has norm Autograd ArrayBox with value nan
@Maria_Schuld
@isaacdevlugt

Hi @RX1 ,

Unfortunately due to non-trivial classical processing of the state vector, the MottonenStatePreparation template is not always fully differentiable.

I’m not sure if this is the cause of the problem though because I wasn’t able to reproduce your problem since the code you have shared doesn’t include everything that I would need to run it. If you share the rest of your code including the creation of the device, para_init, and other variables I can try to see if I can reproduce your problem.

I hope this is helpful.
Please let me know if you have any additional questions!

1 Like

So if I convert MottonenStatePreparation to controlled RY and CNOT gates via compute_decomposition and re-express MottonenStatePreparation in terms of basic quantum gates, is this fully differentiable? Like this:

@CatalinaAlbornoz
@isaacdevlugt

Hi @RX1 , I still don’t think this can be fully differentiable but you can try.