Hi, I’ve been trying to do a binary classification by modifying one of the codes available at Quantum neural network — Strawberry Fields . I would appreciate a lot if you could help me. Code:
import numpy as np
import tensorflow as tf
import strawberryfields as sf
from strawberryfields import ops
## data
import matplotlib.pyplot as plt
##generate data
np.random.seed(42)
num_samples_per_class = 200
##Class 0 with mean [1, 2] and sdv [0.1, 0.5]
class_0 = np.random.normal(loc=[1, 2], scale=[0.1, 0.5], size=(num_samples_per_class, 2))
##Clase 1 with mean [2, 3] sdv [0.2, 0.3]
class_1 = np.random.normal(loc=[2, 3], scale=[0.2, 0.3], size=(num_samples_per_class, 2))
data = np.vstack([class_0, class_1])
labels = np.array([0] * num_samples_per_class + [1] * num_samples_per_class)
#Normal
data= (data - data.min()) / (data.max() - data.min())
plt.figure(figsize=(8, 6))
plt.scatter(data[labels == 0][:, 0], data[labels == 0][:, 1], c='red', label='Clase 0')
plt.scatter(data[labels == 1][:, 0], data[labels == 1][:, 1], c='blue', label='Clase 1')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.title('Normalized data')
plt.show()
target_state = tf.constant(labels, dtype=tf.complex64)
## interferometer
def interferometer(params, q):
"""Parameterised interferometer acting on ``N`` modes.
Args:
params (list[float]): list of length ``max(1, N-1) + (N-1)*N`` parameters.
* The first ``N(N-1)/2`` parameters correspond to the beamsplitter angles
* The second ``N(N-1)/2`` parameters correspond to the beamsplitter phases
* The final ``N-1`` parameters correspond to local rotation on the first N-1 modes
q (list[RegRef]): list of Strawberry Fields quantum registers the interferometer
is to be applied to
"""
N = len(q)
theta = params[:N*(N-1)//2]
phi = params[N*(N-1)//2:N*(N-1)]
rphi = params[-N+1:]
if N == 1:
# the interferometer is a single rotation
ops.Rgate(rphi[0]) | q[0]
return
n = 0 # keep track of free parameters
# Apply the rectangular beamsplitter array
# The array depth is N
for l in range(N):
for k, (q1, q2) in enumerate(zip(q[:-1], q[1:])):
# skip even or odd pairs depending on layer
if (l + k) % 2 != 1:
ops.BSgate(theta[n], phi[n]) | (q1, q2)
n += 1
# apply the final local phase shifts to all modes except the last one
for i in range(max(1, N - 1)):
ops.Rgate(rphi[i]) | q[i]
## layers
def layer(params, q):
"""CV quantum neural network layer acting on ``N`` modes.
Args:
params (list[float]): list of length ``2*(max(1, N-1) + N**2 + n)`` containing
the number of parameters for the layer
q (list[RegRef]): list of Strawberry Fields quantum registers the layer
is to be applied to
"""
N = len(q)
M = int(N * (N - 1)) + max(1, N - 1)
int1 = params[:M]
s = params[M:M+N]
int2 = params[M+N:2*M+N]
dr = params[2*M+N:2*M+2*N]
dp = params[2*M+2*N:2*M+3*N]
k = params[2*M+3*N:2*M+4*N]
# begin layer
interferometer(int1, q)
for i in range(N):
ops.Sgate(s[i]) | q[i]
interferometer(int2, q)
for i in range(N):
ops.Dgate(dr[i], dp[i]) | q[i]
ops.Kgate(k[i]) | q[i]
## weigths
def init_weights(modes, layers, active_sd=0.0001, passive_sd=0.1):
"""Initialize a 2D TensorFlow Variable containing normally-distributed
random weights for an ``N`` mode quantum neural network with ``L`` layers.
Args:
modes (int): the number of modes in the quantum neural network
layers (int): the number of layers in the quantum neural network
active_sd (float): the standard deviation used when initializing
the normally-distributed weights for the active parameters
(displacement, squeezing, and Kerr magnitude)
passive_sd (float): the standard deviation used when initializing
the normally-distributed weights for the passive parameters
(beamsplitter angles and all gate phases)
Returns:
tf.Variable[tf.float32]: A TensorFlow Variable of shape
``[layers, 2*(max(1, modes-1) + modes**2 + modes)]``, where the Lth
row represents the layer parameters for the Lth layer.
"""
# Number of interferometer parameters:
M = int(modes * (modes - 1)) + max(1, modes - 1)
# Create the TensorFlow variables
int1_weights = tf.random.normal(shape=[layers, M], stddev=passive_sd)
s_weights = tf.random.normal(shape=[layers, modes], stddev=active_sd)
int2_weights = tf.random.normal(shape=[layers, M], stddev=passive_sd)
dr_weights = tf.random.normal(shape=[layers, modes], stddev=active_sd)
dp_weights = tf.random.normal(shape=[layers, modes], stddev=passive_sd)
k_weights = tf.random.normal(shape=[layers, modes], stddev=active_sd)
weights = tf.concat(
[int1_weights, s_weights, int2_weights, dr_weights, dp_weights, k_weights], axis=1
)
weights = tf.Variable(weights)
return weights
## Optimization
##set the random seed
tf.random.set_seed(137)
np.random.seed(137)
##define width and depth of CV quantum neural network
modes = 2
layers = 20
cutoff_dim = 400
## engine
##initialize engine and program
eng = sf.Engine(backend="tf", backend_options={"cutoff_dim": cutoff_dim})
qnn = sf.Program(modes)
##initialize QNN weights
weights = init_weights(modes, layers) # our TensorFlow weights
num_params = np.prod(weights.shape) # total number of parameters in our model
## constructor
#Create array of Strawberry Fields symbolic gate arguments, matching
#the size of the weights Variable.
sf_params = np.arange(num_params).reshape(weights.shape).astype(str)
sf_params = np.array([qnn.params(*i) for i in sf_params])
##Construct the symbolic Strawberry Fields program by
##looping and applying layers to the program.
with qnn.context as q:
#Encodig
ops.Dgate(data[:, :1]) | q[0]
ops.Dgate(data[:, 1:]) | q[1]
for k in range(layers):
layer(sf_params[k], q)
## cost
def cost(weights):
# Create a dictionary mapping from the names of the Strawberry Fields
# symbolic gate parameters to the TensorFlow weight values.
mapping = {p.name: w for p, w in zip(sf_params.flatten(), tf.reshape(weights, [-1]))}
# run the engine
state = eng.run(qnn, args=mapping).state
ket = state.ket()
difference = tf.reduce_sum(tf.abs(ket - target_state))
fidelity = tf.abs(tf.reduce_sum(tf.math.conj(ket) * target_state)) ** 2
return difference, fidelity, ket, tf.math.real(state.trace())
## train
##set up the optimizer
opt = tf.keras.optimizers.Adam()
cost_before, fidelity_before, _, _ = cost(weights)
##Perform the optimization
for i in range(20):
# reset the engine if it has already been executed
if eng.run_progs:
eng.reset()
with tf.GradientTape() as tape:
loss, fid, ket, trace = cost(weights)
# one repetition of the optimization
gradients = tape.gradient(loss, weights)
opt.apply_gradients(zip([gradients], [weights]))
# Prints progress at every rep
if i % 1 == 0:
print("Rep: {} Cost: {:.4f} Fidelity: {:.4f} Trace: {:.4f}".format(i, loss, fid, trace))
print("\nFidelity before optimization: ", fidelity_before.numpy())
print("Fidelity after optimization: ", fid.numpy())
print("\nTarget state: ", target_state.numpy())
print("Output state: ", np.round(ket.numpy(), decimals=3))
And this is the error:
---------------------------------------------------------------------------
TypingError Traceback (most recent call last)
Cell In[1], line 242
237 ## train
238
239
240 ##set up the optimizer
241 opt = tf.keras.optimizers.Adam()
--> 242 cost_before, fidelity_before, _, _ = cost(weights)
244 ##Perform the optimization
245 for i in range(20):
246 # reset the engine if it has already been executed
Cell In[1], line 228, in cost(weights)
225 mapping = {p.name: w for p, w in zip(sf_params.flatten(), tf.reshape(weights, [-1]))}
227 # run the engine
--> 228 state = eng.run(qnn, args=mapping).state
229 ket = state.ket()
231 difference = tf.reduce_sum(tf.abs(ket - target_state))
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\engine.py:570, in LocalEngine.run(self, program, args, compile_options, **kwargs)
565 if c.op.measurement_deps and eng_run_options["shots"] > 1:
566 raise NotImplementedError(
567 "Feed-forwarding of measurements cannot be used together with multiple shots."
568 )
--> 570 return super()._run(
571 program_lst, args=args, compile_options=compile_options, **eng_run_options
572 )
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\engine.py:306, in BaseEngine._run(self, program, args, compile_options, **kwargs)
303 p.bind_params(args)
304 p.lock()
--> 306 _, self.samples, self.samples_dict = self._run_program(p, **kwargs)
307 self.run_progs.append(p)
309 if isinstance(p, TDMProgram) and received_rolled:
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\engine.py:430, in LocalEngine._run_program(self, prog, **kwargs)
427 for cmd in prog.circuit:
428 try:
429 # try to apply it to the backend and, if op is a measurement, store it in values
--> 430 val = cmd.op.apply(cmd.reg, self.backend, **kwargs)
431 if val is not None:
432 for i, r in enumerate(cmd.reg):
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\ops.py:508, in Gate.apply(self, reg, backend, **kwargs)
506 temp = [rr.ind for rr in reg]
507 # call the child class specialized _apply method
--> 508 self._apply(temp, backend, **kwargs)
509 self.p[0] = original_p0
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\ops.py:1537, in Dgate._apply(self, reg, backend, **kwargs)
1533 r, phi = par_evaluate(self.p)
1535 self._check_for_complex_args([r, phi], "Dgate(r, phi)")
-> 1537 backend.displacement(r, phi, *reg)
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\backends\tfbackend\backend.py:189, in TFBackend.displacement(self, r, phi, mode)
187 with tf.name_scope("Displacement"):
188 remapped_mode = self._remap_modes(mode)
--> 189 self.circuit.displacement(r, phi, remapped_mode)
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\backends\tfbackend\circuit.py:446, in Circuit.displacement(self, r, phi, mode)
444 r = self._maybe_batch(r)
445 phi = self._maybe_batch(phi)
--> 446 new_state = ops.displacement(
447 r,
448 phi,
449 mode,
450 self._state,
451 self._cutoff_dim,
452 self._state_is_pure,
453 self._batched,
454 dtype=self._dtype,
455 )
456 self._update_state(new_state)
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\backends\tfbackend\ops.py:1047, in displacement(r, phi, mode, in_modes, cutoff, pure, batched, dtype)
1045 r = tf.cast(r, dtype)
1046 phi = tf.cast(phi, dtype)
-> 1047 matrix = displacement_matrix(r, phi, cutoff, batched, dtype)
1048 output = single_mode_gate(matrix, mode, in_modes, pure, batched)
1049 return output
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\backends\tfbackend\ops.py:354, in displacement_matrix(r, phi, cutoff, batched, dtype)
347 if batched:
348 return tf.stack(
349 [
350 single_displacement_matrix(r_, phi_, cutoff, dtype=dtype.as_numpy_dtype)
351 for r_, phi_ in tf.transpose([r, phi])
352 ]
353 )
--> 354 return single_displacement_matrix(r, phi, cutoff, dtype=dtype.as_numpy_dtype)
File C:\ProgramData\anaconda3\Lib\site-packages\tensorflow\python\ops\custom_gradient.py:343, in Bind.__call__(self, *a, **k)
342 def __call__(self, *a, **k):
--> 343 return self._d(self._f, a, k)
File C:\ProgramData\anaconda3\Lib\site-packages\tensorflow\python\ops\custom_gradient.py:297, in custom_gradient.<locals>.decorated(wrapped, args, kwargs)
295 """Decorated function with custom gradient."""
296 if context.executing_eagerly():
--> 297 return _eager_mode_decorator(wrapped, args, kwargs)
298 else:
299 return _graph_mode_decorator(wrapped, args, kwargs)
File C:\ProgramData\anaconda3\Lib\site-packages\tensorflow\python\ops\custom_gradient.py:543, in _eager_mode_decorator(f, args, kwargs)
541 """Implement custom gradient decorator for eager mode."""
542 with record.VariableWatcher() as variable_watcher:
--> 543 result, grad_fn = f(*args, **kwargs)
544 flat_args = composite_tensor_gradient.get_flat_tensors_for_gradients(
545 nest.flatten(args))
546 flat_kwargs = composite_tensor_gradient.get_flat_tensors_for_gradients(
547 nest.flatten(kwargs))
File C:\ProgramData\anaconda3\Lib\site-packages\strawberryfields\backends\tfbackend\ops.py:331, in single_displacement_matrix(r, phi, cutoff, dtype)
329 r = r.numpy()
330 phi = phi.numpy()
--> 331 gate = displacement_tw(r, phi, cutoff, dtype)
333 # NOTE: tested when testing the gate and its gradients; also tested independently in thewalrus
334 def grad(dy): # pragma: no cover
File C:\ProgramData\anaconda3\Lib\site-packages\numba\core\dispatcher.py:468, in _DispatcherBase._compile_for_args(self, *args, **kws)
464 msg = (f"{str(e).rstrip()} \n\nThis error may have been caused "
465 f"by the following argument(s):\n{args_str}\n")
466 e.patch_message(msg)
--> 468 error_rewrite(e, 'typing')
469 except errors.UnsupportedError as e:
470 # Something unsupported is present in the user code, add help info
471 error_rewrite(e, 'unsupported_error')
File C:\ProgramData\anaconda3\Lib\site-packages\numba\core\dispatcher.py:409, in _DispatcherBase._compile_for_args.<locals>.error_rewrite(e, issue_type)
407 raise e
408 else:
--> 409 raise e.with_traceback(None)
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<built-in function array>) found for signature:
>>> array(list(array(complex128, 2d, C))<iv=None>)
There are 2 candidate implementations:
- Of which 2 did not match due to:
Overload in function 'impl_np_array': File: numba\np\arrayobj.py: Line 5240.
With argument(s): '(list(array(complex128, 2d, C))<iv=None>)':
Rejected as the implementation raised a specific error:
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<intrinsic np_array>) found for signature:
>>> np_array(list(array(complex128, 2d, C))<iv=None>, none)
There are 2 candidate implementations:
- Of which 2 did not match due to:
Intrinsic in function 'np_array': File: numba\np\arrayobj.py: Line 5214.
With argument(s): '(list(array(complex128, 2d, C))<iv=None>, none)':
Rejected as the implementation raised a specific error:
TypingError: array(complex128, 2d, C) not allowed in a homogeneous sequence
raised from C:\ProgramData\anaconda3\Lib\site-packages\numba\core\typing\npydecl.py:482
During: resolving callee type: Function(<intrinsic np_array>)
During: typing of call at C:\ProgramData\anaconda3\Lib\site-packages\numba\np\arrayobj.py (5251)
File "..\..\ProgramData\anaconda3\Lib\site-packages\numba\np\arrayobj.py", line 5251:
def impl(object, dtype=None):
return np_array(object, dtype)
^
raised from C:\ProgramData\anaconda3\Lib\site-packages\numba\core\typeinfer.py:1086
During: resolving callee type: Function(<built-in function array>)
During: typing of call at C:\ProgramData\anaconda3\Lib\site-packages\thewalrus\fock_gradients.py (62)
File "..\..\ProgramData\anaconda3\Lib\site-packages\thewalrus\fock_gradients.py", line 62:
def displacement(r, phi, cutoff, dtype=np.complex128): # pragma: no cover
<source elided>
sqrt = np.sqrt(np.arange(cutoff, dtype=dtype))
mu = np.array([r * np.exp(1j * phi), -r * np.exp(-1j * phi)])
^
These are the versions I’m using: Tensorflow 2.13.0, strawberryfields 0.23.0