Primitives

Contents

Primitives#

See Primitives for how they are used. Each primitive’s run() hands back a job, the job’s result() a result with one entry per input circuit.

Estimator#

class QCut.CutEstimator(backend)[source]#

Bases: object

Expectation values of cut circuits, run on any backend or sampler.

Shaped like FiQCI-EMS’s estimator: made once for a backend, then run() with the cut circuits and the observables to estimate on them. A cut circuit comes from QCut.get_locations_and_subcircuits() or QCut.find_cuts(), and is run with the options it was made with.

The estimator does not transpile, much as qiskit’s primitives take circuits already written for their target. For a backend with a coupling map, transpile each cut circuit for that same backend with QCut.transpile_subcircuits() first.

Parameters:

backend – the backend or V2 sampler to run on. A sampler, like a backend without a coupling map, takes the circuits as they are.

run(circuits: CutCircuit | list[CutCircuit], observables: str | Pauli | SparsePauliOp | SparseObservable | Mapping[str | Pauli, float] | _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | _NestedSequence[complex | bytes | str] | list[str | Pauli | SparsePauliOp | SparseObservable | Mapping[str | Pauli, float] | _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | _NestedSequence[complex | bytes | str]], shots: int = 4096, max_batch_size: int = 100, parameter_values: dict | list[dict] | None = None, **run_options) → CutEstimatorJob[source]#

Submit the circuits and return without waiting for their results.

Parameters:
  • circuits – a cut circuit or a list of them.

  • observables – what to estimate, taken as qiskit’s estimator takes them – a label, a Pauli, a SparsePauliOp, a SparseObservable, a {label: coefficient} mapping, or a nested sequence of those. One circuit takes them as given. With a list of circuits, give a list with one entry per circuit, or one observable for every circuit.

  • shots (int) – shots per experiment circuit, as in QCut.submit_experiments().

  • max_batch_size (int) – the most circuits submitted in one run call. Each input circuit is submitted in batches of its own.

  • parameter_values – {parameter or name: value} to bind into the circuit, paired with the circuits the way observables is. For a sweep, list the same circuit once per set of values.

  • **run_options – passed on to every run call on the backend.

Returns:

a handle on every circuit’s experiment, whose result() gives the expectation values.

Return type:

CutEstimatorJob

Raises:
  • QCutError – something other than a cut circuit was given, one is not transpiled for this backend although it has a coupling map, or one has parameters left unbound.

  • ValueError – the observables or parameter values do not have one entry per circuit, or the observables do not fit their circuit.

estimate(circuits: CutCircuit | list[CutCircuit], observables: str | Pauli | SparsePauliOp | SparseObservable | Mapping[str | Pauli, float] | _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | _NestedSequence[complex | bytes | str] | list[str | Pauli | SparsePauliOp | SparseObservable | Mapping[str | Pauli, float] | _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | _NestedSequence[complex | bytes | str]], shots: int = 4096, max_batch_size: int = 100, parameter_values: dict | list[dict] | None = None) → CutRunEstimate[source]#

Work out what run() will cost with the same arguments, submitting nothing.

Returns:

the jobs, circuits and shots of the whole run, and indexed by circuit, each circuit’s own share.

Return type:

CutRunEstimate

class QCut.primitives.CutEstimatorJob(jobs: list[QCutJob])[source]#

Bases: QCutPrimitiveJob[CutEstimatorResult]

A CutEstimator run, whose result() is a CutEstimatorResult.

expectation_values(index: int | None = None) → list[ndarray] | ndarray[source]#

Wait for the run and return the expectation values.

Parameters:

index (int) – the input circuit to return them for, or None for every circuit’s, in the order the circuits were given.

standard_errors(index: int | None = None) → list[ndarray] | ndarray[source]#

Wait for the run and return the standard errors.

Parameters:

index (int) – the input circuit to return them for, or None for every circuit’s, in the order the circuits were given.

class QCut.primitives.CutEstimatorResult(entries: list)[source]#

Bases: Sequence

What a CutEstimator run gives back: one entry per input circuit.

expectation_values() → list[ndarray][source]#

Every circuit’s expectation values, in the order the circuits were given.

standard_errors() → list[ndarray][source]#

Every circuit’s standard errors, in the order the circuits were given.

class QCut.primitives.CutEstimatorEntry(expectation_values: ndarray, standard_errors: ndarray, raw: RawResult)[source]#

