qiskit-ibm-runtime

repository·main·Indexed 42 days ago

https://github.com/qiskit/qiskit-ibm-runtime

IBM Quantum client for IBM Quantum Compute (formerly Qiskit Runtime). It provides optimized execution environments for Qiskit primitives, such as SamplerV2 and EstimatorV2, on IBM Quantum hardware, featuring built-in error suppression and mitigation.

Tokens
22.9K
Snippets
56
Records
92
Agent score
54%

What's inside qiskit-ibm-runtime

  1. Understand how `shape` and input shapes interact

    main

    The shape parameter must be broadcastable from your input extrinsic shapes. The following rules apply:

    1. Alignment: Input shapes must align from the right with shape.
    2. Expansion: Input shapes with size-1 dimensions can expand to match shape.
    3. Randomization Axes: Axes in shape that exceed the input dimensions enumerate additional independent randomizations.
    Input extrinsicshapeResult
    (10,)(10,)10 configurations, 1 randomization each
    (10,)(5, 10)10 configurations, 5 randomizations each
    (10,)(2, 3, 10)10 configurations, 2×3=6 randomizations each
    (4, 1)(4, 5)4 configurations, 5 randomizations each
    (4, 3)(2, 4, 3)4×3=12 configurations, 2 randomizations each
    (4, 3)(2, 1, 3)4×3=12 configurations, 2 randomizations each (the 1 expands to 4)
  2. Understand intrinsic and extrinsic axes for input shapes

    main

    When providing inputs to the executor, shapes are composed of two parts:

    1. Intrinsic axes (rightmost): Determined by the data type. For example, a circuit with $n$ parameters has an intrinsic shape of (n,).
    2. Extrinsic axes (leftmost): Your sweep dimensions. These define how many configurations you want to run.

    Common Input Shapes: | Input type | Intrinsic shape | Example full shape (for 5 configs) | | :--- | :--- | :| | Parameter values ($n$ parameters) | (n,) | (5, n) | | Scalar inputs (e.g., noise scale) | () | (5,) | | Observables | varies | depends on type |

    Example of combining axes: If you have parameter_values with shape (4, 1, 2) (4 configs, 1 noise dimension, 2 parameters) and noise_scale with shape (3,) (3 configs, scalar intrinsic), the extrinsic shapes (4, 1) and (3,) broadcast to (4, 3). The final shape is (4, 3, 2).

  3. Understand the deprecation and removal policy

    main

    To prevent breaking user code, qiskit-ibm-runtime follows a strict deprecation policy:

    • Warning Period: No feature is removed or changed without active warnings for at least three months or two complete version cycles.
    • Guaranteed Alternatives: There will always be a way to achieve valid goals without issuing warnings.
    • Removal Timeline: A feature is removed at least three months after the version containing the warnings was first released. Removal cannot occur in the minor version immediately following the warning version (e.g., if warnings are added in 0.20.0, the earliest removal is 0.22.0).
    • Frozen State: Once a feature is marked as deprecated, it is considered 'frozen'. The maintainers will only provide critical bug fixes and will not add new functionality to it until it is removed.
  4. Use ibm_quantum_schemas for programmatic interfacing

    main

    The ibm_quantum_schemas package provides Pydantic models that define the input and output structures for IBM Quantum primitives and programs. Using these schemas allows for easier and more robust programmatic interfacing with IBM Quantum services by ensuring data conforms to expected formats.

    import ibm_quantum_schemas
    # Use the Pydantic models provided by the package to validate or construct
    # inputs and outputs for IBM Quantum primitives and programs.
  5. Predict output array shapes from input broadcasting

    main

    Output arrays follow the pattern: [Extrinsic Shape] + [Intrinsic Shape].

    • Extrinsic shape: Matches the broadcasted shape of all inputs.
    • Intrinsic shape: Determined by the output type.

    Common Output Intrinsic Shapes:

    Output typeIntrinsic shapeDescription
    Classical register data(num_shots, creg_size)Bitstring data from measurements
    Expectation values() or (n_obs,)Scalar or per-observable

    Example Calculation: If inputs broadcast to an extrinsic shape of (4, 3), and you have a circuit with 1024 shots and a 3-bit classical register:

    • Extrinsic: (4, 3)
    • Intrinsic: (1024, 3)
    • Full Output Shape: (4, 3, 1024, 3)

    To access a specific configuration in the result, index into the extrinsic dimensions: result[0]["meas"][config_idx_0, config_idx_1, :, :].

    # Input extrinsic shapes: (4, 1) and (3,) $\rightarrow$ (4, 3)
    # Output for classical register "meas":
    #   extrinsic: (4, 3)
    #   intrinsic: (1024, 3)  - shots $\times$ bits
    #   full shape: (4, 3, 1024, 3)
    
    result = executor.run(program).result()
    meas_data = result[0]["meas"]
    print(meas_data.shape)  # (4, 3, 1024, 3)
    
    # Access a specific configuration (e.g., index 2, 1)
    config_2_1 = meas_data[2, 1, :, :]  # shape (1024, 3)
  6. Use the `shape` parameter for randomization and broadcasting

    main

    When using a samplex, the executor can perform multiple independent randomizations (e.g., for twirling) for the same circuit configuration. This is controlled by the shape parameter in program.append().

    • Default behavior: If shape is omitted or matches the input extrinsic shape, you get one execution per configuration. While randomized, you do not benefit from averaging over multiple randomizations.
    • Multiple randomizations: To run multiple randomizations per configuration, extend the shape with additional axes. These extra axes represent independent random realizations.
    • Multi-dimensional grids: You can organize randomizations into a multi-dimensional grid (e.g., shape=(2, 14, 10)) to facilitate structured analysis or statistical grouping.
    # 20 randomizations for each of 10 parameter configurations
    program.append(
        template,
        samplex=samplex,
        samplex_arguments={
            "parameter_values": np.random.rand(10, 3),  # extrinsic (10,)
        },
        shape=(20, 10),  # 20 randomizations × 10 configurations
    )
    # Output shape for "meas": (20, 10, num_shots, creg_size)
  7. How broadcasting works in the executor

    main

    Broadcasting is the mechanism used to combine input arrays of different shapes to define a grid of experimental configurations. The executor follows NumPy-style broadcasting rules.

    The Core Rule: Two shapes are compatible if, when aligned from the right, each pair of dimensions is either equal or one of them is 1. A dimension of size 1 is "stretched" to match the other.

    Broadcasting Examples:

    • Shape (5,) and (5,) $\rightarrow$ (5,) (element-wise pairing)
    • Shape (3, 1) and (4,) $\rightarrow$ (3, 4) (3x4 grid of combinations)
    • Shape (2, 1, 5) and (3, 1) $\rightarrow$ (2, 3, 5) (all combinations across dimensions)
    • Shape (3,) and (4,) $\rightarrow$ Error (incompatible shapes)

    Broadcasting only applies to extrinsic axes (the leftmost dimensions representing your sweep). The intrinsic axes (the rightmost dimensions determined by the data type) are always preserved.

  8. Identify the public API of qiskit-ibm-runtime

    main

    The public API consists of all packages, modules, classes, functions, methods, and attributes that are publicly documented in the hosted API documentation.

    Key Rules for Users:

    • Documentation is required: Simply having a docstring in the source code is not enough; the object must be rendered in the official API documentation to be considered public.
    • Import from high-level packages: You should always import objects from the highest-level package that exports them. For example, even if an object is physically located in qiskit_ibm_runtime.json, if it is documented as being in qiskit_ibm_runtime, you should use the latter import path.
    • Experimental features: Some components may be marked as experimental. These are part of the public interface but are not subject to the stability guarantees of semantic versioning and may change without notice.
  9. How Sessions work to reduce queuing delays

    main

    A Session is used when you need to call a primitive (like Estimator) iteratively without incurring queuing delays for every single job. A session starts when the first job within it is submitted, and subsequent jobs within that same session are prioritized by the scheduler.

    Best Practices:

    • Use sessions only when necessary for iterative algorithms (e.g., optimization loops).
    • Avoid running excessive classical code between iterative calls within a session, as this can lock the QPU and consume expensive QPU time.
    • While Sampler can be used in a session, Estimator is the primary use case.

    To use a session, wrap your primitive execution within a with Session(backend=backend): context manager and pass the session object to the primitive's mode parameter.

    from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator
    
    service = QiskitRuntimeService()
    backend = service.least_busy(operational=True, simulator=False)
    
    with Session(backend=backend) as session:
        estimator = Estimator(mode=session)
        # Run iterative jobs here
        job = estimator.run([(isa_circuit, isa_observables, params)])
        print(f"Session ID: {session.session_id}")
  10. How Primitives work in IBM Quantum Compute

    main

    Primitives are the fundamental building blocks for quantum algorithms in this service. They accept Primitive Unified Blocs (PUBs), which are vectorized inputs (typically tuples) that allow grouping multiple circuits with different parameter sets, observables, or both into a single execution.

    IBM Quantum Compute provides optimized implementations of these primitives with built-in error suppression and mitigation features (e.g., readout mitigation, ZNE, PEC).

    Workflow:

    1. Map classical inputs to a quantum problem.
    2. Translate the problem for optimized execution (transpilation).
    3. Execute using a primitive (Sampler or Estimator).
    4. Post-process results into classical format.
  11. How the Executor and QuantumProgram work together

    main

    The Executor is a runtime program used to execute QuantumProgram objects on IBM backends.

    A QuantumProgram is an iterable of QuantumProgramItems, where each item represents a specific task. These tasks can be:

    1. Static circuits: A QuantumCircuit with non-parametrized gates.
    2. Parametrized circuits with fixed values: A parametrized QuantumCircuit paired with an array of parameter values.
    3. Parametrized circuits with randomized values: A parametrized QuantumCircuit paired with a samplomatic.samplex.Samplex object to generate randomized parameter sets.

    You initialize a QuantumProgram by specifying the number of shots to perform for every configuration of each item in the program.

    from qiskit_ibm_runtime.quantum_program import QuantumProgram
    
    # Initialize a program with 1024 shots per task configuration
    program = QuantumProgram(shots=1024)
  12. Manage workload execution using Instances

    main

    Instances are virtual servers that manage your workload execution (quantum programs and classical compute tasks like error mitigation). They are identified by a Cloud Resource Name (CRN).

    When you specify an instance, only backends and jobs belonging to that instance are available. If no instance is specified, the service searches across all instances in your account.

    Instance Selection Logic (if not specified):

    1. If you have only one instance, it is selected by default.
    2. If multiple instances exist, the one that has access to the requested backend is selected.
    3. Otherwise, the first instance with access to the backend is selected.

    You can also influence selection using region and plans_preference parameters.

    from qiskit_ibm_runtime import QiskitRuntimeService
    
    # List all accessible instances
    service = QiskitRuntimeService(channel='ibm_quantum_platform')
    print(service.instances())
    
    # Specify instance at service level (sets default for the service)
    service = QiskitRuntimeService(channel='ibm_quantum_platform', instance="IBM_CLOUD_INSTANCE_1")
    backend1 = service.backend("ibmq_manila")
    
    # Specify instance at backend level (overwrites service-level specification)
    backend2 = service.backend("ibmq_manila", instance="IBM_CLOUD_INSTANCE_2")