text:
# Quantum Autoencoder with MNIST classification

## What’s a quantum autoencoder and why should you care?

Quantum autoencoders are awesome but you may be wondering what’s even a normal autoencoder and what benefits come from it. To answer this we must give a brief overview of classical autoencoders. In the deep learning field it is common to see/use huge neural networks (a neural network it’s just a set of interconnected neurons) to achive a goal (classify images, generate images, create music, etc..). But what if we could achive the same goal with fewer neurons? That would be awesome. Well, we can achive that by using an autoencoder. That is to generate a smaller neural network while getting the same results approximately.

Now, a quantum autoencoder applies the same principle of a classical autoencoder but instead of applying the process to neurons we apply the process to a statevector. So informally, a quantum state autoencoder is a circuit that takes a statevector as input and it outputs a reduced version of that statevector. And to get the original statevector (approximately) from the encoded statevector we can apply the opposite of a QAE, that is, a Quantum state decoder. This is a great idea because as you might know we don’t get access to a lot of resources in NISQ machines so by applying a QAE we can reduce the use of those resources.

Graphically, a quantum state autoencoder and decoder can be seen as the following:

As you can see in the image, we have a $$4\times4$$ statevector that we want to encode into a $$2\times2$$ statevector. We can do this by applying the autoencoder to our circuit. One thing worth noticing is that two qubits were set to 0 state in the process. Nonetheless, there wasn’t a loss of information because we encoded that information inside the two last qubits. Now, if we want to have the original $$4 \times 4$$ statevector we need to apply the decoder to our circuit. Notice that we have to include the qubits that were set in the 0 state.

The general process to construct a quantum autoencoder is [1] [6]:

• Generate a statevector that we want to reduce.
• Create an anzast for a set of parameters (this is represented in the image as the AUTOENCODER).
• Create the decoder by finding the inverse of the AUTOENCODER anzast.
• Get the cost of the AUTOENCODER by comparing the original state with the restored state from the DECODER.

## Objectives

Now that we saw what is and why autoencoders matter we propose in the following post how to construct one, step by step. And even further, We are going to apply it to a variational circuit that classifies images to show the capabilities of a quantum autoencoder. So the contents of this post are:

• First, we present the statevector creation that we want to reduce.
• Secondly, We present how to build a quantum autoencoder.
• Then, We present the decoder circuit.
• After that, We present how to optimize the circuits and see the results of this optimization process.
• Then, We present the quantum classifier and the results from a classification.
• Finally, we make some conclusions and present the list of references.

# Contruction of a quantum autoencoder

Below are the necessary imports for this project:

import numpy as np
# Quiskit libraries
from qiskit import QuantumCircuit, transpile, Aer, IBMQ, execute, QuantumRegister, ClassicalRegister
from qiskit.tools.jupyter import *
from qiskit.visualization import *
from qiskit.circuit import Parameter, ParameterVector

#MNIST set libraries for the acquisition and pre-processing data.
import tensorflow as tf

#Graph libraries
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme()

## Statevector generation

We initialize the data for our quantum circuit; using Tensorflow since it already has the MNIST dataset.

Each set has ten classes representing integer values from 0 to 9. For this project, we will only classify 0 and 1, so we filter them. And given that pixel values go from 0 to 255, we normalize them to get [0,1].

#Loading the MNIST set divided by a train set and a test set
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

# Rescale the images from [0,255] to the [0.0,1.0] range.
x_train, x_test = x_train[..., np.newaxis]/255.0, x_test[..., np.newaxis]/255.0

#Showing the length of the train and test sets
print("Number of images in the training set:", len(x_train))
print("Number of images in the test set:", len(x_test))

#Function to filter the 0 and 1 labels of the MNIST set
'''
Input = x_label and y_label sets
Output = x_label and y_label sets filtered
'''
def filter_01(x, y):
keep = (y == 0) | (y == 1)
x, y = x[keep], y[keep]
return x,y

x_train, y_train = filter_01(x_train, y_train) #Filter the train set
x_test, y_test = filter_01(x_test, y_test) #Filter the test set

#Showing the length of the train and test sets after filtering the data
print("Number of images in the training set:", len(x_train))
print("Number of images in the test set:", len(x_test))

Number of images in the training set: 12665
Number of images in the test set: 2115

Below there is an image with the original size 28x28 px

#Plotting the first element of the train set
plt.imshow(x_train[0, :, :, 0])
plt.colorbar()

<matplotlib.colorbar.Colorbar at 0x1db303d3040>

### Image resizing

Tensorflow has the function tf.image.resize that decreases the images by the following possible methods:

• bilinear: Bilinear interpolation. If antialias is true, becomes a hat/tent filter function with radius 1 when downsampling.
• lanczos3: Lanczos kernel with radius 3. High-quality practical filter but may have some ringing, especially on synthetic images.
• lanczos5: Lanczos kernel with radius 5. Very-high-quality filter but may have stronger ringing.
• bicubic: Cubic interpolant of Keys. Equivalent to Catmull-Rom kernel. Reasonably good quality and faster than Lanczos3Kernel, particularly when upsampling.
• gaussian: Gaussian kernel with radius 3, sigma = 1.5 / 3.0.
• nearest: Nearest neighbor interpolation. antialias has no effect when used with nearest neighbor interpolation.
• area: Anti-aliased resampling with area interpolation. antialias has no effect when used with area interpolation; it always anti-aliases.
• mitchellcubic: Mitchell-Netravali Cubic non-interpolating filter. For synthetic images (especially those lacking proper prefiltering), less ringing than Keys cubic kernel but less sharp.

We use the method nearest, resizing the image from 28x28px to 8x8px.

#resizing the image from 28x28 to 8x8 by the nearest method
x_train_small = tf.image.resize(x_train, (8,8), method='nearest', preserve_aspect_ratio=True).numpy()
x_test_small = tf.image.resize(x_test, (8,8), method='nearest', preserve_aspect_ratio=True).numpy()

We tranform our images into an amplitude state.

#Plotting the first element of the train set after the resizing
plt.imshow(x_train_small[0,:,:,0], vmin=0, vmax=1)
plt.colorbar()

<matplotlib.colorbar.Colorbar at 0x1db305a4b80>

#Reshaping the train and test test to a 64x1 matriz
x_train = x_train_small.reshape(len(x_train_small), 64)
x_test = x_test_small.reshape(len(x_test_small), 64)
x_train.shape,x_test.shape

((12665, 64), (2115, 64))

#Showing the first element of the train set
x_train_small[0]

array([[[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ]],

[[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.93333333],
[0.92941176],
[0.        ],
[0.        ]],

[[0.        ],
[0.        ],
[0.        ],
[0.98823529],
[0.99215686],
[0.74117647],
[0.        ],
[0.        ]],

[[0.        ],
[0.        ],
[0.69803922],
[0.0745098 ],
[0.        ],
[0.        ],
[0.76470588],
[0.        ]],

[[0.        ],
[0.        ],
[0.98823529],
[0.        ],
[0.        ],
[0.        ],
[0.58039216],
[0.        ]],

[[0.        ],
[0.        ],
[0.88235294],
[0.        ],
[0.44705882],
[0.        ],
[0.        ],
[0.        ]],

[[0.        ],
[0.        ],
[0.98823529],
[0.98823529],
[0.        ],
[0.        ],
[0.        ],
[0.        ]],

[[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ],
[0.        ]]])

### Transforming into qubits

Since the limited equipment (CPU) and its capabilities, it’s not possible to use all images of the MNIST dataset. That’s why we will experiment with the following eight options:

• The first 5 images of the training set.
• The first 10 images of the training set.
• The first 12 images of the training set.
• The first 50 images of the training set.
• The first 70 images of the training set.
• The first 100 images of the training set.
• The first 200 images of the training set.
• The first 500 images of the training set.
x_train = (x_train)
x_test = (x_test)

x_train.shape,x_test.shape

((12665, 64), (2115, 64))

We convert the 8x8 matrix of each image into a 64x1 vector.

#Showing the first element of the train set
x_train[0]

array([0.        , 0.        , 0.        , 0.        , 0.        ,
0.        , 0.        , 0.        , 0.        , 0.        ,
0.        , 0.        , 0.93333333, 0.92941176, 0.        ,
0.        , 0.        , 0.        , 0.        , 0.98823529,
0.99215686, 0.74117647, 0.        , 0.        , 0.        ,
0.        , 0.69803922, 0.0745098 , 0.        , 0.        ,
0.76470588, 0.        , 0.        , 0.        , 0.98823529,
0.        , 0.        , 0.        , 0.58039216, 0.        ,
0.        , 0.        , 0.88235294, 0.        , 0.44705882,
0.        , 0.        , 0.        , 0.        , 0.        ,
0.98823529, 0.98823529, 0.        , 0.        , 0.        ,
0.        , 0.        , 0.        , 0.        , 0.        ,
0.        , 0.        , 0.        , 0.        ])

Images that have no valuable information for the training set, i.e. zero vectors, are deleted.

#Deleting no valuable information for the training set
k = 0 c

while k < len(x_train):
a = x_train[k].copy() #make a copy of the actual element
#Verfify if it has some valuable data
if np.sum(a) == 0.:
#If not has valuable data
print(k,x_train[k])
x_train = np.delete(x_train, k, axis=0) #Delete the actual element from the x_label
y_train = np.delete(y_train, k, axis=0) #Delete the actual element from the y_label
k -= 1 #Take back one value of the counter to match the new set length
k+=1

Images that have no valuable information for the test set, i.e. zero vectors, are deleted.

#Deleting no valuable information for the test set
k = 0

while k < len(x_test): #Deleting no valuable information for the training set
a = x_test[k].copy()
#Verfify if it has some valuable data
if np.sum(a) == 0.:
#If not has valuable data
print(k,x_test[k])
x_test = np.delete(x_test, k, axis=0) #Delete the actual element from the x_label
y_test = np.delete(y_test, k, axis=0) #Delete the actual element from the y_label
k -= 1 #Take back one value of the counter to match the new set length
k+=1

Now, we renormalize so we can interpret a vector state and then apply it to our quantum circuit model according to the next mathematical expression:

$$\frac{input-vector}{\sqrt{\sum_{i=0}^{n-1} (input-vector_i)^2}}$$, Where input-vector is the 64x1 vector that we’ll tranform into a vector state $$| \psi \rangle$$.

import cmath
#Funtion to normalize the data of an array
'''
Input = Array with n values
Output = Array with normalized valued
'''
def Normalize(row):
#We calculate the squareroot of the sum of the square values of the row
suma = np.sqrt(np.sum(row**2))
if suma == 0.:
#If the sum is zero we return a 0
return 0.0
#Else we divide each value between the sum value above
row = row/suma
return row

#Normalize the training set data
for i in range(len(x_train)):
x_train[i] = Normalize(x_train[i])

#Normalize the test set data
for i in range(len(x_test)):
x_test[i] = Normalize(x_test[i])

