def test_add_broadcast_reverse(self): """Add cyphertext by scalar value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) other = 2 func = np.add re = func(other, re) self.arithmetic_evaluator(re, other, func)
def test_add_array_reverse(self): """Add cyphertext by (3) numpy array value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) other = np.array([2, 3, 4]) func = np.add re = func(other, re) self.arithmetic_evaluator(re, other, func)
def test_multiply_array_reverse(self): """Multiply cyphertext by (3) numpy array.""" re = ReArray(plaintext=self.data, **self.reseal_args) other = np.array([2, 3, 4]) func = np.multiply re = func(other, re) self.arithmetic_evaluator(re, other, func)
def test_add_re(self): """Add cyphertext to cyphertext.""" re = ReArray(plaintext=self.data, **self.reseal_args) other = re func = np.add re = func(re, other) self.arithmetic_evaluator(re, np.array(other), func)
def test_multiply_re(self): """Multiply cyphertext by cyphertext.""" re = ReArray(plaintext=self.data, **self.reseal_args) other = re func = np.multiply re = func(re, other) self.arithmetic_evaluator(re, np.array(other), func)
def test_multiply_broadcast(self): """Multiply cyphertext by scalar value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) other = 2 func = np.multiply re = func(re, other) self.arithmetic_evaluator(re, other, func)
def test_forward(self): x = self.data cyphertext = ReArray(x, **self.reseal_args) node = Decrypt() plaintext = node.forward(cyphertext) np.testing.assert_array_almost_equal(plaintext, x, decimal=4, verbose=True)
def test_isfinite(self): """Check that is finite gives us back results we are expecting.""" x = self.data args = self.reseal_args a = ReArray(x, **args) f_0 = np.isfinite(a) f_1 = np.isfinite(x) self.assertIsInstance(f_0, np.ndarray) np.testing.assert_array_almost_equal(f_0, f_1, decimal=1, verbose=True)
def test_pickle(self): """Ensure that pickling is still possible at this higher dimension.""" import pickle re = ReArray(plaintext=self.data, **self.reseal_args) dump = pickle.dumps(re) re = pickle.loads(dump) self.assertIsInstance(re, ReArray) out = np.array(re) self.assertIsInstance(out, np.ndarray) self.assertEqual(out.shape, self.data.shape)
def test_forward_enc(self): """Check encrypted forward pass works as expected.""" data = self.data edata = ReArray(data, **self.reseal_args) self.assertTrue(np.array(edata).shape == data.shape) self.assertIsInstance(edata, ReArray) acti = self.test_forward(data=edata) self.assertIsInstance(acti, ReArray) plain = np.array(acti) self.assertIsInstance(plain, np.ndarray)
def test_forward_decrypt(self): """Decrypt forward pass without re-encryption.""" x = self.data cypher = ReArray(x, self.reseal_args) node = Rotate() plaintext = node.forward(cypher) self.assertIsInstance(plaintext, np.ndarray) np.testing.assert_array_almost_equal(plaintext, x, decimal=4, verbose=True)
def test_sum(self): data = np.array([[1.0, 2.0, 3.0], [1.0, 2.0, 3.0]]) re = ReArray(plaintext=data, **self.reseal_args) sum = np.sum(re, axis=0) # can only sum first axis self.assertIsInstance(sum, ReArray) plain_sum = np.array(sum) truth = np.sum(data, axis=0) np.testing.assert_array_almost_equal(plain_sum, truth, decimal=1, verbose=True)
def test_forward_rotation(self): """Check that encryption parameters have been changed, on axis.""" x = self.data encryptor = ReArray( np.array([1]), { "scheme": 2, # seal.scheme_type.CKK, "poly_modulus_degree": 8192 * 2, # 438 # "coefficient_modulus": [60, 40, 40, 60], "coefficient_modulus": [45, 30, 30, 30, 30, 45 ], # coefficient mod length of 6 "scale": pow(2.0, 30), "cache": True, }) node = Rotate(encryptor=encryptor) cyphertext_in = ReArray(x, **self.reseal_args) cyphertext_out = node.forward(cyphertext_in) self.assertIsInstance(cyphertext_out, ReArray) out_cm = cyphertext_out.cyphertext[0].coefficient_modulus in_cm = cyphertext_in.cyphertext[0].coefficient_modulus self.assertNotEqual(in_cm, out_cm)
def test_forward_with_encryptor(self): """Check cyphertext is generated from encryptor properly.""" plaintext = self.data encryptor = ReArray(np.array([1]), **self.reseal_args) node = Rotate(encryptor=encryptor) cyphertext = node.forward(plaintext) self.assertIsInstance(cyphertext, ReArray) x = np.array(cyphertext) np.testing.assert_array_almost_equal(x, plaintext, decimal=4, verbose=True)
def test_equality(self): """Check that ReArray param equality is being calculated properly.""" a_arg = self.reseal_args = { "scheme": seal.scheme_type.CKKS, "poly_modulus_degree": 8192, "coefficient_modulus": [60, 40, 40, 60], "scale": pow(2.0, 40), "cache": True, } b_arg = self.reseal_args = { "scheme": seal.scheme_type.CKKS, "poly_modulus_degree": 8192, "coefficient_modulus": [60, 40, 60], # <-- changed this "scale": pow(2.0, 40), "cache": True, } # TODO check changing every attribute of rearray not just coef_mod a = ReArray(np.array([1]), **a_arg) b = ReArray(np.array([1]), **b_arg) self.assertEqual(a, a) self.assertNotEqual(a, b)
def test_forward_encrypt_axis(self): x = self.data axis = 1 encryptor = ReArray( np.array([1]), { "scheme": 2, # seal.scheme_type.CKK, "poly_modulus_degree": 8192 * 2, # 438 # "coefficient_modulus": [60, 40, 40, 60], "coefficient_modulus": [45, 30, 30, 30, 30, 45 ], # coefficient mod length of 6 "scale": pow(2.0, 30), "cache": True, }) node = Rotate(encryptor=encryptor, axis=1) cyphertext_in = ReArray(x, **self.reseal_args) cyphertext_lst_out = node.forward(cyphertext_in) self.assertIsInstance(cyphertext_lst_out, list) self.assertIsInstance(cyphertext_lst_out[0], ReArray) np.testing.assert_array_almost_equal(cyphertext_lst_out, cyphertext_in, decimal=4, verbose=True)
def test_temp_encryptor_generator(self): """Check that FHE parameters are suitable for associated cost.""" cost = 10 factor = 1 parms = ckks_param_heuristic(cost=cost) # print(parms) x = np.array([0.5, 0.3, 0.7, 0.9, 0.1]) cyphertxt = ReArray(x, **parms) for _ in tqdm(range(cost)): cyphertxt = np.multiply(cyphertxt, factor) # print("RUN") # cyphertext should be at edge of computational chain # check by trying to tip it over the edge with one more mult with self.assertRaises(ValueError) as context: np.multiply(cyphertxt, factor) self.assertTrue("scale out of bounds" in str(context.exception)) # print(x) # TODO: generalise next test for all factors np.testing.assert_array_almost_equal(cyphertxt, x, decimal=4, verbose=True)
def test_add_ndarray(self): re = ReArray(plaintext=self.data, **self.reseal_args) filter = np.arange(3 * 3 * 3) filter.shape = (3, 3, 3) with self.assertRaises(ArithmeticError): re = re + filter
def test_subtract_array_reverse(self): """Subtract cyphertext by (3) numpy array value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) with self.assertRaises(TypeError): re = np.array([2, 3, 4]) - re
def test_repr(self): re = ReArray(plaintext=self.data, **self.reseal_args) self.assertIsInstance(re.__repr__(), str)
def test_numpify(self): """Ensure data is intact when decrypted.""" re = ReArray(plaintext=self.data, **self.reseal_args) out = np.array(re) self.assertIsInstance(out, np.ndarray) self.assertEqual(out.shape, self.data.shape)
def test_error_slot_overflow(self): """Testing that correctly errors when the data overflows encryption.""" data = np.arange(64 * 320 * 320 * 3) data.shape = (64, 320, 320, 3) # making it waay to big with self.assertRaises(OverflowError): ReArray(plaintext=data, **self.reseal_args)
def test__error_data_type(self): """Testing that correctly errors when the data overflows encryption.""" data = np.arange(64 * 32 * 32 * 3) data.shape = (64, 32, 32, 3) with self.assertRaises(TypeError): ReArray(plaintext=data.tolist(), **self.reseal_args)
def test_object_creation(self): """Checking that the object creation is completed properly.""" re = ReArray(plaintext=self.data, **self.reseal_args) self.assertIsInstance(re, ReArray)
def test_subtract_re(self): """Subtract cyphertext by cyphertext.""" re = ReArray(plaintext=self.data, **self.reseal_args) with self.assertRaises(TypeError): re = re - re
def test_subtract_broadcast_reverse(self): """Subtract cyphertext by scalar value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) with self.assertRaises(TypeError): re = 2 - re
def test_floor_divide_broadcast_reverse(self): """Divide cyphertext by scalar value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) with self.assertRaises(TypeError): re = 2 // re
def test_floor_divide_array_reverse(self): """Divide cyphertext by (3) numpy array value broadcast.""" re = ReArray(plaintext=self.data, **self.reseal_args) with self.assertRaises(TypeError): re = np.array([2, 3, 4]) // re
def test_ann_shapes(self): """Test both numpy and ReArray input result in desired ann output.""" import copy x_dummy = ReArray(self.data, **self.reseal_args) x = [] num_inputs = 5 for i in range(num_inputs): r = ReArray(clone=x_dummy, plaintext=self.data) x.append(r) self.assertIsInstance(x[i], ReArray) ann = Layer_ANN(weights=(num_inputs, ), bias=self.bias) np_ann = copy.deepcopy(ann) previous_activation = None for i in range(10): debug = {} print("ANN ITERATION:", i) debug["iteration"] = i # FORWARD PASS TEST activations = ann.forward(x) np_activations = np_ann.forward(np.array(x)) # check that output is equal in shape to any single input ndarray # also check that ReArray and numpy produce the same results self.assertEqual(activations.shape, x_dummy.shape) self.assertEqual(np_activations.shape, x_dummy.shape) # self.assertListEqual( # np.around(np.array(activations), # decimals=2).flatten().tolist(), # np.around(np.array(np_activations), # decimals=2).flatten().tolist(), # ) a = np.array(np_activations) # print(a, "\n", a.shape) for _ in range(1, a.ndim): a = a.sum(axis=-1) # print(a, "\n", a.shape) a = np.around(a.mean(axis=0), decimals=5) debug["activation-np"] = a # CHECK IF MORE ACCURATE PREDICTION # print(a) current_loss = 1 - a debug["target"] = 1 debug["loss-np"] = current_loss if previous_activation is not None: previous_loss = 1 - previous_activation txt = "loss somehow more inacurate activations".format() # print("current:", abs(current_loss), # "previous:", abs(previous_loss)) # self.assertLess(abs(current_loss), abs(previous_loss), txt) previous_activation = a # BACKWARD PASS TEST gradient = ann.backward(1 - a) # print("GRADIENT", gradient) np_gradient = np_ann.backward(1 - a) debug["gradient-np"] = np_gradient print(debug) # we desire the resultant gradient to be of shape # (num_inputs, num_batches) desired_shape = (num_inputs, ) + (len(x_dummy), ) self.assertEqual(gradient.shape, desired_shape) self.assertEqual(np_gradient.shape, desired_shape) # self.assertListEqual( # np.around(np.array(gradient), # decimals=2).flatten().tolist(), # np.around(np.array(np_gradient), # decimals=2).flatten().tolist(), # ) # UPDATE ANN ann.update() np_ann.update()
def test_floor_divide_re(self): """Floor divide cyphertext by cyphertext.""" re = ReArray(plaintext=self.data, **self.reseal_args) with self.assertRaises(TypeError): re = re // re