Error with binary classification

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

Hey @Cristian_F_Zapata! Welcome to the forum :slight_smile:

I’ll have to get back to you on this one :thinking:. Hang tight and I’ll get back to you as soon as I can :slight_smile:

I can’t narrow down precisely what the issue is, but it seems to not be an SF issue — TypingError: Failed in nopython mode pipeline means that Numba can’t compile the python source code. So, there’s something much lower-level going on with your code here.

I’d encourage you to do a Google search for the error message — I did so and there were a lot of results to sift through. Let us know if you find the solution!

1 Like