Simulator API
Warning
qrobot_simulator is experimental and its interfaces may change between minor releases.
qrobot_simulator provides two small closed-loop worlds for studying how sensor
histories become robot actions. Each world separates four concerns:
the world advances physical state and computes normalized sensor readings;
a robot passes those readings to its brain;
the brain returns normalized actuator values;
an optional live view renders state without participating in the simulation.
This common boundary allows each world to run a classical or quantum robot. Headless
methods use the same world dynamics without constructing Matplotlib figures. Quantum
robots additionally require a running Redis server for communication among
independently scheduled qUnits. Each simulator also has a separate analysis package
for controlled inputs and recorded measurements; these tools observe the simulation
without becoming part of the world or robot behavior.
Install the simulator dependencies and start either interactive example with:
poetry install -E simulator
poetry run python examples/grasping_world.py --gripper quantum
poetry run python examples/bug_world.py --controller quantum
The grasping-world notebook explains the sensors, robot brains, and fixed comparison. The bug-world notebook explains the predator/prey interaction and the perceptual and cognitive layers.
Grasping world
The grasping world contains a stationary gripper and a blue ball moving along its
sensor axis. ReactiveGripper uses only the latest readings. ClassicalGripper
averages fixed temporal windows, while QuantumGripper processes equal-duration
histories through its qBrain. All three use the same world interface:
from qrobot_simulator.grasping_world import ClassicalGripper, GraspingWorld
world = GraspingWorld.demo(ClassicalGripper(), seed=7)
world.run_robot_headless(duration=20.0, dt=0.05)
print(world.correct_grips, world.missed_grips, world.empty_grips)
GraspingWorld.demo() keeps the ball’s random movement for interactive use. The
grasping analysis API provides predefined ball visits and
raw recording when the same situation must be measured across different brains.
GraspingWorldLiveView can display the same world or save a frame; it is unnecessary for headless runs.
- class qrobot_simulator.grasping_world.GraspingWorld(arena: GraspingArena, gripper: BaseGripper, ball: BallPrey, config: GraspingWorldConfig = GraspingWorldConfig(near_distance=5.0, far_distance=20.0, minimum_distance=5.0, grippable_distance=15.0, consumption_time=2.5, ball_distance_scale=0.16, sensor_offset_x=0.55, arena_width=12.0, arena_height=6.0, arena_cell_size=1.0), prey_config: BallPreyConfig = BallPreyConfig(radius=0.32, color='#2878d0', max_distance=45.0, initial_distance_range=(24.0, 40.0), respawn_distance_range=(30.0, 45.0), initial_velocity_range=(-1.0, 1.0), velocity_kick_range=(-4.0, 4.0), max_speed=7.0, motion_change_interval=(0.35, 1.1), max_near_duration=1.0, escape_speed=5.0), elapsed: float = 0.0, touch_pressed: bool = False, readings: dict[str, float]=<factory>, correct_grips: int = 0, missed_grips: int = 0, empty_grips: int = 0, consumed_prey: int = 0, premature_releases: int = 0, seed: int | None = None, grip_response_times: list[float] = <factory>, _inside_visit: bool = False, _visit_gripped: bool = False, _consumption_started_at: float | None = None, _visit_started_at: float | None = None, _rng: Random = <factory>)[source]
Represent one stationary gripper and one wandering ball prey.
- Parameters:
arena – Visible checkerboard dimensions.
gripper – Classical or quantum gripper controlled by an actuator.
ball – Ball prey moving in front of the gripper.
elapsed – Simulated time in seconds.
touch_pressed – Whether caught prey presses the touch sensor.
readings – Latest normalized proximity and touch readings.
correct_grips – Closing transitions that caught prey.
missed_grips – Grippable visits that ended uncaught.
empty_grips – Closing transitions made without grippable prey.
consumed_prey – Captured prey held until consumption completed.
premature_releases – Captured prey released before consumption completed.
seed – Seed controlling random ball movement.
- classmethod demo(gripper: BaseGripper, seed: int | None = None, config: GraspingWorldConfig = GraspingWorldConfig(near_distance=5.0, far_distance=20.0, minimum_distance=5.0, grippable_distance=15.0, consumption_time=2.5, ball_distance_scale=0.16, sensor_offset_x=0.55, arena_width=12.0, arena_height=6.0, arena_cell_size=1.0), prey_config: BallPreyConfig = BallPreyConfig(radius=0.32, color='#2878d0', max_distance=45.0, initial_distance_range=(24.0, 40.0), respawn_distance_range=(30.0, 45.0), initial_velocity_range=(-1.0, 1.0), velocity_kick_range=(-4.0, 4.0), max_speed=7.0, motion_change_interval=(0.35, 1.1), max_near_duration=1.0, escape_speed=5.0), prey: BallPrey | None = None) GraspingWorld[source]
Create the interactive world with randomly wandering prey.
- Parameters:
gripper – Configured gripper to place in the world.
seed – Optional seed for reproducible random prey movement.
prey – Optional configured prey instance; otherwise one is sampled.
- Returns:
Initialized world with its first sensor snapshot.
- run_headless(controller: Callable[[dict[str, float]], float] | None, duration: float, dt: float = 0.01) GraspingWorld[source]
Advance a controller without constructing or refreshing a live view.
- Parameters:
controller (callable) – Function mapping the latest normalized readings to gripper activation.
duration (float) – Positive simulated duration in seconds.
dt (float) – Positive fixed physics integration step in seconds.
- Returns:
This world after the requested simulated duration.
- Return type:
- run_robot_headless(duration: float, dt: float = 0.01) GraspingWorld[source]
Run any gripper through the same headless world interface.
- class qrobot_simulator.grasping_world.ClassicalGripper(config: ClassicalGripperConfig = ClassicalGripperConfig(x=2.0, y=3.0, color='#704214', half_width=0.55, half_height=0.65, gripper_threshold=0.5, sampling_period=0.1, proximity_tau=10, empty_gripper_tau=50), brain: BaseGripperBrain | None = None)[source]
Gripper that filters recent readings with ordinary arithmetic averages.
- class qrobot_simulator.grasping_world.ReactiveGripper(config: ReactiveGripperConfig = ReactiveGripperConfig(x=2.0, y=3.0, color='#704214', half_width=0.55, half_height=0.65, gripper_threshold=0.5, proximity_threshold=0.5, contact_threshold=0.5), brain: BaseGripperBrain | None = None)[source]
Gripper that responds immediately without filtering sensor readings.
- class qrobot_simulator.grasping_world.QuantumGripper(redis_config: RedisConfig | None = None, speed: float = 1.0, config: QuantumGripperConfig = QuantumGripperConfig(x=2.0, y=3.0, color='#704214', half_width=0.55, half_height=0.65, gripper_threshold=0.5, sensor_keys=('proximity', 'touch'), sampling_period=0.1, proximity_tau=10, empty_gripper_tau=50, proximity_query=(1.0,), empty_gripper_query=(1.0,), touch_default_input=1.0, qunit_dimensions=1), brain: BaseGripperBrain | None = None)[source]
Gripper controlled by the AngularModel-based qBrain.
- class qrobot_simulator.grasping_world.GraspingWorldLiveView(arena: GraspingArena, interactive: bool = True)[source]
Display a persistent view of the gripper encounter and qBrain state.
- save(path: Path) Path[source]
Save the current frame to an image.
Parent directories are created when needed.
- Parameters:
path – Destination image path.
- Returns:
Destination path after the figure is saved.
- update(world: GraspingWorld, signals: dict[str, float | None] | None = None, phase: str = 'RUNNING') None[source]
Refresh physical geometry and observable qBrain values.
- Parameters:
world – Current simulation state to display.
signals – Latest observable qBrain outputs, when available.
phase – Short label describing the simulation phase.
Bug world
The bug world contains one controlled brown bug, blue prey, and a red predator.
ClassicalBug maps stereo RGB and proximity readings directly to five actions.
QuantumBug can include intermediate prey and threat qUnits before producing the
same actions. BugWorld records contacts, boundary crossings, and complete
time-series data:
from qrobot_simulator.bug_world import BugWorld, ClassicalBug
world = BugWorld.demo(ClassicalBug(), seed=7)
record = world.run_recorded_headless(duration=20.0, dt=0.05)
print(world.bitten_prey, world.predator_bites)
BugWorldLiveView displays the world state and current brain diagnostics without changing the simulation.
- class qrobot_simulator.bug_world.BugWorld(board: ~qrobot_simulator.bug_world.world.arena.Chessboard, bug: ~qrobot_simulator.bug_world.robots.base_bug.BaseBug, prey: list[~qrobot_simulator.bug_world.robots.blue_prey.BluePrey], predator: ~qrobot_simulator.bug_world.robots.red_predator.RedPredator, config: ~qrobot_simulator.bug_world.world.config.WorldConfig = WorldConfig(board_columns=18, board_rows=12, board_cell_size=1.0, prey_spawns=(('prey 1', 9.5, 6.2, 3.141592653589793, 'deterministic'), ('prey 2', 8.8, 1.6, 2.6, 'random')), predator_spawn=('predator', 1.5, 6.5, -0.5), proximity_distance=1.25, proximity_half_angle_degrees=25.0, eye_angle=0.5235987755982988, eye_distance_scale=4.5, eye_angular_exponent=12, min_eye_distance=0.1, bug_bite_reach=0.45, predator_bite_reach=0.15, bite_cue_duration=0.45), elapsed: float = 0.0, readings: dict[str, float] = <factory>, bitten_prey: int = 0, predator_bites: int = 0, bug_biting: bool = False, predator_biting: bool = False, boundary_crossings: ~collections.Counter[str] = <factory>)[source]
Represent the bug, prey, predator, arena, sensors, and scores.
- Parameters:
board – Checkerboard containing every robot.
bug – Classically or quantum-controlled bug body.
prey – Blue prey population.
predator – Red predator pursuing the bug.
config – Arena, sensor, and contact configuration.
elapsed – Total simulated time in seconds.
readings – Latest bug sensor snapshot.
bitten_prey – Number of prey bitten by the bug.
predator_bites – Number of predator contacts scored against the bug.
bug_biting – Whether the bug bite indicator is visible.
predator_biting – Whether the predator bite indicator is visible.
boundary_crossings – Cumulative crossings keyed by robot name.
- classmethod demo(bug: BaseBug | None = None, seed: int | None = None, config: WorldConfig = WorldConfig(board_columns=18, board_rows=12, board_cell_size=1.0, prey_spawns=(('prey 1', 9.5, 6.2, 3.141592653589793, 'deterministic'), ('prey 2', 8.8, 1.6, 2.6, 'random')), predator_spawn=('predator', 1.5, 6.5, -0.5), proximity_distance=1.25, proximity_half_angle_degrees=25.0, eye_angle=0.5235987755982988, eye_distance_scale=4.5, eye_angular_exponent=12, min_eye_distance=0.1, bug_bite_reach=0.45, predator_bite_reach=0.15, bite_cue_duration=0.45), prey_config: PreyConfig = PreyConfig(color='#2878d0', flee_distance=2.2, flee_speed=1.0, wander_speed=0.55, deterministic_turn=0.22, random_turn_range=(-0.65, 0.65), random_choice_interval=(0.7, 1.8), initial_wander_turn=0.2), predator_config: PredatorConfig = PredatorConfig(color='#d43c32', max_speed=1.0, pursuit_speed=0.62, bite_period=3.0, random_noise_range=(-0.3, 0.3), random_choice_interval=(0.8, 1.6))) BugWorld[source]
Create the configured demonstration ecosystem.
- Parameters:
bug – Existing controlled bug. A classical bug is created when omitted.
seed – Optional seed controlling random prey and predator motion.
config – Arena, population, sensor, and contact configuration.
prey_config – Shared motion configuration for blue prey.
predator_config – Motion and contact configuration for the predator.
- Returns:
Initialized world with its first sensor snapshot.
- run_headless(controller: Callable[[dict[str, float]], dict[str, float]], duration: float, dt: float = 0.01) Counter[str][source]
Run a controller without constructing or refreshing a live view.
- Parameters:
controller (callable) – Function mapping the latest readings to named actuator activations.
duration (float) – Positive simulated duration in seconds.
dt (float) – Positive fixed physics integration step in seconds.
- Returns:
Number of physics steps spent in each displayed behavior.
- Return type:
collections.Counter
- run_recorded_headless(duration: float, dt: float = 0.01) BugRunRecord[source]
Run the configured brain and retain raw samples and discrete events.
- run_robot_headless(duration: float, dt: float = 0.01) Counter[str][source]
Run any configured bug brain through the same headless world interface.
- class qrobot_simulator.bug_world.ClassicalBug(config: ClassicalBugConfig = ClassicalBugConfig(name='bug', start_x=5.5, start_y=4.0, start_heading=0.0, color='#704214', radius=0.3, max_speed=1.0, max_turn=0.7, sensor_keys=('proximity', 'lr', 'lg', 'lb', 'rr', 'rg', 'rb'), actuator_keys=('bite', 'forward', 'backward', 'rotate_left', 'rotate_right'), bite_activation_threshold=0.5, forward_gain=1.0, backward_gain=1.0, rotation_gain=1.0, color_detection_threshold=0.25, proximity_threshold=0.5, search_activation=0.3), brain: BaseBugBrain | None = None)[source]
Physical bug driven by an injectable deterministic brain.
- Parameters:
config – Body and classical-controller configuration.
brain – Complete alternative brain, or
Nonefor the default.
- class qrobot_simulator.bug_world.QuantumBug(redis_config: RedisConfig | None = None, speed: float = 1.0, config: QuantumBugConfig = QuantumBugConfig(name='bug', start_x=5.5, start_y=4.0, start_heading=0.0, color='#704214', radius=0.3, max_speed=1.0, max_turn=0.7, sensor_keys=('proximity', 'lr', 'lg', 'lb', 'rr', 'rg', 'rb'), actuator_keys=('bite', 'forward', 'backward', 'rotate_left', 'rotate_right'), bite_activation_threshold=0.5, forward_gain=1.0, backward_gain=1.0, rotation_gain=1.0, topology='cognitive', sensor_period=0.01, cognitive_period=0.1, perceptual_tau=10, cognitive_tau=5, proximity_query=(1.0,), red_query=(1.0, 0.0, 0.0), blue_query=(0.0, 0.0, 1.0), bite_threshold=0.75, forward_threshold=0.25, backward_threshold=0.75, rotation_threshold=0.75), brain: BaseBugBrain | None = None)[source]
Physical bug driven by an injectable quantum or alternative brain.
- Parameters:
redis_config – Redis database used by the default qBrain.
speed – Ratio between simulated time and wall-clock time.
config – Body and qBrain configuration.
brain – Complete alternative brain, or
Nonefor the default.
- class qrobot_simulator.bug_world.BugWorldLiveView(board: Chessboard, interactive: bool = True)[source]
Display a persistent view of the world, sensors, and scores.