Fraud Detection code using Strawberryfields

Hello, @nathan or @Maria_Schuld or @Tom_Bromley I am working on the github repo of fraud detection code. https://github.com/XanaduAI/quantum-neural-networks/blob/master/fraud_detection/fraud_detection.py
I am getting an error when ever I am executing -

# construct the two-mode Strawberry Fields engine
eng, q = sf.Engine(mode_number)

# construct the circuit
with eng:
    input_qnn_layer()

    for i in range(depth):
        qnn_layer(i)

# run the engine (in batch mode)
state = eng.run("tf", cutoff_dim=cutoff, eval=False, batch_size=batch_size)
# extract the state
ket = state.ket()

I am getting error like -

TypeError Traceback (most recent call last)
in ()
3 # ===================================================================================
4 # construct the two-mode Strawberry Fields engine
----> 5 eng, q = sf.Engine(mode_number)
6
7 # construct the circuit
/usr/local/lib/python3.7/dist-packages/strawberryfields/engine.py in init(self, backend, backend_options)
66 self.backend = backend
67 else:
—> 68 raise TypeError(“backend must be a string or a BaseBackend instance.”)
69
70 @abc.abstractmethod
TypeError: backend must be a string or a BaseBackend instance.

I have modified the code as per the latest coding convention of strawberryfield plugins -

eng= sf.Engine(backend="tf")

q = sf.Program(mode_number)

# construct the circuit

with eng:

    input_qnn_layer()

    for i in range(depth):

        qnn_layer(i)

# run the engine (in batch mode)

state = eng.run("tf", cutoff_dim=cutoff, eval=False, batch_size=batch_size)

# extract the state

ket = state.ket()

However getting an error like -

AttributeError Traceback (most recent call last)
in ()
8
9 # construct the circuit
—> 10 with eng:
11 input_qnn_layer()
12
AttributeError: enter

Please suggest something.

Hey @Satanik_Mitra,

I am having trouble reproducing your error but would like to help!

Could you post a minimum working example, i.e., a small code block that I can just copy & paste and execute, including all imports and variables? And can you confirm your SF version (import strawberryfields as sf and then print the output of sf.about()) ? Thanks!

PS: Maybe reinstalling SF can solve the problem…your errors look like something is very genuinely wrong with running your code.

import numpy as np
import os

import tensorflow as tf

import strawberryfields as sf
from strawberryfields.ops import Dgate, BSgate, Kgate, Sgate, Rgate
import sys
sys.path.append("..")
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()


# ===================================================================================
#                                   Hyperparameters
# ===================================================================================

# Two modes required: one for "genuine" transactions and one for "fradulent"
mode_number = 2
# Number of photonic quantum layers
depth = 4

# Fock basis truncation
cutoff = 10
# Number of batches in optimization
reps = 30000

# Label for simulation
simulation_label = 1

# Number of batches to use in the optimization
batch_size = 24

# Random initialization of gate parameters
sdev_photon = 0.1
sdev = 1

# Variable clipping values
disp_clip = 5
sq_clip = 5
kerr_clip = 1

# If loading from checkpoint, previous batch number reached
ckpt_val = 0

# Number of repetitions between each output to TensorBoard
tb_reps = 100
# Number of repetitions between each model save
savr_reps = 1000

model_string = str(simulation_label)

# Target location of output
folder_locator = './outputs/'

# Locations of TensorBoard and model save outputs
board_string = folder_locator + 'tensorboard/' + model_string + '/'
checkpoint_string = folder_locator + 'models/' + model_string + '/'

# ===================================================================================
#                                   Loading the training data
# ===================================================================================

# Data outputted from data_processor.py
data_genuine = np.loadtxt('creditcard_genuine_1.csv', delimiter=',')
data_fraudulent = np.loadtxt('creditcard_fraudulent_1.csv', delimiter=',')

# Combining genuine and fraudulent data
data_combined = np.append(data_genuine, data_fraudulent, axis=0)
data_points = len(data_combined)

# ===================================================================================
#                                   Setting up the classical NN input
# ===================================================================================

# Input neurons
input_neurons = 10
# Widths of hidden layers
nn_architecture = [10, 10]
# Output neurons of classical part
output_neurons = 14

# Defining classical network parameters
input_classical_layer = tf.placeholder(tf.float32, shape=[batch_size, input_neurons])

layer_matrix_1 = tf.Variable(tf.random_normal(shape=[input_neurons, nn_architecture[0]]))
offset_1 = tf.Variable(tf.random_normal(shape=[nn_architecture[0]]))

layer_matrix_2 = tf.Variable(tf.random_normal(shape=[nn_architecture[0], nn_architecture[1]]))
offset_2 = tf.Variable(tf.random_normal(shape=[nn_architecture[1]]))

layer_matrix_3 = tf.Variable(tf.random_normal(shape=[nn_architecture[1], output_neurons]))
offset_3 = tf.Variable(tf.random_normal(shape=[output_neurons]))

