Media · Integer programming · ForecastingCJ AI Center, with CJ CGVWrite-up October 2026 · 8 min read

Thirty rules and one day of screenings

Which film goes on which screen, how many times, and at what minute — under more than thirty business rules, with a target share for every film. This is how a cinema's daily schedule became a solvable decision problem, and a small version of it you can run.

Built withPythonOR-Tools CP-SATLightGBMTemporal Fusion TransformerFastAPIRedisDockerKubernetes
screen 1screen 2screen 3screen 4screen 5screen 6prime time
A day's board: starts staggered so the lobby never jams, the top film in prime time on the biggest screen, every gap long enough to clean and short enough not to waste the screen.

Why a schedule is hard to write by hand

The schedule decides attendance, occupancy, concession sales and how well prime time is used. It is also bound by a long list of rules. The same film needs a minimum interval between starts; different films need spacing for entry, exit and the concession queue; popular films belong in prime time; dubbed children's films need child-friendly hours; each film has a target share of the day. With more than thirty such rules, producing a schedule that obeys all of them while also chasing revenue is close to impossible by hand — so in practice some rules quietly give way.

Rules become constraints

The work translated each business rule into a mathematical constraint of an integer program over screens, show slots, films and start times on a five-minute grid. A few representative families:

Rule familyBecomes
Show chainEach screen is a chain of shows; the next start is at least the cleaning time after the previous end, and not so late that the screen sits idle.
Opening and closingThe first show starts inside an opening window; the last ends before closing.
Audience fitChildren's dubbed titles start early enough; adult-rated titles start late enough.
Lobby flowNo two screens start within the same ten minutes.
Prime timeThe film with the largest target plays the biggest screen in prime time.
Target shareEach film's share of seats, not shows, stays within a tolerance of its target.

Because variables and constraints multiply across films, screens and slots, a direct formulation is slow. Much of the engineering went into tractability: sharing variables between rules, choosing which structures to model explicitly, and reformulating constraints so the solver can prune early.

Live model, solved on a Python server with Google OR-Tools CP-SAT in about eight seconds. Top: a rule-of-thumb plan — one film per screen, back to back, round start times. Bottom: the optimized plan. Red outlines mark shows that break a rule; the share bars compare each film's seat share with its target. Change the theatre size or draw a new day. Films, screens and demand are generated. Open the live model on its own page ↗

How it's built

Architecture, stack and core formulation

A constraint-programming service that turns a day's films, screens and business rules into a timetable, with staged relaxation so a schedule always comes back — and an attendance forecast alongside.

1 · Request

From JSON to a model

Films, screens, opening hours, pre-sold sessions and rule settings mapped to typed objects; ineligible screens and films dropped.

FastAPIdataclassesRedis
2 · Model

CP-SAT on a 5-minute grid

Slots per screen with optional intervals, film choice, start/end/gap times and a seat-weighted share constraint.

OR-Tools CP-SAT
3 · Solve

Staged relaxation

Hard rules only → hard + soft → relaxed hard rules as priced penalties, each stage hinted by the previous one.

CP-SAT hints
4 · Validate

Independent rule check

A validator re-checks every rule on the result and reports any rule given up during relaxation.

validator
5 · Forecast

Attendance by site × film × day

Gradient-boosted trees with a Tweedie objective and a Temporal Fusion Transformer, against a log-target baseline.

LightGBMTFT
Stack
LayerTechnologyWhat it does here
ServicePython 3.11, FastAPI / uvicorn, Redis, Docker, KubernetesAsync solve with callback, one pod per workload
SolverGoogle OR-Tools CP-SATIntervals + NoOverlap, element and domain constraints, decision strategies, hints
Search controlStaged time budget; worker count matched to the container CPU limitPredictable runtime and memory per request
Speed-upsRedundant cuts, DP-built exact seat-sum domains, symmetry breakingTighter model and faster proofs
ForecastLightGBM (Tweedie), Temporal Fusion Transformer, blendingOccupancy by site and time slot, ≈10% MAE
Core formulation
y[s,k,g] ∈ {0,1}   slot k of screen s plays film group g        active[s,k] ∈ {0,1}
start, end, gap    integer minutes on a 5-minute grid

end[s,k]     = start[s,k] + Σ_g y[s,k,g] · (ads + runtime_g)          if active[s,k]
start[s,k+1] = end[s,k] + gap[s,k],        clean_min ≤ gap ≤ clean_max
active[s,k+1] ≤ active[s,k]                                            fill slots from the front

seat share   (share_m − δ)·SEATS ≤ Σ_{sessions of m} seats ≤ (share_m + δ)·SEATS

relaxed stages   min  Σ_r price_tier(r) · violation_r  +  soft terms
  • Always a schedule. Each relaxation stage caps total penalty at the previous stage's value and starts from its solution as hints.
  • Exact domains. Reachable seat totals per film are pre-computed by dynamic programming, so the share constraint uses exact domains instead of loose bounds.
  • Memory. Fewer parallel workers cut peak memory sharply with no loss in quality, so worker count follows the container's CPU limit.
In production vs in the live model
ComponentIn productionIn the live model above
Rules30+ client rules as constraints and priced penaltiesTen rule families on a generated multiplex
SolveStaged relaxation inside a total time budgetHard → relaxed → greedy fallback within 8 s on a 2-core server
ForecastLightGBM / TFT attendance modelNot reproduced; demand comes from the generator

Inside the model

Seat-weighted share, preferred time slots

The objective balances three things: closeness to the target share of seats, the number of shows, and expected attendance — shows of films that draw better in the evening are worth more in the evening. Share is written linearly by multiplying through by total seats, so the solver never divides.

Occupancy prediction

Alongside the scheduler, an occupancy model estimates how well a film will do by site and time slot, using film performance, site characteristics, time of day, show sequence and the number of daily screenings as inputs. It reached about 10% MAE, and it is what gives "expected attendance" a meaning inside the objective.

When rules collide

Some days have no schedule that satisfies every rule — a pre-sold show fixed in an awkward place, or a film mix that cannot fit. The production design separates rules that may never bend from rules that can be relaxed with a penalty, and steps down through stages instead of returning an empty board. The live model keeps that idea in miniature: if the strict model finds nothing in time, a relaxed model runs, and as a last resort a rule-respecting greedy plan is returned.

What it changed

30+business rules expressed as constraints of one model
≈10%MAE of the occupancy prediction model
Autoschedules generated that are hard to produce manually

The model generates schedules that respect the full rule set — something very hard to achieve by hand — and the occupancy model gives the objective a measure of demand. The larger shift was in the process: scheduling moved from a manual, experience-driven task to a structured decision problem built from data, rules, constraints, prediction and optimization, which is the foundation for revenue-oriented scheduling end to end.

Limitations

About the demo and confidentiality

Screens, seat counts, film titles, runtimes, targets and demand curves in the embedded model are generated from a seed. No theatre, rule-code system, interface field or operational figure from the real project appears here, and the solver shown is a re-implementation of the modelling approach, not the production engine.

Taehee Lee · Data Scientist / Applied AI Scientist, CJ AI CenterRule-to-constraint formulation, tractability work, occupancy prediction. Demo re-implemented on synthetic data for this site.