API reference

Each client has its own full reference, generated from its source. This page says where to find it and describes what all of them share: the result you get back, how to read it, and what an error looks like.

LanguagePackageReferenceSource
Pythonquicopt on PyPIMkDocs ↗quicopt-python ↗
JuliaQuicoptClientDocumenter ↗QuicoptClient.jl ↗
Rquicopt on r-universepkgdown ↗quicopt-r ↗
Rubyquicopt gemYARD ↗quicopt-ruby ↗

All four are open source under Apache 2.0. Installation: Client libraries.

Python at a glance

from quicopt import Client

client = Client()                 # key from QUICOPT_API_KEY
result = client.solve(model)      # a MathOpt or Pyomo model
print(result.display)
  • Client() — reads your key from QUICOPT_API_KEY (API keys). To pass a key in code, name it: Client(api_key="…"). The first positional argument is the service address, so Client("…") would not set your key. timeout=60.0 is the per-request timeout in seconds.
  • client.solve(model, *, gzip=False) — sends the model and returns a Result. gzip=True compresses the request body, which helps with large models.
  • client.submit(model) — the same without waiting; see async jobs.

The full Python reference, every argument included, is in the MkDocs documentation ↗.

The Result object

FieldTypeMeaning
statusstroptimal · feasible · heuristic · time_limit · iteration_limit · infeasible · unbounded
objectivefloat or Noneobjective value; None when there is no solution
feasiblebool or Nonewhether the returned solution satisfies all constraints; None where that's undefined (e.g. an unconstrained heuristic)
solutiondict[str, float]variable name → value; empty for infeasible/unbounded
model_classstr or Nonehow the service classified the model (lp, qp, milp, qubo, …)
solve_time_secondsfloatsolve wall time
displaystrthe framed console report — print it and you get the result view from the examples, shots block included

With the MathOpt front-end, solution keys are your variable names from the model — name your variables and you can read the result without bookkeeping. The Pyomo front-end currently returns generic x1, x2, … keys.

The shots block

result.display opens with a shots block. The service takes several shots at your model, and the block is one row per shot: the value it reached, how long it took, and ◀ best on the one that won.

├── shots
│   ├── 1 · primal-dual · global   42    0.0s  ◀ best
│   ├── 2 · primal-dual · global   42   0.01s
│   └── 3 · primal                 32   0.01s

Each row is labelled method · certificate. A primal shot only ever holds a solution; a primal-dual shot also maintains a bound on what the best solution could be, which is what puts it in a position to certify. The certificate is what the shot ended up proving: global for a proven optimum, local for a point proven optimal in its own neighbourhood, and no certificate at all where it proved neither — a good value, not a guarantee.

Read the block when you want to know how much weight the answer carries; result.status is the one-word summary of it. How many rows you get depends on the model, so don't count on a fixed number.

Status semantics

  • optimal — the value is certified, and the shots block shows how far the proof reaches: global, or local on a non-convex model where local optimality is what can be proven. feasible: True.
  • heuristic — the best value found across the shots, with nothing in the block certifying it (typical for QUBO); feasible is None when there are no constraints to check.
  • feasible — the solution satisfies every constraint, but no shot proved it optimal.
  • time_limit — the solve ran out of time and returns the best point found; check feasible before you use it.
  • iteration_limit — the solver stopped at its iteration cap and returns the best point found (can happen on hard NLPs); treat it like heuristic.
  • infeasible — no assignment satisfies the constraints; objective is None, solution is empty. This is a regular status, not an error — check result.status rather than catching exceptions. There's a worked example.
  • unbounded — the objective can be improved without limit; also a regular status.

Errors: QuicoptError

Anything the service declines raises QuicoptError with three useful fields:

from quicopt import Client, QuicoptError

try:
    result = client.solve(model)
except QuicoptError as e:
    print(e.reason)     # stable code, e.g. "unsupported_model" or "invalid_key"
    print(e.display)    # the service's readable message
FieldMeaning
reasona stable snake_case code, e.g. unsupported_model for a problem class that isn't solved yet
displaythe framed, human-readable message
status_codethe HTTP status behind it

Remember: infeasible and unbounded are results, not errors — you get a normal Result for those.

Async jobs: client.submit()

For a long solve, don't block — submit the model and poll a job handle:

job = client.submit(model)      # returns immediately

job.status()                    # {"status": "queued" | "running" | "done" | "failed", ...}
result = job.result()           # polls until done, returns the same Result as solve()
print(job.log())                # the solver log so far
job.delete()                    # remove the job and its stored result
MethodMeaning
job.status()the job's state and a tail of its log
job.result(wait=True, timeout=None, poll=0.5)the finished Result; it waits up to 120 s by default (longer if the job has a time limit) and then raises; with wait=False it fetches once and raises if the job isn't done yet
job.log()the job's full plain-text log
job.delete()delete the job and its stored result on the server

submit() takes the same models and the same gzip option as solve().

Questions

Something unclear in a client, or a model that does not behave as expected? Tell us:

Questions? Talk to us.

Whether something's unclear or you want to try Quicopt on your real models — tell us what you're optimizing.

Talk to us →