Heterogeneous compute contract

Can OrbitRoute route to your hardware?

Five public endpoints answer that question without a meeting, an NDA, or our SDK: a vocabulary for describing compute that is not a GPU, a dry-run validator, and a deterministic benchmark graded by the same scorer that grades our own baseline. All five are read-only and stateless — nothing here writes to the fleet or to production routing.

Read this before anything else: no non-GPU fleet is live. This contract is a published vocabulary and a grading harness, not capacity. OrbitRoute's production routing runs against GPU capacity whose class (demo, modeled, or federated) is disclosed per option in every decision; no CPU, NPU, FPGA, or QPU hardware receives work on this platform today, and none is under contract. A routing request that says nothing about modalities is a GPU request; one that names non-GPU hardware still gets a GPU decision — and the response now says so explicitly (the modality echo).
1. The vocabulary 2. The two rules 3. The honest modality echo 4. The benchmark and the number to beat 5. The reference result — ties and all 6. The five endpoints 7. From benchmark to directory

1. The vocabulary

A resource is described by three enums. A QPU is not modelled as a fast GPU: capacity is described in units native to the modality ({"qubits": 40}, not a synthetic GPU-equivalence).

Modality (5)What it is
cpuGeneral-purpose classical compute.
gpuThe default. Every node advertised before this contract existed is a GPU node and behaves exactly as it did.
npuNeural accelerators — inference-shaped silicon.
fpgaReconfigurable logic; defaults to requiring classical pre/post processing.
qpuQuantum processors; also defaults to requiring classical pre/post processing.
Problem class (7)Why it exists
inferenceWhat a legacy GPU node is taken to run. A request that states no problem class is one of these.
training
linear_algebraDense/sparse kernels a CPU, GPU, or FPGA might each legitimately claim.
signal_processingFFT/filtering pipelines — classic FPGA/NPU territory.
combinatorial_optimizationScheduling, assignment, routing — the shape of OrbitRoute's own benchmark instance.
quboQuadratic unconstrained binary optimization — the native encoding of annealers.
samplingDistributions a device produces natively rather than computes.
Evidence level (4)Provenance of the claimMay receive work
self_reportedTyped into the endpoint. The default for anything an operator sends — the least privileged value, never the most.no — listed only
simulator_measuredMeasured on a simulator. Enough to run the benchmark; not enough to take live jobs.no — listed only
hardware_measuredMeasured on the physical device.yes
third_party_verifiedMeasured and attested by someone who is not the operator.yes

2. The two rules

Rule 1 — an unstated requirement means GPU. A routing request that does not state acceptable_modalities is GPU-only, and a resource never claims to run everything: it declares the problem classes it can actually run. Both defaults point the same way — newly advertised hardware can never silently capture existing traffic. Backward compatibility here is a safety property, not a courtesy.
Rule 2 — capability is evidence-gated. A self_reported or simulator_measured profile is directory-listed but not compute-eligible: it appears in the public directory with its evidence level attached, and it receives no work until the claim is hardware_measured or third_party_verified. Being listed is publicity; being routable is earned.

Both rules are enforced in code (eligible_for() and is_routable()) and stated machine-readably by GET /api/v1/compute/contract, so the page you are reading can be checked against the running system.

3. The honest modality echo

POST /api/v1/route accepts problem_class and acceptable_modalities. Because no non-GPU fleet is live, stating them cannot change the decision — so instead of ignoring them silently, the response annotates itself. A GPU-native request (fields unset, or ["gpu"]) gets compute_modality_note: null and exactly the response it always got.

POST /api/v1/route X-API-Key
Request naming a QPU
{"model_name": "annealer-probe", "input_size_mb": 8,
 "estimated_compute_seconds": 5,
 "problem_class": "qubo", "acceptable_modalities": ["qpu"]}
