Lyapunov-based Safe RL
Lyapunov safe RL gives the agent an energy meter that must drop on every step, so only actions that move toward the goal count as safe.
Why Does This Exist?
Average constraint methods say the agent is safe if its total cost stays below a threshold. That lets a single step spike into danger as long as the average still looks fine. A robot arm can snap a joint once and the average will forgive it. A trading policy can blow past a risk limit for one hour and still pass the monthly audit.
Safe RL introduces that average constraint view with Constrained MDPs. This page adds the stricter per step certificate used when you need stability, not just a good average. You will learn the Lyapunov decrease rule, the Lyapunov critic that enforces it, and the feasible set it protects. If you want the broad safety landscape first, read Safe RL. If you want planning with a learned model, see model-based RL.
Think of It Like This
A marble in a mixing bowl
Imagine a marble rolling inside a smooth mixing bowl. Height is energy. The rim is the safety boundary and the bottom is your goal.
You allow only pushes that leave the marble lower than it started. A gentle push toward the center passes because height drops from 4.0 to 2.25. A push up the wall fails because height climbs from 4.0 to 5.29, even though the marble is still inside the bowl. Staying inside is not enough. It must settle lower each time.
Where the analogy stops: a real bowl gets gravity for free. In RL you must learn the energy function from data, and a bad estimate lets unsafe actions slip through.
How It Actually Works
A Lyapunov function is a learned energy meter over states. Call the current state , the action , and the next state . Call the stage cost , a non negative number that is zero only at the goal. Call a small positive margin rate. A valid satisfies at the goal and everywhere else.
The decrease condition
The policy is certified stable when every step obeys the discrete decrease rule:
In words: the next energy must sit below the current energy by at least a small margin. For the simple version with folded into a strict drop, read it as . If an action breaks the inequality, the safety filter blocks it and picks the closest passing action instead. That is the whole certificate. No drop, no step.
The Lyapunov critic and the feasible set
In practice is a neural critic trained much like a value function, using transitions to predict long term cost to goal. The policy then optimizes reward subject to the critic constraint:
Here means the average over stochastic next states, is the random next state, and comes from . The feasible set is one sublevel of :
Pick the threshold so the starting state sits inside. The decrease rule makes invariant. Once inside, a certified policy can never climb out, because climbing would need to rise. This is stronger than trust region steps in TRPO, which limit how far the policy moves but do not certify each state stays inside an energy level.
Worked example with L(x) = x squared in 1D
Take the 1D candidate . The goal is with . Set the safe threshold , so the safe set is . Start at :
Two candidate actions propose two next states.
Safe candidate: .
The change is negative, so the step passes. With and , the required drop is . The actual clears it with room to spare.
Risky candidate: .
The change is positive, so the filter blocks it. Notice the trap: , so the state is still inside the safe set. Inside is not enough. Without a drop, the policy could drift uphill until it exits. The diagram shows this as two arrows from the ring: the inward mint arrow lands on and the outward amber arrow is stopped before .
One more step completes the picture. From , pick :
Again negative, again accepted. Repeated drops steer to and to .
Code
The snippet below is the exact check the filter runs. It uses the same numbers as the worked example.
def lyapunov(x: float) -> float: """Energy candidate L(x) = x squared.""" return x * x
def passes_decrease(x: float, x_next: float, alpha: float = 0.2) -> bool: """Return True only when the Lyapunov value shrinks enough.""" stage_cost: float = x * x return lyapunov(x_next) <= lyapunov(x) - alpha * stage_cost
if __name__ == "__main__": start = 2.0 safe_next = 1.5 risky_next = 2.3 print(f"L(start) = {lyapunov(start)}") print(f"L(safe) = {lyapunov(safe_next)}, passes = {passes_decrease(start, safe_next)}") print(f"L(risky) = {lyapunov(risky_next)}, passes = {passes_decrease(start, risky_next)}")# -> expected output:L(start) = 4.0L(safe) = 2.25, passes = TrueL(risky) = 5.29, passes = FalseWatch Out For
An invalid Lyapunov candidate passes the code but proves nothing
A function with , with negative values, or with flat valleys away from the goal can still shrink on your test batch and fool the filter. Symptom: training looks safe, then the deployed policy rests at a non goal state where bottoms out above zero.
Fix it by construction. Pin in code, use a positive head such as a squared norm plus a small quadratic, and probe on a grid before trusting it. If any gives , reject the candidate.
Checking the continuous slope instead of the next state drop
Learners copy from control textbooks and check gradients in a discrete simulator. Symptom: the gradient points downhill at , yet the finite step lands uphill at , so rises from to while the slope check smiled and waved it through.
Fix it by always evaluating on the realized next state, averaged over stochastic outcomes. In discrete RL the only decrease that counts is the measured drop.
The Quick Version
- Lyapunov safe RL certifies each step with an energy function that must shrink, so is the gate every action passes through.
- The feasible set is one energy level , and the decrease rule keeps a certified policy inside it for all future steps.
- A Lyapunov critic learns long term cost to goal, and the policy may only pick actions with expected drop .
- In the 1D check with , the move to drops energy to and passes, while to lifts energy to and is blocked.
- Staying inside the safe set is not enough. An action inside the boundary still fails when it climbs uphill.