# Creating hidden layers and output
layer_1 = tf.nn.elu(tf.matmul(input_classical_layer, layer_matrix_1) + offset_1)
layer_2 = tf.nn.elu(tf.matmul(layer_1, layer_matrix_2) + offset_2)

output_layer = tf.nn.elu(tf.matmul(layer_2, layer_matrix_3) + offset_3)

# ===================================================================================
#                                   Defining QNN parameters
# ===================================================================================

# Number of beamsplitters in interferometer
bs_in_interferometer = int(1.0 * mode_number * (mode_number - 1) / 2)

with tf.name_scope('variables'):
    bs_variables = tf.Variable(tf.random_normal(shape=[depth, bs_in_interferometer, 2, 2]
                                                , stddev=sdev))
    phase_variables = tf.Variable(tf.random_normal(shape=[depth, mode_number, 2], stddev=sdev))

    sq_magnitude_variables = tf.Variable(tf.random_normal(shape=[depth, mode_number]
                                                          , stddev=sdev_photon))
    sq_phase_variables = tf.Variable(tf.random_normal(shape=[depth, mode_number]
                                                      , stddev=sdev))
    disp_magnitude_variables = tf.Variable(tf.random_normal(shape=[depth, mode_number]
                                                            , stddev=sdev_photon))
    disp_phase_variables = tf.Variable(tf.random_normal(shape=[depth, mode_number]
                                                        , stddev=sdev))
    kerr_variables = tf.Variable(tf.random_normal(shape=[depth, mode_number], stddev=sdev_photon))

parameters = [layer_matrix_1, offset_1, layer_matrix_2, offset_2, layer_matrix_3, offset_3, bs_variables,
              phase_variables, sq_magnitude_variables, sq_phase_variables, disp_magnitude_variables,
              disp_phase_variables, kerr_variables]


# ===================================================================================
#                                   Constructing quantum layers
# ===================================================================================


# Defining input QNN layer, whose parameters are set by the outputs of the classical network
def input_qnn_layer():
    with tf.name_scope('inputlayer'):
        Sgate(tf.clip_by_value(output_layer[:, 0], -sq_clip, sq_clip), output_layer[:, 1]) | q[0]
        Sgate(tf.clip_by_value(output_layer[:, 2], -sq_clip, sq_clip), output_layer[:, 3]) | q[1]

        BSgate(output_layer[:, 4], output_layer[:, 5]) | (q[0], q[1])

        Rgate(output_layer[:, 6]) | q[0]
        Rgate(output_layer[:, 7]) | q[1]

        Dgate(tf.clip_by_value(output_layer[:, 8], -disp_clip, disp_clip), output_layer[:, 9]) \
        | q[0]
        Dgate(tf.clip_by_value(output_layer[:, 10], -disp_clip, disp_clip), output_layer[:, 11]) \
        | q[1]

        Kgate(tf.clip_by_value(output_layer[:, 12], -kerr_clip, kerr_clip)) | q[0]
        Kgate(tf.clip_by_value(output_layer[:, 13], -kerr_clip, kerr_clip)) | q[1]


# Defining standard QNN layers
def qnn_layer(layer_number):
    with tf.name_scope('layer_{}'.format(layer_number)):
        BSgate(bs_variables[layer_number, 0, 0, 0], bs_variables[layer_number, 0, 0, 1]) \
        | (q[0], q[1])

        for i in range(mode_number):
            Rgate(phase_variables[layer_number, i, 0]) | q[i]

        for i in range(mode_number):
            Sgate(tf.clip_by_value(sq_magnitude_variables[layer_number, i], -sq_clip, sq_clip),
                  sq_phase_variables[layer_number, i]) | q[i]

        BSgate(bs_variables[layer_number, 0, 1, 0], bs_variables[layer_number, 0, 1, 1]) \
        | (q[0], q[1])

        for i in range(mode_number):
            Rgate(phase_variables[layer_number, i, 1]) | q[i]

        for i in range(mode_number):
            Dgate(tf.clip_by_value(disp_magnitude_variables[layer_number, i], -disp_clip,
                                   disp_clip), disp_phase_variables[layer_number, i]) | q[i]

        for i in range(mode_number):
            Kgate(tf.clip_by_value(kerr_variables[layer_number, i], -kerr_clip, kerr_clip)) | q[i]


# ===================================================================================
#                                   Defining QNN
# ===================================================================================

# construct the two-mode Strawberry Fields engine
eng, q = sf.Engine(mode_number)

# construct the circuit
with eng:
    input_qnn_layer()

    for i in range(depth):
        qnn_layer(i)

# run the engine (in batch mode)
state = eng.run("tf", cutoff_dim=cutoff, eval=False, batch_size=batch_size)
# extract the state
ket = state.ket()

@Maria_Schuld Now in the defining QNN part I am getting the error. I have to change the tensorflow version as the placeholder method is not working with current TF version. My strawberryField version is -

