pulsatrix
Loading...
Searching...
No Matches
pulsatrix::CartPoleEnv Class Reference

The classic cart-pole balancing task (Barto, Sutton & Anderson 1983), the same equations OpenAI Gym's own CartPoleEnv implements – cited as the canonical, independently-verifiable reference for this class's correctness tests, not reused as a code or runtime dependency. More...

#include <cartpole_env.hpp>

Inheritance diagram for pulsatrix::CartPoleEnv:
Collaboration diagram for pulsatrix::CartPoleEnv:

Public Member Functions

 CartPoleEnv (DeviceBackend *backend, int64_t max_steps=200, uint32_t seed=42)
 Constructs a fresh, not-yet-reset CartPole environment.
 
Tensor reset () override
 Starts a new episode from a small random state: each of the 4 state variables drawn i.i.d. uniform in [-0.05, 0.05] from the internal LCG.
 
Tensor reset (const Tensor &initial_state) override
 Starts a new episode from an exact caller-supplied state, bypassing the LCG.
 
StepResult step (const Tensor &action) override
 Advances the physics one timestep (tau = 0.02 s) under the given action.
 
int64_t observation_dim () const override
 4 – (x, x_dot, theta, theta_dot).
 
int64_t action_dim () const override
 2 – push left (0) or push right (1).
 
bool is_discrete () const override
 True – CartPole's action space is discrete.
 
int64_t step_count () const
 Steps taken since the last reset().
 
int64_t max_steps () const
 Episode length limit this environment was constructed with.
 
- Public Member Functions inherited from pulsatrix::Environment
virtual ~Environment ()=default
 

Static Public Attributes

static constexpr double kXThreshold = 2.4
 Cart position past which the episode terminates.
 
static constexpr double kThetaThreshold = 0.20943951
 Pole angle (radians) past which the episode terminates (~12 degrees).
 

Detailed Description

The classic cart-pole balancing task (Barto, Sutton & Anderson 1983), the same equations OpenAI Gym's own CartPoleEnv implements – cited as the canonical, independently-verifiable reference for this class's correctness tests, not reused as a code or runtime dependency.

State is (x, x_dot, theta, theta_dot): cart position, cart velocity, pole angle from vertical (radians), pole angular velocity. The observation is exactly that 4-vector, shape (1, 4). Two discrete actions: 0 pushes the cart left, 1 pushes it right.

