Constraint Solver

Kairos Constraint

Hocheffizienter Constraint-Satisfaction-Solver. Weist Entities auf einem N-dimensionalen Grid oder einer Zeitachse zu, erfüllt harte Regeln und minimiert weiche Penalties.

Pricing

2.000Credits
Einreichgebühr pro Solve

Einmalig pro Request, unabhängig von Dauer und Ergebnis.

+300pro angefangene Solve-Sekunde
POST/v1/kairos/constraint

Löst ein Constraint-Satisfaction-Problem. Die vollständige Lösung liegt hinter einem Pre-signed URL.

Parameter

NameTypStatusDefaultBeschreibung
dimensionsarray<Dimension>
Ja
Variablen-Achsen (≥ 1). Jeder Eintrag definiert eine Dimension mit eindeutiger id — siehe Abschnitt Dimensionen.
entitiesarray<Entity>
Ja
Was platziert wird (≥ 1). Jede Entity hat min_placements / max_placements und optional pro Dimension eine Einschränkung — siehe Abschnitt Entities.
constraintsarray<Constraint>Optional[]Liste der Regeln, die auf der Lösung gelten müssen (hard) oder zu Penalty-Kosten führen (soft). 15 Typen verfügbar — siehe Abschnitt Constraints.
configConfigOptional{}Solver-Einstellungen: Zeitbudget, Early-Stopping, Korrelations-ID, URL-Gültigkeit — siehe Abschnitt Config.

Request

curl -X POST "https://api.centra.dbpg.io/v1/kairos/constraint" \
  -H "Authorization: Bearer sk-live-your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": [
      { "type": "range", "id": "slot", "size": 6, "ordered": true },
      { "type": "set",   "id": "room", "values": ["main", "a", "b"] }
    ],
    "entities": [
      { "id": "keynote",
        "dimensions": { "slot": { "values": [0] }, "room": { "values": ["main"] } },
        "min_placements": 1, "max_placements": 1 },
      { "id": "talk_a",
        "dimensions": { "slot": {}, "room": { "values": ["a", "b"] } },
        "min_placements": 1, "max_placements": 1 }
    ],
    "constraints": [
      { "type": "not_same_slot", "hard": true,
        "entity_a": "talk_a", "entity_b": "keynote", "dimension": "slot" }
    ],
    "config": { "time_limit_seconds": 60, "request_id": "example-1" }
  }'

Response

{
  "status": "optimal",
  "request_id": "example-1",
  "warnings": [],
  "statistics": {
    "timing": { "model_build_seconds": 0.01, "solve_seconds": 0.03, "total_seconds": 0.04 },
    "problem_size": { "dimensions": 2, "entities": 2, "constraints": 1, "variables": 18, "cells": 18 },
    "total_penalty": 0, "best_bound": 0, "gap_percent": 0.0,
    "soft_constraint_violations": []
  },
  "result_url": "https://...s3.eu-central-1.amazonaws.com/csp/example-1.json?...",
  "result_expires_at": "2026-04-24T17:00:00+00:00",
  "usage": {
    "credits_used": 2300,
    "remaining_balance": 7700
  }
}

Dimensionen

Jede Dimension ist eine Achse, auf der Entities Werte annehmen können. Drei Typen. Jede Dimension braucht eine eindeutige id innerhalb des Requests.

set — Kategoriale Werte

Liste diskreter, namentlich aufgelisteter Werte. Geeignet für Räume, Teams, Produkte, Farben — alles, wo die Werte keinen natürlichen Zahlenbezug haben.

Parameter

NameTypStatusDefaultBeschreibung
type"set"
Ja
Discriminator, muss exakt "set" sein.
idstring
Ja
Eindeutige Kennung der Dimension, wird in Entities und Constraints referenziert.
valuesarray<string>
Ja
Erlaubte String-Werte. Die Reihenfolge ist nur signifikant, wenn ordered=true gesetzt ist.
orderedbooleanOptionalfalseDefault false. Wenn true, gelten die Werte als sortiert — relevant für sequence, proximity, spacing.
{ "type": "set", "id": "room", "values": ["r1", "r2", "r3"], "ordered": false }