#Showing the state sum of the training set
print("The sum of the states from the training set 0",np.sum(x_train[0]**2))

The sum of the states from the training set 0 1.0

## Construction of the Autoencoder

For this part 6 qubits are considered, since 64 = $$2^6$$. We’ll map with the amplitude method, and just with 1 layer.

Later on, the circuit used for this application will be shown.

n=6 #Number of qubits
num_layers = 1 #Number of layers
#Making a ndarray of floats based on the number of layers
params = np.random.random(10*(num_layers))

It’s verified that the input vector with index zero is normalized to a state vector.

#Showing the normalized values of the first element of the training set
x_train[0]

array([0.        , 0.        , 0.        , 0.        , 0.        ,
0.        , 0.        , 0.        , 0.        , 0.        ,
0.        , 0.        , 0.30173661, 0.30046881, 0.        ,
0.        , 0.        , 0.        , 0.        , 0.31948582,
0.32075362, 0.23961437, 0.        , 0.        , 0.        ,
0.        , 0.22566856, 0.02408822, 0.        , 0.        ,
0.24722117, 0.        , 0.        , 0.        , 0.31948582,
0.        , 0.        , 0.        , 0.18763453, 0.        ,
0.        , 0.        , 0.2852552 , 0.        , 0.1445293 ,
0.        , 0.        , 0.        , 0.        , 0.        ,
0.31948582, 0.31948582, 0.        , 0.        , 0.        ,
0.        , 0.        , 0.        , 0.        , 0.        ,
0.        , 0.        , 0.        , 0.        ])

By entering the state vector as input to the initialize(vectorstate,qubits) function/method, we obtain a 6-qubit quantum circuit.

An observation of this function is that the computational cost may vary depending on the method generated, especially if 0 amplitude states predominate.

#Function to create a quantum circuit based on the number of qubit and a
#vector of complex amplitudes to initialize to
'''
Input: Number of qubits, vector of complex amplitudes
Output: Quantum Circuit
'''
def input_data(n,inputs):
circuit = QuantumCircuit(n,1) #create the quantum circuit with n qubits
#initialization of the circuit with the vector of amplitudes
circuit.initialize(inputs,range(0,n,1))
circuit.barrier() #Draw a barrier
return circuit

#Example of a quantum circuit with the first row of te trainig set
input_data(n,x_train[0]).draw(output="mpl")

In this step we created the quantum variational circuit that represents the quantum autoencoder. We considered as reference the quantum circuit proposed in [1], but some problems arose due to the computational cost, so we also considered [2], [3], [4], [5] to generate our own.

#Function to create a quantum variational circuit
'''
Input: number of qubits, number of layers, parameters to initialized the circuit
Output: Quantum Circuit
'''
def vqc(n, num_layers,params):
#Set the number of layers and qubits
#ParameterVectors are initialized with a string identifier and an integer specifying the vector length
parameters = ParameterVector('θ', 10*(num_layers))
len_p = len(parameters)
circuit = QuantumCircuit(n, 1) #create the quantum circuit with n qubits

#Creating the circuit for each layer
for layer in range(num_layers):
#Applying a ry gate in each qubit
for i in range(n):
#the rotation of the ry gate is defined in the parameters list
#based on the layer
circuit.ry(parameters[(layer)+i], i)
circuit.barrier() #Create a barrier

circuit.cx(2,0) #Apply a CNOT gate between the qubit 2 and 0
circuit.cx(3,1) #Apply a CNOT gate between the qubit 3 and 1
circuit.cx(5,4) #Apply a CNOT gate between the qubit 5 and 4
circuit.barrier() #Create a barrier

#Apply a RY gate in the qubit 0 with the rotation specified in the parameter list
circuit.ry(parameters[6+(layer)],0)
#Apply a RY gate in the qubit 1 with the rotation specified in the parameter list
circuit.ry(parameters[7+(layer)],1)
#Apply a RY gate in the qubit 4 with the rotation specified in the parameter list
circuit.ry(parameters[8+(layer)],4)
circuit.barrier() #Create a barrier

circuit.cx(4,1) #Apply a CNOT gate between the qubit 4 and 1
circuit.barrier() #Create a barrier

#Apply a RY gate in the qubit 1 with the rotation specified in the parameter list
circuit.ry(parameters[9+(layer)], 1)
circuit.barrier() #Create a barrier

#Creating a parameters dictionary
params_dict = {}
i = 0
for p in parameters:
#The name of the value will be the string identifier and an integer specifying the vector length
params_dict[p] = params[i]
i += 1
#Assign parameters using the assign_parameters method
circuit = circuit.assign_parameters(parameters = params_dict)
return circuit

The circuit of our tensor network is affected by 10 q-gates $$Ry(\theta)$$ and 4 $$C_{not}$$. Considering the cost as linked to the number of $$C_{not}$$, it will be 4.

#An example with 6 quibits, one layer and 10 parameters
vqc(n,num_layers,params).draw(output="mpl")

From [6] we considered the Swap-test to seek the value of y. Adittionally, we took the general idea of the swap test from [7] which is the following:

 Given two states $$\phi_0 \phi_1 \rangle$$, where the first one is the reference vector$$ 0 \rangle$$and the second one is state that we want to eliminate (with the autoencoder) to use a smaller number of qubits. If we apply the swap-test and after the measurement of the state$$ 0 \rangle$$we get the$$ 0 \rangle$$ state then we correctly encoded our qubits.
#Fucntion to make a swap test
'''
Input: Number of qubits
Output: Quantum circuit
'''
def swap_test(n):
qubits_values = 2*n+1 #Create a new qubit value to create our circuit
qc = QuantumCircuit(qubits_values) #Create the quantum circuit with the qubits value
qc.h(0) #Applying a H gate to the first qubit
for i in range(n):
#Applying a cswap gate between the first quibit and the i+1 and 2*n-i qubits
qc.cswap(0,i+1,2*n-i)
qc.h(0) #Applying a H gate to the first qubit
qc.barrier() #Create a barrier
return qc
#Example of a swap test with 2 quibits
swap_test(2).draw(output="mpl")

The following cell shows the circuit of the autoencoder to encode 6 qubits into 4 qubits.

size_reduce = 2 #Number of qubits we want to reduce
circuit_init = input_data(n,x_train[0]) #Create a inicial circuit
circuit_vqc = vqc(n,num_layers,params) #Create a quantum variational circuit
circuit_swap_test = swap_test(size_reduce) #Create a swap test circuit

#Create a new circuit based on the size of the initial circuit and the desired qubits to reduce
circuit_full = QuantumCircuit(n+size_reduce+1,1)

#Combine the initial circuit, the quantum variatinal circuit and the swap test
#For the initial circuit and QVC we start at the qubit size_reduce + 1
#For the swap test we start at the qubit 0
circuit_full = circuit_full.compose(circuit_init,[i for i in range(size_reduce+1,n+size_reduce+1)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(size_reduce+1,n+size_reduce+1)])
circuit_full = circuit_full.compose(circuit_swap_test,[i for i in range(2*size_reduce+1)])
circuit_full.draw(output="mpl")

## Quantum autoencoder inverse

Qiskit has an autoinverse function which generates the inverse of a circuit. We can use this function to generate the inverse of our variational circuit.

#We generate the inverse of the QVC
vqc(n,num_layers,params).inverse().draw(output = "mpl")

## Optimization

Now that we know how to construct a quantum autoencoder and it’s inverse we have to optimize the circuit. To do that, we are going to show how to optimize the variational circuit and after that we’ll see how well we did it we the help of a few metrics. We decided to use scikitlearn optimizers instead of qiskit’s because we couldn’t find a way to use it with an amplitude map. We also used the shuffle method to use some different images in each iteration of the optimizer to get better results.

from random import shuffle
from scipy.optimize import minimize

Now, to identify the cost we used the expected value of the z axis. This is defined by:

$\langle Z \rangle = \langle q | Z | q\rangle =\langle q|0\rangle\langle 0|q\rangle - \langle q|1\rangle\langle 1|q\rangle =|\langle 0 |q\rangle|^2 - |\langle 1 | q\rangle|^2$

And after integrating the swap-test criteria we get:

$1 -\langle Z \rangle = 1 - \langle q | Z | q\rangle = 1- [\langle q|0\rangle\langle 0|q\rangle - \langle q|1\rangle\langle 1|q\rangle] = 1 - [|\langle 0 |q\rangle|^2 - |\langle 1 | q\rangle|^2] = 1 - |\langle 0 |q\rangle|^2 + |\langle 1 | q\rangle|^2$

You can check this link for additional information on the expected value: https://qiskit.org/textbook/ch-labs/Lab02_QuantumMeasurement.html

#Function to identify a function cost
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Function cost
'''
def objective_function(params):
costo = 0
shuffle(x_train) #reorganize the order of the train set items
lenght= 5 #We only will consider the first five elements of the taining set
#For each item of the trainig set
for i in range(lenght):

circuit_init = input_data(n,x_train[i])#Create a inicial circuit
circuit_vqc = vqc(n,num_layers,params) #Create a quantum variational circuit
circuit_swap_test = swap_test(size_reduce) #Create a swap test circuit

#Create a new circuit based on the size of the initial circuit and the desired qubits to reduce
circuit_full = QuantumCircuit(n+size_reduce+1,1)

#Combine the initial circuit, the quantum variatinal circuit and the swap test
#For the initial circuit and QVC we start at the qubit size_reduce + 1
#For the swap test we start at the qubit 0
circuit_full = circuit_full.compose(circuit_init,[i for i in range(size_reduce+1,n+size_reduce+1)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(size_reduce+1,n+size_reduce+1)])
circuit_full = circuit_full.compose(circuit_swap_test,[i for i in range(2*size_reduce+1)])
circuit_full.measure(0, 0) #Measure the first qubit
#qc.draw()
shots= 8192 #Number of shots
#Execute the circuit in the qasm_simulator
job = execute( circuit_full, Aer.get_backend('qasm_simulator'),shots=shots )
counts = job.result().get_counts() #Count the results of the execution
probs = {} #Calculate the probabilities of 0 and 1 state
for output in ['0','1']:
if output in counts:
probs[output] = counts[output]/shots #Calculate the average of a state
else:
probs[output] = 0
costo += (1 +probs['1'] -  probs['0']) #Update the actual function cost

return costo/lenght

for i in range(1):
#Minimization of the objective_fucntion by a COBYLA method
minimum = minimize(objective_function, params, method='COBYLA', tol=1e-6)
params = minimum.x #Get the solution array
#Show the cost of the solution array
print(" cost: ",objective_function(params))
print(params)

 cost:  0.2796875
[ 0.62254674  0.26851417  0.18249782  1.72136515  2.0098116  -0.0834305
-1.52525618  0.47399993 -0.21121215 -0.16145395]

When we finish the iterations of the optimizer, if we apply the complex conjugate of the state vector we should get the original information according to [6] and [7]. This proccess is applied to the test set and the training set.

#Function to compress the test set values
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with compress values
'''
def compress_result_test(params):
reduce = [] #List to save the compress values
#For each row in the test set we will
for i in range(len(x_test)):

