Use this file to discover all available pages before exploring further.
View on GitHub
Open this notebook in GitHub to run it yourself
Here we implement the hidden shift algorithm for the family of Boolean bent functions using the Classiq platform.Make sure we have all necessary packages:
!pip install galois
We assume we know how to implement the dual of f and get s according to the algorithm in [1]:
We take a Maiorana-McFarland function with random permutation on the y and h function is the and operation between all the y-variables.
import randomfrom functools import reduceimport numpy as npNUM_VARIABLES = 16# Define the listmy_list = list(range(NUM_VARIABLES // 2))# Get a random permutationrandom.seed(1)random.shuffle(my_list)# Create a permutation dict and its inverseperm_dict = {i: my_list[i] for i in range(NUM_VARIABLES // 2)}inverse_perm_dict = {v: k for k, v in perm_dict.items()}def h(y): return reduce(lambda a, b: a & b, [y[i] for i in range(NUM_VARIABLES // 2)])def h_dual(x): return reduce( lambda a, b: a & b, [x[inverse_perm_dict[i]] for i in range(NUM_VARIABLES // 2)] )def f_func(x, y): return ( reduce( lambda a, b: a ^ b, [x[i] & y[perm_dict[i]] for i in range(NUM_VARIABLES // 2)], ) ) ^ h(y)def f_dual_func(x, y): return ( reduce( lambda a, b: a ^ b, [x[inverse_perm_dict[i]] & y[i] for i in range(NUM_VARIABLES // 2)], ) ) ^ h_dual(x)def shifted(x, y, bits): x = [x[i] for i in range(NUM_VARIABLES // 2)] y = [y[i] for i in range(NUM_VARIABLES // 2)] for bit in bits: if bit < NUM_VARIABLES >> 2: x[bit] = x[bit] ^ 1 else: bit = bit - NUM_VARIABLES // 2 y[bit] = y[bit] ^ 1 return f_func(x, y)
expected_s = "".join("1" if i in shifted_bits else "0" for i in range(NUM_VARIABLES))assert list(sample_results_complex.counts_of_output("s").keys())[0] == expected_s
We now use the second algorithm described in [2].This algorithm only requires implementing f and not its dual; however, it requires O(n) samples from the circuit.
Out of the sampled results, we look for n independent samples, from which we can extract s.One thousand samples should be enough with a very high probability.
# The galois library is a package that extends NumPy arrays to operate over finite fields.# we will use it as our equations are binary equationsimport galois# here we work over Boolean arithmetics - F(2)GF = galois.GF(2)def is_independent_set(vectors): matrix = GF(vectors) rank = np.linalg.matrix_rank(matrix) if rank == len(vectors): return True else: return Falsesamples = [ ([int(i) for i in u], int(b)) for u, b in sample_results_no_dual.counts_of_multiple_outputs( ["target", "ind"] ).keys()]ind_v = []ind_b = []for v, b in samples: if is_independent_set(ind_v + [v]): ind_v.append(v) ind_b.append(b) if len(ind_v) == len(v): # reached max set breakassert len(ind_v) == len(v)
We now solve the equation and extract s:
A = np.array(ind_v)b = np.array(ind_b)# Solve the linear systems = np.linalg.solve(GF(A), GF(b))s