range — Numerischer Bereich

Integer-Werte von 0 bis size-1. Ideal für Zeitslots, Nummern, Ringplätze — alles, wo die Reihenfolge semantisch relevant ist.

Parameter

NameTypStatusDefaultBeschreibung
type"range"
Ja
Discriminator, muss exakt "range" sein.
idstring
Ja
Eindeutige Kennung der Dimension.
sizeinteger ≥ 1
Ja
Anzahl der Werte. Erlaubte Werte sind Integer 0..size-1.
orderedbooleanOptionaltrueDefault true. range-Dimensionen sind standardmässig geordnet.
{ "type": "range", "id": "slot", "size": 40, "ordered": true }

interval — Kontinuierliche Zeitachse

Zeit-Dimension mit konfigurierbarer Dauer. Intern erzeugt der Solver pro Entity ein IntervalVar mit Start/Dauer/Ende, NoOverlap pro Ressourcengruppe ist automatisch.

Parameter

NameTypStatusBeschreibung
type"interval"
Ja
Discriminator, muss exakt "interval" sein.
idstring
Ja
Eindeutige Kennung der Dimension.
horizoninteger ≥ 1
Ja
Gesamtlänge der Zeitachse in Zeiteinheiten (z. B. 480 = 8 Stunden in Minuten).
{ "type": "interval", "id": "time", "horizon": 480 }

Einschränkung: Maximal eine Interval-Dimension pro Request.

Entities

Das sind die Objekte, die der Solver platziert — Unterrichtsstunden, Werbespots, Schichten, Kisten, etc.

Felder

Parameter

NameTypStatusDefaultBeschreibung
idstring
Ja
Eindeutige Kennung der Entity im Request. Wird in Constraints und in der Response referenziert.
namestringOptional""Freitext-Bezeichnung für Display und Logs. Default leerer String.
dimensionsobject
Ja
Dict dim_id → EntityDimensionConfig. Jeder Eintrag legt fest, welche Werte dieser Entity auf der jeweiligen Dimension erlaubt sind. Fehlende Dimensionen bedeuten: alle Werte dieser Dimension erlaubt.
min_placementsinteger ≥ 1
Ja
Minimale Anzahl aktiver Platzierungen für diese Entity (≥ 1).
max_placementsinteger ≥ 1
Ja
Maximale Anzahl aktiver Platzierungen (≥ 1). Muss ≥ min_placements sein.

EntityDimensionConfig

Pro Dimension unter entities[].dimensions. Jedes Feld ist optional.

Parameter

NameTypStatusDefaultBeschreibung
valuesarray<string|integer>OptionalListe erlaubter Werte, Teilmenge der Dimension-Werte. Default: alle Werte der Dimension.
mode"any" | "all"Optional"any""any" = Entity wählt pro Platzierung einen Wert. "all" = Entity belegt alle angegebenen Werte gleichzeitig als Block. Default "any".
sizeinteger ≥ 1Optional1Anzahl benachbarter Werte, die eine Platzierung belegt (≥ 1). Nur bei ordered-Dimensionen sinnvoll — z. B. size=2 für Doppelstunde. Default 1.
durationinteger ≥ 1Optional1Nur bei Interval-Dimensionen. Dauer der Platzierung in Zeiteinheiten. Default 1.
{
  "id": "math_5a",
  "name": "Math 5a with Mueller",
  "dimensions": {
    "slot": {},
    "room": { "values": ["r1", "r2"], "mode": "any", "size": 1 }
  },
  "min_placements": 1,
  "max_placements": 1
}

Constraints

Regeln, die auf der Lösung gelten. Jede Regel ist entweder hart (muss erfüllt sein) oder weich (darf verletzt werden, kostet aber Penalty). Der Solver minimiert die Summe aller weichen Penalties.

Gemeinsame Felder