Response 200 (the annotation; the rest of the decision is unchanged)
"compute_modality_note": {
  "requested_problem_class": "qubo",
  "requested_acceptable_modalities": ["qpu"],
  "unrecognized_modalities": [],
  "unrecognized_problem_class": false,
  "non_gpu_fleet_live": false,
  "processed_as": "gpu",
  "gpu_was_acceptable_to_request": false,
  "routing_effect": "none on the fields, everything on the interpretation — no non-GPU fleet is live, so this request was processed as a GPU request even though acceptable_modalities ['qpu'] did not list gpu as acceptable: the decision below is a GPU-fleet answer, not a qpu one (the stated problem_class 'qubo' likewise changed nothing)",
  "note": "Heterogeneous compute contract: modalities and problem classes are a published vocabulary and grading harness today, not live non-GPU capacity. See GET /api/v1/compute/contract.",
  "contract": "/api/v1/compute/contract"
}

The same sentence is appended to the decision's human-readable reasoning list — the standard every other disclosure on this platform follows (weather source, modeled stations, uncalibrated confidence).

4. The benchmark and the number to beat

The benchmark is a bounded orbital-scheduling instance — 24 jobs, 6 assets, contact windows, power and queue constraints — deterministic in its seed, so two parties can compare results without exchanging data. The baseline is a real earliest-deadline-first planner, not a strawman; beating a bad baseline would prove nothing.

GET /api/v1/compute/benchmark?seed=42 no auth
The classical baseline on the canonical seed-42 instance
"classical_baseline": {
  "solver": "orbitroute-classical-edf-greedy",
  "feasible": true,
  "scheduled_count": 24,      // 24/24 jobs scheduled, zero unscheduled
  "unscheduled_count": 0,
  "total_cost_usd": 3.6299,
  "lateness_penalty": 0.04,
  "objective": 3.6699,        // the number to beat — lower wins
  "wall_clock_ms": 0.255      // a per-run measurement, NOT a constant
}

The objective (cost + lateness penalty) is deterministic for a given seed: 3.6699 on seed 42, feasible at 24/24. wall_clock_ms is whatever that run took on that machine — a per-run measurement; quote your own inclusive of queue and compile time, as the response instructs.

5. The reference result — ties and all

To prove the loop end-to-end with no partner needed, a reference second solver (simulated annealing with a classical warm start) runs against the baseline through the same scorer. Here is its honest record:

Across seeds 0–19, the reference annealer recorded 0 wins and 20 ties against the classical baseline. On seed 42 it also ties: 3.6699 against 3.6699.

A tie is reported as a tie. The warm start means the annealer is never worse than the baseline, and on these instances the earliest-deadline-first schedule is already a strong local optimum the annealer has not escaped within its iteration budget. The point of publishing this is the harness, not a victory: the grading loop is proven end-to-end, the baseline is real, and the number to beat is really the number. If your solver beats 3.6699 on seed 42, that will mean something — winner says whatever the arithmetic says.

6. The five endpoints

GET /api/v1/compute/contract no auth

The vocabulary and the rules, machine-readable: the enums on this page, the routable evidence levels, the profile fields, and where to advertise.

Try it
curl https://www.orbitroute.ai/api/v1/compute/contract
POST /api/v1/compute/validate no auth

Dry-run a profile against a hypothetical request. Writes nothing — a validate call is not an advertisement.

Try it
curl -X POST https://www.orbitroute.ai/api/v1/compute/validate \
  -H "Content-Type: application/json" \
  -d '{"profile": {"modality": "qpu",
                   "eligible_problem_classes": ["qubo"],
                   "evidence_level": "simulator_measured",
                   "capacity": {"qubits": 40}},
       "problem_class": "qubo", "acceptable_modalities": ["qpu"]}'
Response 200 (abridged) — rule 2 in action
{
  "routable": false,
  "routable_reason": "evidence is simulator_measured; directory-listed but not compute-eligible until measured on hardware or verified",
  "eligible_for_request": false,
  "note": "Dry run. Nothing was advertised, stored, or scheduled."
}
GET /api/v1/compute/benchmark no auth

