Documentation

Library manual

Reference for the self-contained C++20 divisible-load scheduling library that powers this portal: what it is for, what it covers, how to build and use it, and how it is licensed. Concepts and notation are defined in the Knowledge base. This manual is a living document and will grow as new problem classes and solvers are added.

Embedding

The library can be embedded in three ways: directly from C++ (include the headers, link the archive), via the C-ABI shared library from any language that can call a shared library, or through the bundled Python ctypes wrapper which requires no compiled extension. The JSON contract (the dict shape returned by dls.solve() and the HTTP API) is the stable surface that front-ends should depend on; the C++ types are internal.

C++ embedding

Include core/dls_instance.hpp and core/solver_registry.hpp, construct a DLSInstance, choose a solver with makeSolver(name), and call solver->solve(inst, cfg). The returned DLSSolution carries the status, makespan, energy, cost, activation sequence, and the full LoadFragment vector with Gantt timing.

// embed the library in a C++20 project
#include "core/dls_instance.hpp"
#include "core/solver_registry.hpp"
#include <iostream>

int main() {
    // build the instance
    dls::DLSInstance inst;
    inst.setTotalLoad(1000.0);

    dls::Processor p1, p2, p3;
    p1.commStartup = 0.1;  p1.commRate = 0.11; p1.computeRate = 0.52;
    p2.commStartup = 0.2;  p2.commRate = 0.21; p2.computeRate = 0.22;
    p3.commStartup = 0.3;  p3.commRate = 0.31; p3.computeRate = 0.32;
    p3.memoryLimit = 1500.0;    // B: caps this worker at 1500 units/installment
    inst.processors() = {p1, p2, p3};

    // pick a solver and run it
    auto solver = dls::makeSolver("best-rate");
    dls::SolverConfig cfg;
    cfg.seed = 42;           // optional: set for reproducible GA runs
    cfg.timeLimitSeconds = 5.0;  // optional: wall-clock budget
    dls::DLSSolution sol = solver->solve(inst, cfg);

    // read back the result
    if (!sol.feasible()) { std::cerr << "no feasible schedule\n"; return 1; }
    std::cout << "makespan: " << sol.makespan << "\n";
    for (const auto& f : sol.fragments)
        std::cout << "  P" << f.processorId
                  << "  load=" << f.loadSize
                  << "  [comm " << f.commStart << "→" << f.commFinish
                  << "  comp " << f.computeStart << "→" << f.computeFinish << "]\n";
}

Python embedding

The bundled frontend/dls/__init__.py locates libdls_c.so automatically (first via DLS_LIB, then by searching build*/bin/) and exposes solve(), pareto(), iso_map(), benchmark(), and topology(). Instances are plain dicts; all results are dicts matching the HTTP API response shapes exactly. No pip install, no compiled extension, no pybind11.

import dls

# list solvers registered in this build
print(dls.available_solvers())
# ['auto', 'ga', 'best-rate', 'online', 'single-round', 'exact', ...]

# solve — instance is a plain dict; result is a dict (the JSON contract)
inst = {
    "totalLoad": 1000,
    "processors": [
        {"S": 0.1, "C": 0.11, "A": 0.52, "B": 4000},
        {"S": 0.2, "C": 0.21, "A": 0.22, "B": 5000},
        {"S": 0.3, "C": 0.31, "A": 0.32, "B": 1500},
    ]
}
sol = dls.solve(inst, solver="best-rate")
print(sol["solution"]["makespan"])   # → 302.14
print(sol["lowerBound"])              # → 298.51

# time-energy Pareto sweep (requires an energy model in the instance)
front = dls.pareto(inst, solver="best-rate", points=20)
# returns {"solver", "points": [{"makespan": ..., "energy": ...}, ...]}

# isoefficiency map: sweep processors (x) vs comm rate (y), measure makespan
grid = dls.iso_map(x="procs", y="comm", metric="makespan",
                   xmin=2, xmax=16, xsteps=8,
                   ymin=0.01, ymax=0.5, ysteps=10)
# returns {"xs", "ys", "grid": [[makespan, ...], ...]} (grid[yi][xi])

# portfolio benchmark over 20 random instances
bench = dls.benchmark(
    solvers="single-round,best-rate,ga,exact",
    instances=20, procs=6, load=1000, seed=99
)
# returns {"instances", "provenOptimal", "solvers": [{"name", "avgRelGap", ...}, ...]}
for s in bench["solvers"]:
    print(f"{s['name']:15s}  gap={s['avgRelGap']:.3f}  t={s['avgTimeSec']:.4f}s")

# non-star topology (chain example)
chain_txt = """V 100
node 0.20 0
node 0.30 0.10
node 0.25 0.12"""
result = dls.topology("chain", chain_txt)
# returns {"status", "feasible", "makespan", "loads": [0, α₁, α₂]}