2026-04-06Β·10 min readΒ·sota.io team

Deploy ECLiPSe CLP to Europe β€” ECRC Munich πŸ‡©πŸ‡ͺ (ESPRIT 1988), the EU Constraint Logic Programming System Behind Airline Crew Rostering and Rail Scheduling, on EU Infrastructure in 2026

In 1988, five of Europe's largest technology companies formed a research consortium to solve a problem that neither database systems nor traditional programming languages could handle cleanly: expressing and solving complex combinatorial constraints over large solution spaces. Bull πŸ‡«πŸ‡·, ICL πŸ‡¬πŸ‡§, Siemens πŸ‡©πŸ‡ͺ, Philips πŸ‡³πŸ‡±, and Nixdorf πŸ‡©πŸ‡ͺ pooled resources at the ECRC (European Computer-Industry Research Centre) in Munich πŸ‡©πŸ‡ͺ, funded by the EU ESPRIT programme β€” the European Strategic Programme on Research in Information Technology. The result was ECLiPSe: a constraint logic programming system that extends Prolog with integrated constraint solvers, coroutining, and a rich library of propagation algorithms.

More than three decades later, ECLiPSe remains the canonical EU-native constraint programming system. It is used industrially for airline crew rostering (Lufthansa πŸ‡©πŸ‡ͺ, British Airways πŸ‡¬πŸ‡§ via ILOG), railway timetabling (European rail operators), and resource allocation across logistics, manufacturing, and scheduling. Developed within Europe, funded by European research programmes, and deployed in the operational systems that keep European aviation and rail running on time.

Constraint Logic Programming: Search + Propagation

CLP (Constraint Logic Programming) extends Prolog's unification-based search with constraint propagation: the solver does not merely enumerate possibilities but actively reduces the search space by pruning values inconsistent with the constraint store. The interaction between search (branching) and propagation (constraint narrowing) is what makes CLP dramatically more efficient than naive generate-and-test.

:- use_module(library(ic)).

% Constraint Logic Programming over integer intervals
% Worker scheduling: 4 shifts to assign to 3 workers

solve_schedule(Schedule) :-
    % Variables: each shift assigned to a worker (1..3)
    Schedule = [S1, S2, S3, S4],
    S1 :: 1..3,
    S2 :: 1..3,
    S3 :: 1..3,
    S4 :: 1..3,
    
    % Constraint: each worker gets at most 2 shifts
    occurrences(1, Schedule, C1), C1 #=< 2,
    occurrences(2, Schedule, C2), C2 #=< 2,
    occurrences(3, Schedule, C3), C3 #=< 2,
    
    % Constraint: worker 1 cannot take shift 3 (medical training)
    S3 #\= 1,
    
    % Constraint: adjacent shifts (1,2) and (3,4) need different workers
    S1 #\= S2,
    S3 #\= S4,
    
    % Search: labeling with first-fail heuristic
    labeling(Schedule).

The :: operator declares a variable's domain. #=<, #\= are constraint primitives β€” not tests but propagators that actively prune domains. When S3 #\= 1 executes, ECLiPSe does not just record a constraint; it immediately removes value 1 from S3's domain. If this narrows another domain to a singleton, further propagation fires automatically. The search (labeling) only runs over the already-reduced space.

The ic Library: Interval Constraints

ECLiPSe's primary constraint library is ic (Interval Constraints): a CLP system over integer and real intervals. The ic library implements bounds consistency as its default propagation strength, with full arc consistency available for specific constraints.

:- use_module(library(ic)).

% Real-valued interval constraints: geometric layout
layout(X, Y, W, H) :-
    % Rectangle position and dimensions
    X :: 0.0..100.0,
    Y :: 0.0..100.0,
    W :: 10.0..50.0,
    H :: 10.0..50.0,
    
    % Must fit within 100x100 canvas
    X + W #=< 100.0,
    Y + H #=< 100.0,
    
    % Minimum area
    W * H #>= 200.0,
    
    % Aspect ratio: width at most 3x height
    W #=< 3.0 * H.