Alle Constraint-Typen teilen diese Basis-Felder. Die typ-spezifischen Felder kommen darunter.

Parameter

NameTypStatusDefaultBeschreibung
typestring
Ja
Discriminator. Einer der 15 Typen unten.
idstringOptionalnullOptionale Kennung. Erscheint in response.statistics.soft_constraint_violations[].constraint_id, wenn verletzt.
hardboolean
Ja
true = muss erfüllt sein, ansonsten infeasible. false = darf verletzt werden, kostet aber penalty pro Verletzung.
penaltyinteger ≥ 1AlternativePflicht wenn hard=false. Kosten pro Verletzungs-Einheit (Integer ≥ 1).

Übersicht

Alle 15 Regeltypen im Schnelldurchlauf. Die typ-Spalte ist auf das Detail darunter verlinkt.

#TypModeKurzbeschreibungPflichtfelder
1fill
hardsoft
Jede Zelle muss belegt sein.
2cardinality
hardsoft
Begrenzt Anzahl Platzierungen einer Entity.
entityminmax
3proximity
hardsoft
Mindestabstand zweier Entities auf einer Dimension.
entity_aentity_bdimensionmin_gap
4spacing
soft
Anti-Clump — bestraft benachbarte Platzierungen derselben Entity.
entitydimensionclump_penalty
5same_slot
hardsoft
Zwei Entities müssen denselben Dimensions-Wert teilen.
entity_aentity_bdimension
6not_same_slot
hardsoft
Zwei Entities dürfen keinen Dimensions-Wert teilen.
entity_aentity_bdimension
7sequence
hardsoft
Entity A muss vor Entity B kommen.
firstthendimension
8follows
hardsoft
Entity B muss direkt nach Entity A platziert sein.
entity_aentity_bdimension
9not_follows
hardsoft
Entity B darf nicht direkt nach Entity A platziert sein.
entity_aentity_bdimension
10mutual_exclusion
hardsoft
Aus der Gruppe ist pro Wert nur eine Entity erlaubt.
entitiesdimension
11max_per_value
hardsoft
Obergrenze für Platzierungen einer Entity pro Wert.
entitydimensionmax
12cumulative
hardsoft
Gewichtete Kapazitätsschranke pro Wert.
dimensiondemandsmax_capacity
13pairwise_exclusion
hardsoft
Pro Kombination mehrerer Dimensionen nur eine Entity.
dimensions
14pairwise_fill
hardsoft
Jede Kombination mehrerer Dimensionen muss belegt sein.
dimensions
15expression
hardsoft
Freie Regel in eigener DSL.
rule

Detaillierte Typ-Referenz

fill

Jede Zelle (Kombination aller Dimensions-Werte) muss von mindestens einer Entity belegt sein. Klassisch für vollständige Abdeckung eines Grids.

hardsoft

Keine typ-spezifischen Felder — nur die gemeinsamen Felder oben.

Beispiel

{ "type": "fill", "hard": true }

cardinality

Begrenzt, wie oft eine Entity platziert werden darf. Bei soft: jede fehlende oder überschüssige Platzierung kostet penalty.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entitystring
Ja
ID der zu begrenzenden Entity.
mininteger
Ja
Minimale Anzahl Platzierungen.
maxinteger
Ja
Maximale Anzahl Platzierungen.

Beispiel

{
  "type": "cardinality",
  "hard": false,
  "penalty": 5,
  "entity": "math_5a",
  "min": 4,
  "max": 5
}

proximity

Zwei Entities müssen auf der angegebenen (geordneten) Dimension mindestens min_gap Werte auseinanderliegen.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entity_astring
Ja
ID der ersten Entity.
entity_bstring
Ja
ID der zweiten Entity.
dimensionstring
Ja
Dimension, auf der der Abstand gemessen wird. Muss ordered sein.
min_gapinteger ≥ 1
Ja
Mindestabstand in Werte-Schritten.

Beispiel

{
  "type": "proximity",
  "hard": true,
  "entity_a": "brand_A",
  "entity_b": "brand_B",
  "dimension": "slot",
  "min_gap": 3
}