Bases: object

The expectation values of one input circuit.

expectation_values: ndarray#

One value per observable, shaped like the observables given for the circuit. A single observable gives a zero-dimensional array.

standard_errors: ndarray#

An upper bound on each value’s shot noise, shaped the same. See QCut.estimate_standard_errors().

raw: RawResult#

The raw counts the values were estimated from.

__init__(expectation_values: ndarray, standard_errors: ndarray, raw: RawResult) → None#

Sampler#

class QCut.CutQuasiSampler(backend)[source]#

Bases: object

Quasi-probability distributions of cut circuits, run on any backend or sampler.

Shaped like FiQCI-EMS’s sampler, but what comes back is reconstructed rather than counted: a quasi-probability distribution over the measured bits, which can dip below zero by the estimate’s noise. The qubits are the ones the circuit measured at its end before it was cut, and the bitstrings follow qiskit’s convention, classical bit zero rightmost.

A cut circuit comes from QCut.get_locations_and_subcircuits() or QCut.find_cuts(), and is run with the options it was made with. The sampler does not transpile: for a backend with a coupling map, transpile each cut circuit for that same backend with QCut.transpile_subcircuits() first.

Parameters:

backend – the backend or V2 sampler to run on. A sampler, like a backend without a coupling map, takes the circuits as they are.

run(circuits: CutCircuit | list[CutCircuit], shots: int = 4096, max_batch_size: int = 100, parameter_values: dict | list[dict] | None = None, **run_options) → CutQuasiSamplerJob[source]#

Submit the circuits and return without waiting for their results.

Parameters:
  • circuits – a cut circuit or a list of them, each split from a circuit that ended in the measurements whose distribution is wanted, measuring into every classical bit exactly once.

  • shots (int) – shots per experiment circuit, as in QCut.submit_experiments().

  • max_batch_size (int) – the most circuits submitted in one run call. Each input circuit is submitted in batches of its own.

  • parameter_values – {parameter or name: value} to bind into the circuit. With a list of circuits, a list with one entry per circuit, or one for every circuit. For a sweep, list the same circuit once per set of values.

  • **run_options – passed on to every run call on the backend.

Returns:

a handle on every circuit’s experiment, whose result() gives the distributions.

Return type:

CutQuasiSamplerJob

Raises:
  • QCutError – something other than a cut circuit was given, one is not transpiled for this backend although it has a coupling map, has parameters left unbound, or its final measurements do not fill its classical bits once each.

  • ValueError – the parameter values do not have one entry per circuit.

estimate(circuits: CutCircuit | list[CutCircuit], shots: int = 4096, max_batch_size: int = 100, parameter_values: dict | list[dict] | None = None) → CutRunEstimate[source]#

Work out what run() will cost with the same arguments, submitting nothing.

Returns:

the jobs, circuits and shots of the whole run, and indexed by circuit, each circuit’s own share.

Return type:

CutRunEstimate

class QCut.primitives.CutQuasiSamplerJob(jobs: list[QCutJob])[source]#

Bases: QCutPrimitiveJob[CutQuasiSamplerResult]

A CutQuasiSampler run. result() gives a CutQuasiSamplerResult.

quasi_probabilities(index: int | None = None) → list[QuasiProbabilities] | QuasiProbabilities[source]#

Wait for the run and return the distributions.

Parameters:

index (int) – the input circuit to return it for, or None for every circuit’s, in the order the circuits were given.

standard_errors(index: int | None = None) → list[float | None] | float | None[source]#

Wait for the run and return the standard errors.

Parameters:

index (int) – the input circuit to return it for, or None for every circuit’s, in the order the circuits were given.

class QCut.primitives.CutQuasiSamplerResult(entries: list)[source]#

Bases: Sequence

What a CutQuasiSampler run gives back: one entry per input circuit.

quasi_probabilities() → list[QuasiProbabilities][source]#

Every circuit’s distribution, in the order the circuits were given.

quasi_dists(top: int | None = None) → list[QuasiDistribution][source]#

Every circuit’s distribution as a qiskit QuasiDistribution.

Parameters:

top (int) – as in CutQuasiSamplerEntry.quasi_dist().

standard_errors() → list[float | None][source]#

Every circuit’s standard error, in the order the circuits were given.

class QCut.primitives.CutQuasiSamplerEntry(quasi_probabilities: QuasiProbabilities, raw: RawResult)[source]#

