BookmarkSubscribeRSS Feed

Optimizing the Green Wave: Learning PROC OPTMODEL Through a Traffic Signal Coordination Case Study

Started Wednesday by
Modified Wednesday by
Views 85

 

Traffic engineers dream of the perfect “green wave” — a corridor of synchronized signals that lets vehicles legally glide through intersections without stopping.

 

In this post, we’ll expand on the scenario from the previous post to make it more useful. Instead of just finding signal timing offsets that optimize the green wave for one direction, we will now try optimizing traffic flow in both directions through traffic lights controlling cross traffic.

 

We’ll use a fictional traffic corridor to explore how SAS PROC OPTMODEL can mathematically design such coordination. Along the way, we’ll touch on the most important modeling constructs: sets, parameters, variables, constraints, and objectives — the backbone of any optimization program. We’ll also consider what could go wrong along the road to optimization. The lessons we learn also apply to supply chain and manufacturing contexts.

 

The focus here is on how general, traffic-signal coordination problems are formulated. The introductory specification is intended to be straight-forward enough to build intuition. The cost of this "straight-forwardness" is that most of the constraints and variables described below, don’t contribute to its optimal solution, yet they are necessary in more general traffic-signal problems, Additional complexity, for example, left turns and sensor input, can be added to the formulation in a straight-forward way.

 

 

1. Setting the Stage: The Fictional Corridor

 

Imagine a five-intersection arterial running east–west through a mid-sized city and north-south cross streets with traffic lights at every intersection. Each intersection has a fixed time controller with a 10 second green / 13-second red cycle. The start time for each signals cycle can be offset relative to a master clock timer. To complicate matters slightly, the eastbound (EB) direction travels at 35 mph, while the westbound (WB) direction moves faster at 40 mph.

 

Our goal: find offsets — the time differences between signal cycle starts against a master reference clock — that allow platoons of vehicles traveling simultaneously in both directions along the east-west road to drive without facing a red light. We are not considering optimizing any north-south traffic in this example for simplicity. If a green wave in both directions cannot be found, the objective will minimize the wait times for the east and west bound automobile traffic in the system.

 

01_saspch_blog41.png

Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.

 

 

2. Declaring Sets: The Skeleton of the Model

 

In PROC OPTMODEL, sets define the structure of the problem.

 

set INTERSECTIONS = 1..5;

set DIRECTIONS = {"EB","WB"};

set LINKS_FWD = {i in INTERSECTIONS: i < 5};

set LINKS_REV = {i in INTERSECTIONS: i > 1};

 

Here, intersections are indexed 1–5, with Eastbound/Westbound directions.

 

The forward and reverse link sets describe how traffic flows between intersections. There are 4 links between 5 intersections. This mirrors how real controllers treat phase groups — each link represents a movement that must be coordinated.

 

 

3. Parameters: The Known Quantities

 

Parameters are constants that describe the system — cycle length, green time, travel speed, and distances.

 

num CYCLE = 23;

num GREEN = 10;

num RED = CYCLE - GREEN;

num SPEED_EB = 51; /* ft/sec for 35 mph */

num SPEED_WB = 58; /* ft/sec for 40 mph */

 

These values emulate signal phase durations used in professional traffic systems.

 

The GREEN and RED parameters define the active and inactive portions of each cycle. Adding 10 seconds of green to 13 seconds of red yields 23 seconds for a complete cycle. Speed is coded in feet/sec rather than in mph.

 

By treating cycle lengths as constants, we focus the optimization on traffic light timing relationships. More complex optimization solutions include adaptive signal cycle timing, multiple phases (left turn only, advanced green, pedestrian crossing) and traffic queue clearing constraints for platoons (groups of cars).

 

 

4. Derived Data: Distances and Travel Times

 

The next block records the distances between intersections and converts them into travel times (in feet). This information could be read in from an external dataset for a larger optimization run.

 

num distance {d in DIRECTIONS, i in INTERSECTIONS};

distance["EB",1] = 800;

distance["EB",2] = 740;

distance["EB",3] = 620;

distance["EB",4] = 500;

 

for {i in LINKS_REV} do;

distance["WB",i] = distance["EB",i-1];

end;

 

This reversal for-loop ensures WB geometry mirrors EB corridors.

 

Then, travel times for each link between intersections are derived for each direction based on the different speed limits:

 

num trav_time {d in DIRECTIONS, i in INTERSECTIONS};

for {i in LINKS_FWD} do;

trav_time["EB",i] = distance["EB",i] / SPEED_EB;

trav_time["WB",i] = distance["WB",i] / SPEED_WB;

end;

 

The following table shows the resulting travel times between intersections.

 

02_saspch_blog42.png

 

Eastbound traffic flows from intersections 1 – 5, while westbound traffic flows from intersections 5 – 1 (right to left in the table).

 

 

5. Decision Variables: What We’re Solving For

 

Optimization revolves around decision variables — the unknowns we want to determine.

 

var offset {d in DIRECTIONS, i in INTERSECTIONS} >= 0 <= CYCLE;

 

Each offset represents the start of green time relative to a master reference clock for intersection i in direction d.

 

By restricting them between 0 and CYCLE (recall that cycle time = 23 seconds), we ensure the offsets stay within one signal cycle. It also prevents the optimization run from deciding that an optimal solution would leave a vehicle stopped indefinitely!

 

Intended purpose

 

  • Coordinate signals to form a green wave
  • Align progression in multiple directions
  • Act as primary control levers in optimization

 

Note that in the current program Offsets are treated as fixed parameters, not decision variables. Consequently:

 

  • signal timing is not optimized,
  • progression is evaluated rather than designed.

 

This choice simplifies the model and keeps the focus on visualization and structure, but it also eliminates the core degree of freedom present in professional coordination models.

 

Arrival Time Variables

 

The model defines arrival times for vehicles at each intersection and direction (eastbound and westbound).

 

var arrival {d in DIRECTIONS, i in INTERSECTIONS};

 

Intended purpose

 

  • Represent the time a vehicle reaches each signal
  • Serve as the backbone of time–space diagrams
  • Link vehicle movement to signal timing and coordination

 

Note that in the current program arrival times are not free decision variables. Instead, they are deterministically computed based on:

 

  • an initial departure time, and
  • fixed travel times between intersections.

 

Because arrival times can be computed, the solver does not choose or adjust them. They function as derived quantities rather than optimization decisions.

 

Cycle Index Variables

 

The model introduces integer cycle indices to identify which signal cycle a given arrival belongs to.

 

var k {d in DIRECTIONS, i in INTERSECTIONS} integer >= 0 <=2;

 

Intended purpose

 

  • Track arrivals across repeating signal cycles
  • Support longer corridors and multi cycle progression
  • Enable professional style MILP (Mixed Integer Linear Programming) formulations

 

Note that in the current program although declared as integer variables, cycle indices do not participate in binding constraints and do not affect the objective. As a result, their values are fixed implicitly and never influence feasibility or optimality.

 

They primarily serve a conceptual and instructional role, illustrating how cycle tracking is handled in full scale coordination models.

 

Slack (Late) Variables

 

Slack variables represent delay—specifically, waiting time incurred when a vehicle arrives outside the green window.

 

var late {d in DIRECTIONS, i in INTERSECTIONS} >= 0 ;

 

Intended purpose

 

  • Measure red light delay at intersections
  • Quantify inefficiency in the progression
  • Serve as the primary quantity minimized in the objective

 

Note that in the current program Slack appears only in the green upper bound constraint, consistent with traffic engineering intuition that delay occurs at signals rather than mid block.

 

However:

 

  • slack variables are nonnegative and unbounded, and
  • no competing decision forces them away from zero.

 

As a result, the solver sets all slack variables to zero immediately, and they do not contribute to optimization tradeoffs.

 

 

6. Constraints: The Rules of the Road

 

Constraints enforce logical relationships between variables.

 

This program contains several named constraints that are intended to represent the mechanics of signal progression and green wave coordination in both directions. Conceptually, these constraints mirror how professional traffic coordination models are structured. However, not all declared constraints necessarily influence the optimization outcome.

 

This section walks through the major constraint families in the model, explaining both their intent and their actual effect in the current formulation.

________________________________________

Clarifying the Role of Slack and Constraint Structure

 

Progression Constraints (prog_eb, prog_wb)

 

The progression constraints define the kinematic relationship between arrivals at successive intersections. For the eastbound direction, prog_eb enforces that the arrival time at intersection i+1 equals the arrival time at intersection i plus the fixed travel time between intersections:

 

con prog_EB {i in LINKS_FWD}:

arrival["EB", i+1] = arrival["EB", i] + trav_time["EB", i];

 

A corresponding westbound constraint (prog_wb) applies the same logic in reverse order.

 

Key modeling choice

 

These constraints are written as exact equalities, with no slack variables included. This reflects the assumption that:

 

  • vehicles traverse links at a known speed, and
  • delays occur at signals, not while traveling between them.

 

As a result, the progression constraints serve as structural bookkeeping equations that propagate arrival times downstream rather than decision enforcing constraints.

 

Because arrival times are deterministically defined and never adjusted by the solver, the progression constraints are always satisfied and are removed by the presolver.

________________________________________

Green Window Constraints and Slack (green_lb, green_ub)

 

The green window constraints determine whether a vehicle arrives during the green phase of a signal. These constraints define the interface between signal timing and delay.

 

  • green_lb (lower bound) ensures arrivals occur after the green starts
  • green_ub (upper bound) ensures arrivals occur before the green ends

 

Only the upper bound (green_ub) introduces slack:

 

Arrival ≤ GreenEnd + Late

 

Here, late represents waiting time due to red signal delay.

 

con green_ub {d in DIRECTIONS, i in INTERSECTIONS}:

arrival[d,i] - offset[i] - k[d,i]*CYCLE <= GREEN + late[d,i];

 

Interpretation

 

  • If a vehicle arrives before the green ends, late = 0
  • If it arrives after the green ends, late captures the delay

 

This aligns with traffic engineering intuition: delay is incurred at the signal, not mid block.

 

However, in the current program:

 

  • green start and end times are fixed
  • arrivals are pre-aligned to those windows
  • late is unbounded and lightly penalized

 

As a result, the solver can satisfy green_ub exactly without trade offs, and the constraint is eliminated during presolve. ________________________________________

Why Slack Appears Only in the Green Constraint

 

This separation is intentional and conceptually sound, even though it currently leads to a degenerate model:

 

  • Progression constraints → physics of movement (no delay allowed)
  • Green window constraints → operational delay (captured by slack)

 

The issue is not where slack appears, but that:

 

  • arrival times are not decision variables, and
  • slack is not tightly constrained or meaningfully traded off

 

Thus, the model documents signal delay mechanics correctly but does not yet optimize them.

________________________________________

This formulation highlights an important modeling lesson:

 

Not all constraints are meant to introduce flexibility.

Some exist to propagate structure, while others are the sole gateways for delay and optimization.

 

In the present program:

 

  • prog_eb and prog_wb provide temporal structure only
  • green_ub is the only place where delay can occur
  • but no decision variable allows the solver to change arrivals or offsets

 

NOTE: To activate these constraints in an optimization sense, either arrival times or signal offsets must become true decision variables, and slack must be bounded or heavily penalized.

 

 

7. Objective Function: Defining “Best”

 

Optimization needs a goal — here, we minimize total delay.

 

minimize TotalLate =

  sum {d in DIRECTIONS, i in INTERSECTIONS}

     late[d,i];

 

Unlike optimizing travel in a single direction where a complete green wave cycle may be possible, optimizing traffic in opposite directions faces compromises. Minimizing late arrival time (approach a red signal) is a reasonable optimization objective for this type of problem.

 

 

8. Solving and Visualizing

 

The results show this is a minimization problem and lists the number of variables and constraints. This is a Linear objective problem.

 

03_saspch_blog4a.png

 

The solver identified the problem as MILP (Mixed Integer Linear Programming) and using the Branch and Cut algorithm found an optimal solution objective value of 14.48. This is the number of seconds vehicles face red.

 

04_saspch_blog4b.png

 

In addition to the total travel time shown previously, the solver also computes:

 

  • optimal offsets for each signal’s start time against a master clock
  • the number of seconds of wait time in the late table
  • the signal cycle (k) for a trip through the corridor EB and WB

 

With the first signal cycle being 0, the k table below shows that the EB trip caught the second cycle "1" for intersection 4 and the third cycle "2" for intersection 5.

 

05_saspch_blog4c-1.png

 

These results can be visualized in a Time–Space Diagram, where each line shows vehicle trajectories and green intervals. The corresponding arrival time table is shown for reference.

 

The left side of the diagram EB reads top to bottom through the intersections showing the arrival times at each intersection. Notice that the arrival table for intersection 4 shows the time is 42.35 seconds (in the second table above), but the graph shows the arrival time at intersection 4 EB as 42.35 - 23 = 19.35 seconds into the second traffic cycle for that intersection.

 

The WB direction starts at intersection five and timing progresses from bottom to top on the right side of the plot.

06_saspch_blog4d-1.png

 

 

9 Connecting to Real World Practice

 

Professional traffic control systems like Synchro, TRANSYT, and SCOOT use the same conceptual framework:

 

07_saspch_blog4e.png

 

 

10 Conclusion

 

This PROC OPTMODEL program is more than a code exercise — it’s a miniature traffic laboratory.

 

By defining sets, parameters, variables, constraints, and objectives, we’ve touched on how professional systems coordinate signals to create a green wave.

 

Through this fictional corridor, we’ve learned that optimization isn’t just about numbers — it’s about translating physical behavior into mathematical relationships.

 

And that’s exactly what makes PROC OPTMODEL such a powerful tool for engineers, planners, and data scientists alike.

 

Thanks for reading!

 

 

Find more articles from SAS Global Enablement and Learning here.

 

 

This is the proc optmodel program.

proc optmodel;

/*================================================
SETS
================================================*/
set INTERSECTIONS = 1..5;
set DIRECTIONS = {"EB","WB"};
num LAST_I = card(INTERSECTIONS);
set LINKS_FWD = {i in INTERSECTIONS: i  1};

/*================================================
PARAMETERS
================================================*/
num CYCLE = 23;
num GREEN = 10;
num RED = CYCLE - GREEN;

num SPEED_EB = 51; /* ft/s for 35 mph */
num SPEED_WB = 58; /* ft/s for 40 mph */

/*------------------------------------------------
Distances (ft)
------------------------------------------------*/
num distance {d in DIRECTIONS, i in INTERSECTIONS};

distance["EB",1] = 800;
distance["EB",2] = 740;
distance["EB",3] = 620;
distance["EB",4] = 500;

/* WB reversed geometry */
for {i in LINKS_FWD} do;
distance["WB", i+1] = distance["EB", i];
end;

/*------------------------------------------------
Travel times (s)
------------------------------------------------*/
num trav_time {d in DIRECTIONS, i in INTERSECTIONS};

for {i in LINKS_FWD} do;
trav_time["EB", i] = distance["EB", i] / SPEED_EB;
trav_time["WB", i+1] = distance["WB", i+1] / SPEED_WB;
end;

/*================================================
DECISION VARIABLES
================================================*/

/* Signal offsets (cyclic) */
var offset {i in INTERSECTIONS} >= 0 = 0 = 0 ;

/*================================================
CONSTRAINTS
================================================*/

/*------------------------------------------------
Arrival anchoring
------------------------------------------------*/
con anchor_EB:
arrival["EB",1] = 0;

con anchor_WB:
arrival["WB",LAST_I] = 0;

/*------------------------------------------------
Arrival propagation
------------------------------------------------*/
con prog_EB {i in LINKS_FWD}:
arrival["EB", i+1] =
arrival["EB", i] + trav_time["EB", i];

con prog_WB {i in LINKS_REV}:
arrival["WB", i-1] =
arrival["WB", i] + trav_time["WB", i];

con green_lb {d in DIRECTIONS, i in INTERSECTIONS}:
arrival[d,i] - offset[i] - k[d,i]*CYCLE >= 0;

con green_ub {d in DIRECTIONS, i in INTERSECTIONS}:
arrival[d,i] - offset[i] - k[d,i]*CYCLE <= GREEN + late[d,i];

/*------------------------------------------------
Reference signal
------------------------------------------------*/
con anchor_offset:
offset[1] = 0;

/*================================================
OBJECTIVE
================================================*/
min TotalLate =
sum {d in DIRECTIONS, i in INTERSECTIONS}
late[d,i];

/*================================================
SOLVE & INSPECT
================================================*/
solve;

print trav_time;
print offset;
print arrival;
print k;
print late;

/*------------------------------------------------
Create plotting data (arrival modulo cycle)
------------------------------------------------*/

/* Arrival time within cycle (modulo CYCLE) */
num arrival_mod {d in DIRECTIONS, i in INTERSECTIONS};

for {d in DIRECTIONS, i in INTERSECTIONS} do;
arrival_mod[d,i] = arrival[d,i] - k[d,i] * CYCLE;
end;

create data arrival_plot from
[d i] = {d in DIRECTIONS, i in INTERSECTIONS}
direction = d
intersection = i
time_in_cycle = arrival_mod[d,i];

/* Green window bounds for plotting */
num green_start_plot {i in INTERSECTIONS};
num green_end_plot {i in INTERSECTIONS};

for {i in INTERSECTIONS} do;
green_start_plot[i] = offset[i];
green_end_plot[i] = offset[i] + GREEN;
end;

create data green_plot from
[i] = {i in INTERSECTIONS}
intersection = i
green_start = green_start_plot[i]
green_end = green_end_plot[i];

quit;

proc sort data=arrival_plot;
by direction intersection;
run;

proc sort data=green_plot;
by intersection;
run;

proc sgpanel data=arrival_plot;
panelby direction / layout=columnlattice onepanel;
series x=time_in_cycle y=intersection /
markers lineattrs=(thickness=2);
scatter x=time_in_cycle y=intersection /
markerattrs=(symbol=circlefilled);

colaxis label="Time within cycle (seconds)"
values=(0 to 90 by 10);
rowaxis label="Intersection"
reverse discreteorder=data;

title "Time–Space Diagrams for EB and WB Traffic";
run;

Contributors
Version history
Last update:
Wednesday
Updated by:

Catch up on SAS Innovate 2026

Nearly 200 sessions are now available on demand with the SAS Innovate Digital Pass.

Explore Now →

SAS AI and Machine Learning Courses

The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.

Get started

Article Tags