def test_copy_same_values(self): """ Basic test: the copy should have similarly valued attribute values as the original. """ pos = torch.tensor([1.], dtype=torch.float) vel = torch.tensor([2.], dtype=torch.float) acc = torch.tensor([3.], dtype=torch.float) mass = 4 attraction_funct = ATTRACT_FUNCT stiffnesses = generate_stiffness_dict(0.5, 0.6, 0.7, 0.8) original = Node(pos, vel, acc, mass, attraction_funct, **stiffnesses) copy = original.copy() self.assertFalse(copy is original) self.assertTrue(torch.allclose(acc, copy.acc)) self.assertTrue(torch.allclose(vel, copy.vel)) self.assertTrue(torch.allclose(pos, copy.pos)) self.assertEqual(mass, copy.mass) self.assertTrue(attraction_funct is copy._attraction_function) self.assertAlmostEqual(copy.marble_stiffness, stiffnesses["marble_stiffness"]) self.assertAlmostEqual(copy.node_stiffness, stiffnesses["node_stiffness"]) self.assertAlmostEqual(copy.marble_attraction, stiffnesses["marble_attraction"]) self.assertAlmostEqual(copy.node_attraction, stiffnesses["node_attraction"])
def visualization_demo(): attract_funct = NewtonianGravity() marbles = set() marble_1 = Marble(np.array([460, 460]), np.array([0, 0]), np.array([0, 0]), mass=20, attraction_function=attract_funct, datum=None, marble_stiffness=1, node_stiffness=0, marble_attraction=0, node_attraction=1) marble_2 = Marble(np.array([550, 550]), np.array([10, 10]), np.array([0, 0]), mass=40, attraction_function=attract_funct, datum=None, marble_stiffness=1, node_stiffness=0, marble_attraction=0, node_attraction=1) marble_3 = Marble(np.array([450, 500]), np.array([2, -2]), np.array([0, 0]), mass=40, attraction_function=attract_funct, datum=None, marble_stiffness=1, node_stiffness=0, marble_attraction=0, node_attraction=1) marbles = set((marble_1, marble_2, marble_3)) node_1 = Node(np.array([500, 500]), np.array([0, 0]), np.array([0, 0]), mass=200, attraction_function=attract_funct, marble_stiffness=1, node_stiffness=1, marble_attraction=1, node_attraction=1) node_2 = Node(np.array([400, 400]), np.array([0, 0]), np.array([0, 0]), mass=200, attraction_function=attract_funct, marble_stiffness=0.7, node_stiffness=1, marble_attraction=1, node_attraction=1) nodes = set((node_1, node_2)) model = NenwinModel(nodes, marbles) simulation = Simulation(model, None, None, MockPipe()) visualization = NenwinVisualization((1500, 1000), simulation, model) visualization.run(10, 0.01)
def test_reset(self): # Node and Marble have some arbitrary nonzero initial motion vars. node = Node(torch.tensor([1.]), torch.tensor([2.]), torch.tensor([3.]), 4, NewtonianGravity(), 1, 1, 1, 1) marble = Marble(torch.tensor([1.1]), torch.tensor([2.2]), torch.tensor([3.3]), 4.4, NewtonianGravity(), None) model = NenwinModel([node], [marble]) model.make_timestep(10) model.make_timestep(10) model.make_timestep(10) # Verify that the motion variables have changed self.assertFalse(check_close(node.pos, node.init_pos)) self.assertFalse(check_close(marble.vel, marble.init_vel)) self.assertFalse(check_close(marble.acc, marble.init_acc)) model.reset() # Now they should be the original values again. self.assertTrue(check_close(node.pos, node.init_pos)) self.assertTrue(check_close(node.vel, node.init_vel)) self.assertTrue(check_close(node.acc, node.init_acc)) self.assertTrue(check_close(marble.pos, marble.init_pos)) self.assertTrue(check_close(marble.vel, marble.init_vel)) self.assertTrue(check_close(marble.acc, marble.init_acc))
def test_particle_gradients_moving_node(self): """ Base case: Marble being attracted by a moving Node, when using Marble's variables to compute loss, also Node.pos should receive gradients when computing backprop on the loss. """ self.node = Node(pos=ZERO, vel=torch.tensor([0.1]), acc=ZERO, mass=1, attraction_function=NewtonianGravity(), marble_stiffness=1, node_stiffness=1, marble_attraction=1, node_attraction=0) self.model = NenwinModel([self.node], [self.marble]) self.model.make_timestep(1.0) self.model.make_timestep(1.0) self.model.make_timestep(1.0) loss = 2 * self.marble.acc loss.backward() self.assertIsNotNone(self.marble.init_pos.grad) self.assertIsNotNone(self.node.init_pos.grad) self.assertIsNotNone(self.node.init_vel.grad) self.assertIsNotNone(self.node._PhysicalParticle__mass.grad)
def test_repr(self): pos = torch.tensor([0], dtype=torch.float) vel = torch.tensor([1], dtype=torch.float) acc = torch.tensor([2], dtype=torch.float) mass = 3.0 attraction_function = NewtonianGravity() marble_stiffness = 0.4 node_stiffness = 0.5 marble_attraction = 0.6 node_attraction = 0.7 node = Node(pos, vel, acc, mass, attraction_function, marble_stiffness, node_stiffness, marble_attraction, node_attraction) # Some numerical errors occurs when converting from float to FloatTensor marble_stiffness_float_repr = \ convert_scalar_param_to_repr(marble_stiffness) node_stiffness_float_repr = convert_scalar_param_to_repr( node_stiffness) marble_attraction_float_repr = \ convert_scalar_param_to_repr(marble_attraction) node_attraction_float_repr = \ convert_scalar_param_to_repr(node_attraction) expected = f"Node({repr(pos)},{repr(vel)},"\ + f"{repr(acc)},{mass},NewtonianGravity(),"\ + f"{marble_stiffness_float_repr}," \ + f"{node_stiffness_float_repr},{marble_attraction_float_repr}," \ + f"{node_attraction_float_repr})" result = repr(node) self.assertEqual(expected, result)
def test_repr_3(self): """ Base case: Nodes and Marbles given. """ marble_pos = torch.tensor([1.]) marble_vel = torch.tensor([2.]) marble_acc = torch.tensor([3.]) marble = Marble(marble_pos, marble_vel, marble_acc, 8, None, None, 0.4, 0.5, 0.6, 0.7) marble_2 = marble.copy() node = Node(ZERO, ZERO, ZERO, 0, None, 0, 0, 0, 0) node_2 = Node(torch.tensor([11.]), torch.tensor([12.]), torch.tensor([13.]), 14, None, 0, 0, 0, 0) expected = f"NenwinModel({repr(set([node, node_2]))}," \ + f"{repr(set([marble, marble_2]))})" result = repr(NenwinModel([node, node_2], [marble, marble_2])) self.assertEqual(expected, result)
def test_parameters(self): pos = torch.tensor([1], dtype=torch.float) vel = torch.tensor([2], dtype=torch.float) acc = torch.tensor([3], dtype=torch.float) mass = 4 attraction_funct = ATTRACT_FUNCT stiffnesses = generate_stiffness_dict(0.5, 0.6, 0.7, 0.8) node = Node(pos, vel, acc, mass, attraction_funct, **stiffnesses) named_params = node.named_parameters() expected_names = { '_Node__marble_stiffness': stiffnesses["marble_stiffness"], '_Node__node_stiffness': stiffnesses["node_stiffness"], '_Node__marble_attraction': stiffnesses["marble_attraction"], '_Node__node_attraction': stiffnesses["node_attraction"] } self.assertTrue( check_named_parameters(expected_names, tuple(named_params)))
def test_attraction_node_2(self): """ Corner case: should not attract any node if the multiplier is 0. """ node_attraction = 0 node = Node(torch.tensor([-1.], dtype=torch.float), ZERO, ZERO, 0, None, 0, 0, 1, 1) particle = create_particle(0, 0, 0, node_attraction) result = particle.compute_attraction_force_to(node) self.assertEqual(result, 0)
def __generate_locker_nodes(positions: Tuple[Tuple[int]]) -> Node: nodes = [] for pos in positions: new_node = Node(pos=np.array(pos), vel=ZERO, acc=ZERO, mass=LOCKER_NODE_MASS, attraction_function=ATTRACTION_FUNCTION, **NODE_STIFFNESSES) nodes.append(new_node) return nodes
def test_attraction_node_1(self): """ Check if the node_attraction multiplier is used to compute force to a node. """ node_attraction = 0.5 # Position -1 ensures it will be pulled towards '+' x-direction node = Node(torch.tensor([-1.], dtype=torch.float), ZERO, ZERO, 0, None, 0, 0, 0, 0) particle = create_particle(0, 0, 0, node_attraction) result = particle.compute_attraction_force_to(node) self.assertEqual(result, ATTRACT_FUNCT.value * node_attraction)
def test_stiffness_to_node(self): """ Base case: 1% stiffness to a single Node. """ node_stiffness = 0.01 node = Node(torch.tensor([1.], dtype=torch.float), ZERO, ZERO, 0, ATTRACT_FUNCT, 0, 0, 1, 1) particle = create_particle(0, node_stiffness, 0, 0) expected = (1 - node_stiffness) * ATTRACT_FUNCT.value result = particle.compute_experienced_force(set([node])) self.assertEqual( expected, result, "node_stiffness, " + f"got: {result}, exptected:{expected}")
def create_particle(marble_stiffness, node_stiffness, marble_attraction, node_attraction) -> Node: """ Simply attempt to create a Node with given parameters, and 0 or None for all other parameter values. """ return Node(ZERO, ZERO, ZERO, 0, ATTRACT_FUNCT, marble_stiffness=marble_stiffness, node_stiffness=node_stiffness, marble_attraction=marble_attraction, node_attraction=node_attraction)
def test_stiffness_zero(self): """ Corner case: 100% stiffness to any particle. """ node_stiffness = 1 marble_stiffness = 1 marble = Marble(torch.tensor([1.], dtype=torch.float), ZERO, ZERO, 0, ATTRACT_FUNCT, None) node = Node(torch.tensor([1.], dtype=torch.float), ZERO, ZERO, 0, ATTRACT_FUNCT, 0, 0, 1, 1) particle = create_particle(marble_stiffness, node_stiffness, 0, 0) expected = ZERO result = particle.compute_experienced_force(set([node])) self.assertTrue( torch.allclose(expected, result), "zero stiffness, " + f"got: {result}, exptected:{expected}")
def test_stiffness_to_set(self): """ Base case: multiple other particles in input set of compute_experienced_force() """ node_stiffness = 0.1 marble_stiffness = 0.6 node = Node(torch.tensor([1.], dtype=torch.float), ZERO, ZERO, 0, ATTRACT_FUNCT, 0, 0, 1, 1) marble = Marble(torch.tensor([-1.], dtype=torch.float), ZERO, ZERO, 0, ATTRACT_FUNCT, None, 0, 0, 1, 1) particle = create_particle(marble_stiffness, node_stiffness, 0, 0) expected = (1 - node_stiffness)*ATTRACT_FUNCT.value \ - (1 - marble_stiffness)*ATTRACT_FUNCT.value result = particle.compute_experienced_force(set([node, marble])) self.assertAlmostEqual(expected, result.item(), delta=1e-5)
def setUp(self): self.node = Node(pos=ZERO, vel=ZERO, acc=ZERO, mass=1, attraction_function=NewtonianGravity(), marble_stiffness=1, node_stiffness=1, marble_attraction=1, node_attraction=0) self.marble = Marble(pos=np.array([5]), vel=ZERO, acc=ZERO, mass=1, attraction_function=ATTRACT_FUNCT, datum=None) self.model = NenwinModel([self.node], [self.marble])
def __generate_canon_nodes() -> List[Node]: positions = [ [110, 10], [110, 90], [90, 20], [90, 80], [70, 30], [70, 70], [50, 40], [50, 60], ] # Exponential masses: (10^n) masses = [ 10000, 10000, 1000, 1000, 100, 100, 10, 10, ] # Quadratic masses (x², starting at x=10): # masses = [ # 100000000, 100000000, # 10000, 10000, # 100, 100, # 10, 10, # 1000 # ] nodes = [] for pos, mass in zip(positions, reversed(masses)): new_node = Node(pos=np.array(pos), vel=ZERO, acc=ZERO, mass=mass, attraction_function=ATTRACTION_FUNCTION, **NODE_STIFFNESSES) nodes.append(new_node) return nodes
def test_make_timestep_2(self): """ Base case: initial stationairy Marble, gets attracted and accelerated. """ node = Node(pos=ZERO, vel=ZERO, acc=ZERO, mass=1, attraction_function=ATTRACT_FUNCT, marble_stiffness=1, node_stiffness=1, marble_attraction=1, node_attraction=0) marble = Marble(pos=np.array([5]), vel=ZERO, acc=ZERO, mass=1, attraction_function=ATTRACT_FUNCT, datum=None) model = NenwinModel([node], [marble]) time_passed = 1 expected_pos, expected_vel = runge_kutta_4_step(marble.pos, marble.vel, -ATTRACT_FUNCT.value, duration=time_passed) model.make_timestep(time_passed) self.assertTrue(torch.allclose(marble.pos, expected_pos, atol=0.01)) self.assertTrue(torch.allclose(marble.vel, expected_vel, atol=0.01)) self.assertTrue( torch.isclose(marble.acc, torch.tensor(-ATTRACT_FUNCT.value), atol=0.01)) self.assertTrue(torch.allclose(node.pos, ZERO)) self.assertTrue(torch.allclose(node.vel, ZERO)) self.assertTrue(torch.allclose(node.acc, ZERO))
def generate_dummy_node() -> Marble: """ Create a Node without meaningfull parameters. """ return Node(ZERO, ZERO, ZERO, 0, None, 0, 0, 0, 0)