circuit_init = input_data(n,x_test[i]) #Create a inicial circuit
circuit_vqc = vqc(n,num_layers,params) #Create a quantum variational circuit

#Create a new circuit based on the size of the initial circuit and the desired qubits to reduce
circuit_full = QuantumCircuit(n,n-size_reduce)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
len_cf = len(circuit_full) #Known the length of the circuit
#For each n - the the desired qubits to reduce we will
for i in range(n-size_reduce):
circuit_full.measure(size_reduce+i, i) #Measure the circuit in the position size_reduce+i
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('qasm_simulator'),shots=8192 )
result = job.result().get_counts() #Get the results of the execution
#Get the probabilities of each state
probs = {k: np.sqrt(v / 8192) for k, v in result.items()}
reduce.append(probs) #Save the probabilities

return reduce

#Call the compress_result_test function with the parameters defined above
reduce_img =compress_result_test(params)
test_reduce = [] #List to save the new values of the image reduction
#for each value in the reduce_img list
for i in reduce_img:
index_image = [] #List to save the reduction values
#We now take in count we want a 4X4 image
for j in range(16):
bin_index = bin(j)[2:] #We take the binary value of j from the 2 position to the end
while len(bin_index) <4: #While bin_index is less than 4
bin_index = '0'+bin_index #We concatenate a 0 string at the beginnig
try:
#We try to save the element of the row in the position bin_index
index_image.append(i[bin_index])
except:
index_image.append(0) #If we can't, we only save a 0

#We save the new imagen values in the test_recuce list
test_reduce.append(np.array(index_image))

#Function to compress the training set values
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with compress values
'''
def compress_result_train(params):
reduce = [] #List to save the compress values
#For each row in the training set we will
for i in range(len(x_train)):
circuit_init = input_data(n,x_train[i]) #Create a inicial circuit
circuit_vqc = vqc(n,num_layers,params) #Create a quantum variational circuit

#Create a new circuit based on the size of the initial circuit and the desired qubits to reduce
circuit_full = QuantumCircuit(n,n-size_reduce)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
len_cf = len(circuit_full) #Known the length of the circuit
#For each n - the the desired qubits to reduce we will
for i in range(n-size_reduce):
circuit_full.measure(size_reduce+i, i) #Measure the circuit in the position size_reduce+i
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('qasm_simulator'),shots=8192 )
result = job.result().get_counts() #Get the results of the execution
#Get the probabilities of each state
probs = {k: np.sqrt(v / 8192) for k, v in result.items()}
reduce.append(probs) #Save the probabilities

return reduce

#Call the compress_result_train function with the parameters defined above
reduce_img =compress_result_train(params)
train_reduce = [] #List to save the new values of the image reduction
#for each value in the reduce_img list
for i in reduce_img:
index_image = [] #List to save the reduction values
#We now take in count we want a 4X4 image
for j in range(16):
bin_index = bin(j)[2:] #We take the binary value of j from the 2 position to the end
while len(bin_index) <4: #While bin_index is less than 4
bin_index = '0'+bin_index #We concatenate a 0 string at the beginnig
try:
#We try to save the element of the row in the position bin_index
index_image.append(i[bin_index])
except:
index_image.append(0) #If we can't, we only save a 0

#We save the new imagen values in the train_recuce list
train_reduce.append(np.array(index_image))

In the next cell we can appreciate the first 5 images of the test set which we reduced from a 8x8 size to 4x4 size.

plt.figure()

#subplot(r,c) provide the no. of rows and columns
f, axarr = plt.subplots(5,1)

# use the created array to output your multiple images. In this case I have stacked 4 images vertically
axarr[0].imshow(x_test[0].reshape(8,8)*255)
axarr[1].imshow(x_test[1].reshape(8,8)*255)
axarr[2].imshow(x_test[2].reshape(8,8)*255)
axarr[3].imshow(x_test[3].reshape(8,8)*255)
axarr[4].imshow(x_test[4].reshape(8,8)*255)

<matplotlib.image.AxesImage at 0x1db360c24c0>

<Figure size 432x288 with 0 Axes>

#subplot(r,c) provide the no. of rows and columns
f, axarr = plt.subplots(5,1)

# use the created array to output your multiple images. In this case I have stacked 4 images vertically
axarr[0].imshow(test_reduce[0].reshape(4,4)*255)
axarr[1].imshow(test_reduce[1].reshape(4,4)*255)
axarr[2].imshow(test_reduce[2].reshape(4,4)*255)
axarr[3].imshow(test_reduce[3].reshape(4,4)*255)
axarr[4].imshow(test_reduce[4].reshape(4,4)*255)

<matplotlib.image.AxesImage at 0x1db36449ee0>

### Data decompression

 After applying the complex conjugate, as we state earlier, we should get the orignal input value $$\phi \rangle$$
#We generate the inverse of the QVC
vqc(n,num_layers,params).inverse().draw(output = "mpl")

#Function to decode the test set values compressed
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with decode values
'''

def decoder_result_test(params):
reduce = [] #List to save the decoded values
#For each row in the test set reduced we will
for i in range(len(test_reduce)):

#Create a initial circuit with 6 qubits and a list of 48 zeros and the i row of the test reduced values
circuit_init = input_data(6,np.concatenate((np.zeros(48), test_reduce[i]), axis=0))
#Create the inverse VQC
circuit_vqc = vqc(n,num_layers,params).inverse()

#Create a new circuit to combine the inicial circuit and the VQC
circuit_full = QuantumCircuit(n,n)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('statevector_simulator') )
result = job.result().get_statevector() #Get the results of the execution
reduce.append(result) #Save the results
return reduce

#Call the decoder_result_test function
decoder =decoder_result_test(params)

plt.figure()

#subplot(r,c) provide the no. of rows and columns
f, axarr = plt.subplots(5,1)

# use the created array to output your multiple images. In this case I have stacked 4 images vertically
axarr[0].imshow(decoder[0].real.reshape(8,8)*255)
axarr[1].imshow(decoder[1].real.reshape(8,8)*255)
axarr[2].imshow(decoder[2].real.reshape(8,8)*255)
axarr[3].imshow(decoder[3].real.reshape(8,8)*255)
axarr[4].imshow(decoder[4].real.reshape(8,8)*255)

<matplotlib.image.AxesImage at 0x1db334b5100>

<Figure size 432x288 with 0 Axes>

#Function to decode the training set values compressed
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with decode values
'''
def decoder_result_train(params):
reduce = [] #List to save the decoded values
#For each row in the test set reduced we will
for i in range(len(train_reduce)):
#Create a initial circuit with 6 qubits and a list of 48 zeros and the i row of the test reduced values
circuit_init = input_data(n,np.concatenate((np.zeros(48), train_reduce[i]), axis=0))
#Create the inverse VQC
circuit_vqc = vqc(n,num_layers,params).inverse()

#Create a new circuit to combine the inicial circuit and the VQC
circuit_full = QuantumCircuit(n,n)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('statevector_simulator') )
result = job.result().get_statevector() #Get the results of the execution
reduce.append(result) #Save the results
return reduce

#Call the decoder_result_train function
decoder_train =decoder_result_train(params)

### How well we did it?

To find out how well we optimize the variational circuit we need to know how to compare images.

To compare the reconstructed images obtained from the autoencoder and the original images we propose the following metrics:

• Mean square error (MSE)
$MSE=\frac{1}{m n} \sum_{i=0}^{m-1} \sum_{j=0}^{n-1}[I(i, j)-K(i, j)]^{2},$

whre $$m$$ is the image height $$I$$, n the width of the image $$K$$ and $$i$$,$$j$$ the psotions $$x,y$$ images pixels; the closer to 0 get the better the result.

• Peak signal-to-noise ratio (PSNR)
$PSNR = 10×log_{10}(\frac{(mxn)^2}{MSE},$

where $$m$$ is the image height $$I$$, n the width of the image $$K$$ and $$MSE$$ the mean square error; the bigger the better.

• structural similarity index measure (SSIM)
$\operatorname{SSIM}(x, y)=\frac{\left(2 \mu_{x} \mu_{y}+c_{1}\right)\left(2 \sigma_{x y}+c_{2}\right)}{\left(\mu_{x}^{2}+\mu_{y}^{2}+c_{1}\right)\left(\sigma_{x}^{2}+\sigma_{y}^{2}+c_{2}\right)},$

where $$\mu$$ is the mean, $$\sigma$$ is the standard deviation and $$c$$ is covariance [1]; The worst case is -1 and the best is 1.

#Function to calculate Mean square error
'''
Input: 2 list with the images values
Output: the mean square error
'''
def mse(imageA, imageB):
err = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2)
err /= float(imageA.shape[0] * imageA.shape[1])
return err

from skimage.metrics import structural_similarity as ssim

### Comparing the training set

In the next section we’ll see the results of the autoencoder using the different metrics that we previously described.

import math
ssim_list = [] #List to save the structural similarity index measure
mse_list = [] #List to save the Mean square error
psnr_list = [] #List to save the Peak signal-to-noise ratio

#For each row of the training set we will
for i in range(len(x_train)):
#Reshape to a 8X8 image of the training set
test_img = x_train[i].reshape(8,8)*255
#Reshape to a 8X8 image of the decoded trainig set
decoded_img = decoder_train[i].real.reshape(8,8)*255
#Calculate the MSE between the reshaped decoded image and the trainig set image
Y = float(mse(decoded_img,test_img))
#Calculate the SSIM between the reshaped decoded image and the trainig set image
ssim_list.append(ssim(decoded_img.astype("float"),test_img.astype("float")))
mse_list.append(Y) #Save the MSE value
aux = (64**2)/Y #Calculate the PSNR
psnr_list.append(10*math.log10(aux)) #Save the PSRN value

#Plotting the MSE results of the training set
from matplotlib import pyplot as plt
plt.plot(mse_list)
plt.show()

#Plotting the PSNR results of the training set
from matplotlib import pyplot as plt
plt.plot(psnr_list)
plt.show()

#Plotting the SSIM results of the training set
from matplotlib import pyplot as plt
plt.plot(ssim_list)
plt.show()

### Comparing the test set

We’ll do the same procedure to see the results of the autocoder with the test set.

ssim_list = [] #List to save the structural similarity index measure
mse_list = [] #List to save the Mean square error
psnr_list = [] #List to save the Peak signal-to-noise ratio

#For each row of the test set we will
for i in range(len(x_test)):
#Reshape to a 8X8 image of the training set
test_img = x_test[i].reshape(8,8)*255
#Reshape to a 8X8 image of the decoded trainig set
decoded_img = decoder[i].real.reshape(8,8)*255
#Calculate the MSE between the reshaped decoded image and the test set image
Y = float(mse(decoded_img,test_img))
#Calculate the SSIM between the reshaped decoded image and the test set image
ssim_list.append(ssim(decoded_img.astype("float"),test_img.astype("float")))
mse_list.append(Y) #Save the MSE value
aux = (64**2)/Y #Calculate the PSNR
psnr_list.append(10*math.log10(aux)) #Save the PSRN value

#Plotting the MSE results of the test set
from matplotlib import pyplot as plt
plt.plot(mse_list)
plt.show()

#Plotting the PSNR results of the test set
from matplotlib import pyplot as plt
plt.plot(psnr_list)
plt.show()

#Plotting the SSIM results of the test set
from matplotlib import pyplot as plt
plt.plot(ssim_list)
plt.show()

We repeat the same process but now with not random inputs and save the data at csv files which will use to do a classification with reduced images.

#Loading the MNIST set divided by a train set and a test set
(x_train_c, y_train_c), (x_test_c, y_test_c) = tf.keras.datasets.mnist.load_data()

# Rescale the images from [0,255] to the [0.0,1.0] range.
x_train_c, x_test_c = x_train_c[..., np.newaxis]/255.0, x_test_c[..., np.newaxis]/255.0

x_train_c, y_train_c = filter_01(x_train_c, y_train_c) #Filter the train set
x_test_c, y_test_c = filter_01(x_test_c, y_test_c) #Filter the test set

#resizing the images from 28x28 to 8x8 by the nearest method
x_train_c = tf.image.resize(x_train_c, (8,8), method='nearest', preserve_aspect_ratio=True).numpy()
x_test_c = tf.image.resize(x_test_c, (8,8), method='nearest', preserve_aspect_ratio=True).numpy()

#Normalize the training set data
for i in range(len(x_train_c)):
x_train_c[i] = Normalize(x_train_c[i])

#Normalize the test set data
for i in range(len(x_test)):
x_test_c[i] = Normalize(x_test_c[i])

#Reshaping the train and test test to a 64x1 matriz
x_train_c = x_train_c.reshape(len(x_train_small), 64)
x_test_c = x_test_c.reshape(len(x_test_small), 64)

x_train_c.shape

(12665, 64)

#Function to compress the training set values
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with compress values
'''
def compress_result_train(params):
reduce = [] #List to save the compress values
#For each row in the training set we will
for i in range(len(x_train_c)):
circuit_init = input_data(n,x_train_c[i]) #Create a inicial circuit
circuit_vqc = vqc(n,num_layers,params) #Create a quantum variational circuit

