def __generate_nand_marbles(input_1: bool, input_2: bool) -> Tuple[Marble]: marble1 = Marble(np.array([20, 10]), vel=ZERO, acc=ZERO, mass=BOOL_TO_MASS[input_1], attraction_function=ATTRACTION_FUNCTION, datum=input_1, **STIFFNESS_AND_ATTRACTION) marble2 = Marble(np.array([20, 30]), vel=ZERO, acc=ZERO, mass=BOOL_TO_MASS[input_2], attraction_function=ATTRACTION_FUNCTION, datum=input_2, **STIFFNESS_AND_ATTRACTION) return marble1, marble2
def test_velocity_weighted_distance_2(self): """ Base case: vel weight not zero. """ pos_1 = torch.tensor([0.0, 15.0]) m1 = Marble(pos_1, ZERO, ZERO, 0, None, None) pos_2 = torch.tensor([0.0, 0.0]) vel_2 = torch.tensor([0.0, 30.0]) m2 = Marble(pos_2, vel_2, ZERO, 0, None, None) expected = torch.tensor(25.0) pos_weight = 1 vel_weight = 1/3 result = velocity_weighted_distance(m1, m2, pos_weight, vel_weight) torch.testing.assert_allclose(result, expected)
def test_emit_output_type(self): """ Output should be a EmittedMarble, not just a normal Marble. """ prototype = Marble(ZERO, ZERO, ZERO, 0, None, None) delay = 0 emitter = MarbleEmitter(prototype, delay) output = emitter.emit() self.assertIsInstance(output, EmittedMarble)
def test_error_if_no_other_marbles(self): """ Cannot find closest Marble if no other Marbles exist. """ target = Marble(torch.tensor([12.87, 2.9]), ZERO, ZERO, 0, None, None) model = NenwinModel([], [target]) with self.assertRaises(RuntimeError): find_closest_marble_to(target, model)
def test_attraction_marble_2(self): """ Corner case: should not attract any node if the multiplier is 0. """ marble_attraction = 0 marble = Marble(torch.tensor([-1.], dtype=torch.float), ZERO, ZERO, 0, None, None) particle = create_particle(0, 0, marble_attraction, 0) result = particle.compute_attraction_force_to(marble) self.assertEqual(result, 0)
def test_make_timestep_1(self): """ Base base: single moving Marble, with constant velocity. """ marble = Marble(ZERO, np.array([10]), ZERO, 1, None, None, 0, 0, 0, 0) model = NenwinModel([], [marble]) model.make_timestep(time_passed=1) expected_new_pos = torch.tensor([10], dtype=torch.float) self.assertTrue(torch.allclose(marble.pos, expected_new_pos))
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 test_find_closest_marble_to_1(self): """ Base case: target particle not in Model. """ marble_positions = ( (10, 10), (5, 10), (12.9, 3.2), (9.9, -0.7) ) marbles = [Marble(torch.tensor(pos, dtype=torch.float), ZERO, ZERO, 0, None, None) for pos in marble_positions] model = NenwinModel([], marbles) target = Marble(torch.tensor([12.87, 2.9]), ZERO, ZERO, 0, None, None) expected = marbles[2] self.assertIs(find_closest_marble_to(target, model), expected)
def set_up_full_emitter(self) -> Tuple: prototype = Marble(ZERO, ZERO, ZERO, 0, None, None, 0, 0, 0, 0) delay = 1.1 stored_mass = 1.2 initial_time_passed = 1.3 relative_prototype_pos = torch.tensor([1.4]) emitter = MarbleEmitterVariablePosition(prototype, delay, stored_mass, initial_time_passed, relative_prototype_pos) output = (prototype, delay, stored_mass, initial_time_passed, relative_prototype_pos, emitter) return output
def test_attraction_marble_1(self): """ Check if the marble_attraction multiplier is used to compute force to a node. """ marble_attraction = 0.5 # Position -1 ensures it will be pulled towards '+' x-direction marble = Marble(torch.tensor([-1.], dtype=torch.float), ZERO, ZERO, 0, None, None) particle = create_particle(0, 0, marble_attraction, 0) result = particle.compute_attraction_force_to(marble) self.assertEqual(result, ATTRACT_FUNCT.value * marble_attraction)
def __heads_on_to_bit_marble(locker_positions) -> Marble: """ Create a Marble that horizontally collides into the bit center, approaching from the left. """ pos = np.array([ max(map(lambda x: x[0], locker_positions)), sum(map(lambda x: x[1], locker_positions)) / len(locker_positions) ]) reader_marble = Marble(pos, np.array([-10, 0]), ZERO, 100, ATTRACTION_FUNCTION, None, **READER_MARBLE_STIFFNESS) return reader_marble
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 cannon_experiment(): """ Simulate a NAND gate using a Nenwin architecture. Also visualizes the simulation and prints the result. """ marble = Marble(pos=np.array([50, 50]), vel=np.array([10, 0]), acc=ZERO, mass=-1, attraction_function=ATTRACTION_FUNCTION, datum=None, **MARBLE_STIFFNESS) nodes = __generate_canon_nodes() run([marble], nodes)
def test_stiffness_to_marble(self): """ Base case: 50% stiffness to a single Marble. """ marble_stiffness = 0.5 marble = Marble(torch.tensor([1.], dtype=torch.float), ZERO, ZERO, 0, ATTRACT_FUNCT, None) particle = create_particle(marble_stiffness, 0, 0, 0) expected = (1 - marble_stiffness) * ATTRACT_FUNCT.value result = particle.compute_experienced_force(set([marble])) self.assertEqual( expected, result, "marble_stiffness, " + f"got: {result}, exptected:{expected}")
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 test_emit_with_location(self): """ MarbleEmitterNodes typically do not emit Marbles at their own position, but at the boundary of their radius. Since MarbleEmitterNodes can move, also the position of the emitted Marble may vary with time, hence MarbleEmitters should be able to set the position of the emitted Marble. """ mass = 10 prototype = Marble(ZERO, ZERO, ZERO, mass, None, None, 0, 0, 0, 0) emitter = MarbleEmitterVariablePosition(prototype, 0, stored_mass=mass) spawn_pos = torch.tensor([1], dtype=torch.float) result = emitter.emit(spawn_pos) self.assertTrue(torch.allclose(spawn_pos, result.pos), f"{spawn_pos} != {result.pos}")
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 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_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 test_emit_with_rel_pos(self): """ Emitted Marbles can have a fixed initial pos set. """ mass = 10 prototype = Marble(ZERO, ZERO, ZERO, mass, None, None, 0, 0, 0, 0) rel_prototype_pos = torch.tensor([11.3]) emitter = MarbleEmitterVariablePosition( prototype, 0, stored_mass=mass, rel_prototype_pos=rel_prototype_pos) spawn_pos = torch.tensor([1], dtype=torch.float) result = emitter.emit(spawn_pos) self.assertTrue( torch.allclose(spawn_pos + rel_prototype_pos, result.pos), f"{spawn_pos} != {result.pos}")
def bit_read_experiment(bit_state: bool = True): """ A bit in state '1' will recoil a reader-Marble into a different angle than it approached (because it is just moving towards a position slightly off the center of the bit), and it would pass in a straight line if the bit is set to '0'. """ locker_nodes = __generate_locker_nodes(LOCKER_POSITIONS) reader_marble = __at_angle_toward_bit_marble(BIT_POS, 0.25 * np.pi, 100, READER_MARBLE_STIFFNESS, 0.03) bit_marble = Marble(**BIT_MARBLE_SETTTINGS) # Any position in line of the movement of the reader Marble, # will work as long as it is far enough from the bit not to capture # the Marble *before* it hit the bit. false_output_reader_pos = reader_marble.pos + 17 * reader_marble.vel false_output_eater = MarbleEaterNode(false_output_reader_pos, ZERO, ZERO, 1, ATTRACTION_FUNCTION, radius=6, **NODE_STIFFNESSES) true_output_eater = MarbleEaterNode(np.array([250, 100]), ZERO, ZERO, 1, ATTRACTION_FUNCTION, radius=6, **NODE_STIFFNESSES) if bit_state: marbles = (bit_marble, reader_marble) else: marbles = (reader_marble, ) output = run(marbles, locker_nodes + [false_output_eater, true_output_eater]) print("Read state of bit:", __model_output_to_bit_state(output))
def __at_angle_toward_bit_marble( bit_position: torch.Tensor, angle: float, horizontal_distance: float, stiffnesses: Dict[str, float], x_error: Optional[float] = 0, mass: Optional[float] = 100, speed_multiplier: Optional[float] = 0.1) -> Marble: """ Create a Marble that collides into the bit center, approaching from the top-left at the given angle. """ vertical_distance = np.tan(angle) * horizontal_distance pos = np.array([ bit_position[0] + horizontal_distance + x_error, bit_position[1] + vertical_distance ]) reader_marble = Marble( pos, speed_multiplier * (bit_position - pos) + np.array([x_error, 0]), ZERO, mass, ATTRACTION_FUNCTION, None, **stiffnesses) return reader_marble
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_marble() -> Marble: """ Create a Marble without meaningfull parameters. """ return Marble(ZERO, ZERO, ZERO, 0, None, None, 0, 0, 0, 0)
""" import unittest import numpy as np import multiprocessing from nenwin.input_placer import InputPlacer from nenwin.output_reader import OutputReader from nenwin.simulation import Simulation, UICommands, UIMessage from nenwin.model import NenwinModel from nenwin.node import Marble from nenwin.test.test_aux import ZERO # Just an arbitrary non-trivial object # ('None' would also be returned when faultly no return value is specified) MOCK_KEYWORD = "test" MOCK_MARBLE = Marble(ZERO, ZERO, ZERO, 0, None, None) class SimulationTestCase(unittest.TestCase): def setUp(self): self.model = MockNenwinModel() self.input_placer = MockInputPlacer() self.output_reader = MockOutputReader() self.pipe, self.__other_pipe_end = multiprocessing.Pipe(duplex=True) self.simulation = Simulation(self.model, self.input_placer, self.output_reader, self.__other_pipe_end) def test_handle_commands_stop(self): """ Base case: test if stop command executed. """
def setUp(self): self.__prototype = Marble( ZERO, ZERO, ZERO, 1, ATTRACT_FUNCT, None, 0, 0, 0, 0)