The full instance (assets, contact windows, jobs, constraints, objective definition) plus the classical baseline above. ?seed=, ?jobs=, ?assets= are yours to vary; seed 42 with 24 jobs / 6 assets is the canonical instance.

Try it
curl "https://www.orbitroute.ai/api/v1/compute/benchmark?seed=42"
GET /api/v1/compute/benchmark/reference no auth

The reference comparison from section 5, freshly computed: both lanes on the same instance, graded by the same scorer, with the annealer's full assignment included so anyone can re-grade it. Deterministic given (seed, budget_ms) on this deployment.

Try it
curl "https://www.orbitroute.ai/api/v1/compute/benchmark/reference?seed=42"
POST /api/v1/compute/benchmark/score no auth

Grade your assignment with the same scorer that graded ours — no home-field advantage. An infeasible assignment is named infeasible, not scored; unscheduled jobs are penalised, not ignored. Both behaviours are shown below, on responses captured from real calls to this endpoint.

Try it — a deliberately broken submission (one job, in a window that does not exist)
curl -X POST https://www.orbitroute.ai/api/v1/compute/benchmark/score \
  -H "Content-Type: application/json" \
  -d '{"seed": 42,
       "assignment": [{"job_id": "J000", "asset_id": "A00", "start_s": 98}]}'
Response 200 (captured verbatim) — infeasible is named infeasible
{
  "seed": 42,
  "submitted": {
    "feasible": false,
    "violations": [
      "J000 does not fit a contact window on A00"
    ],
    "scheduled_count": 0,
    "unscheduled_count": 24,
    "total_cost_usd": 0.0,
    "lateness_penalty": 0.0,
    "objective": 240.0
  },
  "classical_baseline_objective": 3.6699,
  "objective_delta_vs_baseline": 236.3301,
  "beats_classical_baseline": false,
  "verdict": "infeasible — not scored against the baseline"
}

The honesty rule in action: a broken submission is not quietly graded as a loss — the verdict names it infeasible, violations says exactly why, and the 23 jobs the submission never mentioned are counted as unscheduled rather than ignored (objective 240.0 = 24 unscheduled × the instance's published 10.0-per-job penalty).

A feasible submission — the reference annealer's own assignment, re-graded
# pull the reference assignment out of /benchmark/reference, submit it back
curl -s "https://www.orbitroute.ai/api/v1/compute/benchmark/reference?seed=42" \
  | jq '{seed: 42, assignment: .annealer.assignment}' \
  | curl -X POST https://www.orbitroute.ai/api/v1/compute/benchmark/score \
      -H "Content-Type: application/json" -d @-
Response 200 (captured verbatim) — a real tie, reported as a tie
{
  "seed": 42,
  "submitted": {
    "feasible": true,
    "violations": [],
    "scheduled_count": 24,
    "unscheduled_count": 0,
    "total_cost_usd": 3.6299,
    "lateness_penalty": 0.04,
    "objective": 3.6699
  },
  "classical_baseline_objective": 3.6699,
  "objective_delta_vs_baseline": 0.0,
  "beats_classical_baseline": false,
  "verdict": "does not beat the classical baseline"
}

Feasible at 24/24 and exactly the baseline objective — the tie from section 5, graded through the public scorer. If your solver's objective comes in under 3.6699, beats_classical_baseline flips to true; the verdict is arithmetic, not editorial.

When you are ready to be listed, the compute profile rides the existing federation advertisement: add a compute object to POST /api/v1/federation/nodes (walkthrough on /federation). The 201 echoes exactly what your profile changes — today a non-GPU modality keeps a node out of the GPU routing fleet, and since no non-GPU fleet is live, a listed non-GPU node receives no work. That echo, this page, and the modality note all say the same thing on purpose.

Where the routing numbers come from: /methodology. The full customer surface: /api-reference. Whether it is up right now: /status.