#Create a new circuit based on the size of the initial circuit and the desired qubits to reduce
circuit_full = QuantumCircuit(n,n-size_reduce)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
len_cf = len(circuit_full) #Known the length of the circuit
#For each n - the the desired qubits to reduce we will
for i in range(n-size_reduce):
circuit_full.measure(size_reduce+i, i) #Measure the circuit in the position size_reduce+i
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('qasm_simulator'),shots=8192 )
result = job.result().get_counts() #Get the results of the execution
#Get the probabilities of each state
probs = {k: np.sqrt(v / 8192) for k, v in result.items()}
reduce.append(probs) #Save the probabilities

return reduce

#Call the compress_result_train function with the parameters defined above
reduce_train_c = compress_result_train(params)

#Function to compress the test set values
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with compress values
'''
def compress_result_test(params):

reduce = [] #List to save the compress values
#For each row in the test set we will
for i in range(len(x_test_c)):
circuit_init = input_data(n,x_test_c[i]) #Create a inicial circuit
circuit_vqc = vqc(n,num_layers,params) #Create a quantum variational circuit

#Create a new circuit based on the size of the initial circuit and the desired qubits to reduce
circuit_full = QuantumCircuit(n,n-size_reduce)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
len_cf = len(circuit_full) #Known the length of the circuit
#For each n - the the desired qubits to reduce we will
for i in range(n-size_reduce):
circuit_full.measure(size_reduce+i, i) #Measure the circuit in the position size_reduce+i
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('qasm_simulator'),shots=8192 )
result = job.result().get_counts() #Get the results of the execution
#Get the probabilities of each state
probs = {k: np.sqrt(v / 8192) for k, v in result.items()}
reduce.append(probs) #Save the probabilities

return reduce

#Call the compress_result_test function with the parameters defined above
reduce_test_c = compress_result_test(params)

test_reduce = [] #List to save the new values of the image reduction
#for each value in the reduce_img list
for i in reduce_test_c:
index_image = [] #List to save the reduction values
#We now take in count we want a 4X4 image
for j in range(16):
bin_index = bin(j)[2:] #We take the binary value of j from the 2 position to the end
while len(bin_index) <4: #While bin_index is less than 4
bin_index = '0'+bin_index #We concatenate a 0 string at the beginnig
try:
#We try to save the element of the row in the position bin_index
index_image.append(i[bin_index])
except:
index_image.append(0) #If we can't, we only save a 0

#We save the new imagen values in the test_recuce list
test_reduce.append(np.array(index_image))

train_reduce = [] #List to save the new values of the image reduction
#for each value in the reduce_img list
for i in reduce_train_c:
index_image = [] #List to save the reduction values
#We now take in count we want a 4X4 image
for j in range(16):
bin_index = bin(j)[2:] #We take the binary value of j from the 2 position to the end
while len(bin_index) <4: #While bin_index is less than 4
bin_index = '0'+bin_index #We concatenate a 0 string at the beginnig
try:
#We try to save the element of the row in the position bin_index
index_image.append(i[bin_index])
except:
index_image.append(0) #If we can't, we only save a 0

#We save the new imagen values in the train_recuce list
train_reduce.append(np.array(index_image))

#Function to decode the training set values compressed
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with decode values
'''
def decoder_result_train_c(params):
reduce = [] #List to save the decoded values
#For each row in the test set reduced we will
for i in range(len(train_reduce)):
#Create a initial circuit with 6 qubits and a list of 48 zeros and the i row of the test reduced values
circuit_init = input_data(n,np.concatenate((np.zeros(48), train_reduce[i]), axis=0))
circuit_vqc = vqc(n,num_layers,params).inverse() #Create the inverse VQC

#Create a new circuit to combine the inicial circuit and the VQC
circuit_full = QuantumCircuit(n,n)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('statevector_simulator') )
result = job.result().get_statevector() #Get the results of the execution
reduce.append(result) #Save the results
return reduce

#Call the decoder_result_train function
decoder_train_c =decoder_result_train_c(params)
len(decoder_train_c) #Get the length of the decoder train list

12665

#Function to decode the test set values compressed
'''
Input: An array of parameters(vector of complex amplitudes)
Output: Array with decode values
'''
def decoder_result_test_c(params):
reduce = [] #List to save the decoded values
#For each row in the test set reduced we will
for i in range(len(test_reduce)):

#Create a initial circuit with 6 qubits and a list of 48 zeros and the i row of the test reduced values
circuit_init = input_data(6,np.concatenate((np.zeros(48), test_reduce[i]), axis=0))
#Create the inverse VQC
circuit_vqc = vqc(n,num_layers,params).inverse()

#Create a new circuit to combine the inicial circuit and the VQC
circuit_full = QuantumCircuit(n,n)

#Combine the initial circuit, the quantum variatinal circuit
circuit_full = circuit_full.compose(circuit_init,[i for i in range(n)])
circuit_full = circuit_full.compose(circuit_vqc,[i for i in range(n)])
#We will execute the full circuit in the qasm simulator
job = execute( circuit_full, Aer.get_backend('statevector_simulator') )
result = job.result().get_statevector() #Get the results of the execution
reduce.append(result) #Save the results
return reduce

#Call the decoder_result_test function
decoder_c =decoder_result_test_c(params)

### Save results

We save 2 csv files as train.csv and test.csv with the compressed images obtained from our autoencoder, the first to the training set and the second for the test set.

import pandas as pd

df = pd.DataFrame(train_reduce) #Dataframe for the training reduce set
df[16] = y_train #The class of each value is at the end of the dataframe
df.to_csv("train_1.csv",index=False) #Create the csv file with the results

df = pd.DataFrame(test_reduce) #Dataframe for the test reduce set
df[16] = y_test #The class of each value is at the end of the dataframe
df.to_csv("test_1.csv",index=False) #Create the csv file with the results

## Results of our quantum autoencoder

Due the results obtained we define an histogram by a metric in the best cases and we contruct the following graphs.

## MSE

The results closer to 0 are the best results, looking at a visual approach, our best case is with 200 images.

## PSNR

The results with the larger value than the axis, are the best results, looking at a visual approach, our best case is with 200 images.

## SSIM

The results closer to 1 are the best results, looking at a visual approach, our best case is with 200 images.

Therefore we consider the results obtained from 200 images to elaborate a binary classifier.

# The binary classifier

Now well present how to construct the binary classifier that classifies the mnist dataset. First, We import the libraries we need to realized this work, using qiskit machine learning.

# Scikit Imports
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Qiskit Imports
from qiskit import Aer, execute
from qiskit.circuit import QuantumCircuit, Parameter, ParameterVector
from qiskit.circuit.library import PauliFeatureMap, ZFeatureMap, ZZFeatureMap
from qiskit.circuit.library import TwoLocal, NLocal, RealAmplitudes, EfficientSU2
from qiskit.circuit.library import HGate, RXGate, RYGate, RZGate, CXGate, CRXGate, CRZGate
from qiskit_machine_learning.kernels import QuantumKernel

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import csv

We get data from csv files to process this values in the quantum variational classifier, passing an input vector 16x1 and another to label.

sample_train = [] #List from the sample train
label_train = [] #List from the label train
with open('train.csv', newline='') as csvfile: #Open the csv file that we before create
for row in reader: #We go for each row of the file
sample_train.append(row[0:-1]) #The values of the samples are all except the last cell
label_train.append(row[-1]) #The class of that row is the last cell
sample_train = np.array(sample_train).astype(np.float) #Convert the sample train to a numpy float array
label_train = np.array(label_train).astype(np.float) #Convert the label train to a numpy float array
sample_train.shape, label_train.shape #Show the shape of the arrays

((12665, 16), (12665,))

sample_test = [] #List from the sample test
label_test = [] #List from the label test
with open('test.csv', newline='') as csvfile: #Open the csv file that we before create
for row in reader: #We go for each row of the file
sample_test.append(row[0:-1]) #The values of the samples are all except the last cell
label_test.append(row[-1]) #The class of that row is the last cell
sample_test = np.array(sample_test).astype(np.float) #Convert the sample test to a numpy float array
label_test = np.array(label_test).astype(np.float) #Convert the label test to a numpy float array
sample_test.shape, label_test.shape #Show the shape of the arrays

((2115, 16), (2115,))

We generate the input vector to the train set and the test set with dimensions 16x1.

sample_train = sample_train.reshape(len(sample_train), 16) #Reshape the sample train to a 16X1 array
sample_test = sample_test.reshape(len(sample_test), 16) #Reshape the sample test to a 16X1 array

We contruct the classifier using 4 qubits and due this, we need to decrease the number of tests on our devices, allowing to run the examples using Hold-out 70-30 method, i.e. 70% training and 30% test.