% Mixed integer/real: production planning
production_plan(Units, Cost, Revenue) :-
    Units :: 0..1000,          % integer: units produced
    Cost :: 0.0..50000.0,      % real: production cost
    Revenue :: 0.0..100000.0,  % real: revenue
    
    % Cost model: fixed + variable
    Cost #= 5000.0 + 45.0 * Units,
    
    % Revenue model
    Revenue #= 120.0 * Units,
    
    % Profitability constraint
    Revenue - Cost #>= 10000.0,
    
    % Integer labeling for units
    indomain(Units).

The key innovation of the ic library over earlier CLP(FD) systems is unified handling of integers and reals in the same constraint store. This is essential for engineering and financial optimisation problems where integer decisions (units, personnel counts) interact with continuous quantities (costs, times, flows).

Arc Consistency and Bounds Propagation

ECLiPSe implements both arc consistency (AC) and bounds consistency (BC), and allows the user to choose per-constraint:

Arc consistency (AC-5 algorithm): For a binary constraint C(X, Y), a value v in dom(X) is arc-consistent if there exists at least one value w in dom(Y) such that C(v, w) holds. AC removes values from domains that have no support. Full AC is complete but expensive for large domains.

Bounds consistency (BC): Weaker but cheaper β€” only prunes the minimum and maximum bounds of each domain, not interior values. Sufficient for most arithmetic constraints.

:- use_module(library(ic)).
:- use_module(library(ic_global)).

% Arc consistency example: alldifferent constraint
% Full AC (expensive but complete domain pruning)
permutation_problem(Vars) :-
    Vars = [A, B, C, D, E],
    Vars :: 1..5,
    
    % alldifferent with full arc consistency
    alldifferent(Vars),   % IC global constraint: AC-propagation
    
    % Additional arithmetic constraint (bounds propagation)
    A + B #= 6,
    C #> D,
    
    labeling(Vars).

% Bounds consistency example: cumulative scheduling
machine_schedule(Tasks) :-
    % Tasks: [Start, Duration, Resource]
    Tasks = [[S1,3,2], [S2,2,3], [S3,4,1], [S4,1,2]],
    
    % All starts in 0..20
    ( foreach([S,_,_], Tasks) do S :: 0..20 ),
    
    % Cumulative: total resource usage ≀ 4 at any time point
    % Bounds propagation over time axis
    ( foreach([S,D,R], Tasks), foreach(S, Starts),
      foreach(D, Durations), foreach(R, Resources) do true ),
    cumulative(Starts, Durations, Resources, 4),
    
    ( foreach([S,_,_], Tasks) do indomain(S) ).

The cumulative constraint is ECLiPSe's canonical industrial constraint: it encodes "total resource consumption across all active tasks must not exceed capacity." This single constraint, with its bounds-propagation implementation, encodes the core of most crew rostering and machine scheduling problems.

Coroutining: Constraint Suspension and Waking

ECLiPSe's most distinctive implementation feature is coroutining via suspension: a goal can suspend on a variable until a specific condition holds (instantiation, domain narrowing, bounds update), then wake automatically when the condition is triggered.

:- use_module(library(ic)).

% Custom propagator via coroutining
% "Never schedule two tasks in the same room at overlapping times"

:- meta_predicate no_overlap(+, +, +, +).

no_overlap(Start1, Dur1, Start2, Dur2) :-
    End1 is Start1 + Dur1,
    End2 is Start2 + Dur2,
    % Either task1 finishes before task2 starts, or vice versa
    ( End1 #=< Start2 ; End2 #=< Start1 ).

% Demon (coroutine): re-fires whenever bounds change
:- demon propagate_room_conflict/4.

propagate_room_conflict(S1, D1, S2, D2) :-
    % Wake when any bound narrows
    suspend(propagate_room_conflict(S1, D1, S2, D2),
            4,       % priority
            [S1->min, S1->max, S2->min, S2->max]),
    % Perform propagation with current bounds
    ic:get_bounds(S1, MinS1, MaxS1),
    ic:get_bounds(S2, MinS2, MaxS2),
    E1Min is MinS1 + D1, E1Max is MaxS1 + D1,
    E2Min is MinS2 + D2, E2Max is MaxS2 + D2,
    % If task1 must start before task2 can end, enforce S1+D1 =< S2
    ( E1Min > MaxS2 -> S1 + D1 #=< S2 ; true ),
    ( E2Min > MaxS1 -> S2 + D2 #=< S1 ; true ).

This coroutining model β€” where user-defined propagators suspend and wake on domain events β€” is what allows ECLiPSe to implement custom constraint solvers in Prolog itself. The ILOG solver, which powered airline crew rostering at Lufthansa πŸ‡©πŸ‡ͺ and British Airways πŸ‡¬πŸ‡§ in the 1990s-2000s, used ECLiPSe's suspension infrastructure as its extensibility layer.

EU Industrial Applications

Airline Crew Rostering: Lufthansa πŸ‡©πŸ‡ͺ and British Airways πŸ‡¬πŸ‡§

Crew rostering is the canonical ECLiPSe industrial application. An airline must assign pilots and cabin crew to flights such that:

These are hard constraints (legal requirements) and soft constraints (quality objectives) over thousands of crew members and hundreds of daily flights. The problem is NP-hard for even small instances.

:- use_module(library(ic)).
:- use_module(library(ic_global)).

% Crew rostering: EASA Part-FCL + EU OPS compliance
% Simplified: 3-day roster for 4 crew, 6 flights

crew(c1; c2; c3; c4).
flight(f1, 06:00, 09:30, lhr, fra).
flight(f2, 10:00, 11:30, fra, cdg).
flight(f3, 12:00, 14:00, cdg, ams).
flight(f4, 07:00, 10:30, ams, fra).
flight(f5, 11:00, 14:30, fra, mad).
flight(f6, 15:00, 18:00, mad, lhr).

% Decision variable: assignment(Crew, Flight) in {0=unassigned, 1=assigned}
assignment_matrix(Assignments) :-
    numlist(1, 4, Crews), numlist(1, 6, Flights),
    ( foreach(C, Crews), foreach(Row, Assignments) do
        ( foreach(F, Flights), foreach(Var, Row) do
            Var :: 0..1,
            % EU OPS: max 3 flights per crew per day
            arg(F, flight_day(F), Day),
            ( foreach(V, DayFlights) do
                flights_on_day(C, Day, DayFlights)
            ),
            sum(DayFlights) #=< 3
        )
    ),
    
    % Each flight covered by exactly 2 crew (captain + first officer)
    ( foreach(F, Flights) do
        ( foreach(Row, Assignments), foreach(V, ColF) do nth1(F, Row, V) ),
        sum(ColF) #= 2
    ),
    
    % EU OPS: minimum 11h rest between duty periods
    enforce_rest_constraints(Assignments),
    
    % EASA Part-FCL: type rating validity
    enforce_type_ratings(Assignments),
    
    % Labeling with cost optimisation
    ( foreach(Row, Assignments) do labeling(Row) ).

ILOG (the company that commercialised ECLiPSe technology before being acquired by IBM πŸ‡ΊπŸ‡Έ) reported a 15-20% reduction in crew cost at major European airlines through CLP-based rostering compared to manual scheduling. The savings come from better constraint propagation finding more legal roster combinations β€” directly translating to fewer reserve crew and reduced overtime.

SNCF πŸ‡«πŸ‡·: Railway Maintenance Scheduling

SNCF (SociΓ©tΓ© Nationale des Chemins de Fer FranΓ§ais) uses constraint programming for maintenance scheduling of the French high-speed rail network. The TGV network requires maintenance windows that must:

:- use_module(library(ic)).

% Railway maintenance scheduling: EPSF regulatory compliance
% Simplified: Paris-Lyon line, 3-day planning horizon

maintenance_job(j1, track_inspection, 4, 2).   % job(ID, type, duration_h, track_km)
maintenance_job(j2, catenary_service, 3, 5).
maintenance_job(j3, signalling_test, 6, 3).
maintenance_job(j4, ballast_tamping, 8, 10).

% Maintenance windows: 01:00-05:00 (4-hour window), 3 nights
window(n1, 60, 300).    % Night 1: minutes 60..300 from Day0 midnight
window(n2, 1500, 1740). % Night 2
window(n3, 2940, 3180). % Night 3

schedule_maintenance(Schedule) :-
    findall(J, maintenance_job(J, _, _, _), Jobs),
    
    % Assign each job to a start time
    ( foreach(J, Jobs), foreach(S, Schedule) do
        maintenance_job(J, _, Dur, _),
        S :: 0..3180,
        
        % Must start and end within a maintenance window
        ( member(W, [n1, n2, n3]),
          window(W, WStart, WEnd),
          S #>= WStart,
          S + Dur * 60 #=< WEnd
        ),
        
        % EPSF: minimum 12h recovery between jobs on same track segment
        ( foreach(J2, Jobs), J2 \= J do
            maintenance_job(J2, _, Dur2, Km2),
            maintenance_job(J, _, _, Km1),
            ( abs(Km1 - Km2) < 3 ->
                % Adjacent track segments: enforce gap
                ( S2 in Schedule,
                  nth1(Idx2, Jobs, J2),
                  nth1(Idx2, Schedule, S2),
                  ( S + Dur * 60 + 720 #=< S2 ;
                    S2 + Dur2 * 60 + 720 #=< S )
                )
            ; true )
        )
    ),
    
    % Optimise: complete highest-priority jobs in earliest windows
    labeling(Schedule).

Philips πŸ‡³πŸ‡±: Manufacturing Resource Allocation

Philips (one of the original ECRC consortium members) uses constraint programming for wafer fabrication scheduling in semiconductor manufacturing. Each wafer batch must pass through a sequence of processing steps, each requiring specific equipment and certified operators:

:- use_module(library(ic)).
:- use_module(library(ic_global)).

% Wafer fab scheduling: equipment qualification + capacity
% Based on Philips Semiconductors (now NXP πŸ‡³πŸ‡±) use case

wafer_batch(wb1, [litho, etch, deposit, implant]).
wafer_batch(wb2, [litho, deposit, etch]).
wafer_batch(wb3, [implant, anneal, deposit]).

% Equipment: qualified for specific steps
equipment(litho_1, litho, 8).    % machine, process, capacity_wafers_h
equipment(litho_2, litho, 8).
equipment(etch_1, etch, 12).
equipment(etch_2, etch, 12).
equipment(cvd_1, deposit, 6).
equipment(implant_1, implant, 4).
equipment(anneal_1, anneal, 10).

schedule_fab(Assignments) :-
    findall(WB, wafer_batch(WB, _), Batches),
    
    ( foreach(WB, Batches), foreach(BatchSched, Assignments) do
        wafer_batch(WB, Steps),
        ( foreach(Step, Steps), foreach(start(Step, S, E), BatchSched),
          param(WB) do
            S :: 0..480,      % 8-hour shift in minutes
            findall(Equip, equipment(Equip, Step, _), Equips),
            % Must use a qualified machine for this step
            ( member(Equip, Equips),
              equipment(Equip, Step, Cap),
              Duration is 60 / Cap * 25,   % 25 wafers per batch
              E #= S + round(Duration)
            )
        ),
        
        % Precedence: steps must execute in order
        ( fromto(BatchSched, [start(_, _, E)|Rest], Rest, []) do
            ( Rest = [start(_, S2, _)|_] -> E #=< S2 ; true )
        )
    ),
    
    % Capacity: each machine handles at most 1 batch at a time
    ( foreach(step(Step, Equip), AllAllocations) do
        ( foreach(BA, Assignments), foreach(start(Step, S, E), StepTimes) do
            member(start(Step, S, E), BA)
        ),
        no_overlap_times(StepTimes)
    ),
    
    ( foreach(BA, Assignments) do
        ( foreach(start(_, S, _), BA) do indomain(S) )
    ).

ECLiPSe Architecture: Prolog + CLP + CHR

ECLiPSe integrates three computing paradigms in a single runtime:

ComponentParadigmCore abstraction
Prolog engineLogic programmingUnification, backtracking, Horn clauses
ic libraryCLP(FD/R)Interval constraints, arc consistency
CHRConstraint Handling RulesUser-defined propagation rules
CoroutiningSuspension/wakeDemand-driven constraint propagation

CHR (Constraint Handling Rules) allows users to define new constraint propagators declaratively:

:- use_module(library(chr)).

% CHR rules: define "leq" (less-or-equal) constraint
:- chr_constraint leq/2.

% Reflexivity: leq(X,X) is trivially true
leq(X, X) <=> true.

% Antisymmetry: leq(X,Y) + leq(Y,X) => X = Y
leq(X, Y), leq(Y, X) <=> X = Y.

% Transitivity: leq(X,Y) + leq(Y,Z) => leq(X,Z)
leq(X, Y) \ leq(Y, Z) <=> leq(X, Z).

% Example usage
?- leq(A, B), leq(B, C), leq(C, A).
% CHR fires antisymmetry + transitivity:
% A = B = C

CHR propagators fire eagerly as constraints are added, performing sound transformations of the constraint store. This lets ECLiPSe users implement domain-specific constraint libraries β€” for example, temporal constraints, probability intervals, or custom resource models β€” with the same propagation infrastructure as the built-in ic constraints.

EU Provenance: ECRC Munich and ESPRIT

The ECRC (European Computer-Industry Research Centre) was established in Munich πŸ‡©πŸ‡ͺ in 1984 under the EU ESPRIT (European Strategic Programme on Research in Information Technology) programme. ESPRIT was the European Commission's response to the Japanese Fifth Generation Computer Systems project and the US strategic computing initiative β€” a targeted investment in European IT research capability.

Consortium memberCountryRole
BullπŸ‡«πŸ‡· FranceComputing infrastructure, systems
ICLπŸ‡¬πŸ‡§ United KingdomMainframes, database systems
SiemensπŸ‡©πŸ‡ͺ GermanyIndustrial computing, automation
PhilipsπŸ‡³πŸ‡± NetherlandsConsumer electronics, semiconductors
NixdorfπŸ‡©πŸ‡ͺ GermanyBusiness systems (later Siemens-Nixdorf)

Key developers:

ECLiPSe was developed under EU ESPRIT funding, at a Munich research centre established by five European companies, with a team drawn from across the EU. It is the direct institutional product of European collaborative research. The current open-source release is maintained with contributions from IC-Parc (Imperial College London) and the broader European CLP community.

CRA 2027, NIS2, EU AI Act

EU AI Act Article 13 (Transparency and provision of information): CLP solutions are inherently transparent. When ECLiPSe finds a crew assignment satisfying all constraints, the assignment is justified by the constraint store: assign(c1, f3) because no_conflict(c1, f1, f3) and type_rating_valid(c1, a320) and rest_period_satisfied(c1, 14). Unlike neural network decisions, CLP decisions have complete, inspectable derivation chains. This directly satisfies the EU AI Act's requirement for high-risk AI systems (which include "AI systems used to dispatch or establish priorities in the servicing of emergency services") to provide human-understandable decision rationales.

EU AI Act Article 9(2)(e) β€” Risk management: CLP provides formal proofs of constraint satisfaction. If a crew roster is SATISFIABLE under EASA Part-FCL and EU OPS constraints, ECLiPSe has proved that the schedule is legally compliant β€” not merely tested it against a sample. If UNSATISFIABLE, it has proved no legal roster exists under the given parameters. This transforms compliance checking from probabilistic testing to formal verification.

NIS2 Article 21(2)(a) β€” Policy compliance checking: CLP can encode network security policies as constraints and verify that all proposed configurations satisfy the policy. A configuration space that is UNSATISFIABLE under security constraints means no valid configuration can violate the policy β€” a stronger guarantee than testing individual configurations.

CRA 2027 Article 13 β€” Systematic security properties: ECLiPSe's constraint propagation can verify configuration invariants across an entire product family simultaneously β€” not just individual instances. "No configuration in this product line exposes port 22 while also enabling anonymous authentication" is expressible as a CLP integrity constraint, verifiable in polynomial time.

Deploying ECLiPSe to sota.io

sota.io is a EU-native Platform-as-a-Service on German infrastructure β€” GDPR-compliant by default, with managed PostgreSQL, private networking, and zero DevOps overhead. Running ECLiPSe constraint solving on sota.io keeps all problem specifications, constraint models, and solutions within EU jurisdiction.

# ECLiPSe CLP on sota.io β€” EU infrastructure, GDPR-compliant
FROM ubuntu:22.04

# Install ECLiPSe CLP
RUN apt-get update && apt-get install -y wget tar && \
    wget -q https://eclipseclp.org/Distribution/CurrentRelease/ECLiPSe_7.1_13.tgz && \
    tar -xzf ECLiPSe_7.1_13.tgz -C /opt && \
    rm ECLiPSe_7.1_13.tgz && \
    ln -s /opt/ECLiPSe_7.1/bin/x86_64_linux/eclipse /usr/local/bin/eclipse

WORKDIR /workspace
COPY *.ecl ./
COPY solve.pl ./

CMD ["/usr/local/bin/eclipse", "-f", "solve.pl"]
% solve.pl β€” ECLiPSe HTTP service for constraint solving
:- use_module(library(ic)).
:- use_module(library(ic_global)).

:- module(solver).

% REST endpoint: solve a scheduling problem
solve_crew_roster(Input, Solution) :-
    parse_roster_problem(Input, Flights, Crew, Constraints),
    
    % Build constraint model
    create_assignment_variables(Flights, Crew, Vars),
    apply_regulatory_constraints(Vars, Constraints),
    apply_optimisation_objectives(Vars),
    
    % Solve with timeout
    timeout(
        labeling(Vars),
        30,   % 30 second timeout
        (Solution = timeout, !)
    ),
    
    extract_roster(Vars, Solution).
# sota.io deployment configuration
name: eclipse-solver
build:
  context: .
  dockerfile: Dockerfile

resources:
  cpu: 2          # Constraint solving is CPU-intensive
  memory: 4096    # Large domain problems need memory
  
env:
  ECLIPSE_HOME: /opt/ECLiPSe_7.1
  SOLVER_TIMEOUT: 300
  MAX_SOLUTIONS: 1

# Scale for parallel problem instances
replicas: 2
# Deploy ECLiPSe solver to sota.io
sota deploy --name eclipse-solver \
  --dockerfile ./Dockerfile \
  --cpu 2 --memory 4096 \
  --env SOLVER_TIMEOUT=300

# Submit a crew rostering problem
curl -X POST https://eclipse-solver.freedom.sota.io/roster \
  -H "Content-Type: application/json" \
  -d '{
    "flights": ["LH400", "LH401", "LH402"],
    "crew": ["c001", "c002", "c003", "c004"],
    "horizon_days": 3,
    "regulation": "eu_ops"
  }'

# Response includes solution + constraint justification
# { "status": "SATISFIABLE",
#   "roster": { "c001": ["LH400", "LH402"], "c002": ["LH401"] },
#   "cost": 1240,
#   "constraints_active": 47 }

The free tier on sota.io covers development and small-scale constraint validation (single-server scheduling, team rostering up to ~20 variables). For production airline or rail scheduling instances β€” thousands of variables, industrial constraint sizes β€” horizontal scaling adds solver replicas, each handling independent problem instances in parallel.


See Also


ECLiPSe CLP: Joachim Schimpf πŸ‡©πŸ‡ͺ + Kees Shen πŸ‡³πŸ‡± + Micha Meier πŸ‡©πŸ‡ͺ (ECRC Munich, EU ESPRIT 1988 β€” Bull πŸ‡«πŸ‡·, ICL πŸ‡¬πŸ‡§, Siemens πŸ‡©πŸ‡ͺ, Philips πŸ‡³πŸ‡±, Nixdorf πŸ‡©πŸ‡ͺ). Later: IC-Parc, Imperial College London πŸ‡¬πŸ‡§. Mark Wallace πŸ‡¬πŸ‡§ (modelling methodology). ECLiPSe open-source since 2006. Mozilla Public License. eclipseclp.org. Constraint Handling Rules (CHR): Thom FrΓΌhwirth πŸ‡©πŸ‡ͺ (Ulm University, ICLP 1991 β†’ UniversitΓ© de Paris 6 / LIP6 πŸ‡«πŸ‡·). ILOG (commercialised ECLiPSe scheduler technology) acquired by IBM πŸ‡ΊπŸ‡Έ 2009 β†’ now IBM ILOG CPLEX CP Optimizer.