spacing

Anti-Clump-Regel: verhindert, dass Platzierungen derselben Entity in direkt benachbarten Werten stehen. Immer soft — hard=false ist Pflicht.

soft

Typ-spezifische Felder

NameTypStatusBeschreibung
entitystring
Ja
ID der Entity, deren Platzierungen auseinandergehalten werden sollen.
dimensionstring (ordered)
Ja
Geordnete Dimension, auf der die Nachbarschaft geprüft wird.
clump_penaltyinteger ≥ 1
Ja
Strafe pro direkt benachbartem Paar derselben Entity.

Beispiel

{
  "type": "spacing",
  "hard": false,
  "penalty": 10,
  "entity": "lightning",
  "dimension": "slot",
  "clump_penalty": 50
}

same_slot

Wenn Entity A aktiv ist, muss mindestens eine Platzierung von B denselben Dimensions-Wert haben. Für Kopplung zwischen Entities.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entity_astring
Ja
ID der ersten Entity (die die Kopplung auslöst).
entity_bstring
Ja
ID der zweiten Entity (die denselben Wert haben muss).
dimensionstring
Ja
Dimension, auf der die Wertgleichheit gilt.

Beispiel

{
  "type": "same_slot",
  "hard": true,
  "entity_a": "a",
  "entity_b": "b",
  "dimension": "day"
}

not_same_slot

Entity A und Entity B dürfen nicht denselben Dimensions-Wert haben. Für Entkopplung.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entity_astring
Ja
ID der ersten Entity.
entity_bstring
Ja
ID der zweiten Entity.
dimensionstring
Ja
Dimension, auf der die Werte unterschiedlich sein müssen.

Beispiel

{
  "type": "not_same_slot",
  "hard": true,
  "entity_a": "a",
  "entity_b": "b",
  "dimension": "day"
}

sequence

Die früheste aktive Platzierung von first muss vor der frühesten von then liegen. Für Reihenfolge-Abhängigkeiten.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
firststring
Ja
ID der Entity, die zuerst kommen muss.
thenstring
Ja
ID der Entity, die danach kommt.
dimensionstring (ordered)
Ja
Geordnete Dimension, auf der die Reihenfolge definiert ist.

Beispiel

{
  "type": "sequence",
  "hard": true,
  "first": "intro",
  "then": "advanced",
  "dimension": "slot"
}

follows

Wenn Entity A an Position t aktiv ist, muss Entity B an Position t+1 aktiv sein. Für direkte zeitliche Nachfolge.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entity_astring
Ja
ID der vorherigen Entity.
entity_bstring
Ja
ID der Entity, die direkt danach kommen muss.
dimensionstring (ordered)
Ja
Geordnete Dimension, auf der die Nachfolge definiert ist.

Beispiel

{
  "type": "follows",
  "hard": true,
  "entity_a": "a",
  "entity_b": "b",
  "dimension": "slot"
}

not_follows

Wenn Entity A an Position t, darf Entity B nicht an Position t+1 sein. Negativform von follows.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entity_astring
Ja
ID der vorherigen Entity.
entity_bstring
Ja
ID der Entity, die nicht direkt danach kommen darf.
dimensionstring (ordered)
Ja
Geordnete Dimension, auf der die Nachfolge geprüft wird.

Beispiel

{
  "type": "not_follows",
  "hard": true,
  "entity_a": "a",
  "entity_b": "b",
  "dimension": "slot"
}

mutual_exclusion

Aus der angegebenen Gruppe darf pro Dimensions-Wert höchstens eine Entity aktiv sein.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entitiesarray<string>
Ja
Liste der Entity-IDs, die sich gegenseitig ausschliessen.
dimensionstring
Ja
Dimension, auf der die Exklusivität gilt.

Beispiel

{
  "type": "mutual_exclusion",
  "hard": true,
  "entities": ["a", "b", "c"],
  "dimension": "slot"
}

max_per_value

Obergrenze, wie oft eine einzelne Entity pro Dimensions-Wert platziert werden darf.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
entitystring
Ja
ID der betroffenen Entity.
dimensionstring
Ja
Dimension, auf der die Obergrenze gilt.
maxinteger ≥ 1
Ja
Maximale Anzahl Platzierungen pro Wert (≥ 1).

Beispiel

{
  "type": "max_per_value",
  "hard": true,
  "entity": "brand_x",
  "dimension": "slot",
  "max": 1
}

cumulative

Gewichtete Kapazitätsschranke: Summe der Gewichte aller auf einem Dimensions-Wert aktiven Entities darf max_capacity nicht überschreiten. Klassisch für Ressourcen mit Kapazität.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
dimensionstring
Ja
Dimension, auf der die Kapazität gemessen wird.
demandsobject<entity_id, integer>
Ja
Dict entity_id → Gewicht. Entities ohne Eintrag haben Gewicht 0.
max_capacityinteger ≥ 1
Ja
Kapazitätsschranke pro Dimensions-Wert (≥ 1).

Beispiel

{
  "type": "cumulative",
  "hard": true,
  "dimension": "slot",
  "demands": {
    "truck_a": 3,
    "truck_b": 2,
    "truck_c": 4
  },
  "max_capacity": 6
}

pairwise_exclusion

Für jede Kombination der angegebenen Dimensions-Werte darf höchstens eine Entity aktiv sein. Generalisiert die Zellen-Exklusivität auf eine beliebige Dimensions-Teilmenge. Klassisch: "Ein Lehrer nie gleichzeitig in zwei Räumen" mit dimensions=[slot, teacher].

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
dimensionsarray<string> (≥ 1)
Ja
Liste der Dimensions-IDs, deren Kombinationen exklusiv sind (≥ 1 Element).

Beispiel

{
  "type": "pairwise_exclusion",
  "hard": true,
  "dimensions": ["slot", "teacher"]
}

pairwise_fill

Für jede Kombination der angegebenen Dimensions-Werte muss mindestens eine Entity aktiv sein. Generalisiert fill auf eine beliebige Dimensions-Teilmenge.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
dimensionsarray<string> (≥ 1)
Ja
Liste der Dimensions-IDs, deren Kombinationen belegt sein müssen (≥ 1 Element).

Beispiel

{
  "type": "pairwise_fill",
  "hard": true,
  "dimensions": ["slot", "room"]
}

expression

Freie Regel in client-eigener DSL für Spezialfälle. Wird vom Verifier nicht inhaltlich geprüft — nur vom Solver ausgewertet. Nur verwenden, wenn keine der anderen 14 Typen passt.

hardsoft

Typ-spezifische Felder

NameTypStatusBeschreibung
rulestring (DSL)
Ja
Regel-String in eigener Syntax.

Beispiel

{
  "type": "expression",
  "hard": true,
  "rule": "<custom DSL>"
}

Config

Alle Config-Felder sind optional. Defaults sind im Schema gesetzt.

Felder

Parameter

NameTypStatusDefaultBeschreibung
time_limit_secondsfloat (1–600)Optional60Maximale Zeit, die der Solver nach einer Lösung suchen darf. Zwischen 1 und 600 Sekunden. Default 60.
stop_after_first_solutionbooleanOptionalfalsetrue = Solver liefert die erste gültige Lösung zurück und beendet sich. Keine Optimierung weiterer Lösungen. Default false.
relative_gap_limitfloat (0–1) | nullOptionalnullStoppt den Solver, sobald der relative Gap zur theoretischen Untergrenze ≤ X ist. Beispiel 0.05 = 5 %. Default null.
absolute_gap_limitinteger ≥ 0 | nullOptionalnullStoppt den Solver, sobald der absolute Penalty-Gap ≤ X ist. Default null.
request_idstring | nullOptionalnullEigene Korrelations-ID. Wird 1:1 in die Response echoed und erscheint in CloudWatch-Logs zur Zuordnung.
result_url_expires_ininteger (60–604800)Optional10800Gültigkeit des Pre-signed URLs in Sekunden. Zwischen 60 und 604800 (7 Tage). Default 10800 (3 h).
hintsarray<HintAssignment>Optional[]Liste von Warm-Start-Vorgaben aus einer vorherigen Lösung. Jeder Eintrag hat die Felder entity und values.