train_size = 700 #Number of samples of the training sample
sample_train = sample_train[:train_size] #Reduce the sample train
label_train = label_train[:train_size] #Reduce the label train

test_size = 300 #Number of samples of the test sample
sample_test = sample_test[:test_size] #Reduce the sample test
label_test = label_test[:test_size] #Reduce the label test

The next thing is to map the classical vector to a quantum state for this we will use the ZZFeatureMap function (“You can use another one like PauliFeatureMap or ZFeatureMap”).

Then we will initialize a quantum kernel from which we can calculate each element of this matrix in a quantum computer by calculating the transition amplitude. This provides us with an estimate of the kernel quantum matrix, which we can then use in a kernel machine learning algorithm, in this case it will be used in a vector support machine

#map the classical vector to a quantum state with the ZZFeatureMap, 16 qubits, 1 repetiton and a linear entanglement
zz_map = ZZFeatureMap(feature_dimension=16, reps=1, entanglement='linear', insert_barriers=True)
#Create the quantum kernel with the ZZFeatureMap
zz_kernel = QuantumKernel(feature_map=zz_map, quantum_instance=Aer.get_backend('statevector_simulator'))
#Show the ZZFeatureMap with 16 qubits
zz_map.draw(output="mpl")

We build the training and test matrices for the quantum kernel. For each pair of data points in the training data set

#We create the matrix_train with the sample train values
matrix_train = zz_kernel.evaluate(x_vec=sample_train)
#We create the matrix test with the sample test and sample train values
matrix_test = zz_kernel.evaluate(x_vec=sample_test, y_vec=sample_train)

We use the quantum kernel test and training matrices in a classical support vector machine classification algorithm.

#We use a SVC with a precomputed kerner
zzpc_svc = SVC(kernel='precomputed')
#We fit the model with the train matrix and the label train
zzpc_svc.fit(matrix_train, label_train)
#We calculate the score of the model with the matrix test and label test
zzpc_score = zzpc_svc.score(matrix_test, label_test)

#We show the score value
print(f'Precomputed kernel classification test score: {zzpc_score}')

Precomputed kernel classification test score: 0.97

We test the algorithm by seeing how the test set classification does.

#Make a prediction using the test matrix
predictions = zzpc_svc.predict(matrix_test)

As can be seen, out of 300 samples, only 9 were not classified correctly.

#We need to see how many prediction go wrong
for prediction,label in zip(predictions,label_test):
if(prediction != label): #If the prediction is different from the class
print(prediction, label)

1.0 0.0
1.0 0.0
1.0 0.0
1.0 0.0
0.0 1.0
0.0 1.0
0.0 1.0
1.0 0.0
0.0 1.0

## Validate for the set of 200 images

The same process is repeated but considering that the hold-out validation method was considered valid, the test should be done with different sets at random, which will be carried out with the compressed set of 200 images.

sample_train = [] #List from the sample train
label_train = [] #List from the label train
with open('train_200.csv', newline='') as csvfile: #Open the csv file that we before create
for row in reader: #We go for each row of the file
sample_train.append(row[0:-1]) #The values of the samples are all except the last cell
label_train.append(row[-1]) #The class of that row is the last cell
sample_train = np.array(sample_train).astype(np.float) #Convert the sample train to a numpy float array
label_train = np.array(label_train).astype(np.float) #Convert the label train to a numpy float array

sample_test = [] #List from the sample test
label_test = [] #List from the label test
with open('test_200.csv', newline='') as csvfile: #Open the csv file that we before create
for row in reader: #We go for each row of the file
sample_test.append(row[0:-1]) #The values of the samples are all except the last cell
label_test.append(row[-1]) #The class of that row is the last cell
sample_test = np.array(sample_test).astype(np.float) #Convert the sample test to a numpy float array
label_test = np.array(label_test).astype(np.float) #Convert the label test to a numpy float array

#Show the shape of the arrays
sample_train.shape, label_train.shape, sample_test.shape, label_test.shape

((12665, 16), (12665,), (2115, 16), (2115,))

7 iterations are generated with different images of fixed ranges

score = [] #List to save the scoore of the 7 iterations
for i in range(7):
train_size = 700 #Number of samples of the training sample

sample_train_1 = sample_train[i*train_size:(i+1)*train_size] #select the values of the sample train
label_train_1 = label_train[i*train_size:(i+1)*train_size] #select the values of the label train

test_size = 300 #Number of samples of the test sample
sample_test_1 = sample_test[i*test_size:(i+1)*test_size] #select the values of the sample test
label_test_1 = label_test[i*test_size:(i+1)*test_size] #select the values of the label test

#map the classical vector to a quantum state with the ZZFeatureMap, 16 qubits, 1 repetiton and a linear entanglement
zz_map = ZZFeatureMap(feature_dimension=16, reps=1, entanglement='linear', insert_barriers=True)
#Create the quantum kernel with the ZZFeatureMap
zz_kernel = QuantumKernel(feature_map=zz_map, quantum_instance=Aer.get_backend('statevector_simulator'))
#We create the matrix_train with the sample train values
matrix_train = zz_kernel.evaluate(x_vec=sample_train_1)
#We create the matrix test with the sample test and sample train values
matrix_test = zz_kernel.evaluate(x_vec=sample_test_1, y_vec=sample_train_1)

#We use a SVC with a precomputed kerner
zzpc_svc = SVC(kernel='precomputed')
#We fit the model with the train matrix and the label train
zzpc_svc.fit(matrix_train, label_train_1)
#We calculate the score of the model with the matrix test and label test
zzpc_score = zzpc_svc.score(matrix_test, label_test_1)
#We show the score value
print(f'Precomputed kernel classification test score: {zzpc_score}')
score.append(zzpc_score) #Save the actual score
del matrix_train, matrix_test #Delete the values of the train and test matrix

Precomputed kernel classification test score: 0.97
Precomputed kernel classification test score: 0.9366666666666666
Precomputed kernel classification test score: 0.96
Precomputed kernel classification test score: 0.9633333333333334
Precomputed kernel classification test score: 0.9833333333333333
Precomputed kernel classification test score: 0.9633333333333334
Precomputed kernel classification test score: 0.9466666666666667

The mean value to the set gave it by the autocoder using the hold out 70-30 method is:

#Calculate a mean value of the seven iterations scores
sum(score)/len(score)

0.9604761904761905

# Conclusions.

Today’s quantum models are unlikely to outperform the classical model on classical data, especially in today’s classical data sets that may have more than a million data points. However:

1) We can partially resolve the problem for big data sets with certain data reductions that diminish the cost and the qubits.

2) Some data sets are easier to learn for quantum models than for classical models.

In this project, we propose a classification method of 0’s and 1’s through an autoencoder. By this, we could reduce the number of qubits needed to be reproducible in a real quantum computer, getting a cost of 4, and classification performance of 96.04%.

## Authors

• <NAME> linkedin
• <NAME>
• <NAME> linkedin

# References

[1] <NAME>. (2020). Quantum autoencoders with enhanced data encoding.

[2] <NAME>. (2019). Lectures on Quantum Tensor Networks.

[3] <NAME> & <NAME> & <NAME>. (2021). Quantum Machine Learning Tensor Network States. Frontiers in Physics. 8. 586374. 10.3389/fphy.2020.586374.

[4] <NAME>. & <NAME>. (2016). Supervised Learning with Quantum-Inspired Tensor Networks.

[5] <NAME> & <NAME> & <NAME>. (2020). Quantum-Classical Machine learning by Hybrid Tensor Networks

[6] <NAME> & <NAME> & <NAME>. (2016). Quantum autoencoders for efficient compression of quantum data. Quantum Science and Technology. 2. 10.1088/2058-9565/aa8072.

[7] <NAME> & <NAME> & <NAME>. (2020). The controlled SWAP test for determining quantum entanglement.

Tags:

Categories:

Updated:


text:
## marixoxo76 Group Title 13r^-4(r^-5)^5/8(r^2)^-2 solve with steps please. one year ago one year ago

1. marixoxo76 Group Title

$\frac{ 13r ^{-4}(r ^{-5})^{5} }{ 8(r ^{2})^{-2} }$

2. nickersia Group Title

$\frac{ 13*\frac{ 1 }{ r ^{4} } (\frac{ 1 }{ r ^{5} })^{5} }{ 8*\frac{ 1 }{ (r ^{2})^{2} } }$ Do you know how to do that?

3. marixoxo76 Group Title

Not really... can you explain please?

4. nickersia Group Title

$r ^{-100} = \frac{ 1 }{ r ^{100} }$ Is that clear?

5. nickersia Group Title

$r ^{-4} = \frac{ 1 }{ r ^{4} }$

6. nickersia Group Title

So now you have to simplify it $\frac{ 13 *\frac{ 1 }{ r ^{4} * r ^{25} } }{ 8 * \frac{ 1 }{ r ^{4} } }$ 25 because 5*5 = 25 $\frac{ 13*\frac{ 1 }{ r ^{29} } }{ 8 * \frac{ 1 }{ r ^{4} } }$ $\frac{ \frac{ 13 }{ r ^{29} } }{ \frac{ 8 }{ r ^{4} } }$ (very up number is 13, hard to read) $\frac{ 13 * r ^{4} }{ 8 * r ^{29} }$ 29-4=25 $\frac{ 13 }{ 8r ^{25} }$ That should be resoult :)

7. marixoxo76 Group Title

ah, perfect! thank you so much i was struggling with problems like these thanks got it :)

8. nickersia Group Title

Nice to hear that! Good luck!

format: plain


text:
Easy diff. eq. problem

1. Sep 27, 2005

schattenjaeger

