def test_mul_inverse(self): ga = GeometricAlgebra(metric=dual_metric) # a = 2 a = ga.fill([], fill_value=2.0, kind="scalar") # b = 3 + 3e0 b = ga.fill([], fill_value=3.0, kind="mv") # a * b = 2 * (3 + 3e0) = 6 + 6e0 c = a * b self.assertEqual(c, 6.0 + 6.0 * ga.basis_mvs[0]) # a^-1 = 1 / 2 a_inv = a.inverse() self.assertEqual(a_inv, 0.5) # c = a * b # => a_inv * c = b self.assertEqual(a_inv * c, b) # Since a is scalar, should commute too. # => c * a_inv = b self.assertEqual(c * a_inv, b) # b is not invertible and will throw an exception self.assertRaises(Exception, b.inverse)
def test_geometric_sandwich_product_dense_v_v(self): sta = GeometricAlgebra([1, -1, -1, -1]) geom_tensor = tf.concat( [tf.zeros([32, 6, 1]), tf.ones([32, 6, 4]), tf.zeros([32, 6, 11])], axis=-1) vector_blade_indices = [1, 2, 3, 4] result_indices = tf.concat([ sta.get_kind_blade_indices(BladeKind.VECTOR), sta.get_kind_blade_indices(BladeKind.TRIVECTOR) ], axis=0) geom_prod_layer = GeometricSandwichProductDense( sta, 8, blade_indices_kernel=vector_blade_indices, blade_indices_bias=result_indices, bias_initializer=tf.keras.initializers.RandomNormal()) result = geom_prod_layer(geom_tensor) # vector * vector * ~vector + vector -> vector + trivector self.assertTrue(sta.is_pure(result, result_indices))
def test_exp_eq_approx_exp_e01_e02(self): pga = GeometricAlgebra(pga_signature) # a = 3e01 + 5e02 a = 3 * pga.e01 + 5 * pga.e02 # exp(a) = 1 + 3e01 + 5e02 self.assertTensorsApproxEqual(pga.approx_exp(a), pga.exp(a))
def test_exp_eq_approx_exp_e12_e23(self): pga = GeometricAlgebra(pga_signature) # a = 3e12 + 5e23 a = 3 * pga.e12 + 5 * pga.e23 # exp(a) ~= 0.90 - 0.22e12 -0.37e23 self.assertTensorsApproxEqual(pga.approx_exp(a), pga.exp(a))
def test_mul_tf_mv(self): ga = GeometricAlgebra(metric=dual_metric) zero = ga.zeros([], kind="scalar") zero_tf = ga.as_mv(tf.convert_to_tensor(0.0, dtype=tf.float32)) one = ga.ones([], kind="scalar") one_tf = ga.as_mv(tf.convert_to_tensor(1.0, dtype=tf.float32)) eps = ga.ones([], kind="pseudoscalar") ten = ga.fill([], fill_value=10.0, kind="scalar") ten_tf = ga.as_mv(tf.convert_to_tensor([10.0], dtype=tf.float32)) self.assertEqual(one * one_tf, one) self.assertEqual(one_tf * one, one) self.assertEqual(zero * one_tf, zero) self.assertEqual(one_tf * zero, zero) self.assertEqual(zero_tf * one, zero) self.assertEqual(one * zero_tf, zero) self.assertEqual(one_tf * eps, eps) self.assertEqual(eps * one_tf, eps) self.assertEqual(zero_tf * zero, zero) self.assertEqual(zero * zero_tf, zero) self.assertEqual(ten_tf * zero, zero) self.assertEqual(zero * ten_tf, zero) self.assertEqual(ten * zero_tf, zero) self.assertEqual(zero_tf * ten, zero) self.assertEqual((ten_tf * eps) * eps, zero) self.assertEqual(ten_tf * one, ten) self.assertEqual(one * ten_tf, ten) self.assertEqual(ten * one_tf, ten) self.assertEqual(one_tf * ten, ten)
def test_inverse(self): pga = GeometricAlgebra(pga_signature) # a = 3e12 + 5e23 a = 3 * pga.e12 + 5 * pga.e23 # a_inv: -0.09*e_12 + -0.15*e_23 a_inv = pga.inverse(a) # a a_inv should be 1 self.assertTensorsApproxEqual(pga.geom_prod(a, a_inv), 1 * pga.e(""))
def test_geom_exp_serializable(self): # Create algebra ga = GeometricAlgebra([1, 1, 1]) inputs = ga.from_tensor_with_kind( tf.random.normal([3], seed=0), BladeKind.BIVECTOR ) # Create model self._test_layer_serializable(GeometricAlgebraExp( ga ), inputs)
def test_geometric_product_dense_sequence(self): sta = GeometricAlgebra([1, -1, -1, -1]) tensor = tf.ones([20, 6, 4]) vector_blade_indices = [1, 2, 3, 4] mv_blade_indices = list(range(16)) # vector * vector + vector -> scalar + bivector + vector scalar_bivector_blade_indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sequence = tf.keras.Sequential([ TensorToGeometric(sta, blade_indices=vector_blade_indices), GeometricProductDense( sta, 8, blade_indices_kernel=vector_blade_indices, blade_indices_bias=vector_blade_indices, bias_initializer=tf.keras.initializers.RandomNormal()), GeometricToTensor(sta, blade_indices=scalar_bivector_blade_indices) ]) result = sequence(tensor) self.assertEqual(result.shape[-1], len(scalar_bivector_blade_indices))
def test_geom_to_tensor_serializable(self): # Create algebra sta = GeometricAlgebra([1, -1, -1, -1]) vector_blade_indices = [1, 2, 3, 4] # Create model self._test_layer_serializable(GeometricToTensor( sta, blade_indices=vector_blade_indices ), tf.random.normal([1, 2, 3, sta.num_blades], seed=0))
def test_tensor_to_geom_serializable(self): # Create algebra sta = GeometricAlgebra([1, -1, -1, -1]) vector_blade_indices = [1, 2, 3, 4] # Create model self._test_layer_serializable(TensorToGeometric( sta, blade_indices=vector_blade_indices ), tf.random.normal([1, 2, 3, len(vector_blade_indices)], seed=0))
def test_sandwich_elementwise_serializable(self): # Create algebra sta = GeometricAlgebra([1, -1, -1, -1]) vector_blade_indices = [1, 2, 3, 4] mv_blade_indices = list(range(16)) # Create model self._test_layer_serializable(GeometricSandwichProductElementwise( sta, blade_indices_kernel=mv_blade_indices, blade_indices_bias=vector_blade_indices ), tf.random.normal([3, 6, sta.num_blades], seed=0))
def test_mul_py_mv(self): ga = GeometricAlgebra(metric=dual_metric) zero = ga.zeros([], kind="scalar") zero_py = 0.0 one = ga.ones([], kind="scalar") one_py = 1.0 eps = ga.ones([], kind="pseudoscalar") ten = ga.fill([], fill_value=10.0, kind="scalar") ten_py = 10.0 self.assertEqual(one * one_py, one) self.assertEqual(one_py * one, one) self.assertEqual(zero * one_py, zero) self.assertEqual(one_py * zero, zero) self.assertEqual(zero_py * one, zero) self.assertEqual(one * zero_py, zero) self.assertEqual(one_py * eps, eps) self.assertEqual(eps * one_py, eps) self.assertEqual(zero_py * zero, zero) self.assertEqual(zero * zero_py, zero) self.assertEqual(ten_py * zero, zero) self.assertEqual(zero * ten_py, zero) self.assertEqual(ten * zero_py, zero) self.assertEqual(zero_py * ten, zero) self.assertEqual((ten_py * eps) * eps, zero) self.assertEqual(ten_py * one, ten) self.assertEqual(one * ten_py, ten) self.assertEqual(ten * one_py, ten) self.assertEqual(one_py * ten, ten)
def test_batched_auto_diff_square(self): """Test automatic differentiation using dual numbers for the square function. Use batch with identical elements. f(x) = x^2 f'(x) = d/dx f(x) = 2x """ ga = GeometricAlgebra(metric=dual_metric) batch_shape = [3, 4] one = ga.ones(batch_shape, kind="scalar") five = 5.0 * ga.ones(batch_shape, kind="scalar") eps = ga.ones(batch_shape, kind="pseudoscalar") x = one + eps self.assertEqual(x[""], one) self.assertEqual(x["0"], eps) self.assertAllElementsEqualTo(x.scalar, 1.0) self.assertEqual(x.mv_of_kind("pseudoscalar"), eps) self.assertAllElementsEqualTo(x.tensor_of_kind("scalar"), 1.0) self.assertAllElementsEqualTo(x.tensor_of_kind("pseudoscalar"), 1.0) # f(1) = 1^2 = 1, f'(1) = 2 x_squared = x * x self.assertAllElementsEqualTo(x_squared.scalar, 1.0) self.assertEqual(x_squared["0"], 2.0 * eps) y = five + eps self.assertEqual(y[""], five) self.assertEqual(y["0"], eps) self.assertAllElementsEqualTo(y.scalar, 5.0) self.assertEqual(y.mv_of_kind("pseudoscalar"), eps) self.assertAllElementsEqualTo(y.tensor_of_kind("scalar"), 5.0) self.assertAllElementsEqualTo(y.tensor_of_kind("pseudoscalar"), 1.0) # f(5) = 5^2 = 25, f'(5) = 10 y_squared = y * y self.assertAllElementsEqualTo(y_squared.scalar, 25.0) self.assertEqual(y_squared["0"], eps * 10.0)
def test_geom_prod_conv1d_serializable(self): # Create algebra sta = GeometricAlgebra([1, -1, -1, -1]) vector_blade_indices = [1, 2, 3, 4] mv_blade_indices = list(range(16)) # Create model self._test_layer_serializable(GeometricProductConv1D( sta, filters=8, kernel_size=3, padding="SAME", stride=2, blade_indices_kernel=mv_blade_indices, blade_indices_bias=vector_blade_indices ), tf.random.normal([3, 8, 4, sta.num_blades], seed=0))
def test_geometric_to_tensor(self): sta = GeometricAlgebra([1, -1, -1, -1]) gt_tensor = tf.ones([32, 4]) geom_tensor = tf.concat( [tf.zeros([32, 1]), tf.ones([32, 4]), tf.zeros([32, 11])], axis=-1) vector_blade_indices = [1, 2, 3, 4] geom_to_tensor_layer = GeometricToTensor(sta, vector_blade_indices) self.assertTensorsEqual(geom_to_tensor_layer(geom_tensor), gt_tensor)
def test_geometric_product_dense_v_v(self): sta = GeometricAlgebra([1, -1, -1, -1]) geom_tensor = tf.concat( [tf.zeros([32, 6, 1]), tf.ones([32, 6, 4]), tf.zeros([32, 6, 11])], axis=-1 ) vector_blade_indices = [1, 2, 3, 4] geom_prod_layer = GeometricProductDense( sta, 8, blade_indices_kernel=vector_blade_indices, blade_indices_bias=vector_blade_indices, bias_initializer=tf.keras.initializers.RandomNormal() ) result = geom_prod_layer(geom_tensor) # vector * vector + vector -> scalar + bivector + vector expected_result_indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] self.assertTrue(sta.is_pure(result, expected_result_indices))
def test_tensor_with_kind_to_geometric(self): sta = GeometricAlgebra([1, -1, -1, -1]) tensor = tf.ones([32, 4]) gt_geom_tensor = tf.concat( [tf.zeros([32, 1]), tf.ones([32, 4]), tf.zeros([32, 11])], axis=-1) vector_blade_indices = [1, 2, 3, 4] tensor_kind_to_geom_layer = TensorWithKindToGeometric( sta, BladeKind.VECTOR) self.assertTensorsEqual(tensor_kind_to_geom_layer(tensor), gt_geom_tensor)
def test_geometric_product_dense_s_mv(self): sta = GeometricAlgebra([1, -1, -1, -1]) geom_tensor = tf.concat( [tf.ones([20, 6, 1]), tf.zeros([20, 6, 15])], axis=-1) mv_blade_indices = list(range(16)) geom_prod_layer = GeometricProductDense( sta, 8, blade_indices_kernel=mv_blade_indices, blade_indices_bias=mv_blade_indices) result = geom_prod_layer(geom_tensor) # scalar * multivector + multivector -> multivector # Check that nothing is zero (it would be extremely unlikely # but not impossible to randomly get a zero here). self.assertTrue(tf.reduce_all(result != 0.0))
def test_tfga_mul_mv_mv(num_elements, benchmark): ga = GeometricAlgebra([1, -1, -1, -1]) a = tf.ones([num_elements, ga.num_blades]) b = tf.ones([num_elements, ga.num_blades]) benchmark(_tfga_mul, ga, a, b)
def test_auto_diff_square(self): """Test automatic differentiation using dual numbers for the square function. f(x) = x^2 f'(x) = d/dx f(x) = 2x """ ga = GeometricAlgebra(metric=dual_metric) one = ga.from_scalar(1.0) five = ga.from_scalar(5.0) eps = ga.from_tensor_with_kind(tf.ones(1), kind="pseudoscalar") x = one + eps # f(1) = 1^2 = 1, f'(1) = 2 x_squared = ga.geom_prod(x, x) self.assertTensorsEqual(ga.select_blades(x_squared, ""), 1.0) self.assertTensorsEqual(ga.select_blades(x_squared, "0"), 2.0) y = five + eps # f(5) = 5^2 = 25, f'(5) = 10 y_squared = ga.geom_prod(y, y) self.assertTensorsEqual(ga.select_blades(y_squared, ""), 25.0) self.assertTensorsEqual(ga.select_blades(y_squared, "0"), 10.0)
def test_mul_tf_mv(self): ga = GeometricAlgebra(metric=dual_metric) zero = ga.from_scalar(0.0) one = ga.from_scalar(1.0) eps = ga.from_tensor_with_kind(tf.ones(1), kind="pseudoscalar") ten = ga.from_scalar(10.0) zero_tf = tf.convert_to_tensor([0, 0], dtype=tf.float32) one_tf = tf.convert_to_tensor([1, 0], dtype=tf.float32) eps_tf = tf.convert_to_tensor([0, 1], dtype=tf.float32) ten_tf = tf.convert_to_tensor([10, 0], dtype=tf.float32) self.assertTensorsEqual(ga.geom_prod(one, one_tf), one) self.assertTensorsEqual(ga.geom_prod(one_tf, one), one) self.assertTensorsEqual(ga.geom_prod(zero, one_tf), zero) self.assertTensorsEqual(ga.geom_prod(one_tf, zero), zero) self.assertTensorsEqual(ga.geom_prod(zero_tf, one), zero) self.assertTensorsEqual(ga.geom_prod(one, zero_tf), zero) self.assertTensorsEqual(ga.geom_prod(one_tf, eps), eps) self.assertTensorsEqual(ga.geom_prod(eps, one_tf), eps) self.assertTensorsEqual(ga.geom_prod(zero_tf, zero), zero) self.assertTensorsEqual(ga.geom_prod(zero, zero_tf), zero) self.assertTensorsEqual(ga.geom_prod(ten_tf, zero), zero) self.assertTensorsEqual(ga.geom_prod(zero, ten_tf), zero) self.assertTensorsEqual(ga.geom_prod(ten, zero_tf), zero) self.assertTensorsEqual(ga.geom_prod(zero_tf, ten), zero) self.assertTensorsEqual(ga.geom_prod(ga.geom_prod(ten_tf, eps), eps), zero) self.assertTensorsEqual(ga.geom_prod(ten_tf, one), ten) self.assertTensorsEqual(ga.geom_prod(one, ten_tf), ten) self.assertTensorsEqual(ga.geom_prod(ten, one_tf), ten) self.assertTensorsEqual(ga.geom_prod(one_tf, ten), ten)
def test_mul_mv_mv(self): ga = GeometricAlgebra(metric=dual_metric) zero = ga.from_scalar(0.0) one = ga.from_scalar(1.0) eps = ga.from_tensor_with_kind(tf.ones(1), kind="pseudoscalar") ten = ga.from_scalar(10.0) self.assertTensorsEqual(ga.geom_prod(eps, eps), zero) self.assertTensorsEqual(ga.geom_prod(one, one), one) self.assertTensorsEqual(ga.geom_prod(zero, one), zero) self.assertTensorsEqual(ga.geom_prod(one, zero), zero) self.assertTensorsEqual(ga.geom_prod(one, eps), eps) self.assertTensorsEqual(ga.geom_prod(eps, one), eps) self.assertTensorsEqual(ga.geom_prod(zero, zero), zero) self.assertTensorsEqual(ga.geom_prod(ten, zero), zero) self.assertTensorsEqual(ga.geom_prod(zero, ten), zero) self.assertTensorsEqual(ga.geom_prod(ga.geom_prod(ten, eps), eps), zero) self.assertTensorsEqual(ga.geom_prod(ten, one), ten) self.assertTensorsEqual(ga.geom_prod(one, ten), ten)
def test_mul_inverse(self): ga = GeometricAlgebra(metric=dual_metric) # a = 2 a = ga.from_tensor_with_kind(tf.fill([1], 2.0), kind="scalar") # b = 3 + 3e0 b = ga.from_tensor_with_kind(tf.fill([2], 3.0), kind="mv") # a * b = 2 * (3 + 3e0) = 6 + 6e0 c = ga.geom_prod(a, b) self.assertTensorsEqual(c, ga.from_scalar(6.0) + 6.0 * ga.e("0")) # a^-1 = 1 / 2 a_inv = ga.inverse(a) self.assertTensorsEqual(ga.select_blades(a_inv, ""), 0.5) # c = a * b # => a_inv * c = b self.assertTensorsEqual(ga.geom_prod(a_inv, c), b) # Since a is scalar, should commute too. # => c * a_inv = b self.assertTensorsEqual(ga.geom_prod(c, a_inv), b) # b is not invertible and will throw an exception self.assertRaises(Exception, ga.inverse, b)