Response

Bei Status optimal und feasible liegt die vollständige Lösung hinter result_url. Die Felder im Wire-Body:

Status-Werte

HTTP-Statuscode und Abrechnungslogik pro Ergebnis-Klassifikation.

statusHTTPAbrechnungBedeutung
optimal200Jabeweisbar optimale Lösung
feasible200Jagültige Lösung, Optimalität nicht bewiesen (Zeitlimit erreicht oder Early-Stop)
infeasible200Jabeweisbar keine Lösung möglich — diagnostics-Block liefert Details
unknown200JaTimeout ohne Ergebnis und ohne Unlösbarkeitsbeweis
invalid400NeinRequest schematisch ok, aber logisch ungültig (z. B. min > max)

assignments

Liste aller Belegungen. Nur in der S3-Datei enthalten, nicht im Wire-Body. Bei Status infeasible / unknown / invalid fehlt der Block komplett.

Parameter

NameTypStatusBeschreibung
entitystring
Ja
ID der platzierten Entity.
placementinteger
Ja
Laufnummer der Platzierung für diese Entity (1..max_placements).
valuesobject
Ja
Konkrete Werte pro Dimension für diese Platzierung. Bei Interval-Dimensionen enthält das Objekt zusätzlich einen Schlüssel mit dem Suffix _end für den Endzeitpunkt.

statistics

Performance- und Qualitätsmetriken. Immer im Wire-Body enthalten (auch bei offloadeten Responses).

Parameter

NameTypStatusBeschreibung
timing.model_build_secondsfloat
Ja
Zeit für Variablen- und Constraint-Aufbau vor dem eigentlichen Solve.
timing.solve_secondsfloat
Ja
Reine Solver-Zeit.
timing.total_secondsfloat
Ja
Ende-zu-Ende im Solver (model_build + solve). Basis der Abrechnung.
problem_size.dimensionsinteger
Ja
Anzahl Dimensionen im Request.
problem_size.entitiesinteger
Ja
Anzahl Entities im Request.
problem_size.constraintsinteger
Ja
Anzahl Constraints im Request.
problem_size.variablesinteger
Ja
Anzahl interner Solver-Variablen. Indikator für Problemgrösse.
problem_size.cellsinteger
Ja
Anzahl Zellen (Kombinationen aller Dimensions-Werte).
placements_per_entityobject
Ja
Wie oft jede Entity in der Lösung platziert wurde.
total_penaltyinteger
Ja
Summe aller Soft-Verletzungs-Kosten in der Lösung. 0 = keine Soft-Verletzungen.
best_boundinteger
Ja
Vom Solver bewiesene Untergrenze des Penalty. Bei total_penalty == best_bound ist die Lösung beweisbar optimal.
gap_percentfloat
Ja
(total_penalty - best_bound) / total_penalty × 100. 0 = optimal.
soft_constraint_violationsarray
Ja
Pro verletzter Soft-Constraint ein Eintrag mit constraint_id, type, violation_count, penalty_total.

diagnostics

Nur bei Status infeasible oder unknown vorhanden. Hilft bei der Fehlersuche, warum der Solver keine Lösung gefunden hat.

Parameter

NameTypStatusBeschreibung
messagestring
Ja
Menschenlesbare Kurzzusammenfassung des Problems.
capacity_conflictsarrayOptionalListe der Dimensions-Werte, bei denen die Nachfrage grösser ist als die Kapazität. Pro Eintrag: dimension, value, min_demand, available_slots, entities.
conflicting_constraintsarrayOptionalListe der Constraints, die das Problem unlösbar machen. Pro Eintrag: index, type, description.