Given: y=(x-c)^2 is a solution to
(y')^2=4y

If y(x0)=y0, is that the ONLY solution?
I'm not terribly clear on the existence and uniqueness theorem(the technical math aspect isn't so bad for me, it's the stuff like that)so fill in where I'm wrong!

so for the exist. and uniq. theorem to apply, y'=f(x,y), f(x,y) and the partial of f in terms of y must be continuous.

Well, the function then is 2y^(1/2). It's only continuous when y >or= 0, and the partial is y^(-1/2) which is only continuous when y>0. So the function isn't guaranteed to have a unique solution unless y0>0, right?

so to further learn I solved it, and got for my answer, y=(x+c)^2. Which is basically the same thing as the given solution(since c could be taken as positive or negative)

so, uh, how do I intepret that? Is it not guaranteed an unique solution but happens to only have one or did I somehow not find one?

2. Sep 27, 2005

saltydog

For the equation:

$$(y^{'})^2=4y\tag{1}$$

Solving for $y^{'}$, I'd take the square root of both sides? Two equations result when that happens right? Solutions to each of these equations will then satisfy the original equation. What does that tell you about uniqueness?

Suppose I had to calculate what c would have to be in order for the solution of (1) to pass through the point ($x_0,y_0$).

Then I'd need to solve:

$$y_0=(x_0-c)^2$$

for c. Taking square roots of both sides and not forgetting the plus and minus part, What can c be in this case?

The existence and uniqueness theorem you mention applies to equations of the form:

$$y^{'}=f(x,y)$$

Treating each of the two separate ODEs calculated above separately, the consequence of this theorem applies.


text:
# Why do USGS earthquake magnitudes differ from those published by other agencies?

Magnitude estimates for a given earthquake can vary between reporting agencies due to differences in methodology, data availability, and inherent uncertainties in seismic data. Individual agencies use magnitude estimation procedures designed to meet the agency's specific needs and monitoring capabilities. Even for well-recorded events, differences in magnitude of 0.2 or 0.3 units are common and representative of the inherent uncertainty of the magnitude estimation process.

Obtaining an accurate measure of an earthquake's size is difficult. Earthquakes are complex processes that occur below the Earth’s surface away from direct observation and measurement. Determining a single number to represent an earthquake’s size has inherent uncertainties due to our assumptions about the material in which they occur and our inability to fully reconstruct the hidden process.

Multiple methods are used to estimate magnitude. Fundamentally different calculation methods are specified by magnitude type. The USGS National Earthquake Information Center (NEIC) routinely calculates as many as a dozen different magnitude types. Multiple magnitude types are necessary because no single method is capable of accurately estimating the size of all earthquakes. Some magnitude types are calculated to provide a consistent comparison to past earthquakes, for which modern techniques cannot be applied. Attempts have been made to calibrate these magnitudes to the original Richter Scale, but since different techniques often measure different physical processes there can be large discrepancies between different magnitude types calculated for the same earthquake. These differences can be as large as 0.5 even in magnitude ranges where both techniques are considered valid. Some magnitudes types are fast to calculate and can be completely automated, while others require manual processing by a trained seismologist. Changes in the preferred USGS magnitude in the minutes and hours following significant earthquakes are often a result of a change in magnitude type as well as the inclusion of more data. This iterative procedure addresses the desire for rapid information and allows for timely improvement to derived USGS products such as ShakeMap and PAGER.

Even for a given magnitude type, there can be differences in magnitude estimates from different agencies. These differences generally arise due to the use of different Earth models, data availability and data processing. The USGS National Earthquake Information Center reports on earthquakes worldwide. We strive for global consistency in the methods used for calculating earthquake parameters. This approach requires a systematic process as well as a more generic model of Earth's physical properties. A seismic network that is focused on a specific region will often tune their underlying models to their area of interest and monitoring history.

To obtain the most consistent and meaningful information from magnitude estimates, researchers and policy makers should consider and specify both the magnitude type and source of that information. For example, local networks are generally the best source for consistent quality information in that region whereas global monitoring agencies such as the USGS National Earthquake Information Center can provide valuable information when comparing earthquakes across the globe.

## Related Content

Filter Total Items: 12

### What is UTC, and why don’t you report earthquakes in the local time where the earthquake occurred?

UTC stands for Coordinated Universal Time, and for this purpose is the same as GMT ( Greenwich Mean Time). Since the USGS and other seismic network agencies record earthquakes around the globe in all the various time zones, using a single standard time reference is best for record-keeping and exchange of data. Also, we tried converting Coordinated...

### Why do so many earthquakes occur at a depth of 10km?

Ten kilometers is a "fixed depth". Sometimes data are too poor to compute a reliable depth for an earthquake. In such cases, the depth is assigned to be 10 km. Why that number? In many areas around the world, reliable depths tend to average 10 km or close to it. For example, if we made a histogram of the reliable depths in such an area, we'd...

### How fast does the earthquake information get posted to the website, get sent out via the Earthquake Notification Service (ENS), ATOM feeds, etc?

USGS earthquake information mechanisms are all triggered by the same system, so they all receive the information at the same time. The time it takes for the system to receive the information primarily depends on the size and location of the earthquake: An earthquake in California is processed and posted to the system in 2.5 minutes (on average)...

### Where can I see current or past seismograms?

The USGS Earthquake Hazards Program has helicorders (seismogram displays) available for several areas in the United States and the World. Our research partner IRIS (Incorporated Research Institutions for Seismology) has two applications, the Station Monitor and the Global Seismogram Viewer , for viewing seismograms. IRIS also supplies software...

### When are tsunami information and links included on the Earthquake Event pages?

Tsunami information is added to individual USGS event pages for earthquakes under two conditions: If we get information (alerts) from any of NOAA's Tsunami Warning Centers, or For any earthquake of magnitude 5.0 or greater that occurs in the Pacific Ocean, Indonesia, Papua New Guinea, the Caribbean Sea, or Hawaii For More Information, See NOAA's...

### Why/When does the USGS update the magnitude of an earthquake?

The USGS often updates an earthquake's magnitude in the hours and sometimes days following the event. Updates occur as more data become available for analysis and more time-intensive analysis is performed. Additional updates are possible as part of the standard procedure of assembling a final earthquake catalog. There are physical and operational...

### Why do some earthquakes disappear from the map/list?

The USGS and networks contributing to the Advance National Seismic System (ANSS) take great effort to provide accurate and timely earthquake information. Occasionally our systems produce erroneous information that is released to the public via our web pages or Earthquake Notification System . These mistakes are generally promptly identified by...

### Why isn't the fault on which the earthquake occurred or the distance to the nearest fault provided?

Seismologists evaluate the hypocenter location and the focal mechanism of an earthquake to decide if the earthquake occurs on a named fault. Research shows that many earthquakes occur on small, un-named faults located near well-known faults. For example, most of the aftershocks of the 1989 M6.9 Loma Prieta earthquake occurred on small, subsidiary...

### Did I feel an earthquake? Can I report feeling an earthquake?

Report an earthquake experience or related observation through the Did You Feel It? citizen science webpage. The best way to do this is to click on the earthquake that you think you felt on one of the lists on the Earthquakes webpage, and then select the "Tell Us!" link. If you don't see the earthquake you think you felt, use the green "Report an...

### Why is the earthquake that was reported/recorded by network X, or that I felt, not on the map/list?

The maps and lists show events which have been located by the USGS and contributing agencies within the last 30 days. They should not be considered to be complete lists of all events in the US and adjacent areas and especially should not be considered to be complete lists of all events M4.5+ in the world. In most cases, we locate and report on...

### Where can I find current earthquake lists and maps for the world or for a specific area?

The Earthquake Hazards Program Latest Earthquakes Map displays earthquakes in near-realtime and up to the past 30 days of earthquakes. The interface includes three panels: a list of earthquakes, a map, and a settings/options panel. You can pan and zoom the map to view specific areas. Click on an event on the list or map for additional information...
Filter Total Items: 4
Date published: May 25, 2017

### Updated USGS Earthquake Monitoring Strategy Released

The USGS Earthquake Hazards Program recently released a new strategic plan for earthquake monitoring entitled the “Advanced National Seismic System – Current Status, Development Opportunities, Priorities, 2017-2027.”

Date published: January 23, 2012

### A 100-year-long History of Earthquakes and Seismic Monitoring in Hawaii

The Hawaiian Volcano Observatory’s 1912–2012 Centennial—100 Years of Tracking Eruptions and Earthquakes

HAWAI‘I ISLAND, Hawaii —The history of earthquakes and seismic monitoring in Hawai‘i during the past century will be the topic of a presentation at the University of Hawai‘i at Hilo on Thursday, January 26, at 7:00 p.m.

Date published: May 3, 2010

More than $7 million in cooperative agreements will be awarded for earthquake monitoring by the U.S Geological Survey in 2010. This funding will contribute to the development and operation of the USGS Advanced National Seismic System (ANSS). Date published: September 24, 2009 ### Recovery Act Funds Will Upgrade Earthquake Monitoring USGS will Grant Universities$5 Million to Beef Up Public Safety Grants totaling \$5 million under the American Recovery and Reinvestment Act are being awarded to 13 universities nationwide to upgrade critical earthquake monitoring networks and increase public safety.

Filter Total Items: 6
December 31, 2017

### Earthquake Catalog Map Results Example

Example of results returned when searching the USGS Earthquake Catalog. The ANSS Comprehensive Earthquake Catalog (ComCat) contains earthquake source parameters (e.g. hypocenters, magnitudes, phase picks and amplitudes) and other products (e.g. moment tensor solutions, macroseismic information, tectonic summaries, maps) produced by contributing seismic

...
February 24, 2017

### Earthquakes Greater Than or Equal to Magnitude 2.7 Since 1980

USGS charts showing the number of earthquakes greater than or equal to magnitude 2.7 since 1980 in the five focus areas identified as having especially high ground-shaking hazard in the central and eastern U.S. in 2017.

November 21, 2016

### Magnitude 6.9 Earthquake off Japan on November 21, 2016

USGS map of the magnitude 6.9 earthquake off the coast of Japan on November 21, 2016.

August 31, 2016

October 17, 1989

### Seismographs at the U.S. Geological Survey

Seismographs at the U.S. Geological Survey record (1) north-south horizontal, (2) east-west horizontal, and (3) vertical components of the earthquake.

### Cumulative number of earthquakes with a magnitude of 3.0 or larger in the central and eastern United States, 1973-2014

Cumulative number of earthquakes with a magnitude of 3.0 or larger in the central and eastern United States, 1973-2014. The rate of earthquakes began to increase starting around 2009 and accelerated in 2013-2014.


text:
You are seeing poliastro blog. If you want to see documentation, the latest version can be found in docs.poliastro.space.

# Moving forward with the Cesium application

I feel like in the last couple of weeks I got to work a little in several aspects of the project. While this didn't allow me to focus on a single feature, it allowed me to make progress in various different areas.

## Bug resolving and 2D mode

First and foremost, I got rid of a particularly frustrating bug in the application. Certain javascript events (such as the one that allows the inertial view), where tied to a single Viewer and thus wouldn't trigger when the Viewer was destroyed (which is the case when, for example, loading the data from the file). I also made a few minor changes in the code and introduced the option to set the Viewer mode to 2D. This isn't any useful as of now, but it will be with the new feature I'm planning to introduce, which brings me to...

## Groundtrack plotting

Cesium allows users to define animated polyline paths, allowing us to visualize an orbit's groundtrack. What is even more exciting, is that Cesium automatically projects the path when on 2D scene mode, allowing us to see the orbit's groundtrack even on orthographic projection.

I have most of the work laid out and I'm planning to open a pull request next week. Cesium has a problem when dealing with particularly long paths and in that case the path needs to be broken down and dynamically loaded.

## Satellite pass predictor

I made an initial commit for the pass predictor based on Escobal's paper and I also started working on a helper class to work on the Ellipsoidal coordinate system (altough technically, the class is restricted to spheroids, since they are easier to work with and planetary bodies can be accurately approximated by them). It offers methods for converting coordinates from the ellipsoidal to the Cartesian system and vice versa and determining whether a point is visible to the SpheroidLocation. Additionally, it is also equiped with a few other helpful math functions (e.g. determining the plane tangential to the point on the spheroid).

## Next steps

In the next couple of weeks, I'm planning to finally finish the pass predictor and at least complete the first commit of the groundtrack plotter.


text:
BiEntropy, TriEntropy and Primality history
|
Submitted by: <NAME>
View times: 13 | Release time: 2021-11-15
(0)
(0)
Overview

The order and disorder of binary representations of the natural numbers < 28 is measured using the BiEntropy function. Significant differences are detected between the primes and the non-primes. The BiEntropic prime density is shown to be quadratic with a very small Gaussian distributed error. The work is repeated in binary using a Monte Carlo simulation of a sample of natural numbers < 232 and in trinary for all natural numbers < 39 with similar but cubic results. Author found a significant relationship between BiEntropy and TriEntropy such that he can discriminate between the primes and numbers divisible by six. Author discusses the theoretical basis of these results and show how they generalise to give a tight bound on the variance of Pi(x)–Li(x) for all x. This bound is much tighter than the bound given by <NAME> in 1901 as an equivalence for proof of the Riemann Hypothesis. Since the primes are Gaussian due to a simple induction on the binary derivative, this implies that the twin primes conjecture is true. He also provide absolutely convergent asymptotes for the numbers of Fermat and Mersenne primes in the appendices.

This video is adapted from 10.3390/e22030311

View More


text:
# Intersection (set theory)

In mathematics, the intersection of two sets ${\displaystyle A}$ and ${\displaystyle B,}$ denoted by ${\displaystyle A\cap B,}$[1] is the set containing all elements of ${\displaystyle A}$ that also belong to ${\displaystyle B}$ or equivalently, all elements of ${\displaystyle B}$ that also belong to ${\displaystyle A.}$[2]

Type The intersection of two sets ${\displaystyle A}$ and ${\displaystyle B,}$ represented by circles. ${\displaystyle A\cap B}$ is in red. Set operation Set theory The intersection is the set of elements that exists in both set ${\displaystyle A}$ and set ${\displaystyle B}$. ${\displaystyle A\cap B=\{x:x\in A{\text{ and }}x\in B\}}$


text:
## Areas of Mathematics

For one of my upcoming talks I am trying to include an exhaustive mindmap showing the different areas of Mathematics, and somehow, how they relate to each other. Most of the information I am using has been processed from years of exposure in the field, and a bit of help from Wikipedia.

But I am not entirely happy with what I see: my lack of training in the area of Combinatorics results in a rather dry treatment of that part of the mindmap, for example. I am afraid that the same could be told about other parts of the diagram. Any help from the reader to clarify and polish this information will be very much appreciated.

And as a bonus, I included a $\LaTeX$ script to generate the diagram with the aid of the tikz libraries.

\tikzstyle{level 2 concept}+=[sibling angle=40]
\begin{tikzpicture}[scale=0.49, transform shape]
\path[mindmap,concept color=black,text=white]
node[concept] {Pure Mathematics} [clockwise from=45]
child[concept color=DeepSkyBlue4]{
node[concept] {Analysis} [clockwise from=180]
child {
node[concept] {Multivariate \& Vector Calculus}
[clockwise from=120]
child {node[concept] {ODEs}}}
child { node[concept] {Functional Analysis}}
child { node[concept] {Measure Theory}}
child { node[concept] {Calculus of Variations}}
child { node[concept] {Harmonic Analysis}}
child { node[concept] {Complex Analysis}}
child { node[concept] {Stochastic Analysis}}
child { node[concept] {Geometric Analysis}
[clockwise from=-40]
child {node[concept] {PDEs}}}}
child[concept color=black!50!green, grow=-40]{
node[concept] {Combinatorics} [clockwise from=10]
child {node[concept] {Enumerative}}
child {node[concept] {Extremal}}
child {node[concept] {Graph Theory}}}
child[concept color=black!25!red, grow=-90]{
node[concept] {Geometry} [clockwise from=-30]
child {node[concept] {Convex Geometry}}
child {node[concept] {Differential Geometry}}
child {node[concept] {Manifolds}}
child {node[concept,color=black!50!green!50!red,text=white] {Discrete Geometry}}
child {
node[concept] {Topology} [clockwise from=-150]
child {node [concept,color=black!25!red!50!brown,text=white]
{Algebraic Topology}}}}
child[concept color=brown,grow=140]{
node[concept] {Algebra} [counterclockwise from=70]
child {node[concept] {Elementary}}
child {node[concept] {Number Theory}}
child {node[concept] {Abstract} [clockwise from=180]
child {node[concept,color=red!25!brown,text=white] {Algebraic Geometry}}}
child {node[concept] {Linear}}}
node[extra concept,concept color=black] at (200:5) {Applied Mathematics}
child[grow=145,concept color=black!50!yellow] {
node[concept] {Probability} [clockwise from=180]
child {node[concept] {Stochastic Processes}}}
child[grow=175,concept color=black!50!yellow] {node[concept] {Statistics}}
child[grow=205,concept color=black!50!yellow] {node[concept] {Numerical Analysis}}
child[grow=235,concept color=black!50!yellow] {node[concept] {Symbolic Computation}};
\end{tikzpicture}

## More on Lindenmayer Systems

We briefly explored Lindenmayer systems (or L-systems) in an old post: Toying with Basic Fractals. We quickly reviewed this method for creation of an approximation to fractals, and displayed an example (the Koch snowflake) based on tikz libraries.

I would like to show a few more examples of beautiful curves generated with this technique, together with their generating axiom, rules and parameters. Feel free to click on each of the images below to download a larger version.

Note that any coding language with plotting capabilities should be able to tackle this project. I used once again tikz for $\text{\LaTeX}$, but this time with the tikzlibrary lindenmayersystems.

 name : Dragon Curve axiom : X order : 11 step : 5pt angle : 90 rules : X -> X+YF+ Y -> -FX-Y  name : Gosper Space-filling Curve axiom : XF order : 5 step : 2pt angle : 60 rules : XF -> XF+YF++YF-XF--XFXF-YF+ YF -> -XF+YFYF++YF+XF--XF-YF  name : <NAME> Island axiom : F+F+F+F order : 4 step : 1pt angle : 90 rules : F -> F+F-F-FF+F+F-F  name : Sierpinski Arrowhead axiom : F order : 8 step : 3.5pt angle : 60 rules : G -> F+G+F F -> G-F-G  name : ? axiom : F+F+F+F order : 4 step : 2pt angle : 90 rules : F -> FF+F+F+F+F+F-F  name : ? axiom : F+F+F+F order : 4 step : 3pt angle : 90 rules : F -> FF+F+F+F+FF

Would you like to experiment a little with axioms, rules and parameters, and obtain some new pleasant curves with this method? If the mathematical properties of the fractal that they approximate are interesting enough, I bet you could attach your name to them. Like the astronomer that finds through her telescope a new object in the sky, or the zoologist that discover a new species of spider in the forest.

## Some results related to the Feuerbach Point

Given a triangle $\triangle ABC,$ the circle that goes through the midpoints of each side, $D, E, F,$ is called the Feuerbach circle. It has very surprising properties:

• It also goes through the feet of the heights, points $G, H, I.$
• If $Oc$ denotes the orthocenter of the triangle, then the Feuerbach circle also goes through the midpoints of the segments $OcA, OcB, OcC.$ For this reason, the Feuerbach circle is also called the nine-point circle.
• The center of the Feuerbach circle is the midpoint between the orthocenter and circumcenter of the triangle.
• The area of the circumcircle is precisely four times the area of the Feuerbach circle.

Most of these results are easily shown with sympy without the need to resort to Gröbner bases or Ritt-Wu techniques. As usual, we realize that the properties are independent of rotation, translation or dilation, and so we may assume that the vertices of the triangle are $A=(0,0), B=(1,0)$ and $C=(r,s)$ for some positive parameters $r,s>0.$ To prove the last statement, for instance we may issue the following:

>>> import sympy
>>> from sympy import *
>>> A=Point(0,0)
>>> B=Point(1,0)
>>> r,s=var('r,s')
>>> C=Point(r,s)
>>> D=Segment(A,B).midpoint
>>> E=Segment(B,C).midpoint
>>> F=Segment(A,C).midpoint
>>> simplify(Triangle(A,B,C).circumcircle.area/Triangle(D,E,F).circumcircle.area)
4

But probably the most amazing property of the nine-point circle, is the fact that it is tangent to the incircle of the triangle. With exception of the case of equilateral triangles, both circles intersect only at one point: the so-called Feuerbach point.

## An Automatic Geometric Proof

We are familiar with that result that states that, on any given triangle, the circumcenter, centroid and orthocenter are always collinear. I would like to illustrate how to use Gröbner bases theory to prove that the incenter also belongs in the previous line, provided the triangle is isosceles.

We start, as usual, indicating that this property is independent of shifts, rotations or dilations, and therefore we may assume that the isosceles triangle has one vertex at $A=(0,0)$, another vertex at $B=(1,0)$ and the third vertex at $C=(1/2, s)$ for some value $s \neq 0.$ In that case, we will need to work on the polynomial ring $R=\mathbb{R}[s,x_1,x_2,x_3,y_1,y_2,y_3,z],$ since we need the parameter $s$ free, the variables $x_1$ and $y_1$ are used to input the conditions for the circumcenter of the triangle, the variables $x_2$ and $y_2$ for centroid, and the variables $x_3$ and $y_3$ for the incenter (note that we do not need to use the orthocenter in this case).

We may obtain all six conditions by using sympy, as follows:

>>> import sympy
>>> from sympy import *
>>> A=Point(0,0)
>>> B=Point(1,0)
>>> s=symbols("s",real=True,positive=True)
>>> C=Point(1/2.,s)
>>> T=Triangle(A,B,C)
>>> T.circumcenter
Point(1/2, (4*s**2 - 1)/(8*s))
>>> T.centroid
Point(1/2, s/3)
>>> T.incenter
Point(1/2, s/(sqrt(4*s**2 + 1) + 1))

This translates into the following polynomials

$h_1=2x_1-1, h_2=8sy_1-4s^2+1$ (for circumcenter)
$h_3=2x_2-1, h_4=3y_2-s$ (for centroid)
$h_5=2x_3-1, h_6=(4sy_3+1)^2-4s^2-1$ (for incenter)

The hypothesis polynomial comes simply from asking whether the slope of the line through two of those centers is the same as the slope of the line through another choice of two centers; we could use then, for example, $g=(x_2-x_1)(y_3-y_1)-(x_3-x_1)(y_2-y_1).$ It only remains to compute the Gröbner basis of the ideal $I=(h_1, \dotsc, h_6, 1-zg) \subset \mathbb{R}[s,x_1,x_2,x_3,y_1,y_2,y_3,z].$ Let us use SageMath for this task:

sage: R.<s,x1,x2,x3,y1,y2,y3,z>=PolynomialRing(QQ,8,order='lex')
sage: h=[2*x1-1,8*r*y1-4*r**2+1,2*x2-1,3*y2-r,2*x3-1,(4*r*y3+1)**2-4*r**2-1]
sage: g=(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)
sage: I=R.ideal(1-z*g,*h)
sage: I.groebner_basis()
[1]

This proves the result.

## Sympy should suffice

I have just received a copy of Instant SymPy Starter, by <NAME>—a no-nonsense guide to the main properties of SymPy, the Python library for symbolic mathematics. This short monograph packs everything you should need, with neat examples included, in about 50 pages. Well-worth its money.

To celebrate, I would like to pose a few coding challenges on the use of this library, based on a fun geometric puzzle from cut-the-knot: Rhombus in Circles

Segments $\overline{AB}$ and $\overline{CD}$ are equal. Lines $AB$ and $CD$ intersect at $M.$ Form four circumcircles: $(E)=(ACM), (F)=(ADM), (G)=(BDM), (H)=(BCM).$ Prove that the circumcenters $E, F, G, H$ form a rhombus, with $\angle EFG = \angle AMC.$

Note that if this construction works, it must do so independently of translations, rotations and dilations. We may then assume that $M$ is the origin, that the segments have length one, $A=(2,0), B=(1,0),$ and that for some parameters $a>0, \theta \in (0, \pi),$ it is $C=(a+1) (\cos \theta, \sin\theta), D=a (\cos\theta, \sin\theta).$ We let SymPy take care of the computation of circumcenters:

import sympy
from sympy import *

# Point definitions
M=Point(0,0)
A=Point(2,0)
B=Point(1,0)
a,theta=symbols('a,theta',real=True,positive=True)
C=Point((a+1)*cos(theta),(a+1)*sin(theta))
D=Point(a*cos(theta),a*sin(theta))

#Circumcenters
E=Triangle(A,C,M).circumcenter
F=Triangle(A,D,M).circumcenter
G=Triangle(B,D,M).circumcenter
H=Triangle(B,C,M).circumcenter

Finding that the alternate angles are equal in the quadrilateral $EFGH$ is pretty straightforward:

In [11]: P=Polygon(E,F,G,H)

In [12]: P.angles[E]==P.angles[G]
Out[12]: True

In [13]: P.angles[F]==P.angles[H]
Out[13]: True

To prove it a rhombus, the two sides that coincide on each angle must be equal. This presents us with the first challenge: Note for example that if we naively ask SymPy whether the triangle $\triangle EFG$ is equilateral, we get a False statement:

In [14]: Triangle(E,F,G).is_equilateral()
Out[14]: False

In [15]: F.distance(E)
Out[15]: Abs((a/2 - cos(theta))/sin(theta) - (a - 2*cos(theta) + 1)/(2*sin(theta)))

In [16]: F.distance(G)
Out[16]: sqrt(((a/2 - cos(theta))/sin(theta) - (a - cos(theta))/(2*sin(theta)))**2 + 1/4)

Part of the reason is that we have not indicated anywhere that the parameter theta is to be strictly bounded above by $\pi$ (we did indicate that it must be strictly positive). The other reason is that SymPy does not handle identities well, unless the expressions to be evaluated are perfectly simplified. For example, if we trust the routines of simplification of trigonometric expressions alone, we will not be able to resolve this problem with this technique:

In [17]: trigsimp(F.distance(E)-F.distance(G),deep=True)==0
Out[17]: False

Finding that $\angle EFG = \angle AMC$ with SymPy is not that easy either. This is the second challenge.

How would the reader resolve this situation?

Categories: Geometry, puzzles, sage, Teaching

## A nice application of Fatou’s Lemma

Let me show you an exciting technique to prove some convergence statements using exclusively functional inequalities and Fatou’s Lemma. The following are two classic problems solved this way. Enjoy!

Exercise 1 Let ${(X, \mathcal{F}, \mu)}$ be a measurable space and suppose ${\{f_n\}_{n\in \mathbb{N}}}$ is a sequence of measurable functions in ${L_1(\mu)}$ that converge almost everywhere to a function ${f \in L_1(\mu)},$ and such that the sequence of norms ${\big\{ \lVert f_n \rVert_1 \big\}_{n \in \mathbb{N}}}$ converges to ${\lVert f \rVert_1}$. Prove that the sequence of integrals ${\int_E \lvert f_n \rvert\, d\mu}$ converges to the integral ${\int_E \lvert f \rvert\, d\mu}$ for every measurable set ${E}$.

Proof: Note first that

$\displaystyle \begin{array}{rcl} \lvert f_n - f\rvert\, \boldsymbol{1}_E \leq \lvert f_n - f \rvert \leq \lvert f_n \rvert + \lvert f \rvert. \end{array}$

Set then ${g_n = \lvert f_n \rvert + \lvert f \rvert - \lvert f_n-f \rvert\, \boldsymbol{1}_E}$ (which are non-negative functions) and apply Fatou’s Lemma to that sequence. We have then

$\displaystyle \begin{array}{c} \int_X \liminf_n g_n\, d\mu \leq \liminf_n \int_X g_n\, d\mu \\ \\ \int_X \liminf_n \big( \lvert f_n \rvert + \lvert f \rvert - \lvert f_n - f \rvert\, \boldsymbol{1}_E \big)\, d\mu \leq \liminf_n \int_X \big( \lvert f_n \rvert + \lvert f \rvert - \lvert f_n - f \rvert\, \boldsymbol{1}_E \big)\, d\mu \\ \\ \int_X \big( 2\lvert f \rvert - \limsup_n \lvert f_n-f \rvert\, \boldsymbol{1}_E\big)\, d\mu \leq \liminf_n \int_X \lvert f_n \rvert\, d\mu + \lVert f \rVert_1 - \limsup_n \int_E \lvert f_n -f \rvert\, d\mu \\ \\ 2\lVert f\rVert_1 \leq 2\lVert f \rVert_1 - \limsup_n \int_E \lvert f_n - f\rvert\, d\mu, \end{array}$

which implies

$\displaystyle \begin{array}{rcl} 0 \leq \liminf_n \int_E \lvert f_n-f \rvert\, d\mu \leq \limsup_n \int_E \lvert f_n-f \rvert\, d\mu \leq 0. \end{array}$

It must then be ${\int_E \lvert f_n - f \rvert\, d\mu = 0}$. But this proves the statement, since

$\displaystyle \begin{array}{c} \bigg\lvert \int_E \lvert f_n\rvert\, d\mu - \int_E \lvert f \rvert\, d\mu \bigg\rvert = \bigg\lvert \int_E \big( \lvert f_n \rvert - \lvert f \rvert \big)\, d\mu \bigg\rvert \\ \leq \int_E \Big\lvert \lvert f_n \rvert - \lvert f \rvert \Big\rvert\, d\mu \leq \int_E \lvert f_n - f \rvert\, d\mu \end{array}$

$\Box$

Exercise 2 Let ${(X, \mathcal{F}, \mu)}$ be a finite measure space and let ${1. Suppose that ${\{ f_n \}_{n \in \mathbb{N}}}$ is a sequence of measurable functions in ${L_p(\mu)}$ whose norms are uniformly bounded in ${n}$ and which converge almost everywhere to a function ${f}$. Prove that the sequence ${\big\{ \int_X f_ng\, d\mu \big\}_{n \in \mathbb{N}}}$ converges to ${\int_x fg\, d\mu}$ for all ${g \in L_q(\mu)}$ where ${q}$ is the conjugate exponent of ${p}$.

Proof: The proof is very similar to the previous problem. We start by noticing that under the hypotheses of the problem,

$\displaystyle \begin{array}{c} \bigg\lvert \int_x f_ng\, d\mu - \int_X fg\, d\mu \bigg\rvert = \bigg\lvert \int_X (f_n -f)g\, d\mu \bigg\rvert \\ \leq \lVert (f_n-f)g \rVert_1 \leq \lVert f_n - f \rVert_p\, \lVert g \rVert_q. \end{array}$

If we prove that ${\lim_n \lVert f_n-f \rVert_p = 0}$, we are done.

We will achieve this by using the convexity of ${\lvert \cdot \rvert^p}$, since in that case it is

$\displaystyle \begin{array}{rcl} \frac{\lvert f_n - f \rvert^p}{2^p} \leq \tfrac{1}{2} \lvert f_n \rvert^p + \tfrac{1}{2} \lvert f \rvert^p. \end{array}$

hence,

$\displaystyle \begin{array}{rcl} \lvert f_n -f \rvert^p \leq 2^{p-1} \big( \lvert f_n \rvert^p + \lvert f \rvert^p \big). \end{array}$

Set then ${g_n = 2^{p-1} \big( \lvert f_n\rvert^p + \lvert f \rvert^p\big) - \lvert f_n-f \rvert^p}$ (which are non-negative functions) and apply Fatou’s Lemma as before. $\Box$

Categories: Analysis, Teaching

## Have a child, plant a tree, write a book

Or more importantly: rear your children to become nice people, water those trees, and make sure that your books make a good impact.

I recently enjoyed the rare pleasure of having a child (my first!) and publishing a book almost at the same time. Since this post belongs in my professional blog, I will exclusively comment on the latter: Learning SciPy for Numerical and Scientific Computing, published by Packt in a series of technical books focusing on Open Source software.

Keep in mind that the book is for a very specialized audience: not only do you need a basic knowledge of Python, but also a somewhat advanced command of mathematics/physics, and an interest in engineering or scientific applications. This is an excerpt of the detailed description of the monograph, as it reads in the publisher’s page:

It is essential to incorporate workflow data and code from various sources in order to create fast and effective algorithms to solve complex problems in science and engineering. Data is coming at us faster, dirtier, and at an ever increasing rate. There is no need to employ difficult-to-maintain code, or expensive mathematical engines to solve your numerical computations anymore. SciPy guarantees fast, accurate, and easy-to-code solutions to your numerical and scientific computing applications.

Learning SciPy for Numerical and Scientific Computing unveils secrets to some of the most critical mathematical and scientific computing problems and will play an instrumental role in supporting your research. The book will teach you how to quickly and efficiently use different modules and routines from the SciPy library to cover the vast scope of numerical mathematics with its simplistic practical approach that is easy to follow.

The book starts with a brief description of the SciPy libraries, showing practical demonstrations for acquiring and installing them on your system. This is followed by the second chapter which is a fun and fast-paced primer to array creation, manipulation, and problem-solving based on these techniques.

The rest of the chapters describe the use of all different modules and routines from the SciPy libraries, through the scope of different branches of numerical mathematics. Each big field is represented: numerical analysis, linear algebra, statistics, signal processing, and computational geometry. And for each of these fields all possibilities are illustrated with clear syntax, and plenty of examples. The book then presents combinations of all these techniques to the solution of research problems in real-life scenarios for different sciences or engineering — from image compression, biological classification of species, control theory, design of wings, to structural analysis of oxides.

The book is also being sold online in Amazon, where it has been received with pretty good reviews. I have found other random reviews elsewhere, with similar welcoming comments: