Resource estimates¶
Count the qubits, gates, depth, measurements and memory of a planned computation before running it, and read each count with its label. The Estimate resources guide explains the counting rules, and Estimate fault-tolerant resources covers compiled Clifford+T counts and physical projections.
import nwqlib
from nwqlib.resources import ResourceContext
nwqlib.estimate(plan) returns a WorkloadEstimate with one quantity per metric and location. Read one with quantity:
import nwqlib
from nwqlib import Eigenproblem
from nwqlib.algorithms import Lanczos
from nwqlib.resources import ResourceContext
problem = Eigenproblem(A=[[1.5, -1], [-1, 0.5]])
method = Lanczos(initial_state=[1, 0], krylov_dimension=2)
selected = nwqlib.plan(problem, method=method, seed=7)
workload = nwqlib.estimate(selected, context=ResourceContext(basis="cx"))
cx = workload.quantity("cx")
width = workload.quantity("logical_width", location="logical_device")
operations = workload.quantity("operations")
print(cx.interpretation, cx.fact.value.numerator, cx.fact.unit.symbol)
print(width.interpretation, width.fact.value.numerator)
print(operations.interpretation)
upper_bound 3 count
exact 2
unavailable
The Plan uses at most 3 CX gates and exactly 2 qubits. No rule of its circuit blocks gives a total operation count in this basis, so that count is unavailable rather than zero, and operations.fact.reason says why. print(workload) lists every quantity with its label and conditions. Each quantity also keeps its evidence, the kind of basis of the cost rules it came from, for example a proof, a numerical estimate, an observation or an assertion.
An estimate describes the planned logical circuit. It is not a count of compiled gates, which Prepared.inspect_resources gives for a prepared circuit, and it includes no routing, error correction or provider billing.
Estimate a plan¶
estimate ¶
estimate(construction: SelectedConstruction, *, context: ResourceContext | None = None, memo: dict | None = None) -> WorkloadEstimate
Estimate the resources of a planned circuit description without building or running it.
Most callers use nwqlib.estimate(plan),
which calls this function on plan.construction. The estimate walks the
circuit description once per distinct context and keeps symbolic costs
in a compact expression table. It reads no numerical input data,
synthesizes no gates and does not unroll repeated parts. Its work is
limited in proportion to the size checks of the circuit description
(engineering
constants).
The result depends only on the construction and the context. A caller
that estimates several points of one Plan can pass one memo
dictionary for the duration of its operation, and an equal pair of
construction and context then returns the estimate already computed.
The memo dictionary belongs to that caller, and there is no
process-wide cache.
Parameters:
-
construction(SelectedConstruction) –The planned circuit description,
plan.construction. -
context(ResourceContext | None, default:None) –Gate basis, rotation precision, synthesis, resident data and batch schedule.
None, the default, meansResourceContext(): the"selected_logical"basis, no resident data and an unspecified schedule. -
memo(dict | None, default:None) –Optional dictionary owned by the caller, mapping
(construction.content_id, context.content_id)to the estimate already computed for that pair.
Returns:
-
workload(WorkloadEstimate) –One quantity per metric and location. Every quantity is
"planned"and keeps its label, location and original evidence. A metric without an applicable cost rule is present as unavailable, not as zero.
WorkloadEstimate ¶
Bases: Record
Resource estimate of a planned computation: one quantity per metric and location.
nwqlib.estimate(plan) returns it when no
device profile is given, and so does estimate.
Read one metric with quantity(metric), or a width or byte peak with
quantity(metric, location=...), and list everything with quantities
or print(workload). A metric without a cost rule is present and
labeled unavailable, never zero. The estimate is of the logical
circuit description, before compilation, routing or error correction.
exact_evaluations counts grouped exact-statistic requests, not returned
labels, bins or simulator trajectories. A circuit with no terminal
measurement batch declares no measurements, so zero shots and
settings there do not mean that running it is free. The fields below
are read-only.
Attributes:
-
construction_id(ContentID) –Content hash of the planned circuit description that was estimated.
-
context(ResourceContext) –The
ResourceContextthat fixed basis, precision, synthesis and schedule. -
quantities(tuple[ResourceQuantity, ...]) –One
ResourceQuantityper (metric, location), including unavailable ones. -
expressions(tuple[Expression, ...]) –Symbolic cost expressions that symbolic quantities name. Repeats and shared parts are not expanded.
-
parameters(tuple[Parameter, ...]) –Circuit parameters those expressions may use.
-
sources(tuple[Evidence, ...]) –The original evidence records of the cost rules. The arithmetic of the estimate does not make them verified.
-
defined_selections(tuple[ContentID, ...]) –Content hashes of the circuit blocks and classical kernels the run may reach, not a record of built circuits.
-
work_units(NonnegativeInt) –Bookkeeping work of the estimate, not run time, at most
24 * limits.max_steps. -
evaluated_contexts(NonnegativeInt) –Distinct node and call contexts evaluated.
-
limits(AdmissionLimits) –The size limits of the planned circuit description.
-
assumptions(tuple[Text, ...]) –Conditions of the whole estimate, such as
"logical dependency schedule; no physical QEC/routing/factory projection".
Examples:
A two-dimensional Lanczos Plan, estimated in the CX basis, has an exact width of two qubits and an upper bound of three CX gates. No rule gives its total operation count:
>>> import nwqlib
>>> from nwqlib import Eigenproblem
>>> from nwqlib.algorithms import Lanczos
>>> from nwqlib.resources import ResourceContext
>>> problem = Eigenproblem(A=[[1.5, -1], [-1, 0.5]])
>>> method = Lanczos(initial_state=[1, 0], krylov_dimension=2)
>>> selected = nwqlib.plan(problem, method=method, seed=7)
>>> context = ResourceContext(basis="cx")
>>> workload = nwqlib.estimate(selected, context=context)
>>> cx = workload.quantity("cx")
>>> cx.interpretation, cx.fact.value.numerator
('upper_bound', 3)
>>> width = workload.quantity("logical_width", location="logical_device")
>>> width.interpretation, width.fact.value.numerator
('exact', 2)
>>> workload.quantity("operations").interpretation
'unavailable'
quantity ¶
quantity(metric: str, *, location: str | None = None) -> ResourceQuantity
Return the quantity of one metric at one location, without recomputing anything.
Parameters:
-
metric(str) –Metric name, such as
"cx"or"logical_width". -
location(str | None, default:None) –Location of a width or byte peak, for example
"logical_device"or"host".None, the default, selects a total.
Returns:
-
quantity(ResourceQuantity) –The stored quantity.
Raises:
-
ValueError–If no quantity has this metric and location. The message lists up to eight locations of the metric.
ResourceContext ¶
Bases: Record
Settings that fix how a resource estimate counts: gate basis, rotation precision, synthesis, resident data and schedule.
Build it with keyword arguments, for example ResourceContext(basis="cx"),
and pass it as context= to nwqlib.estimate
or estimate. Every argument is
optional, and ResourceContext() counts in the "selected_logical"
basis.
Attributes:
-
basis(MetricBasis) –Default
"selected_logical". Basis in which gate metrics are counted:"selected_logical","cx","clifford_t"or"toffoli". Counts in different bases answer different questions and are never converted into or added to each other. -
precision(Float64 | None) –Default
None. Positive rotation synthesis precision. An arbitrary rotation gets a T count only when the precision and the synthesis are known. -
synthesis(Source | None) –Default
None. Source naming the synthesis method. A block'sResourceLawapplies only under the same choice. -
resident(tuple[Workspace, ...]) –Default
().Workspaceentries that stay in memory for the whole workload and are added to every memory peak. -
capacities(tuple[Limit, ...]) –Default
(). Device capacities asLimitrecords, kept for a later device assessment. The estimate itself allocates nothing and queries no device. -
batch_schedule(Literal['unspecified', 'serial']) –Default
"unspecified"."serial"declares that independent circuit batches run one at a time, so a batch memory peak holds without condition. With"unspecified"such a peak is labeledconditional. The required schedule stays recorded for the execution to follow.
Raises:
-
ValueError–If
precisionis not positive.
Read a quantity¶
Each ResourceQuantity holds its value in fact.value: an exact Rational (read numerator and denominator) or a Float64 (read value). Its interpretation says how to read that value:
interpretation |
Meaning |
|---|---|
exact |
The exact count |
upper_bound |
The count is at most this value, for example a CX bound before gate cancellation and routing, or the maximum over the arms of a branch |
estimate |
Derived from observed, empirically predicted or numerically estimated evidence |
conditional |
Holds only under a stated condition, such as a memory peak that needs independent circuit batches to run one at a time (required_schedule) |
unavailable |
No applicable rule. fact.reason says why, and the value is never read as zero |
An exact count or upper bound is only as strong as its evidence, fact.evidence.kind, so a count derived from an asserted rule stays an assertion. The conditions of a value are in fact.assumptions.
Totals over the whole run have location=None. Widths and memory are peaks at a location, so read them with quantity(metric, location=...):
| Metrics | Value | Unit |
|---|---|---|
Gates: operations, single_qubit, two_qubit, controlled, global_phases, clifford, t, toffoli, ccz, arbitrary_rotations, cx |
Whole run | count |
Depths: logical_depth, t_depth, non_clifford_depth |
Whole run | count |
Calls and measurement: calls, measurements, resets, settings, shots, exact_evaluations, unique_settings, root_setting_declarations, root_repetitions, adaptive_rounds, expected_operations |
Whole run | count |
Work: classical_work, construction_work, preparation_components |
Whole run | count |
Qubits: logical_width, system, clean_ancilla, dirty_ancilla at a register's location |
Peak | count |
Classical registers: classical_registers, classical_bits at "host" |
Peak | count |
Memory: memory, known_memory, input_bytes, analysis_bytes, io_bytes, materialization_bytes, stored_bytes at a workspace's location |
Peak | byte |
The Estimate resources guide defines what shots, settings, exact_evaluations and the other measurement counts include.
ResourceQuantity ¶
Bases: Record
One count or peak of a resource estimate, with its value, label and sources.
WorkloadEstimate.quantity
returns it. The value is fact.value, an exact Rational (read
numerator and denominator) or a Float64, in the unit fact.unit:
count, or byte for byte metrics. A symbolic value is
fact.symbol, and an unavailable one has fact.reason and no value.
interpretation says how to read the value (see Read a
quantity). print(quantity) shows the value, label
and conditions. The fields below are read-only.
Attributes:
-
metric(Metric) –Name of the quantity, such as
cx,shotsorlogical_width. -
fact(Fact) –The value, or the reason it is unavailable, with its unit, scope, conditions (
fact.assumptions) and the weakest evidence kind among the sources that determined it. -
population(Text) –What is counted, for example
"dynamic workload"for a total over the whole run or"simultaneous live footprint"for a peak. -
lifecycle(Literal['planned', 'prepared', 'submitted', 'observed']) –Workflow stage of the quantity:
"planned","prepared","submitted"or"observed". A planning estimate gives only"planned". -
basis(Text) –Gate basis of the estimate.
-
interpretation(Interpretation) –"exact","upper_bound","estimate","conditional"or"unavailable". -
location(Text | None) –Location of a width or byte peak,
Nonefor a total. -
sources(tuple[ContentID, ...]) –Content hashes of all evidence attached to the value, including evidence used only to check a gate recipe against a declared rule.
-
derivation_sources(tuple[ContentID, ...]) –Content hashes of the evidence that determined the value, a subset of
sources. -
required_schedule(Literal['serial_acquisitions'] | None) –"serial_acquisitions"when this peak holds only if independent circuit batches run one at a time, otherwiseNone. Follow it before comparing the peak with a device capacity.
Raises:
-
ValueError–When a record is loaded whose label disagrees with its availability, whose value is negative, not real or, when exact, not an integer (
expected_operationsmay be fractional), whose unit does not match the metric, or whose derivation sources are not among its sources.
Declare costs and memory¶
A circuit block declares its own costs and memory with these records. The estimate reads them and creates no allocation.
ResourceLaw ¶
Bases: Record
The cost of one call of a circuit block in one metric, as the block declares it.
A circuit block of a Plan can declare cost rules for its metrics. The
estimate uses a rule only for a call whose control and adjoint flags, cost
parameters and call arguments, basis, precision and synthesis all match.
The value is a number per call, not a formula, so the estimate can check
it against a known gate recipe and keep its source without running
caller code. Rules in different bases are alternatives, not additions.
Build it with keyword arguments when you write a block (see Compose
blocks). metric, basis, value, interpretation and
evidence are required.
Attributes:
-
metric(Metric) –Required. The count metric the rule supplies.
-
basis(MetricBasis) –Required. Basis the value is counted in.
-
value(NonnegativeInt | Float64) –Required. Nonnegative per-call value, an integer when
interpretationis"exact". -
interpretation(Literal['exact', 'upper_bound', 'estimate']) –Required.
"exact","upper_bound"or"estimate". -
evidence(Evidence) –Required. Where the value comes from. Observed, empirically predicted or numerically estimated evidence makes every quantity derived from it an estimate.
-
bindings(tuple[Binding, ...]) –Default
(). Cost parameters and call arguments that must match exactly for the rule to apply. -
unbound_parameters(tuple[Text, ...]) –Default
(). Formal parameters whose whole accepted range the rule covers instead of one value. Empty means exact matching only. -
controlled(StrictBool) –Default
False. Whether the rule is for the controlled block. -
adjoint(StrictBool) –Default
False. Whether the rule is for the adjoint block. -
precision(Float64 | None) –Default
None. Rotation precision the rule assumes. -
synthesis(Source | None) –Default
None. Synthesis method the rule assumes. -
assumptions(tuple[Text, ...]) –Default
(). Conditions carried into every quantity derived from the rule.
Raises:
-
ValueError–If the value is negative, an exact value is not an integer, a binding is repeated, or a parameter is both bound and listed in
unbound_parameters.
Workspace ¶
Bases: Record
Bytes needed at the same time at one location, as declared by a block or the caller.
A block's workspace lasts for each call. Entries in
ResourceContext.resident last for the whole workload. Sizes are
declarations, not observed allocations, and quantum widths are counted
separately. Build it with keyword arguments. All four fields are
required.
Attributes:
-
location(Text) –Where the bytes live, for example
"host". -
purpose(Literal['input', 'preparation', 'analysis', 'io', 'materialization', 'stored', 'workspace']) –"input","preparation","analysis","io","materialization","stored"or"workspace". -
bytes(NonnegativeInt | None) –Nonnegative byte count, or
Nonewhen unknown. An unknown size makes the memory peak at that location unavailable, never zero. -
source(Source) –Source of the declaration.