Every step yields reward 1.0, including the terminating step (Gym's own convention – the agent is rewarded for having survived through this step). The episode ends when |x| > 2.4, |theta| > 0.20943951 rad (~12 degrees), or max_steps steps have been taken.

Note
The physics constants (gravity, masses, pole length, force magnitude, integration timestep, termination thresholds) are fixed, not constructor-configurable – a deliberate scope cut. Parameterizing them is a future mission's job if a real need appears; a knob with exactly one used value is speculative generality.
Integration is explicit (forward) Euler in Gym's exact order: x and theta are updated from the pre-update velocities. Semi-implicit Euler is a one-line difference that produces measurably different trajectories; do not "fix" this.
Physics is computed in double and stored in double; only the observation Tensor is float. Repeated float-precision Euler steps drift enough over a 200-step episode to make a hand-derived reference trajectory unreproducible, which would undermine this class's whole reason for existing.

Constructor & Destructor Documentation

◆ CartPoleEnv()

pulsatrix::CartPoleEnv::CartPoleEnv ( DeviceBackend *  backend,
int64_t  max_steps = 200,
uint32_t  seed = 42 
)
explicit

Constructs a fresh, not-yet-reset CartPole environment.

Parameters
backendBackend to allocate observation tensors through. Not owned; must outlive this object.
max_stepsEpisode length limit. Must be >= 1. Defaults to 200 – deliberately below Gym's canonical 500, since this is a correctness-test environment rather than a benchmark.
seedSeed for the internal deterministic LCG used by the no-argument reset().
Exceptions
std::invalid_argumentif max_steps < 1 – external boundary.
Note
The DeviceBackend* parameter is not in the mission's stated signature, but a Tensor cannot be constructed without one, and every other allocating type in this codebase takes the backend by injection rather than reaching for a global. Resolved that way; max_steps/seed keep their stated defaults and order.

Member Function Documentation

◆ action_dim()

int64_t pulsatrix::CartPoleEnv::action_dim ( ) const
inlineoverridevirtual

2 – push left (0) or push right (1).

Implements pulsatrix::Environment.

◆ is_discrete()

bool pulsatrix::CartPoleEnv::is_discrete ( ) const
inlineoverridevirtual

True – CartPole's action space is discrete.

Implements pulsatrix::Environment.

◆ max_steps()

int64_t pulsatrix::CartPoleEnv::max_steps ( ) const
inline

Episode length limit this environment was constructed with.

◆ observation_dim()

int64_t pulsatrix::CartPoleEnv::observation_dim ( ) const
inlineoverridevirtual

4 – (x, x_dot, theta, theta_dot).

Implements pulsatrix::Environment.

◆ reset() [1/2]

Tensor pulsatrix::CartPoleEnv::reset ( )
overridevirtual

Starts a new episode from a small random state: each of the 4 state variables drawn i.i.d. uniform in [-0.05, 0.05] from the internal LCG.

Returns
The initial observation, shape (1, 4).
Note
The LCG (the same Numerical-Recipes constants this codebase's tests already use for reproducible pseudo-randomness) is seeded at construction and advances across resets, so two environments built with the same seed produce identical reset sequences and different seeds produce different ones – deterministic and therefore testable, unlike the standard library's implementation-defined engines (\<random\>).

Implements pulsatrix::Environment.

◆ reset() [2/2]

Tensor pulsatrix::CartPoleEnv::reset ( const Tensor &  initial_state)
overridevirtual

Starts a new episode from an exact caller-supplied state, bypassing the LCG.

Parameters
initial_state(x, x_dot, theta, theta_dot), shape (1, 4).
Returns
The initial observation (a copy of initial_state's values), shape (1, 4).
Exceptions
std::invalid_argumentif initial_state's shape is not (1, 4) – external boundary.
Note
Does not check the state against the termination thresholds: pinning an already-terminal state and confirming the very next step() reports done is a legitimate (and tested) use.

Implements pulsatrix::Environment.

◆ step()

StepResult pulsatrix::CartPoleEnv::step ( const Tensor &  action)
overridevirtual

Advances the physics one timestep (tau = 0.02 s) under the given action.

Parameters
actionShape (1, 1), holding the action index as a float: 0 (push left) or 1 (push right), within 1e-4 of an integer.
Returns
The resulting observation (1, 4), reward 1.0, and whether the episode ended.
Exceptions
std::invalid_argumentif neither reset() overload has been called yet, if the action's shape is not (1, 1), or if the decoded index is not within 1e-4 of an integer in [0, action_dim()). All external boundary: an action can originate from an untrusted policy output or, eventually, Python bindings.
Note
Host boundary (GPU-native-kernels Mission 7): the physics is a scalar double-precision update on the host. action may live on any device – one device->host copy of it per call – and the observation is returned through this environment's own backend.
Stepping past a done=true result is allowed and keeps integrating; enforcing "reset after done" would be a second precondition with no caller to serve, and Gym itself only warns. The episode loop is the caller's responsibility.

Implements pulsatrix::Environment.

◆ step_count()

int64_t pulsatrix::CartPoleEnv::step_count ( ) const
inline

Steps taken since the last reset().

Member Data Documentation

◆ kThetaThreshold

constexpr double pulsatrix::CartPoleEnv::kThetaThreshold = 0.20943951
staticconstexpr

Pole angle (radians) past which the episode terminates (~12 degrees).

◆ kXThreshold

constexpr double pulsatrix::CartPoleEnv::kXThreshold = 2.4
staticconstexpr

Cart position past which the episode terminates.


The documentation for this class was generated from the following file: