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.
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.
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
Note that in the current program Offsets are treated as fixed parameters, not decision variables. Consequently:
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
Note that in the current program arrival times are not free decision variables. Instead, they are deterministically computed based on:
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
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
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:
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:
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.
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
This aligns with traffic engineering intuition: delay is incurred at the signal, not mid block.
However, in the current program:
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:
The issue is not where slack appears, but that:
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:
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.
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.
In addition to the total travel time shown previously, the solver also computes:
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.
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.
9 Connecting to Real World Practice
Professional traffic control systems like Synchro, TRANSYT, and SCOOT use the same conceptual framework:
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;
Nearly 200 sessions are now available on demand with the SAS Innovate Digital Pass.
Explore Now →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.