import numpy as np
from itertools import combinations
from math import comb, floor
[docs]def maximum_of_flattening(n):
""" Calculates the maximum of flattening types that can be done.
If the reuslt of the calculation is a float, then, this number turns out
to be the largest integer less or equal to our number.
Examples:
>>> maximum_of_flattening(4)
2
>>> maximum_of_flattening(7)
3
:parameter int n: The number of qubits.
:return: int -- The maximum number of flattening types.
"""
return floor(n / 2)
[docs]def maximum_of_matrices(n, type_of_flattening):
""" Calculates the maximum of matrices for a type of flattening.
If the type of flattening is a divider of the number of qubits, then,
the first result is divided by 2.
Examples:
>>> maximum_of_matrices(2, 1)
1.0
>>> maximum_of_matrices(4, 2)
3.0
:parameter int n: The number of qubits.
:parameter int type_of_flattening : The type of flattening.
:return: float -- The maximum number of matrices.
"""
max_matrices = comb(n, type_of_flattening)
if n % type_of_flattening == 0:
max_matrices = max_matrices / 2
return max_matrices
[docs]def convert_in_binary(number, bits=0):
""" Converts an int into a string containing the number in bits.
Examples:
>>> convert_in_binary(5,3)
"101"
>>> convert_in_binary(5,5)
"00101"
:parameter int number: the number that is going to be converted.
:parameter int bits: the number of bits required.
:return: str -- The converted number.
"""
return format(number, "b").zfill(bits)
[docs]def putting_in_list(number):
""" Puts every figure of the number into a list type.
Example:
>>> putting_in_list("000")
[0, 0, 0]
:parameter str number: The number to split in a string format.
:return: list[int] -- The list with every digit of the number in a case.
"""
number_in_caracters = str(number)
number_in_list = []
for caracter in number_in_caracters:
number_in_list.append(int(caracter))
return number_in_list
[docs]def list_of_combinaisons(n, type_of_flattening):
""" Realizes the possible combinations for a type of flattening.
Examples:
>>> list_of_combinaisons(5, 1)
[(1,), (2,), (3,), (4,), (5,)]
>>> list_of_combinaisons(4, 2)
[(1, 2), (1, 3), (2, 3)]
:parameter int n: The number of qubits.
:parameter int type_of_flattening : The current type of flattening.
:return: list[list[int]] -- The list of every possible combination.
"""
if type_of_flattening == 1:
n = n + 1
l = list(range(1, n))
combinaisons_list = []
for i in combinations(l, type_of_flattening):
combinaisons_list.append(i)
return combinaisons_list
[docs]def list_of_combinaisons_for_invariant(n):
""" Realizes the possible combinations for a type of flattening.
Example:
>>> list_of_combinaisons(4)
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
:parameter int n: The number of qubits.
:return: list[list[int]] -- The list of every possible combination.
"""
L = list(range(n))
combinaisons_list = []
for i in combinations(L, 2):
combinaisons_list.append(i)
return combinaisons_list
[docs]def flattening_and_rank_calculation(n, T):
""" Constructs the matrices of every flattening type.
Example:
>>> flattening_and_rank_calculation(3, [1, 0, 0, 0, 0, 0, 0, 1])
([2, 2, 2], [[array([[1, 0, 0, 0], [0, 0, 0, 1]]), array([[1, 0, 0, 0], [0, 0, 0, 1]]), array([[1, 0, 0, 0], [0, 0, 0, 1]])]])
:parameter int n: The number of qubits.
:parameter list[int] T : A list filled with the coefficients corresponding to the vector.
:return: list[np.array[int]] -- A list of all the matrices after all flatennings.
"""
q = maximum_of_flattening(n)
all_coef_matrices = []
for m in range(1, q + 1):
list_combinaisons = list_of_combinaisons(n, m)
coef_matrices = []
for i in range(len(list_combinaisons)):
l = list_combinaisons[i]
M = [[]]
for h in range(2 ** m - 1):
M.append([])
for j in range(2 ** n):
bin_j = convert_in_binary(j, n)
v = putting_in_list(bin_j)
w = ""
for k in range(m):
w += str(v[l[k] - 1])
M[int(w, 2)].append(T[j])
M = np.array(M)
coef_matrices.append(M)
all_coef_matrices.append(coef_matrices)
return all_coef_matrices