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_eaten_marbles_disappear(self): # Both are generated at the smame pos, so immediately eaten. node = MarbleEaterNode(ZERO, ZERO, ZERO, 10, NewtonianGravity(), 1, 1, 1, 0, 10) marble = Marble(ZERO, ZERO, ZERO, 10, NewtonianGravity(), datum=None) model = NenwinModel([node], [marble]) model.make_timestep(1.0) self.assertSetEqual(model.marbles, set([])) self.assertNotIn(marble, model._NenwinModel__all_particles)
def gen_architecture_c( input_placer_type: type ) -> Tuple[NenwinModel, VelInputPlacer, Tuple[Node]]: """ Generate the following architecture: * The input region is at (0, 0) and has size (6, 1) (So it has vertices {(0, 0), (0, 1), (6, 0), (6, 1)}) * There are two MarbleEaterNodes, at (1, 4) and (5, 4) * There are five normal Nodes, at (1, 2), (2, 3), (3, 2), (4, 3) and (5, 2). Returns: * Model holding the architecture descibed above. * VelInputPlacer with the input region as described above. * Tuple of the two MarbleEaterNodes. """ eater_positions = [(1, 4), (5, 4)] node_positions = [(1, 2), (2, 3), (3, 2), (4, 3), (5, 2)] input_region_pos = np.array((0, 0)) input_region_size = np.array((6, 1)) mass = 1 radius = 0.5 attraction_function = NewtonianGravity() nodes = gen_nodes(attraction_function, mass, node_positions) eater_nodes = gen_eater_nodes(attraction_function, mass, radius, eater_positions) model = NenwinModel(nodes + eater_nodes) input_placer = input_placer_type(input_region_pos, input_region_size) return model, input_placer, eater_nodes
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_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 setup_full_emitter( self) -> Tuple[Emitter, Marble, float, float, float]: """ Create an instance of a MarbleEmitter with a full prototype Marble (not a mock Marble). Also returns the delay, stored_mass and initial_time_passed, which have arbitrary nonzero floating-point values. Returns: * A MarbleEmitter instance * A Marble, the prototype associated with the emitter * The delay of the emitter * The initally stored mass of the emitter * The initial time passed of the emitter """ 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 prototype = Marble(pos, vel, acc, mass, attraction_function, None, marble_stiffness, node_stiffness, marble_attraction, node_attraction) delay = 8.1 stored_mass = 9.1 initial_time_passed = 11.1 emitter = MarbleEmitter(prototype, delay, stored_mass, initial_time_passed) return emitter, prototype, delay, stored_mass, initial_time_passed
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 gen_architecture_b( input_placer_type: type ) -> Tuple[NenwinModel, VelInputPlacer, Tuple[Node]]: """ Same as architecture A, but with four additional Nodes (normal Nodes, no MarbleEaterNodes) at: * (2.5, 2.5), (-2.5, 2.5), (2.5, -2.5), (-2.5, -2.5) So the normal Nodes surround the input-region, and the MarbleEaterNodes are to the left and right of this. Returns: * Model holding the architecture descibed above. * VelInputPlacer with the input region as described above. * Tuple of the two MarbleEaterNodes. """ eater_positions = [(-10, 0), (10, 0)] node_positions = [(0, -5), (-5, 0), (5, 0), (0, 5), (2.5, 2.5), (-2.5, 2.5), (2.5, -2.5), (-2.5, -2.5)] input_region_pos = np.array((-2.5, -1)) input_region_size = np.array((5, 2)) mass = 1 radius = 0.5 attraction_function = NewtonianGravity() nodes = gen_nodes(attraction_function, mass, node_positions) eater_nodes = gen_eater_nodes(attraction_function, mass, radius, eater_positions) model = NenwinModel(nodes + eater_nodes) input_placer = input_placer_type(input_region_pos, input_region_size) return model, input_placer, eater_nodes
def gen_node_at(pos: torch.Tensor, mass: float = 10) -> MarbleEaterNode: output = MarbleEaterNode(pos, vel=ZERO, acc=ZERO, mass=mass, attraction_function=NewtonianGravity(), marble_attraction=1, marble_stiffness=1, node_attraction=0, node_stiffness=0, radius=1) return output
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_funct = NewtonianGravity() expected = f"PhysicalParticle({repr(pos)},{repr(vel)},"\ + f"{repr(acc)},{mass},NewtonianGravity(),{repr(DEVICE)})" result = repr(PhysicalParticle(pos, vel, acc, mass, attraction_funct)) self.assertEqual(expected, result)
def gen_marble_at(pos: torch.Tensor, vel: torch.Tensor = ZERO, mass: float = 10, datum: Any = None) -> Marble: output = Marble(pos=pos, vel=vel, acc=ZERO, mass=mass, attraction_function=NewtonianGravity(), marble_attraction=0, marble_stiffness=1, node_attraction=0, node_stiffness=0, datum=datum) return output
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 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 radius = 8.0 # 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) emitter = MockEmitter(MockPrototype(), 0) expected = f"MarbleEmitterNode({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},"\ + f"{radius}," \ + f"{repr(emitter)})" eater = MarbleEmitterNode(pos, vel, acc, mass, attraction_function, marble_stiffness, node_stiffness, marble_attraction, node_attraction, radius, emitter) result = repr(eater) self.assertEqual(expected, result)
def test_reset_removed_added_marbles(self): """ NenwinModel.reset() should remove all Marbles added with NenwinModel.add_marbles() """ original_marble = Marble(ZERO, ZERO, ZERO, 10, NewtonianGravity(), datum=None) added_marble = original_marble.copy() assert added_marble is not original_marble model = NenwinModel([], [original_marble]) model.add_marbles([added_marble]) model.reset() self.assertIn(original_marble, model.marbles) self.assertIn(original_marble, model._NenwinModel__all_particles) self.assertNotIn(added_marble, model.marbles) self.assertNotIn(added_marble, model._NenwinModel__all_particles)
def gen_architecture_a( input_placer_type: type ) -> Tuple[NenwinModel, VelInputPlacer, Tuple[Node]]: """ Generate the following architecture: * The input region is at (-2.5, -1) and has size (5, 2) (So it has vertices {(-2.5, -1), (-2.5, 1), (2.5, -1), (2.5, 1)}) * There are two MarbleEaterNodes, at (-10, 0) and (10, 0) * There are four normal Nodes, at (0, -5), (-5, 0), (5, 0) and (0, 5). So the normal Nodes surround the input-region, and the MarbleEaterNodes are to the left and right of this. Returns: * Model holding the architecture descibed above. * VelInputPlacer with the input region as described above. * Tuple of the two MarbleEaterNodes. """ eater_positions = [(-10, 0), (10, 0)] node_positions = [(0, -5), (-5, 0), (5, 0), (0, 5)] input_region_pos = np.array((-2.5, -1)) input_region_size = np.array((5, 2)) mass = 1 radius = 0.5 attraction_function = NewtonianGravity() nodes = gen_nodes(attraction_function, mass, node_positions) eater_nodes = gen_eater_nodes(attraction_function, mass, radius, eater_positions) model = NenwinModel(nodes + eater_nodes) input_placer = input_placer_type(input_region_pos, input_region_size) return model, input_placer, eater_nodes
Author: Lulof Pirée October 2020 An experiment to see if an architecture can be made that accelerates a ḿarble in a straight line. """ import numpy as np from typing import List from nenwin.marble_eater_node import MarbleEaterNode from nenwin.node import Node, Marble from nenwin.attraction_functions.attraction_functions import NewtonianGravity from nenwin.architectures.run_and_visualize import run from nenwin.auxliary import generate_stiffness_dict ZERO = np.array([0, 0]) ATTRACTION_FUNCTION = NewtonianGravity() NODE_STIFFNESSES = generate_stiffness_dict(marble_stiffness=1, node_stiffness=1, marble_attraction=1, node_attraction=0) MARBLE_STIFFNESS = generate_stiffness_dict(marble_stiffness=0, node_stiffness=0, marble_attraction=0, node_attraction=0) def cannon_experiment(): """ Simulate a NAND gate using a Nenwin architecture. Also visualizes the simulation and prints the result. """
def setUp(self): self.newtonian = NewtonianGravity()
def test_repr(self): self.assertEqual("NewtonianGravity()", repr(NewtonianGravity()))
def setUp(self): self.attract_funct = NewtonianGravity()