Strawberry Fields: a Python library for continuous-variable quantum circuits.
Copyright 2018-2020 Xanadu Quantum Technologies Inc.

Python version: 3.7.10
Platform info: Linux-5.4.109±x86_64-with-Ubuntu-18.04-bionic
Installation path: /usr/local/lib/python3.7/dist-packages/strawberryfields
Strawberry Fields version: 0.18.0
Numpy version: 1.19.5
Scipy version: 1.4.1
SymPy version: 1.7.1
NetworkX version: 2.5.1
The Walrus version: 0.15.1
Blackbird version: 0.3.0
TensorFlow version: 2.4.1

Thanks for the details! It’s quite a task to bring this old code example up to scratch with latest StrawberryFields & TensorFlow code :slight_smile:

While I haven’t gotten the entire code example to work, there are a few obvious things you could try and fix:

The latest SF syntax requires you to use the program as a context and then create an engine with the right cutoff defined, i.e.,

prog = sf.Program(mode_number)

# construct the circuit
with prog.context as q:

    # construct circuit here

# run the engine 
eng = sf.Engine(backend="tf", backend_options={"cutoff_dim": 5})
state = eng.run(prog)

Furthermore, if I see it correctly, you will have to pass q (which is the program) to the functions that define the layers. Otherwise these functions cannot know what q - the quantum register - actually is. So, maybe try

# construct the circuit
with prog.context as q:

    input_qnn_layer(q)

    for i in range(depth):
        qnn_layer(q, i)

and define the layers with the expression def input_qnn_layer(q):, so they use the q from the program’s context.

I hope these observations will help you to make it work, if not let me know :slight_smile:

And one more tip: when you are debugging, just delete all the clutter and reduce the circuit to a single gate. If the error persists you can use the small piece of code to find the errors, and only add more complexity once the errors are all solved. This will also make our life a lot easier!

Oh and one more comment: You may find it easier to switch to PennyLane, where this demo describes a similar example.

Thank You so much @Maria_Schuld for such a wonderful response. I do implement the changes and come back to you in case any error appears :slight_smile: :slight_smile:

One more thing @Maria_Schuld can you confirm me which strawberryfield and tensorflow version have you used while writing the original code. I was checking the link - https://github.com/XanaduAI/strawberryfields/releases
If possible I can downgrade the strawberry version.

Oh yes, good idea. You find the versions in the requirements.txt file of the repo:

strawberryfields==0.10
tensorflow==1.3

@Maria_Schuld it seems downgrading is difficult. however I am going with the current version of tf and strawberryfields. I have implemented the circuit as per your suggestion that is -

prog = sf.Program(mode_number)

# construct the circuit
with prog.context as q:

input_qnn_layer(q)

for i in range(depth):
    qnn_layer(q,i)

# run the engine 
eng = sf.Engine(backend="tf", backend_options={"cutoff_dim": 5})
state = eng.run(prog) 

I am getting an error like

ValueError: The custom_gradient decorator currently supports keywords arguments only when eager execution is enabled.

Hey @Satanik_Mitra!

it seems downgrading is difficult.

Yes potentially, are you struggling with getting the old version of TF? You can download wheels for TF v0.3 here - note that we also suggest using Python 3.5 or 3.6 (see here).

If you’re just curious to get the code running, it might be simpler to downgrade your dependencies. However, it would be nice to get the fraud detection code updated and compatible with more recent versions of SF and TF. Regarding your custom_gradient decorator error, this is likely due to

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

Ideally, we won’t need to import the v1-compatible version of TensorFlow. If you remove these lines, you’ll get errors like:

AttributeError: module 'tensorflow' has no attribute 'placeholder'

This means a comprehensive update of the script is needed to follow the TF 2.0 style. The best resource for doing this within the context of SF is our ML tutorial here.

If you do manage to successfully update the fraud detection code, we’d be very happy if you make a pull request to the repo!

Thanks,
Tom

Hi @Tom_Bromley and @Maria_Schuld I have successfully executed the code for fraud detection with tensorflow=1.3.0 and SF=0.10. However, I am trying to implement it with current version of SF and TF. I will let you know if I succeed.
Meanwhile, I was wondering about one thing in fraud detection. Why 10 features are chosen from the dataset, why can’t we choose 14 instead? If you kindly put some light onto it.

Hi @Satanik_Mitra!

Why 10 features are chosen from the dataset, why can’t we choose 14 instead?

I believe we just used 10 to keep things manageable, and it may well work if you increase to 14 features. In fact, the original dataset has 28 features, so you could include all of those as long as you ensure there are 14 features input into the quantum part of the network.

However, bear in mind that the 28 features are a result of a PCA, and (this needs to be double checked) the features are ordered by importance. We take the first 10 features, and each new feature added after that may have a diminished usefulness when training the model.

Thanks a lot @Tom_Bromley :slight_smile:

Glad to see you have it solved @Satanik_Mitra :slight_smile: I’m going to close this post as your question is resolved - feel free to begin a new post if you have any other questions!