Table of Contents
βοΈ Airline Crew Optimization & Jeppesen Rave: The Transition Guide
Version: 1.0.0
Domain: Airline Crew Pairing, Rostering, Resource Optimization, & DSL Engineering
Welcome to the definitive compendium for software engineers, optimization specialists, and operations researchers looking to transition into the Airline Crew Scheduling industry.
While general programming skills are highly transferable, the aviation sector operates on a highly specialized tech stackβmost notably the Jeppesen CARMEN suite and its proprietary Rave domain-specific language (DSL). This repository bridges the gap, providing the domain knowledge, mathematical foundations, and technical references needed to confidently apply for roles like Rave Developer, Crew Optimization Engineer, or OR Analyst at major airlines (e.g., Norwegian, Lufthansa, Delta).
π― Target Audience
- Software Engineers looking to transition into airline IT.
- Operations Research (OR) Graduates needing practical industry context.
- Current Airline Staff (Crew Planners, Schedulers) wanting to understand the technical side of the tools they use daily.
ποΈ Repository Structure
This compendium is designed to be read sequentially or used as a reference guide.
π 01. Domain Knowledge
βββ π Aviation Terminology 101
βββ π EASA FTL Regulations
βββ π Union Agreements (CBA)
βββ π The Planning Lifecycle
π 02. Jeppesen Ecosystem
βββ π CARMEN System Architecture
βββ π Python & Bash Integration
βββ π XML Data Structures
βββ π Testing & Simulation
βββ π CI/CD & Regression Testing
βββ π Studio UI & Warnings
π 03. Operations Research
βββ π Combinatorial Optimization
βββ π Column Generation
βββ π Search Space & Pruning
βββ π Cost Functions & Penalties
βββ π Solver Tracing & Debugging
π 04. Rave Language Reference
βββ π Executive Summary & System Overview
βββ π Language Fundamentals & Syntax
βββ π Hierarchical Structure & Levels
βββ π Expressions, Operators & Traversers
βββ π Rules, Legality & Search Space Dynamics
βββ π Rule Classification & Semantic Analysis
βββ π Cost Modeling & Objective Functions
βββ π Workbenches & Tooling Integration
βββ π Code Examples & Case Studies
βββ π Best Practices & Common Pitfalls
βββ π Advanced Semantic Analysis (Deep Dive)
βββ π Common Design Patterns
βββ π Tables and Data Lookups
βββ π Performance Profiling
βββ π Modularization & Overrides
π 05. Career & Interviews
βββ π Resume Tailoring
βββ π Common Interview Questions
βββ π Mock Scenario Test
π 06. Real-World Scenarios
βββ π Fatigue Risk Management
βββ π Union Bidding Preferences
βββ π Disruption Recovery
π Chapter Breakdown
π 01. Domain Knowledge
You cannot write code for an airline if you do not understand how an airline operates. This section covers the business rules:
- The difference between Crew Pairing (building anonymous flight sequences) and Crew Rostering (assigning sequences to named individuals).
- Terminology like Deadheading (transporting crew as passengers), TAFB (Time Away From Base), and Block Time.
π 02. The Jeppesen Ecosystem
Rave does not exist in a vacuum. It sits inside the CARMEN ecosystem. Here, we cover how developers use Python, Bash, and XML to marshal data in and out of the proprietary C/C++ solver engines, large-scale CI/CD regression testing, and integrating backend rules with the Studio GUI.
π 03. Operations Research & Solver Mechanics
You don't need a PhD in math, but you must understand how your code impacts the solver. We break down Column Generation, heuristics, search space pruning, and how to debug the search tree when the solver rejects a specific flight connection.
π 04. Rave Language Reference
Home to our Complete Guide & Technical Reference to the Jeppesen Rave Programming Language. This covers the declarative syntax, the hierarchical domain model (Leg \(\rightarrow\) Duty \(\rightarrow\) Trip), how to write efficient traversers (sum, any, all), enterprise codebase modularization, and how to profile your code for performance.
π 05. Career & Interviews
A curated guide for breaking into the industry, featuring mock interview questions, technical assessments, and tips on how to pass the technical screening at airlines like Norwegian.
π 06. Real-World Scenarios
Practical case studies demonstrating how to apply Rave to real-world airline problems, including Fatigue Risk Management Systems (FRMS), union seniority bidding, and dynamic day-of-ops disruption recovery.
π External Resources & Further Reading
To truly master the intersection of software engineering, Rave DSL, and airline operations research, we highly recommend exploring the following external resources, whitepapers, and communities.
π Academic Papers & Core OR Concepts
- "Airline Crew Scheduling: models, algorithms, and data sets" β A foundational paper by Kasirzadeh et al., covering the mathematical models behind crew pairing and rostering.
- "Column Generation with a Rule Modelling Language for Airline Crew Pairing" β By Hjorring & Hansen, an excellent deep dive into how Rave rules directly integrate with column generation solvers.
- "Using Semantic Analysis to Assist Schedule Optimization" β Master's thesis by Jacob Rippe (Chalmers University), detailing how ASTs and semantic inference are used to classify Final vs. Illegal Subchain rules inside the Rave compiler.
- "DSL Engineering" β By Markus Voelter, an authoritative text on Domain-Specific Language design, separating concerns, and building modular language workbenches.
π’ Industry Standards & Regulations
- EASA Flight Time Limitations (FTL) FAQ β The official European Union Aviation Safety Agency guidelines on fatigue management. Essential reading for modeling hard constraints.
- FAA Part 117 β The US equivalent of EASA FTL, detailing pilot flight and duty time limitations.
π Operations Research Communities
- AGIFORS (Airline Group of the International Federation of Operational Research Societies) β A professional society dedicated to the application of operations research in aviation. Their annual "Crew Management" study group presentations are a goldmine of modern industry practices.
- INFORMS (Institute for Operations Research and the Management Sciences) β The premier international society for OR and analytics professionals.
π» Tooling & Extensions
- VS Code Rave Extension β The community-driven language server and syntax highlighter for writing Jeppesen Rave in Visual Studio Code.
π License
This repository and its contents are protected under a Custom Proprietary Educational License (All Rights Reserved).
This is not an open-source project. You are welcome to view and read this guide for your own personal learning and career preparation. However, you may not copy, distribute, share, adapt, or modify this material.
Commercial use is strictly prohibited. This material may not be used for corporate training, monetized courses, or by commercial entities without explicit, prior written consent. Private individuals are welcome to share direct links to this repository, provided clear attribution is given.
For commercial licensing or corporate training inquiries, please contact the repository owner directly. See the LICENSE file for the complete legal terms.
π¨βπ» About the Author
Ole Christoffer Thorsen is a software engineer and optimization enthusiast specializing in the intersection of backend development, domain-specific languages (DSLs), and operations research.
Recognizing the steep learning curve for developers entering the aviation optimization space, the author built this compendium to demystify the Jeppesen CARMEN ecosystem, the Rave programming language, and the complex business rules governing airline crew scheduling.
01. Domain Knowledge
Aviation Terminology 101: The Language of Crew Optimization
To successfully develop rules and optimization models in the aviation industry, you must first speak the language of the domain. Airline crew scheduling is built upon a rigid, standardized vocabulary that describes time, geographical movement, and crew member states.
The Jeppesen Rave language and CARMEN optimization engine map directly to these domain concepts. Understanding this terminology is the absolute prerequisite for any Rave Developer.
1. The Operational Hierarchy
Crew schedules are constructed using a strict building-block hierarchy.
graph TD
Leg["Leg (Sector)<br>A single flight from Airport A to Airport B"]
Duty["Duty (Working Day)<br>A sequence of legs + ground duties"]
Pairing["Pairing (Trip)<br>A sequence of duties starting & ending at base"]
Roster["Roster (Plan)<br>A month-long schedule for a named crew member"]
Leg --> Duty
Duty --> Pairing
Pairing --> Roster
-
Leg (or Sector): An individual flight from an origin airport to a destination airport. This is the smallest indivisible unit of work in crew scheduling.
-
Duty (or Duty Period): A sequence of flights flown by a crew in a single working day. It begins when a crew member reports for work and ends when they are released into a legal rest period.
-
Pairing (or Trip): A sequence of consecutive flight legs (grouped into duties) that starts from and ends at the base airport of the airline company, flown by a single anonymous crew.
-
Roster (or Plan): The final schedule assigned to a specific, named crew member over a defined planning horizon (typically a month).
2. Core Time & Pay Concepts
- Block Time: The total time from the moment an aircraft pushes back from the departure gate (blocks removed) until it parks at the arrival gate (blocks inserted). Crew pay is often heavily tied to block time.
- Flight Time: The time the aircraft is actually in the air (wheels up to wheels down).
- Briefing / Debriefing (Report / Release): The mandatory time a crew member must be present before a flight (briefing) and the time required after a flight to complete post-flight duties (debriefing).
- TAFB (Time Away From Base): The total elapsed time from the moment a crew member reports for their pairing at their home base until they are released at their home base at the end of the trip. Many union contracts guarantee minimum pay based on a fraction of TAFB.
3. Crew States & Logistics
-
Deadheading: Transporting crew members on flights as passengers rather than as operating crew. This is often required to position a crew member to a different airport to operate a subsequent flight, or to return them to their base.
-
Sit Time (or Connection Time): The ground time between two consecutive operating flights within the same duty period.
-
Layover (or Night Stop): The rest period between two duty periods where the crew sleeps at a hotel in a non-base city.
-
Base Airport: The home airport where a crew member is permanently stationed. All pairings must originate and terminate here.
EASA FTL: Understanding Flight Time Limitations
In commercial aviation, safety is the overriding priority. Fatigue management is strictly governed by international and national regulatory agencies, such as IATA, the FAA (in the US), and EASA (in Europe) (formerly JAR).
For a European carrier like Norwegian, the EASA FTL (Flight Time Limitations) framework dictates the absolute boundaries of crew scheduling. In Rave, these rules are modeled as Final Rules because violating them makes a schedule legally impossible to fly.
1. Flight Duty Period (FDP)
The Flight Duty Period (FDP) is the core metric for fatigue. It begins when a crew member reports for duty and ends when the aircraft engines are switched off at the end of the final flight of that duty.
gantt
title Anatomy of a Flight Duty Period (FDP)
dateFormat HH:mm
axisFormat %H:%M
section Ground
Briefing (Report) :a1, 06:00, 1h
Debriefing (Release) :a2, 14:00, 30m
section Flying
Flight 1 (Block Time) :f1, 07:00, 3h
Sit Time :s1, 10:00, 1h
Flight 2 (Block Time) :f2, 11:00, 3h
section Regulatory
FDP (Flight Duty Period) :active, 06:00, 8h
Total Duty Time :100, 06:00, 8.5h
Factors Limiting FDP
The maximum allowable FDP is not static. A Rave Developer must program logic that dynamically adjusts the maximum FDP based on:
- Time of Day: Duties starting during the "WOCL" (Window of Circadian Low, typically 02:00β05:59) have severely restricted maximum FDPs.
- Number of Sectors: The physical toll of multiple take-offs and landings reduces the maximum FDP. Each sector beyond a certain threshold usually subtracts 30 minutes from the max FDP.
- Acclimatization: If a crew member crosses multiple time zones, their internal body clock is disrupted, triggering stricter limits.
2. Rest Requirements
Rest is heavily regulated to ensure crew recovery.
- Minimum Daily Rest: Typically, a crew member must receive at least 12 hours of rest, or rest equal to the length of the preceding duty period (whichever is greater).
- Reduced Rest: Under strict conditions, rest can be reduced, provided compensatory rest is given subsequently.
- Weekly/Monthly Rest: Regulations enforce minimum uninterrupted rest periods over 7-day and 28-day rolling windows.
3. Implementation in Optimization
When building the optimization models, FTL rules are implemented as hard constraints. If an optimization engine generates a sequence of flights that violates an EASA FTL rule, the sequence is illegal and pruned from the search space.
Union Agreements & CBA: The Complexity of Costs
While regulatory rules (EASA FTL) dictate what is legal, Collective Bargaining Agreements (CBAs)βthe contracts negotiated between airline management and labor unionsβdictate what is expensive.
Airline companies have their own sets of rules and pay structures that optimization engines must evaluate. Unlike FTL rules which are universally applied across Europe, CBAs vary drastically from airline to airline, and even between pilots and cabin crew at the same airline.
1. Pay Guarantees and "Rigs"
Crews are paid a fixed amount aside from the actual hours they fly. To ensure fair compensation, CBAs feature "rigs" (guarantees) that penalize airlines for inefficient scheduling. The solver must calculate the cost of a pairing by evaluating these complicated rules.
pie title Typical Cost Comparison in an Inefficient Duty
"Actual Flight Pay" : 40
"Duty Minimum Guarantee Penalty" : 30
"TAFB Rig Penalty" : 30
Common Cost Structures
- Min Daily Guarantee (MDG): Even if a crew member only flies a single 1-hour flight, they might be guaranteed a minimum of 4 or 5 hours of pay for that duty day.
- Duty Rig: A ratio comparing total elapsed duty time to flight time. (e.g., 1 hour of pay for every 2 hours on duty).
- Trip Rig (TAFB): A ratio calculating pay based on the total Time Away From Base. (e.g., 1 hour of pay for every 3.5 hours away from base).
The Rave cost property will evaluate all these rules and return the MAX(FlightPay, DutyRig, TripRig) to the solver.
2. Rostering Fairness & Bidding
In the rostering phase (assigning trips to named individuals), union rules become highly personal:
- Seniority Bidding: Senior pilots get preferential choices for weekends off, specific destinations, or avoiding night flights.
- Fair Share / Equilibration: The optimizer must distribute the lucrative (high-paying) trips and the exhausting (multi-sector) trips evenly among the crew pool.
3. Soft Constraints vs. Hard Constraints
While a WOCL violation is a hard constraint (illegal), assigning a pilot an undesirable schedule might just be a Soft Constraint. In Rave, soft constraints are implemented by adding artificial mathematical penalties to the cost property. The optimizer will try to avoid these solutions unless no cheaper alternative exists.
The Planning Lifecycle: From Schedule to Day-of-Ops
The airline crew scheduling problem is traditionally solved in distinct, consecutive steps to manage the NP-hard mathematical complexity. A Rave Developer's code spans across this entire lifecycle, but operates differently at each stage.
The Four Stages of Airline Optimization
flowchart LR
A[1. Network Planning<br>Flight Timetable] --> B[2. Crew Pairing<br>Anonymous Trips]
B --> C[3. Crew Rostering<br>Named Assignments]
C --> D[4. Crew Tracking<br>Day-of-Ops Recovery]
1. Network Planning (Schedule Generation)
Months in advance, the commercial planning department defines the flight schedule (the timetable of aircraft moving between cities). The crew optimizer takes this scheduled flight network as its input data.
2. Crew Pairing (Optimization)
In the crew pairing step, the goal is to find the least costly set of pairings (consecutive flights flown by a single crew) that covers every single flight in the given network.
-
State: Crews are anonymous.
-
Focus: Creating efficient, legal blocks of work that minimize deadheading and layover costs.
-
Rave Context: Rule execution focuses heavily on the
Leg,Duty, andTriplevels.
3. Crew Rostering (Assignment)
In the rostering step, the actual crew members are named and assigned to the anonymous pairing teams generated in the previous step.
- State: Crews are identified individuals.
- Focus: Managing vacations, training days, union bidding, and legal rest between pairings.
- Rave Context: Rule execution scales up to the
Roster/Planlevel, looking at 30-day horizons.
4. Crew Tracking (Day-of-Ops / Dispatch)
On the actual day of the flight, reality strikes. Weather, mechanical failures, or crew sickness break the optimized rosters.
- State: Real-time operational recovery.
- Focus: Repairing broken pairings on the fly while maintaining strict EASA FTL legality.
- Rave Context: The Rave rule engine evaluates legality in real-time as dispatchers drag-and-drop flights to different crew members in the Jeppesen UI.
02. Jeppesen Ecosystem
CARMEN System Architecture: How Rave Fits In
To develop effectively in the airline optimization space, you must understand how your code interacts with the broader system architecture. The Jeppesen CARMEN Crew Pairing System relies on a highly modular and decoupled architecture.
The optimization engine itself is a heavy, mathematically intense solver written in lower-level languages (like C/C++). However, embedding the implementation of feasibility checking and cost calculation rules directly into the source code of the optimization engine is not a practical approach, because airline rules change frequently and require constant "what-if" scenario testing.
Jeppesen solves this by using Rave as an external rule modeling language.
1. The Decoupled Architecture
The architecture separates the mathematical search algorithms from the business logic.
flowchart TD
subgraph Data Layer
A[Flight Schedules / Timetables]
B[Crew Requirements & Parameters]
end
subgraph Business Logic Layer
C[Rave DSL Source Code]
D[Rave Compiler]
E[Compiled Dynamic Link Library]
end
subgraph Optimization Layer
F[CARMEN Optimization Engine]
G[Pairing Generator]
H[Search Space / Tree Traversal]
end
A --> F
B --> F
C --> D
D --> E
E <-->|Queries Legality & Cost| G
F --> H
The Workflow
-
Rule Definition: The Rave Developer writes declarative rules modeling the airline's specific constraints and costs.
-
Compilation: A compiler translates the Rave specification into a dynamic link library (DLL) or shared object.
-
Execution: The optimization engine generates sequences of flights (pairings) from the given flight network.
-
Interrogation: For each generated sequence, the optimization engine calls the compiled Rave library to check feasibility and calculate the pairing cost.
2. Separation of Concerns: The Generator vs. The Rule System
The system is carefully designed so that the external application (the optimizer/generator) supplies the raw input data. This modular separation of concerns means that a user may make adjustments to the rules, the external applications, and the input data completely separately.
When the generator builds a trip, it does not "know" why a trip is illegal; it relies entirely on the boolean responses (True/False) returned by the Rave rule system.
Python and Bash Integration: Orchestrating the Solver
While Rave handles the domain-specific business logic, an airline's optimization ecosystem requires heavy orchestration. You cannot simply double-click an .exe to run a massive crew pairing optimization. It requires staging data, configuring parameters, dispatching solver processes, and aggregating results.
This orchestration is predominantly handled using Python and Bash/Linux scripting.
1. The Orchestration Pipeline
The typical workflow for an optimization run spans several stages, managed by shell scripts and Python glue code:
sequenceDiagram
participant Bash as Bash Script (Cron/Airflow)
participant Py as Python Pre-Processor
participant Solver as CARMEN Optimizer
participant Post as Python Post-Processor
Bash->>Py: Trigger run & pass arguments
Py->>Py: Fetch flight data & format XML
Py->>Solver: Launch solver binary with configs
Solver->>Solver: Evaluate search space using Rave rules
Solver-->>Py: Output pairing results
Py->>Post: Parse results, calculate KPIs
Post-->>Bash: Save final reports to DB
2. Using Python for Data Marshalling
Because Rave rules depend on external input data supplied by the application running the Rave code, Python is frequently used to transform upstream airline data (from legacy mainframes or modern APIs) into the exact formats the CARMEN optimizer expects.
Python scripts will typically:
- Parse flight timetables.
- Set bounded parameters for the Rave rules (e.g., dynamically adjusting maximum duty times for a specific "what-if" scenario run).
- Handle solver callbacks.
3. Using Bash for Execution
Optimization runs can take hours or even days, often running on massive computing clusters. Bash scripts are essential for resource management, setting environment variables, and invoking the solver binaries.
-
Environment Setup: Setting library paths to ensure the optimizer finds the compiled Rave dynamic link library.
-
Parallel Execution: Spawning multiple solver instances to explore different parameter configurations simultaneously.
XML Data Structures: Feeding the Rave Engine
A key feature of the Rave language is that it separates the logic from the data. The external application supplies the input data that can be referenced in the Rave code.
At compile-time, Rave does not have access to data; it only knows the structure of the data. During runtime, the Jeppesen Pairing Generator feeds massive amounts of flight, airport, and crew data into the rule evaluation engine. Historically and practically, XML (and specialized proprietary tabular formats) are heavily used to structure this payload.
1. The Keyword Mapping
In Rave, a Keyword acts as a special kind of variable used to refer to this external data. For example, the start time of a flight is supplied by the application running the Rave code and mapped to a keyword.
graph LR
XML["XML/Data Payload<br>(Departure: 10:00Z)"] --> Engine["Optimization Engine"]
Engine --> Map["Keyword Mapping"]
Map --> Rave["Rave Code<br>leg.%start_utc%"]
2. Example Data Representation
While the exact proprietary schema formats vary by airline implementation, the conceptual structure resembles hierarchical XML. The data must perfectly align with Rave's internal hierarchy (Leg \(\rightarrow\) Duty \(\rightarrow\) Trip).
<FlightSchedule>
<Leg id="N01234">
<DepartureAirport>OSL</DepartureAirport>
<ArrivalAirport>BGO</ArrivalAirport>
<StartUTC>2026-08-12T08:00:00Z</StartUTC>
<EndUTC>2026-08-12T08:50:00Z</EndUTC>
<AircraftType>B738</AircraftType>
</Leg>
<Leg id="N01235">
<!-- Subsequent leg data -->
</Leg>
</FlightSchedule>
When the optimization engine reads this Leg data, it populates the keywords. A Rave developer can then reference these fields dynamically in their rules:
// The external XML 'StartUTC' is mapped to keywords.%departure% or leg.%start_utc%
%leg_time% = leg.%end_utc% - leg.%start_utc%;
Because the data is only supplied at runtime, Rave developers must write safe, generic logic that can handle edge cases without failing, as it is not safe to make assumptions about variables in the Rave code at compile-time.
Testing and Simulation in the Jeppesen Studio
Developing rules for combinatorial optimization carries high risk; a single misclassified rule (e.g., misclassifying an Illegal Subchain Rule as a Final Rule) can cause the optimizer to drop valid solutions or explore dead-end subtrees infinitely.
To mitigate this, the Jeppesen ecosystem provides powerful Integrated Development Environment (IDE) tooling, including interactive interpreters and simulators.
1. The In-IDE Interpreter and Simulator
Turnaround time for code generation and compilation can be slow. To allow domain experts and developers to experiment with models quickly, the ecosystem supports an in-IDE interpreter.
Features of the Simulator:
-
Interactive Stepping: Users can "play" with a program, stepping through it in single steps and watching values change dynamically.
-
Calculation Tree Expansion: Because declarative languages evaluate complex trees of dependencies, the debugger can "expand" the calculation tree so users can inspect all intermediate results.
-
Rapid Prototyping: This allows domain experts (who may not be deep programmers) to simulate programs, observe their progress, and stimulate them to see how they react to specific edge-case flight schedules.
2. Tabular Unit Testing
In addition to interactive simulation, the language supports automated testing. Test cases can be expressed in an Excel-like tabular notation.
| Test Case Name | Input (Flight Time) | Expected Result (Pay) | Actual Result | Status |
|---|---|---|---|---|
| Short Flight | 01:00 | 04:00 (Min Guar) | 04:00 | PASS |
| Long Flight | 09:00 | 09:00 | 09:00 | PASS |
-
Domain Expert Friendly: This tabular notation allows non-programmers (like insurance mathematicians or airline union reps) to specify test data for each input value of a rule.
-
Immediate Feedback: The rules are then evaluated by an interpreter, providing immediate visual feedback (e.g., coloring rows red or green) about incorrect rules.
-
Semantic Synchronization: During full production builds, these same unit tests are executed by the generated C/C++ code, expecting the exact same results as the interpreted version, ensuring semantic consistency across the execution engines.
CI/CD and Massive Regression Testing
In the airline industry, deploying a "buggy" rule doesn't just mean a page fails to loadβit means a flight might be grounded because a crew's schedule was built illegally, or the airline might bleed millions of dollars in union penalties.
Because of this, airlines cannot use standard CI/CD pipelines (like simply running 5 minutes of unit tests). They must rely on Massive Regression Testing.
1. The Challenge of Testing Optimization Rules
When a Rave developer changes a rule (e.g., tweaking a duty rig pay calculation), it impacts the entire optimization search space.
- A rule that costs slightly more might cause the solver to choose an entirely different set of pairings for the whole month.
- This "butterfly effect" means unit tests are insufficient. You must test the code against reality.
2. The Regression Testing Pipeline
To safely merge code, airlines run automated regression tests against historical "baselines."
flowchart TD
A[Developer Pushes Rave Code to Git] --> B[CI Server triggers Regression Run]
B --> C[Run Solver on Historical Data: Month 1]
B --> D[Run Solver on Historical Data: Month 2]
C --> E[Generate Output KPIs]
D --> F[Generate Output KPIs]
E --> G[Compare against Production Baseline]
F --> G
G --> H{Are Costs or Legality Worse?}
H -->|Yes| I[Block Merge - Flag Developer]
H -->|No| J[Approve Pull Request]
3. KPI Diffing
Instead of just checking if the code compiles, the CI pipeline (often managed via Jenkins, GitLab CI, or Airflow orchestrators) compares Key Performance Indicators (KPIs).
The pipeline outputs a "diff" report comparing the New Code vs the Current Production Code:
- Total Optimizer Run Time: Did the new rule slow the solver down from 4 hours to 10 hours?
- Total Crew Cost: Did the total cost increase by 5%?
- Unassigned Flights: Did the new legality constraint make it mathematically impossible to cover all flights?
Only if the KPIs show stable or improved performance is the Rave code allowed to be merged into the main branch and deployed to the live system.
Jeppesen Studio UI Integration: The Planner's View
Rave developers do not just write backend code for the invisible optimizer; they also write the code that populates the Jeppesen Studio UI.
When a human Crew Planner drags and drops a flight onto a roster in the UI, Rave evaluates the legality in real-time. But a planner cannot act on a simple binary False from the solverβthey need to know why it is false, and how bad the violation is.
1. Viewpoints for Stakeholders
Different stakeholders need different views and notations of the same data. A financial analyst might want to see the cost breakdown, while a crew dispatcher needs to see safety violations.
Rave handles this by binding rule evaluations to UI projections and text pop-ups.
2. Returning Strings and Remarks
Instead of just failing silently, Rave rules can yield strings (remarks) that surface directly in the Studio application.
// Define a remark string that dynamically injects the calculated values
%fdp_violation_msg% =
"ILLEGAL: Duty exceeds Max FDP. Actual: " + format_time(duty.%elapsed_time%) +
", Max Allowed: " + format_time(%max_allowed_fdp%);
CONSTRAINT easa_fdp_limit OF Duty
COMMENT: "Duty elapsed time must not exceed max allowable FDP"
STATUS: ON;
// If the rule fails, the Studio UI will display this exact string
REMARK: %fdp_violation_msg%;
RULE:
duty.%elapsed_time% <= %max_allowed_fdp%;
ENDCONSTRAINT
3. Color Coding and Rule Severity
In the Jeppesen Studio UI, rules are categorized by severity. Rave developers output specific flags to trigger GUI colors:
- Legal (Green): No rules broken.
- Warning / Soft Violation (Yellow): A soft constraint was broken (e.g., assigning a senior pilot to a weekend they requested off). The planner can save this, but the UI flags it.
- Illegal / Hard Violation (Red): A hard regulatory constraint (like EASA FTL) was broken. The system prevents the planner from saving the roster.
flowchart LR
DragDrop[Planner drags Flight to Roster] --> RaveEval{Rave Rule Engine}
RaveEval -->|Returns True| Green[Studio UI: Green Box]
RaveEval -->|Soft Penalty > 0| Yellow[Studio UI: Yellow Box + Warning Text]
RaveEval -->|Returns False| Red[Studio UI: Red Box + Error Text]03. Operations Research
Combinatorial Optimization: The NP-Hard Nature of Crew Scheduling
To understand why the Jeppesen CARMEN system and the Rave language exist, one must first understand the sheer mathematical scale of airline crew scheduling. Crew costs represent the second major cost item for airline companies after fuel costs. This financial reality makes the optimization of crew costs incredibly attractive, but the scale of the problem is mathematically NP-Hard.
1. The Optimization Problem
To manage the massive computational load, the airline crew scheduling problem is usually solved in two consecutive steps: crew pairing and rostering.
The primary optimization problem solved in the crew pairing step is finding the least costly set of pairings that covers every flight in the given network. A pairing is defined as a sequence of consecutive flight legs that starts from and ends at the base airport of the airline company, and it is flown by a single crew.
graph TD
A[Flight Network Timetable] --> B{Combinatorial Explosion}
B --> C[Millions of Legal Pairings]
C --> D[Optimization Solver]
D --> E[Optimal Set of Pairings]
style B fill:#e53e3e,stroke:#9b2c2c,color:#fff
2. Why is it NP-Hard?
If an airline has 1,000 flights a day, the number of mathematical ways to combine those flights into legal 3-day or 4-day trips is astronomical.
- The Set Covering Problem: The solver must select a subset of pairings such that every flight in the timetable is covered exactly once (or at least once, if deadheading is permitted).
- Deadheading: Transporting crew on flights as passengers (known as deadheading) has major impacts on crew costs and significantly multiplies the number of possible routing combinations.
3. The Role of Rave
Because it is impossible to evaluate every combination upfront, an optimization engine generates sequences of flights and then for each sequence generated, it checks the feasibility with respect to these rules. Rave provides the decoupled, high-level language framework required for describing the feasibility and cost calculation rules that the engine queries millions of times per second.
Column Generation: The Mathematical Core
Because the number of possible legal crew pairings in an airline network is too vast to enumerate, modern airline crew optimization systems rely on an advanced operations research algorithm known as Column Generation.
In the Jeppesen ecosystem, the solver uses Column Generation to break the combinatorial explosion of the crew pairing problem into manageable pieces.
1. The Master Problem vs. The Subproblem
Column Generation splits the optimization task into two interacting mathematical phases:
The Master Problem (The Optimizer)
The Master Problem is a Set Covering problem. It looks at a restricted pool of valid pairings (columns) and attempts to select the best combination to cover all flights at the lowest cost.
The Subproblem (The Pairing Generator)
The Master Problem does not know all possible pairings. It relies on the Subproblem (the Pairing Generator) to create them dynamically. The optimization engine generates a sequence of flights from a given flight network.
2. The Generation Loop
The relationship between the Optimizer, the Generator, and Rave forms a continuous loop:
sequenceDiagram
participant Master as Optimizer (Master Problem)
participant Sub as Generator (Subproblem)
participant Rave as Rave Rule Engine
loop Column Generation Iteration
Master->>Sub: Request new pairings with negative reduced cost
Sub->>Sub: Build new flight sequences
Sub->>Rave: Is this sequence a valid pairing?
Rave-->>Sub: True/False + Cost Value
Sub-->>Master: Return valid, profitable pairings (Columns)
end
Master->>Master: Solve final Integer Program
3. Why Rave is Critical to the Subproblem
During the Subproblem phase, the generator blindly pieces together flights based on mathematical profitability. It has to check all these feasibility rules to ensure whether the sequence forms a valid pairing. If Rave evaluates the rules efficiently, the Subproblem runs quickly; if rules are misconfigured, the generation process bottlenecks.
Search Space and Pruning: Navigating the Tree
During the Pairing Generation phase, the system must piece together flights to form legal trips. The search space of the Generator is a tree structure whose nodes represent all possible trips of a flight schedule.
Understanding how this tree is built and pruned is one of the most critical operations research concepts a Rave Developer must master.
1. Building the Search Tree
The root of the search space is an empty node representing an empty trip starting in some airport. For each leg in the flight schedule starting in that airport, a child node is created. Traversing down the search tree from the root, each child node represents adding a leg to the trip in progress.
graph TD
Root((Empty Trip<br>Base: OSL))
Root --> A(Flight OSL-BGO)
Root --> B(Flight OSL-CPH)
A --> C(Flight BGO-TRD)
A --> D(Flight BGO-OSL)
style Root fill:#4299e1,color:#fff
2. Pruning the Search Space
Because the number of nodes grows exponentially with each level in the tree structure, the generator cannot explore every branch. It must cut off (prune) bad paths as early as possible. This is where Rave rule classification dictates solver behavior:
Final Rules (Hard Pruning)
When a rule is violated for a node n, there exist no legal continuation of n. The generator prunes the search tree instantly.
Illegal Subchain Rules (Soft Pruning / Deferral)
When the rule is violated for a node n, there may exist a legal continuation of n. The generator does not prune the tree and keeps searching, expecting that adding subsequent flights will satisfy the condition.
3. The Dangers of Misclassification
- Misclassifying a Final Rule as an Illegal Subchain: Every violation implies a subtree in search space that is explored for which no subnode can represent a legal trip. This causes severe inefficiency.
- Misclassifying an Illegal Subchain as a Final Rule: The generator incorrectly prunes the search space when there may exist legal continuations, meaning many possible solutions are never found.
Cost Functions and Penalties: Guiding the Solver
In crew pairing optimization, feasibility (legality) is only half the battle. Once the Pairing Generator finds a set of legal trips, the optimizer seeks the least costly set of pairings that covers every flight in the given network.
The cost of a pairing is defined by using complicated rules. Rave provides the declarative framework to calculate these costs, mixing hard operational expenses with "soft penalties" to guide the mathematical solver toward desirable human outcomes.
1. Hard Costs (Financial Realities)
Hard costs are the actual monetary expenses incurred by the airline. These rules are calculated in Rave and passed directly to the objective function of the optimizer.
- Flight Pay / Duty Rigs: Calculating the exact union-mandated pay for a sequence of flights.
- Deadheading Costs: Transporting crew on flights as passengers has major impacts on crew costs.
- Hotel and Per Diem: Factors like layovers at non-base cities incur fixed costs that the solver must calculate.
2. Soft Costs (Penalties)
A mathematically optimal roster might be perfectly legal and extremely cheap, but absolutely exhausting for a human pilot to fly. To prevent this, OR analysts use Soft Penalties.
A soft penalty is an artificial cost added to the actual financial cost of a trip. It "tricks" the optimizer into thinking an undesirable trip is too expensive to select, unless it is the absolutely only way to cover the flight schedule.
Examples of Soft Penalties:
- Short Layovers: If a layover is exactly the legal minimum (e.g., 10 hours), a soft penalty is applied to encourage the solver to find a pairing with a 12-hour layover instead.
- Aircraft Changes: Changing aircraft types mid-duty increases the risk of delays. A penalty of +500 "cost points" is added every time a crew swaps tails.
3. The Objective Function in Rave
In Rave, the optimization engine generates sequences of flights, and it needs to calculate the cost of the pairing by using certain rules.
PROPERTY cost OF Trip
RULE:
%financial_base_cost% + %deadhead_penalties% + %quality_of_life_penalties%;
ENDPROPERTY
By tweaking the weights of these penalties, Operations Researchers tune the solver to balance airline profitability against crew quality of life.
Solver Tracing: "Why wasn't this pairing built?"
The most common support ticket an airline Rave developer receives from the business is: "Flight A arrives in Oslo at 10:00. Flight B departs Oslo at 12:00. Why didn't the optimizer put them in the same pairing?"
To answer this, you cannot just look at the code. You must trace the generator's search tree to find exactly which rule or cost penalty killed that specific connection during the Subproblem phase.
1. The Challenge of Multi-Stage Debugging
Because the optimization process is a multi-stage transformation (building legs into duties, duties into trips, and assessing costs), debugging the overall transformation can become hard.
If a connection wasn't built, one of three things happened:
- Pruned by a Final Rule: A hard legality check killed the branch.
- Killed by a Heuristic: The generator determined the branch was mathematically unpromising before it finished.
- Priced Out: The pairing was generated, but the Master Problem determined another pairing was cheaper.
2. Using Tracing Tools
To debug this, developers use solver tracing tools. These tools keep all intermediate models around for debugging purposes, allowing the developer to inspect the trace and find out where the element was transformed or rejected.
sequenceDiagram
participant Dev as Rave Developer
participant Trace as Trace Log / Debugger
participant Gen as Pairing Generator
Dev->>Gen: Run generation for Flight A -> Flight B only
Gen->>Trace: Output rule evaluation log
Trace-->>Dev: "Rule 'min_sit_time' evaluated to False"
Dev->>Dev: Inspect trace: Sit time was 29 mins, rule requires 30.
3. The "Forced Pairing" Strategy
The ultimate debugging technique is the Forced Pairing Strategy.
If the business asks why a specific pairing wasn't built naturally by the optimizer, you configure the solver to force the creation of that exact pairing, bypassing normal heuristics.
- If it fails to build: The trace log will spit out the exact Rave
CONSTRAINTthat evaluated toFalse. You now know it's a legality issue. - If it builds successfully: You look at the
PROPERTY costof the forced pairing. You will often find a massive soft penalty (e.g., a penalty for a short layover) that made it too expensive for the optimizer to choose naturally. You now know it's a cost/tuning issue.
04. Rave Language Reference
Executive Summary & System Overview
The Jeppesen Rave Programming Language (Rule And Value Evaluator) is a highly specialized, declarative Domain-Specific Language (DSL) engineered specifically to model domain-specific rules, legality constraints, and cost functions for large-scale combinatorial optimization problemsβmost prominently Airline Crew Pairing and Crew Rostering.
In modern commercial aviation, crew costs represent the second highest variable operational expense after fuel. Optimizing crew schedulesβconstructing minimal-cost sequences of flight legs (pairings or trips) and assigning named crew members (rosters)βis an NP-hard combinatorial optimization challenge governed by thousands of multi-layered regulatory, union, contractual, and operational constraints.
Rather than hard-coding these complex, fast-changing business rules into core optimization engines (such as column-generation pairing optimizers or integer programming solvers), Jeppesen decouples rule evaluation from the mathematical optimization solver via Rave.
flowchart TD
A[RAVE DSL CODE<br>Declarative Rules, Hierarchy Levels, Variable & Cost Definitions] --> B[RAVE COMPILER<br>Lexical & Syntactic Analysis, Type Checking, Semantic Analysis]
B --> C[COMPILED RULESET DLL<br>C/C++ Dynamically Linked Library / Evaluation Engine]
C --> D[JEPPESEN OPTIMIZATION ENGINE<br>Pairing Generator, Column Generation, Rostering Solver]
Core Architecture & Key Concepts
- Decoupled Architecture: The Rave compiler translates declarative rule definitions into compiled shared libraries or evaluation engines. The optimization solver queries these libraries dynamically during search tree expansion.
- Hierarchical Data Abstraction: Data is organized in a rigid domain hierarchy:
Leg(individual flight) $ ightarrow$Duty(working day sequence of legs) $ ightarrow$Trip/Pairing(multi-duty sequence starting/ending at base) $ ightarrow$Roster/Plan. - Declarative Computation: Rave replaces imperative loops and stateful iteration with declarative traversers (
sum,count,any,all,first,last,prev,next) that operate implicitly across hierarchical context levels. - Legality vs. Search Guidance: Rave distinguishes between Final Rules (which prune the generation search space statically) and Illegal Subchain Rules (which allow incomplete candidate sub-trips to grow even if temporarily illegal).
1. Language Fundamentals & Syntax
1.1 Lexical Structure & Conventions
Rave is a case-insensitive, textual, declarative programming language. Statements and definitions are delimited by semicolons ;. Comments follow standard C-style syntax:
Identifier naming conventions strictly distinguish variable references from keywords and properties:
- Variables are delimited by percent signs:
%variable_name%. - Keywords (external runtime inputs provided by the solver) are prefixed by context or qualified scopes:
leg.%start_utc%,keywords.%departure%. - Rules begin with the keyword
ruleand terminate withend.
1.2 Domain Data Types
Rave blends standard general-purpose data types with domain-specific temporal and geographical types tailored for airline operations:
| Type Category | Type Keyword | Description & Representation | Example Usage |
|---|---|---|---|
| General | int / integer |
32-bit or 64-bit signed integer values | 3, 142 |
| General | real / float |
Floating-point numbers for costs/weights | 1.5, 350.75 |
| General | bool / boolean |
Boolean truth values | True, False |
| General | string |
Textual string literals | "SAW", "Captain" |
| Domain Specific | time |
Clock time or absolute temporal stamp | 10:30, 24:00 |
| Domain Specific | duration |
Elapsed time duration (HH:MM or minutes) | 00:30, 05:15 |
| Domain Specific | datetime |
Combined calendar date and absolute time | 2026-08-11 14:00 |
| Domain Specific | airport |
3-letter IATA or 4-letter ICAO airport code | "LHR", "JFK", "SAW" |
1.3 Constants & Parameters
Constants and parameters establish fixed thresholds or configurable business rules.
Global Constants Block
Bounded Parameters
Parameters allow optimization analysts to perform "what-if" scenario testing without recompiling the core rule codebase:
1.4 Variables & Identifiers
Variables encapsulate intermediate computations. Because Rave is declarative, variables do not represent mutable memory cells; they define functional expressions bound to a specific context.
%leg_duration% = leg.%arrival_time% - leg.%departure_time%;
%duty_flight_time% = sum(leg(duty), %leg_duration%);
2. Hierarchical Structure & Levels
2.1 The Domain Hierarchy
Airline crew scheduling problems operate across distinct operational granularity levels. Rave formalizes these granularities into a fixed spatial-temporal hierarchy:
graph TD
Plan["Plan / Roster Level<br>(Full roster for an individual crew member over a month)"]
Trip["Solution / Trip Level<br>(Single Pairing / Trip - Sequence of Duties)"]
Duty["Duty Level<br>(Single Working Day - Sequence of Flights)"]
Leg["Leg Level<br>(Single Flight Leg - Departure to Arrival)"]
Plan --> Trip
Trip --> Duty
Duty --> Leg
Leg(Base Activity): An indivisible flight leg from an origin airport to a destination airport with fixed departure and arrival times.Duty(Derived Activity): A sequence of consecutive flight legs flown by a crew member within a single working day.Trip/Pairing(Derived Activity): A complete sequence of duties starting and ending at the crew member's home base airport.Roster/Plan(Derived Activity): A schedule assigned to a specific named crew member over an entire planning horizon.
2.2 Level Contexts & Scope
Every variable and rule expression in Rave belongs to an implicit or explicit Level Context. The level determines:
- The scope of accessible keywords and attributes.
- How frequently the expression is evaluated during search tree traversal.
Referencing leg.%departure_time% requires a Leg context. Referencing sum(leg(duty), ...) evaluates within a Duty context by aggregating over all constituent Leg instances.
2.3 New Instances vs. Continued Instances
During pairing generation, the solver incrementally constructs candidate trips leg-by-leg. This introduces two dynamic search states:
- New Instance: When a candidate leg cannot legally or structurally belong to the current active
Duty, the generator creates a new instance of theDutylevel. - Continued Instance: When a candidate leg is appended to the existing active
Duty, the currentDutyinstance is extended. All expressions scoped to theDutylevel recalculate their values dynamically for this continued instance.
3. Expressions, Operators & Declarative Traversers
3.1 Arithmetic & Relational Operators
Rave provides standard arithmetic operators (+, -, *, /, %) and relational comparison operators (=, <>, >, <, >=, <=).
%is_overtime% = %duty_flight_time% > 08:00;
%layover_rest% = next(duty(trip), duty.%start_time%) - duty.%end_time%;
3.2 Logical Connectives & Conditional Expressions
Logical connectives (and, or, not) build complex boolean constraints. Conditional logic uses pure functional if ... then ... else constructs:
%effective_max_fdp% =
if %start_time_night% then
09:00
else if %flight_count% > 3 then
11:30
else
13:00;
3.3 Traversers & Vector Computations
Traversers are Rave's declarative alternative to loops. They aggregate child properties over a parent level context.
| Traverser | Syntax Example | Description |
|---|---|---|
sum |
sum(leg(duty), %leg_duration%) |
Computes the numerical sum of an expression over all child instances. |
count |
count(leg(duty)) |
Counts the total number of child instances in the parent scope. |
any |
any(leg(duty), leg.%is_deadhead%) |
Returns True if the expression evaluates to True for at least one child. |
all |
all(leg(duty), leg.%is_valid%) |
Returns True if the expression evaluates to True for all child instances. |
max / min |
max(duty(trip), duty.%length%) |
Finds the maximum or minimum value across all child instances. |
avg |
avg(leg(duty), %leg_duration%) |
Computes the arithmetic mean over all child instances. |
3.4 Inter-Instance Navigation (prev, next, first, last)
To evaluate transition rules between adjacent activities, Rave provides spatial-temporal navigation traversers:
// Minimum Sit Time check between consecutive flight legs in a duty
%sit_time_valid% =
for each f1 -> f2 in elements
f2.departure_time - f1.arrival_time >= %min_sit_time%;
// Accessing the rest duration prior to the current duty
%rest_before_duty% =
duty.%start_time% - prev(duty(trip), duty.%end_time%);
// Verifying that a trip originates and terminates at the same base airport
%trip_base_closed% =
first(leg(trip), leg.%dep_airport%) = last(leg(trip), leg.%arr_airport%);
4. Rules, Legality & Search Space Dynamics
4.1 Rule Definition Syntax
A rule in Rave defines a hard constraint. If a rule evaluates to False, the associated entity is declared illegal.
CONSTRAINT max_duty_flight_time OF Duty
COMMENT: "Duty flight time must not exceed 8 hours"
STATUS: ON;
CONSTANTS:
%max_allowed% = 08:00;
RULE:
sum(leg(duty), %leg_duration%) <= %max_allowed%;
ENDCONSTRAINT
Or using standard shorthand syntax:
4.2 Legality Evaluation Semantics
During optimization, the solver calls the compiled Rave shared library to check legality. A candidate sequence \(P = \langle f_1, f_2, \dots, f_k
angle\) is legal if and only if all active rules evaluate to True:
4.3 Search Space Pruning & Optimization Mechanics
The Pairing Generator constructs candidate trips using a depth-first search tree:
graph TD
Root["Root: Base Airport SAW"]
F1["Flight 1"]
F2["Flight 2"]
F3["Flight 3"]
F4["Flight 4 (Candidate Sub-trip)"]
Root --> F1
Root --> F2
F1 --> F3
F1 --> F4
When evaluating a candidate node \(n\) representing an incomplete sub-trip \(P_{sub}\):
- If \(P_{sub}\) violates a Final Rule, the generator immediately prunes the entire subtree rooted at \(n\).
- If \(P_{sub}\) violates an Illegal Subchain Rule, the generator does NOT prune the search tree; it continues appending legs.
5. Advanced Rule Classification & Semantic Analysis
5.1 Final Rules vs. Illegal Subchain Rules
The efficiency of airline schedule generation depends on classifying rules correctly:
- Final Rule Definition: A rule \(r\) is a Final Rule if, whenever \(r\) is violated for a candidate sub-trip node \(n\), no possible continuation subnode \(n_s \in s(n)\) can ever be legal under \(r\): $\(\forall n \left( \neg r(n) \implies \neg \exists n_s \in s(n) : r(n_s) \right)\)$
- Illegal Subchain Rule Definition: A rule \(r\) is an Illegal Subchain Rule if, when \(r\) is violated for a candidate node \(n\), there may still exist a legal continuation subnode \(n_s \in s(n)\): $\(\exists n \left( \neg r(n) \land \exists n_s \in s(n) : r(n_s) \right)\)$
Consequences of Misclassification
- Final Rule misclassified as Illegal Subchain: The solver explores dead-end subtrees, causing severe computational slowdowns.
- Illegal Subchain Rule misclassified as Final: The solver prematurely prunes valid branches, missing feasible or cost-optimal pairings entirely.
5.2 Conditionality & The is_closed Keyword
To prevent Illegal Subchain Rules from prematurely pruning search branches, Rave introduces conditional rule checking via the valid clause and the is_closed solver state variable.
rule trip_must_end_at_base =
valid is_closed;
first(leg(trip), leg.%dep_airport%) = last(leg(trip), leg.%arr_airport%);
end
The keyword is_closed evaluates to False while the generator is actively expanding an incomplete candidate trip, and evaluates to True only when evaluating a completed candidate trip.
5.3 Monotonicity & Abstract Interpretation
By analyzing the mathematical properties of expressions at compile-time, the Rave compiler can automatically infer whether a rule is a Final Rule or an Illegal Subchain Rule.
Definition: Monotonic Sum
A sum expression \(S = \sum e_i\) is a monotonic sum if the subexpression \(e_i\) is guaranteed to be non-negative (\(\ge 0\)). As candidate flight legs are appended to a trip during generation, \(S\) is strictly non-decreasing (\(S_{k+1} \ge S_k\)).
Direction in Legality
- Non-increasing in Legality (NiL): A rule expression \(E\) is NiL if, once it evaluates to
Falsefor a candidate trip, no continuation can ever evaluate toTrue. - Theorem: Any rule comparing a non-decreasing monotonic sum against a constant upper bound (\(S \le C\)) is Non-increasing in Legality and is therefore statically proven to be a Final Rule.
- Non-decreasing in Legality (NdL): A rule expression \(E\) is NdL if, once it evaluates to
True, all continuations remainTrue(\(S \ge C\)). Such rules are Illegal Subchain Rules.
5.4 Type & Attribute Inference Framework
The Rave compiler uses an abstract interpretation and attribute propagation pass over the Abstract Syntax Tree (AST):
flowchart TD
Op["Relational Operator (<=)<br><b>[Inferred: Non-increasing in Legality → FINAL RULE]</b>"]
Sum["sum(leg(duty), e)<br><i>[Inferred: Non-decreasing]</i>"]
Const["Constant Bound (08:00)<br><i>[Inferred: DepOnlyConst]</i>"]
Var["leg.%flight_time%<br><i>[Inferred: Non-negative]</i>"]
Op --- Sum
Op --- Const
Sum --- Var
Attribute Propagation Rules:
-
Range Inference: e1 : NonNegative, e2 : NonNegative => e1 + e2 : NonNegative e : NonNegative => sum(e) : NonDecreasing
-
Direction in Legality Inference: e1 : NonDecreasing, e2 : DepOnlyConst => (e1 <= e2) : NonIncreasingInLegality (NiL) Rule(E : NiL) => Final Rule (Safe to Prune Search Tree)
6. Cost Modeling & Objective Functions
6.1 Cost Property Definitions
In addition to legality rules, Rave models pairing and roster costs. Every derived activity (Duty, Trip, Roster) defines a special property named cost.
PROPERTY cost OF Trip
RULE:
max(
sum(duty(trip), duty.cost),
%guaranteed_trip_pay%
) + %soft_penalty_total%;
ENDPROPERTY
6.2 Guaranteed Pay & Overtime Structures
Airlines pay crew members based on formulas incorporating flight time, duty elapsed time, layover duty credit (TAFB - Time Away From Base), and minimum daily guarantees:
%duty_pay_flight_time% = sum(leg(duty), leg.%block_time%) * %hourly_rate%;
%duty_pay_elapsed_rig% = duty.%elapsed_time% * 0.50 * %hourly_rate%;
%duty_minimum_guarantee% = 04:00 * %hourly_rate%;
PROPERTY cost OF Duty
RULE:
max(
max(%duty_pay_flight_time%, %duty_pay_elapsed_rig%),
%duty_minimum_guarantee%
);
ENDPROPERTY
6.3 Soft Penalty Functions
Soft constraints guide the solver toward desirable solutions by appending penalty weights to the objective function:
%short_layover_penalty% =
if %layover_duration% < 12:00 then
(12:00 - %layover_duration%) * %penalty_weight%
else
0.0;
7. Language Workbenches & Modern Tooling Integration
7.1 VS Code IDE Support (AusbelDev.vscode-rave)
Modern Rave development leverages specialized Language Workbench integration. The Open-VSX / VS Code extension AusbelDev.vscode-rave provides IDE capabilities for Rave developers:
- Syntax Highlighting & Tokenizing: Custom grammars for Rave keywords, levels (
leg,duty,trip), traversers, and operators. - Real-time Diagnostics: On-the-fly syntax validation, identifier resolution, and scope checking.
- Auto-Completion & Snippets: Context-aware completion for keywords, traverser templates, and level properties.
- Hover Tooltips & Symbol Navigation: Instant inline inspection of variable types, definitions, and rule comments.
7.2 Syntax Highlighting, Diagnostics & Language Servers
Using the Language Server Protocol (LSP), Rave syntax trees are mapped dynamically to editor surfaces. Errors such as mismatched level traversers (e.g., attempting sum(duty(leg), ...) which violates the hierarchy direction) are flagged immediately at authoring time.
7.3 Compilation, Debugging & Simulation Pipelines
flowchart LR
A[Source Code<br>*.rave] --> B[Rave Compiler<br>AST & Infer]
B --> C[Shared Library<br>*.so / *.dll]
C --> D[Jeppesen Solver / Test Rig]
D --> E[In-IDE Interpreter & Simulator]
E --> F[Interactive Debugger / Report]
The Rave toolchain supports two execution modes:
- Ahead-of-Time (AOT) Compilation: Compiles Rave ASTs into C/C++ code, which is compiled into dynamically linked libraries (
.dll/.so) for maximum solver execution performance. - Interactive In-IDE Interpretation: Allows rule developers and domain experts to step through rule logic, inspect intermediate variable states, and run tabular unit test suites interactively.
8. Comprehensive Code Examples & Case Studies
8.1 Example 1: Flight Duty Period (FDP) Limits
This module models European Aviation Safety Agency (EASA) / FAA Flight Duty Period limits based on departure time and number of sectors (flight legs).
// ====================================================================
// MODULE: EASA Flight Duty Period (FDP) Regulations
// ====================================================================
CONSTANTS:
%fdp_base_limit% = 13:00;
%fdp_night_limit% = 11:00;
ENDCONSTANTS
// Determine if the duty starts during the circadian low (02:00 - 05:59)
%is_circadian_low_start% =
duty.%start_utc% >= 02:00 and duty.%start_utc% <= 05:59;
// Calculate allowable FDP reduction based on sector count
%sector_penalty% =
if count(leg(duty)) > 2 then
(count(leg(duty)) - 2) * 00:30
else
00:00;
// Calculate net max FDP allowed for the duty
%max_allowed_fdp% =
if %is_circadian_low_start% then
%fdp_night_limit% - %sector_penalty%
else
%fdp_base_limit% - %sector_penalty%;
// Hard Constraint: Actual FDP must not exceed max allowed FDP
CONSTRAINT easa_fdp_limit OF Duty
COMMENT: "Duty elapsed time must not exceed max allowable FDP"
STATUS: ON;
RULE:
duty.%elapsed_time% <= %max_allowed_fdp%;
ENDCONSTRAINT
8.2 Example 2: Minimum Sit Time & Rest Requirements
This module enforces sit times between consecutive legs within a duty and rest periods between consecutive duties.
// ====================================================================
// MODULE: Rest and Sit Time Legality
// ====================================================================
CONSTANTS:
%min_sit_time% = 00:45;
%min_rest_period% = 12:00;
ENDCONSTANTS
// Constraint 1: Minimum sit time between consecutive legs in a duty
CONSTRAINT min_sit_time_check OF Duty
COMMENT: "Connect time between consecutive flights must be at least 45 mins"
STATUS: ON;
RULE:
for each f1 -> f2 in elements
f2.departure_time - f1.arrival_time >= %min_sit_time%;
ENDCONSTRAINT
// Constraint 2: Minimum rest period between consecutive duties in a trip
CONSTRAINT min_inter_duty_rest OF Trip
COMMENT: "Rest period between consecutive duties must meet regulatory minimum"
STATUS: ON;
RULE:
all(duty(trip),
prev(duty(trip), duty.%end_time%) = 00:00 or
(duty.%start_time% - prev(duty(trip), duty.%end_time%)) >= %min_rest_period%
);
ENDCONSTRAINT
8.3 Example 3: Complex Multi-Duty Crew Pairing Cost Model
This example demonstrates a complete commercial crew pairing specification, incorporating multi-duty guarantees, TAFB (Time Away From Base) credits, soft penalties, and home-base closure checks.
// ====================================================================
// MODULE: Comprehensive Crew Pairing Specification
// ====================================================================
CONSTANTS:
%hourly_rate% = 75.00;
%base_airport% = "SAW";
%min_daily_guarantee% = 05:00;
ENDCONSTANTS
// --------------------------------------------------------------------
// 1. Structural Base Closure Rule (Illegal Subchain / Valid Conditional)
// --------------------------------------------------------------------
CONSTRAINT trip_must_start_and_end_at_base OF Trip
COMMENT: "Pairings must originate and terminate at home base SAW"
STATUS: ON;
RULE:
valid is_closed;
first(leg(trip), leg.%dep_airport%) = %base_airport% and
last(leg(trip), leg.%arr_airport%) = %base_airport%;
ENDCONSTRAINT
// --------------------------------------------------------------------
// 2. Duty Level Cost Computation
// --------------------------------------------------------------------
%duty_flight_time% = sum(leg(duty), leg.%block_time%);
%duty_elapsed_time% = duty.%end_time% - duty.%start_time%;
PROPERTY cost OF Duty
RULE:
max(
%duty_flight_time% * %hourly_rate%,
%min_daily_guarantee% * %hourly_rate%
);
ENDPROPERTY
// --------------------------------------------------------------------
// 3. Trip Level Cost Computation (TAFB vs. Sum of Duties)
// --------------------------------------------------------------------
%tafb_duration% = last(leg(trip), leg.%arr_time%) - first(leg(trip), leg.%dep_time%);
%tafb_credit_pay% = (%tafb_duration% / 3.5) * %hourly_rate%;
%sum_duty_costs% = sum(duty(trip), duty.cost);
// Soft penalty for 4-leg duties
%soft_penalty% =
sum(duty(trip), if count(leg(duty)) >= 4 then 150.00 else 0.00);
PROPERTY cost OF Trip
RULE:
max(%sum_duty_costs%, %tafb_credit_pay%) + %soft_penalty%;
ENDPROPERTY
9. Best Practices, Common Pitfalls & Performance Tuning
Best Practices
- Exploit Static Monotonicity: Express upper-bound limitations using non-decreasing monotonic sums (\(S \le C\)) without surrounding them in
valid is_closed;. This allows the Rave compiler to classify them as Final Rules, maximizing search space pruning speed. - Isolate Domain Concepts: Avoid mixing structural level logic with financial calculations. Separate legality rules (
CONSTRAINT) from objective functions (PROPERTY cost). - Minimize Level Context Transitions: Prefer localized traversers (
sum(leg(duty), ...)) over multi-level nested traversers across entire rosters where possible.
Common Pitfalls
- Misusing
valid is_closed;on Final Rules: Wrapping a simple duty flight time rule (sum(leg(duty), leg.%time%) <= 08:00) invalid is_closed;neutralizes its ability to prune incomplete sub-trips, causing massive performance degradation. - Temporal Disdependencies: Subtracting times out of chronological order (e.g.,
start_time - end_time) breaks automatic non-negativity type inference, causing the rule classifier to fall back to unoptimized evaluation. - Implicit Universal Traversal: Calling unbounded
all()orany()traversers over large rosters inside inner generation loops instead of restricting evaluation to the current activeDutyorTrip.
Advanced Semantic Analysis in Rave
As airline rule sets grow in complexity, developers must understand not only how to write functional rules but also how the compiler interprets those rules to optimize the search space. Rave utilizes semantic analysis and static code analysis at compile-time to automatically determine rule properties, guiding the Pairing Generator's traversal strategy.
This document explores the semantic analysis framework used to classify rules, inspired by extended type checking and abstract interpretation.
1. The Goal of Semantic Analysis
The primary goal of analyzing Rave code at compile-time is to automate the detection of patterns that impact generation efficiency.
During Pairing Generation, the system builds candidate trips leg by leg. When an incomplete trip violates a rule, the generator must decide whether to:
- Prune the branch: If no future leg additions can make the trip legal (a Final Rule).
- Continue searching: If future leg additions might eventually make the trip legal (an Illegal Subchain Rule).
By annotating the Abstract Syntax Trees (ASTs) of the code with semantic properties, the compiler can automatically deduce whether a rule is a Final Rule or an Illegal Subchain Rule.
2. Attribute Inference
The Rave compiler uses inference rules (similar to typing rules) to propagate semantic attributes bottom-up through the AST. Every expression is checked against these rules.
Range Attributes
These attributes describe the numerical value range of an expression:
- NonNegative: The expression evaluates to \(\ge 0\) (e.g., a flight's duration).
- NonPositive: The expression evaluates to \(\le 0\).
graph TD
A[Expression: e1 + e2]
B[e1: NonNegative]
C[e2: NonNegative]
B --> A
C --> A
A -.-> D{Inferred: NonNegative}
Dependency & Constantness Attributes
These attributes track whether an expression changes as new legs are added:
- ConstVal: An expression defined by a constant value.
- LevelDependent: An expression whose evaluation changes in continued instances (e.g., adding legs to a duty).
- LevelConstant: An expression whose value does not change as the current level instance grows (e.g., the starting time of a duty remains the same regardless of how many legs are added afterward).
3. Direction in Value (Monotonicity)
Understanding how numerical values change as the generator adds legs is crucial.
- Monotonic Sum: A sum-expression whose subexpression is known to be either nonnegative or nonpositive.
- NonDecreasing: A monotonic sum with a nonnegative subexpression is nondecreasing. As legs are added, the sum can only grow or stay the same.
4. Direction in Legality
By combining range, constantness, and value direction, the compiler infers the Direction in Legality for boolean expressions. This defines how the boolean evaluation behaves during trip generation:
- Non-increasing in Legality (NiL): A boolean expression instance that evaluates to false can never have a continuation that evaluates to true.
- Pattern:
[NonDecreasing Expression] <= [Constant] -
Conclusion: If a rule's defining expression is NiL, it is a Final Rule.
-
Non-decreasing in Legality (NdL): A boolean expression instance that evaluates to true can never have a continuation that evaluates to false.
- Pattern:
[NonDecreasing Expression] >= [Constant] -
Conclusion: If a rule's defining expression is NdL, it is an Illegal Subchain Rule.
-
NonFinal: A boolean expression lacking linear direction (e.g., comparing two variable expressions). These are safely treated as Illegal Subchain Rules to prevent accidental pruning of valid search spaces.
Common Design Patterns in Rave
Writing efficient Rave code requires balancing business logic accuracy with solver performance. Because Rave is declarative, how you structure your logic directly influences how the underlying C/C++ engine evaluates the search space.
This document outlines standard design patterns and performance pitfalls for Rave developers.
1. The "Condition-Guard" Pattern
Not all rules apply to every trip or duty. When writing rules that target specific scenarios (e.g., deadhead flights or international sectors), use the valid keyword to establish a precondition.
Anti-Pattern: Embedding the condition inside the rule logic, forcing the rule to evaluate True for non-applicable cases.
rule min_rest_after_international =
if any(leg(duty), leg.%is_international%) then
%rest_time% >= 14:00
else
True;
end
Best Practice: Use the valid clause. If the valid expression evaluates to False, the generator safely skips the rule entirely.
rule min_rest_after_international =
valid any(leg(duty), leg.%is_international%);
%rest_time% >= 14:00;
end
2. The "Monotonic Limit" Pattern
When limiting accumulative properties (like maximum duty time, maximum sectors, or total flight time), you want the solver to immediately prune the search tree as soon as the limit is breached.
Best Practice: Keep the comparison strict and against a constant or DepOnlyConst bound so the compiler flags it as a Final Rule.
// The compiler recognizes this as a Non-increasing in Legality (NiL) Final Rule
rule max_flight_time_per_duty =
sum(leg(duty), leg.%flight_time%) <= %legal_max_time%;
end
Anti-Pattern: Wrapping a monotonic sum limit inside a valid is_closed; block. This destroys the compiler's ability to prune the tree, as the rule will only be checked upon trip completion, turning an efficient Final Rule into an inefficient Illegal Subchain Rule.
3. Traverser Scope Minimization
Traversers (sum, count, all, any) aggregate data across child elements. Executing wide traversers continuously during generation is computationally expensive.
Anti-Pattern: Traversing the entire trip to find information about the current duty.
Best Practice: Restrict the traversal scope to the smallest required level instance. If you only need to evaluate the current working day, traverse the Duty level.
4. The "Cost Penalty" Pattern
When designing objective functions, separate the base financial costs from "soft" quality-of-life penalties. This makes debugging solver behavior much easier and allows business analysts to tune weights independently.
graph LR
A[Hard Financial Cost<br>Hourly Rate * Block Time] --> C(Total Cost Property)
B[Soft Penalties<br>Base Value * Penalty Weight] --> C
style A fill:#48bb78,color:#fff
style B fill:#ed8936,color:#fff
Implementation:
// 1. Calculate actual union-negotiated pay
%base_pay% = sum(leg(duty), leg.%block_time%) * %hourly_rate%;
// 2. Apply a weighted penalty for undesirable schedules (e.g., 4-sector days)
%fatigue_penalty% =
if count(leg(duty)) >= 4 then
500.0 * %fatigue_weight_param%
else
0.0;
// 3. Aggregate cleanly
PROPERTY cost OF Duty
RULE:
%base_pay% + %fatigue_penalty%;
ENDPROPERTY
Rave Tables and Data Lookups (etables)
In previous examples, we hardcoded constants like %max_flight_time% = 09:00. In a production airline environment, you almost never hardcode business values.
Airlines have thousands of parameters: meal allowances that change by country, maximum duty times that vary by the number of sectors and the time of day, and seniority pay rates. These are stored in proprietary tabular databases called etables (Evaluation Tables).
1. The Structure of an etable
An etable is essentially a CSV or relational database table injected into the Jeppesen environment.
Example Table: max_duty_time.etab
| Sectors | Is_Night_Flight | Max_Duty_Time |
|---|---|---|
| 1 | False | 14:00 |
| 2 | False | 13:00 |
| 3 | False | 12:00 |
| 1 | True | 11:00 |
| 2 | True | 10:00 |
2. Querying Tables in Rave
Rave provides built-in syntax to query these tables dynamically during search tree traversal.
// 1. Define the search keys
%sector_count% = count(leg(duty));
%is_night% = duty.%starts_in_wocl%;
// 2. Perform the table lookup
// syntax: table("table_name").lookup(key1, key2, return_column)
%dynamic_max_duty% =
table("max_duty_time").lookup(%sector_count%, %is_night%, "Max_Duty_Time");
// 3. Apply the dynamic constraint
CONSTRAINT dynamic_max_duty_check OF Duty
COMMENT: "Duty time must respect the dynamic etable limits."
STATUS: ON;
RULE:
duty.%elapsed_time% <= %dynamic_max_duty%;
ENDCONSTRAINT
3. Why etables Matter
By externalizing the parameters into etables, the airline achieves Configuration over Code.
- If the pilot union negotiates a new maximum duty time, the airline's operations staff can simply update the
etablevia a user interface. - The Rave Developer does not need to rewrite, recompile, and redeploy the core Rave codebase. The compiled rules automatically fetch the new data at runtime.
Performance Profiling and Debugging
A classic rite of passage for a Rave developer is writing a rule that perfectly passes all unit tests, but when deployed to the Optimization Solver, causes the run time to balloon from 2 hours to 48 hours.
Because Rave is heavily declarative and relies on traversers, it is very easy to accidentally write operations with \(O(N^3)\) or worse time complexity.
1. The Danger of Nested Traversers
The most common performance killer is nesting broad traversers inside one another.
The Anti-Pattern:
// Iterating over every leg in a month-long roster
%has_long_duty% =
any(leg(roster),
// And for EVERY leg, traversing the entire duty it belongs to!
sum(leg(duty), leg.%flight_time%) > 10:00
);
The Optimized Solution: Traverse at the highest appropriate level.
%has_long_duty% =
any(duty(roster),
// The sum is evaluated once per duty, not once per leg
sum(leg(duty), leg.%flight_time%) > 10:00
);
2. Leveraging Caching and Memoization
Behind the scenes, the Rave compiler tries to optimize execution, but it relies on you to structure variables efficiently.
If you use a complex calculation multiple times, assign it to a variable. The Rave evaluation engine caches (memoizes) the variable's state for the current search tree node.
// BAD: Re-evaluating the traverser twice
%is_high_penalty% =
if sum(leg(duty), leg.%delay_minutes%) > 60 then
sum(leg(duty), leg.%delay_minutes%) * 10.0
else
0.0;
// GOOD: Evaluated once and cached
%total_delay% = sum(leg(duty), leg.%delay_minutes%);
%is_high_penalty% =
if %total_delay% > 60 then
%total_delay% * 10.0
else
0.0;
3. Profiling Tools (Rule Analyzer)
When a generation run is slow, developers use Jeppesen's internal profiling tools (often referred to as the Rule Analyzer or Performance Logs).
These tools output reports showing:
- Hit Count: How many millions of times a rule was evaluated.
- Prune Rate: How many times a Final Rule successfully killed a branch.
- Execution Time (ms): The total CPU time spent evaluating a specific variable.
Optimization Strategy: If a rule has a massive execution time but a 0.01% prune rate, it is mathematically inefficient. It is costing the solver massive CPU cycles without actually helping to narrow down the search space. Developers must rewrite the rule to be more restrictive earlier in the tree, or simplify the math.
Codebase Modularization & Architecture: Overriding Core Rules
In an enterprise environment like Norwegian or Lufthansa, you do not write all your Rave code in a single file. An airline's rule set is massive, and maintaining it requires strict separation of concerns.
Furthermore, Jeppesen provides a "Core" set of standard aviation rules. Airlines take this core and use modular language composition (referencing, extension, and embedding) to adapt it to their specific Collective Bargaining Agreements (CBAs) without making invasive modifications to the base language.
1. Separation of Concerns (Viewpoints)
A domain is typically composed of different concerns, and a good DSL architecture separates these into different viewpoints or modules.
For example, European Flight Time Limitations (EASA FTL) apply to every European airline. However, Norwegian's specific union pay rules only apply to Norwegian.
- Best Practice: Keep EASA rules in a
fll_easa.ravemodule and union pay rules in acba_norwegian.ravemodule. - This allows different stakeholders (regulatory compliance officers vs. payroll analysts) to manage their respective concerns independently.
graph TD
Core[Jeppesen Core Framework<br>Basic hierarchy and traversers]
EASA[EASA FTL Module<br>Strict Legal Constraints]
CBA_Pilot[Norwegian Pilot CBA<br>Union Pay Rules]
CBA_Cabin[Norwegian Cabin Crew CBA<br>Union Pay Rules]
Core --> EASA
EASA --> CBA_Pilot
EASA --> CBA_Cabin
2. Modularity via Import and Extension
Rave allows you to compose languages and rule sets using import statements. A module can extend another module to add new abstractions or override existing ones.
// MODULE: cba_norwegian_pilots.rave
import fll_easa;
import standard_costs;
// Here we extend the base environment with our specific constants
CONSTANTS:
%pilot_hourly_rate% = 120.00;
ENDCONSTANTS
3. Overriding Rules (The "Adapter" Pattern)
Sometimes, the base EASA rule allows a 13-hour duty, but your pilot union agreement strictly caps it at 12 hours. Instead of invasively modifying the core EASA file (which breaks future software updates), you create an adapter or override in your airline-specific module.
By utilizing incremental language extension, developers can fall back to the base language rules while adding higher-level, airline-specific abstractions. This avoids the "DSL Hell" of duplicating thousands of lines of code.05. Career and Interviews
Tailoring Your Resume for Airline Crew Optimization
Breaking into the airline optimization industryβwhether as a Rave Developer, Operations Research (OR) Analyst, or Crew Systems Engineerβrequires a resume that speaks the language of the domain. Airlines like Norwegian, Lufthansa, and Delta look for a specific blend of mathematical intuition, systems engineering, and business logic translation.
This guide will help you translate your standard Software Engineering (SWE) or Operations Research experience into a compelling narrative for airline recruiters.
1. Emphasize Declarative and Constraint Programming
Rave is a declarative Domain-Specific Language (DSL). If you have never used Rave professionally, you need to highlight your ability to think declaratively rather than imperatively.
Keywords to Include:
- Constraint Logic Programming (CLP)
- Declarative Paradigms (e.g., SQL, Prolog, Haskell, or logic rules)
- Domain-Specific Languages (DSLs) / Language Engineering
- Abstract Syntax Trees (AST) & Static Analysis
Example Bullet Point:
Standard: "Wrote business logic for the scheduling application." Tailored: "Engineered declarative business rules and constraints to handle complex scheduling logic, decoupling the rule evaluation from the core execution engine."
2. Highlight Operations Research (OR) & Mathematical Modeling
Crew pairing and rostering are massive NP-hard combinatorial optimization problems. You need to show that you understand the mathematical weight of the code you write.
Keywords to Include:
- Combinatorial Optimization
- Column Generation / Branch-and-Price
- Set Covering / Set Partitioning Problems
- Heuristics and Search Tree Pruning
Example Bullet Point:
Standard: "Improved application performance by 20%." Tailored: "Optimized search space traversal by implementing strict rule classification (e.g., distinguishing between Final and Subchain constraints), resulting in a 20% reduction in solver execution time."
3. Showcase Domain Translation Skills
Airlines need developers who can sit down with a 200-page legal union document (Collective Bargaining Agreement or CBA) and translate it into flawless code.
Keywords to Include:
- Regulatory Compliance
- Business Logic Translation
- Cost Function Modeling
- Cross-functional Collaboration (with domain experts / crew planners)
Example Bullet Point:
Standard: "Gathered requirements from stakeholders to build new features." Tailored: "Partnered with domain experts to translate complex regulatory frameworks and union agreements into executable software constraints and cost penalty functions."
4. Don't Forget the Glue (Python, Linux, XML)
The Jeppesen CARMEN ecosystem is held together by scripting languages. Make sure your resume explicitly lists the tools used to orchestrate these massive backend systems.
Keywords to Include:
- Python (Data Marshalling, Pre/Post-Processing)
- Bash / Linux Shell Scripting
- XML / JSON Data Structures
- C++ (if you have backend solver experience)
Common Interview Questions for Jeppesen Rave Roles
Interviewing for a Rave Developer or Crew Optimization role typically involves a mix of domain knowledge testing, logic puzzles, and systems architecture discussions. Below are common questions you can expect, along with the concepts you should hit in your answers.
1. Domain Knowledge
Q: Can you explain the difference between Crew Pairing and Crew Rostering?
- Answer Strategy: Define the two steps. Crew pairing is the process of grouping consecutive flights into legal trips starting and ending at a base airport, flown by an anonymous crew. Crew rostering takes those generated pairings and assigns them to actual, named crew members while factoring in vacations, training, and individual bidding preferences.
Q: What is deadheading, and why is it important to optimize?
- Answer Strategy: Explain that deadheading means transporting a crew member as a passenger to position them for a flight. It is critical to optimize because it represents a double loss: the airline pays the crew member for their time, and they occupy a seat that could have been sold to a paying customer.
2. Operations Research & Logic
Q: How does a generator traverse a search space, and how do rules prune it?
- Answer Strategy: Mention that the search space is a tree where nodes represent possible trips. The generator traverses the space to find legal trips. Final Rules allow the generator to safely prune dead-end branches. Illegal Subchain Rules cannot prune the tree because adding subsequent flights might satisfy the rule later.
Q: Why do we use soft penalties in a cost function instead of hard constraints?
- Answer Strategy: A hard constraint defines strict legality (e.g., an EASA FTL limit). A soft penalty is an artificial mathematical weight added to a valid solution's cost to discourage the solver from picking it unless necessary. We use soft penalties for "quality of life" issues (like avoiding minimum-rest layovers) to keep the search space feasible while guiding the solver to human-friendly schedules.
3. Systems Architecture
Q: Why do airlines use a DSL like Rave instead of writing the rules directly in the C++ optimization engine?
- Answer Strategy: Airline rules (imposed by regulatory agencies and unions) are highly complex and change constantly. Embedding these rules directly into a solver's source code is not practical. A DSL allows airlines to decouple the business logic from the math engine, enabling analysts to write rules, compile them dynamically, and run "what-if" scenarios without touching the core C++ solver.
Q: If you have an expression whose numerical value never decreases as flights are added, what does this mean for optimization?
- Answer Strategy: This is a non-decreasing monotonic sum. If this sum is compared against a constant upper bound (e.g.,
sum <= limit), the expression is "Non-increasing in Legality" (NiL). Once it evaluates to false, it can never become true again, meaning the compiler can safely classify it as a Final Rule and aggressively prune the search tree.
Mock Scenario Test: Rave Developer Assessment
Many airlines will issue a take-home assignment or live coding test to evaluate your declarative logic skills. Since standard platforms like LeetCode do not support Rave, they will usually ask you to write pseudo-Rave code or evaluate a logic puzzle.
Here is a realistic mock scenario.
π The Scenario
Background:
You are optimizing schedules for a European base. The union CBA states that a crew member's total flight time in a single Duty cannot exceed 9 hours (09:00). Additionally, the union strongly prefers that crews do not sit on the ground between flights for more than 2 hours (02:00).
The Requirements:
- Write a hard constraint to ensure the total flight time in a duty does not exceed
09:00. - Write a soft penalty that adds
50.0cost points for every minute of sit time that exceeds02:00between any two consecutive flights in a duty. - Add this penalty to the total cost of the Duty.
π» Your Task
Write the Rave code to implement these requirements.
Hint: Remember how to use monotonic constraints, the sum traverser, and the for each inter-instance navigator.
β Solution & Explanation
Part 1: The Hard Constraint (Flight Time Limit)
This is a classic monotonic limit. We want this to be a Final Rule so it prunes the search space efficiently.
CONSTANTS:
%max_flight_time% = 09:00;
ENDCONSTANTS
// The compiler will infer this as a Final Rule because flight time is non-negative
CONSTRAINT max_duty_flight_time OF Duty
COMMENT: "Total flight time in a duty must not exceed 9 hours."
STATUS: ON;
RULE:
sum(leg(duty), leg.%flight_time%) <= %max_flight_time%;
ENDCONSTRAINT
Part 2: The Sit Time Soft Penalty
We need to iterate over consecutive legs, check the sit time, and calculate a penalty if it exceeds 2 hours.
CONSTANTS:
%max_sit_time_threshold% = 02:00;
%sit_time_penalty_per_min% = 50.0;
ENDCONSTANTS
// Calculate the penalty for a single connection
%connection_sit_penalty% =
for each f1 -> f2 in elements
if (f2.departure_time - f1.arrival_time) > %max_sit_time_threshold% then
// Convert the excess duration to minutes and multiply by the penalty weight
((f2.departure_time - f1.arrival_time) - %max_sit_time_threshold%) * %sit_time_penalty_per_min%
else
0.0;
// Aggregate the penalties across the entire duty
%total_duty_sit_penalty% = sum(leg(duty), %connection_sit_penalty%);
Part 3: The Duty Cost Property
Finally, we add this penalty to the base cost of the duty.
π§ Interviewer Follow-Up Question:
- Interviewer: "Why didn't you put
valid is_closed;inside themax_duty_flight_timeconstraint?" - Your Response: "Because total flight time is a non-decreasing monotonic sum. By leaving
valid is_closed;out, the compiler correctly identifies it as a Final Rule. If I addedis_closed, it would turn into an Illegal Subchain Rule, the solver would stop pruning the tree early, and performance would plummet."
06. Real World Scenarios
Scenario 1: Fatigue Risk Management Systems (FRMS)
In modern airline operations, simply following basic EASA or FAA flight time limitations is not always enough. Airlines implement Fatigue Risk Management Systems (FRMS) to proactively prevent crew exhaustion by limiting specific patterns of work, even if those patterns are technically legal under basic FTL rules.
π The Scenario
Background: Data from the airline's safety department shows that crews flying more than three consecutive "Early Start" duties (reporting before 06:00 local time) experience a severe drop in alertness.
The Requirements:
- Define what constitutes an "Early Start".
- Write a hard constraint to prevent a crew member from being rostered for more than three consecutive early starts.
- This rule must evaluate at the
Roster(Plan) level, as it spans across multiple pairings/trips.
π» The Rave Solution
// ====================================================================
// MODULE: FRMS - Consecutive Early Starts
// ====================================================================
CONSTANTS:
%early_start_threshold% = 06:00;
%max_consecutive_early_starts% = 3;
ENDCONSTANTS
// 1. Define an Early Start at the Duty level
%is_early_start% =
duty.%start_local_time% < %early_start_threshold%;
// 2. Aggregate across the Roster level to find consecutive patterns
// We use a rolling counter to track consecutive early starts across the month.
%consecutive_early_starts_counter% =
if %is_early_start% then
prev(duty(roster), %consecutive_early_starts_counter%) + 1
else
0;
// 3. Hard Constraint to prevent rosters that violate the FRMS policy
CONSTRAINT max_consecutive_early_starts OF Roster
COMMENT: "A crew member cannot fly more than 3 consecutive early start duties."
STATUS: ON;
RULE:
all(duty(roster), %consecutive_early_starts_counter% <= %max_consecutive_early_starts%);
ENDCONSTRAINT
π§ Developer Notes:
- Level Context: Notice how we define the boolean
%is_early_start%at theDutylevel, but we evaluate theall(...)traverser at theRosterlevel. - Statefulness: The expression
%consecutive_early_starts_counter%uses theprev()traverser to look backward in the roster timeline, effectively creating a stateful rolling counter in a stateless declarative language.
Scenario 2: Union Bidding & Quality of Life Penalties
During the Rostering phase (assigning pairings to named crew members), the optimizer must respect crew preferences and seniority bidding. These are rarely hard constraints; instead, they are "soft constraints" modeled via cost penalties to guide the solver toward crew-friendly schedules.
π The Scenario
Background: The pilot union's Collective Bargaining Agreement (CBA) allows pilots to "bid" (request) specific days off. Because of manpower shortages, the airline cannot guarantee every bid will be honored.
The Requirements:
- Identify if a duty overlaps with a pilot's requested day off.
- Apply a massive cost penalty if a senior pilot's bid is denied.
- Apply a smaller penalty if a junior pilot's bid is denied.
- Add this to the final Roster cost to force the optimizer to respect seniority.
π» The Rave Solution
graph LR
A[Duty overlaps Day Off?] -->|Yes| B{Check Crew Seniority}
A -->|No| C[Penalty = 0]
B -->|High| D[Penalty = 10,000]
B -->|Low| E[Penalty = 1,000]
D --> F[Add to Roster Cost]
E --> F
C --> F
// ====================================================================
// MODULE: CBA - Seniority Bidding & Day Off Penalties
// ====================================================================
CONSTANTS:
%senior_bid_penalty% = 10000.0;
%junior_bid_penalty% = 1000.0;
%seniority_threshold% = 10; // Years of service
ENDCONSTANTS
// 1. Identify if a duty is scheduled on a requested day off (RDO)
// 'crew.%requested_days_off%' is a keyword array provided by the external system
%violates_rdo% =
duty.%start_date% in crew.%requested_days_off%;
// 2. Calculate penalty based on seniority
%rdo_violation_penalty% =
if %violates_rdo% then
if crew.%years_of_service% >= %seniority_threshold% then
%senior_bid_penalty%
else
%junior_bid_penalty%
else
0.0;
// 3. Aggregate across the Roster
%total_bidding_penalty% = sum(duty(roster), %rdo_violation_penalty%);
// 4. Inject into the objective function
PROPERTY cost OF Roster
RULE:
%financial_cost% + %total_bidding_penalty%;
ENDPROPERTY
Scenario 3: Day-of-Ops Disruption Recovery
On the day of operation, weather events or mechanical failures often delay flights. When a flight is delayed, the crew's scheduled sit time shrinks. If it shrinks below the legal minimum, the pairing is "broken" and the optimizer must find a legal recovery solution in real-time.
π The Scenario
Background: Snow in Oslo delays a morning departure. The crew has a tight connection in Bergen. If the connection time drops below 30 minutes, they cannot legally operate the next flight.
The Requirements:
- Dynamically calculate the actual sit time using estimated arrival/departure times rather than scheduled times.
- Flag the connection as illegal if it drops below the absolute minimum.
π» The Rave Solution
// ====================================================================
// MODULE: Day-of-Ops Tracking - Dynamic Sit Times
// ====================================================================
CONSTANTS:
%absolute_min_connection% = 00:30;
ENDCONSTANTS
// 1. Use Estimated times instead of Scheduled times
// The solver dynamically updates these keywords during the day of operation
%actual_arrival% =
if leg.%has_actual_arrival% then
leg.%actual_arrival_time%
else
leg.%estimated_arrival_time%;
%actual_departure% =
if leg.%has_actual_departure% then
leg.%actual_departure_time%
else
leg.%estimated_departure_time%;
// 2. Calculate the dynamic sit time for consecutive legs
CONSTRAINT dynamic_sit_time_check OF Duty
COMMENT: "Real-time connection must not drop below 30 minutes."
STATUS: ON;
RULE:
for each f1 -> f2 in elements
f2.%actual_departure% - f1.%actual_arrival% >= %absolute_min_connection%;
ENDCONSTRAINT
π§ Developer Notes:
- Dynamic Keywords: In day-of-ops tracking, keywords represent live data streams (e.g.,
leg.%estimated_arrival_time%). Rave developers must write fail-safes usingif/elsechecks to use the best available data source at any given second.
PROPRIETARY EDUCATIONAL LICENSE Copyright (c) 2026 Ole Christoffer Thorsen. All Rights Reserved.
-
PERMITTED USE You are granted a limited, non-exclusive, non-transferable license to view, read, and learn from the materials in this repository strictly for personal, private, and non-commercial educational purposes.
-
RESTRICTIONS Unless explicitly permitted in writing by the copyright holder, you may NOT:
- Copy, download for the purpose of redistribution, share, distribute, or publicly display this material.
- Modify, adapt, translate, or create derivative works from this material.
-
Scrape, data-mine, or use this material to train machine learning or artificial intelligence models.
-
LINKING AND ATTRIBUTION Private individuals may share external hyperlinks directing others to this original repository or website. When linking or referencing this material online, you must provide appropriate credit to the author and a clear link back to this repository.
-
COMMERCIAL USE STRICTLY PROHIBITED No commercial entity, organization, or individual may use this material for commercial purposes, corporate training, paid educational courses, or internal company documentation without a formal, written commercial licensing agreement.
For all commercial inquiries, licensing requests, or permissions beyond the scope of this license, please contact the author directly at LinkedIn