Skip to content

Rank candidate plans

scan ranks a finite set of existing Plans by objectives such as requested shots, and returns the nondominated ones. Each candidate is a Plan you have already made, so a different Method setting or shot count needs a new Plan. You plan and execute the candidates yourself.

from nwqlib import Expectation, plan, scan, solve
from nwqlib.algorithms import ExpectationMethod
from nwqlib.search import Candidate, Objective

problem = Expectation(state=[1, 0], observable=[[1, 0], [0, -1]])
plans = tuple(plan(problem, method=ExpectationMethod(), shots=n, seed=7)
              for n in (8, 32))
search = scan(tuple(Candidate(p) for p in plans),
              objectives=(Objective(kind="requested_shots"),))
print(search.selection.nondominated)  # (0,)
selected = search.select(0)
result = solve(selected)             # Executes the original 8-shot Plan.

Candidates

Candidate(plan, *, allocation=None, facts=(), reference=None, prior_work=()) wraps an existing Plan and cannot change its problem, output or shots. facts are existing FramedFact values, the error evidence you have for the candidate, and reference is an existing TargetReference. Supplied prior_work entries are kept as given, with their repeats, scope and source. An empty tuple means that no prior work was recorded, not that the prior work cost zero.

Without a device profile, scan uses the logical resource formulas of each Plan. A profile supplies explicit model data and requires an explicit Allocation on every candidate. An Allocation records the granted devices and placement, for the assessment and as a record of the grant. It does not configure backend ranks, threads, devices or scheduler requests, and execution uses the backend you pass to prepare or solve. Memory on separate devices is not pooled (Check device fit and run time).

Objectives

Each objective is minimized:

  • requested_shots: the shots of the Plan's measurement batches plus the separately declared direct readouts of its experiments. This is the planned number of shots. Retries and later adaptive measurements that the Plan does not specify are outside this count, which does not treat them as zero.
  • logical_width: the largest exact peak of logical qubits at one location, for one measurement run. A peak that holds only when the measurement runs execute one after another requires ResourceContext(batch_schedule="serial"), and the value keeps that condition.
  • predicted_seconds: the predicted times of one explicit model_id and timing scope, summed only under an explicit serial schedule. Every measurement run of the Plan needs a matching prediction. Overlapping scopes are not added together. The sum is conditional on its model and carries no confidence guarantee.

Read the ranking

A row whose values are all available is nondominated when no other such row is at least as good on every listed objective and strictly better on at least one, within this supplied set. Equal rows all remain. Rows with an unavailable value keep their reasons in selection.incomparable and are not ranked as zero. With a profile, the comparison also keeps all five assessment fields when available (applicability, capability, capacity, time and accuracy), including for infeasible rows. The nondominated set (the Pareto front of this set) is neither a feasibility filter nor a global optimum. select(index) returns any original row, including a dominated row that you choose deliberately. Execution rejects a blocked row before preparation.

Run a selected row

Passing a selected row to prepare or solve keeps its original PlanEstimate and Allocation on the Run and the Result. Before submission, NWQLib checks that they belong to the circuits being run. It does not plan again, change shots or evaluate the model again. Because the original Plan is kept, the Result, its observations and the forecast all name one Plan by plan_id. Planning again could attach data to a construction the data did not come from. A plain WorkloadEstimate has no physical forecasts and is not converted into one. Passing only the Plan runs the same Plan directly.

Rescore a comparison

Rescore an existing comparison with scan(search.comparison, objectives=...). It reads the stored quantities and forecasts and rejects a replacement profile, context or assessment time. It runs no planning, resource summation, model evaluation, backend call or reference computation. Blocked rows keep their Method, reason and Allocation, with no Plan made up for them. SearchSelection.model_validate_json(...) checks a saved selection, recomputing its nondominated and incomparable rows from the stored values, and validate_comparison(comparison) checks that the selection belongs to that comparison. Neither restores missing numerical data or circuits.

Limits

These limits bound the operations of one scan call, not CPU time or process memory (engineering constants):

Control Default What it bounds
max_candidates 256 Candidates
max_values 4096 Candidate-objective output cells
max_assessments 4096 Assessment and prediction records
max_pair_comparisons 1000000 Coordinate comparisons for the nondominated set
max_integer_bits 4096 Integer size in exact rational arithmetic

The complete set of new forecasts is checked before any model is evaluated. Reading stored forecasts is bounded too, and identical objects are shared instead of copied. For N rows and k objectives, the nondominated set needs at most N*(N-1)*k coordinate comparisons, which are checked against the limit before it is computed. Counts must be integer rationals, and conditional predicted seconds may be fractional.

Within one call, identical forecasts and resource totals are computed once and reused. SearchWork records the evaluations and reuses of the scan separately from supplied prior work and from execution estimates. It does not infer factorization, synthesis or measurement costs that were never recorded.

Source and code

Ranking has no paper source. Nondominance is the definition given above. The code tour lists the code for each step.