Bases: object

The reconstructed distribution of one input circuit.

quasi_probabilities: QuasiProbabilities#

The distribution over the measured bits, built lazily.

raw: RawResult#

The raw counts it was reconstructed from.

property standard_error: float | None#

An upper bound on each quasi-probability’s shot noise, the same for all.

See QCut.QuasiProbabilities.standard_error.

quasi_dist(top: int | None = None) → QuasiDistribution[source]#

The distribution as a qiskit QuasiDistribution.

Parameters:

top (int) – keep only this many of the most likely outcomes. None, the default, keeps all 2**n of them, which for many measured bits is expensive; quasi_probabilities answers without expanding.

__init__(quasi_probabilities: QuasiProbabilities, raw: RawResult) → None#

Shared#

class QCut.primitives.QCutPrimitiveJob(jobs: list[QCutJob])[source]#

Bases: Generic[R]

A primitive’s run, handed back as soon as everything is submitted.

Shaped like a qiskit job. Each input circuit is one experiment with a QCutJob of its own, in jobs, and this answers for all of them together: the ids and statuses cover every circuit, and cancel() cancels every circuit.

result() waits for every circuit and builds the primitive’s own result, which is what the estimator and the sampler differ in. raw_results() gives the raw counts behind it.

__init__(jobs: list[QCutJob]) → None[source]#
property jobs: list[QCutJob]#

The job of each input circuit, in the order the circuits were given.

property experiments: list#

The experiment each input circuit was cut into.

property num_circuits: int#

How many experiment circuits have been submitted so far, over every input.

property total_shots: int#

How many shots have been submitted so far, over every input.

job_id() → str | None[source]#

The first backend job’s id, None if nothing was submitted.

job_ids() → list[str | None][source]#

Every backend job’s id, circuit by circuit, in submission order.

statuses() → list[JobStatus][source]#

Each input circuit’s status, in the order the circuits were given.

status() → JobStatus[source]#

One status for the whole run.

A circuit failing makes it ERROR, then one being cancelled CANCELLED. Otherwise it is DONE only once every circuit is.

done() → bool[source]#

Whether every circuit finished successfully.

running() → bool[source]#

Whether any circuit is running, or has waves left to submit.

cancelled() → bool[source]#

Whether the run was cancelled.

in_final_state() → bool[source]#

Whether the run is done, failed or cancelled.

wait_for_final_state(timeout: float | None = None, wait: float = 5.0) → None[source]#

Poll until the run reaches a final state.

Parameters:
  • timeout (float) – seconds to wait before giving up, or None to wait as long as it takes.

  • wait (float) – seconds between polls.

Raises:

JobTimeoutError – the run was not finished within timeout.

cancel() → None[source]#

Cancel every circuit’s backend jobs, and submit no further waves.

partial_results() → list[list[PartialBatch]][source]#

Each input circuit’s batches as they stand, with results once they are in.

raw_results(timeout: float | None = None) → list[RawResult][source]#

Wait for every circuit and return its raw counts.

Parameters:

timeout (float) – seconds to wait for the whole run before giving up, or None to wait as long as it takes.

Returns:

one per input circuit, in the order they were given.

Return type:

list[RawResult]

Raises:
  • JobTimeoutError – the run was not finished within timeout.

  • QCutError – the run was cancelled.

  • Exception – whatever a refused batch, a failed job or a failed wave raised.

result(timeout: float | None = None) → R[source]#

Wait for every circuit and return the primitive’s result, built once.

Parameters:

timeout (float) – seconds to wait for the whole run before giving up, or None to wait as long as it takes.

Raises:
  • JobTimeoutError – the run was not finished within timeout.

  • QCutError – the run was cancelled.

  • Exception – whatever a refused batch, a failed job or a failed wave raised.

class QCut.primitives.CutRunEstimate(breakdown: dict[str, JobEstimate]=<factory>, exact: bool = True, inputs: tuple[RunEstimate, ...]=())[source]#

Bases: RunEstimate

What a primitive’s run will cost, over every circuit and for each one.

The totals and breakdown cover the whole run, numbered in submission order. Indexing gives the RunEstimate of one input circuit, in the order the circuits were given, and len counts the circuits.

inputs: tuple[RunEstimate, ...] = ()#
__init__(breakdown: dict[str, JobEstimate]=<factory>, exact: bool = True, inputs: tuple[RunEstimate, ...]=()) → None#