Skip to content

Commit

Permalink
Merge branch 'main' into remove/12145/1
Browse files Browse the repository at this point in the history
  • Loading branch information
1ucian0 authored Nov 27, 2024
2 parents 7a18eea + 6979d91 commit d651919
Show file tree
Hide file tree
Showing 19 changed files with 362 additions and 50 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ license = "Apache-2.0"
#
# Each crate can add on specific features freely as it inherits.
[workspace.dependencies]
bytemuck = "1.19"
bytemuck = "1.20"
indexmap.version = "2.6.0"
hashbrown.version = "0.14.5"
num-bigint = "0.4"
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ we use `measure_all(inplace=False)` to get a copy of the circuit in which all th
qc_measured = qc_example.measure_all(inplace=False)

# 3. Execute using the Sampler primitive
from qiskit.primitives.sampler import Sampler
sampler = Sampler()
job = sampler.run(qc_measured, shots=1000)
from qiskit.primitives import StatevectorSampler
sampler = StatevectorSampler()
job = sampler.run([qc_measured], shots=1000)
result = job.result()
print(f" > Quasi probability distribution: {result.quasi_dists}")
print(f" > Counts: {result[0].meas.get_counts()}")
```
Running this will give an outcome similar to `{0: 0.497, 7: 0.503}` which is `000` 50% of the time and `111` 50% of the time up to statistical fluctuations.
Running this will give an outcome similar to `{'000': 497, '111': 503}` which is `000` 50% of the time and `111` 50% of the time up to statistical fluctuations.
To illustrate the power of Estimator, we now use the quantum information toolbox to create the operator $XXY+XYX+YXX-YYY$ and pass it to the `run()` function, along with our quantum circuit. Note the Estimator requires a circuit _**without**_ measurement, so we use the `qc_example` circuit we created earlier.

```python
Expand All @@ -81,17 +81,17 @@ from qiskit.quantum_info import SparsePauliOp
operator = SparsePauliOp.from_list([("XXY", 1), ("XYX", 1), ("YXX", 1), ("YYY", -1)])

# 3. Execute using the Estimator primitive
from qiskit.primitives import Estimator
estimator = Estimator()
job = estimator.run(qc_example, operator, shots=1000)
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
job = estimator.run([(qc_example, operator)], precision=1e-3)
result = job.result()
print(f" > Expectation values: {result.values}")
print(f" > Expectation values: {result[0].data.evs}")
```

Running this will give the outcome `4`. For fun, try to assign a value of +/- 1 to each single-qubit operator X and Y
and see if you can achieve this outcome. (Spoiler alert: this is not possible!)

Using the Qiskit-provided `qiskit.primitives.Sampler` and `qiskit.primitives.Estimator` will not take you very far.
Using the Qiskit-provided `qiskit.primitives.StatevectorSampler` and `qiskit.primitives.StatevectorEstimator` will not take you very far.
The power of quantum computing cannot be simulated on classical computers and you need to use real quantum hardware to scale to larger quantum circuits.
However, running a quantum circuit on hardware requires rewriting to the basis gates and connectivity of the quantum hardware.
The tool that does this is the [transpiler](https://docs.quantum.ibm.com/api/qiskit/transpiler), and Qiskit includes transpiler passes for synthesis, optimization, mapping, and scheduling.
Expand All @@ -106,7 +106,7 @@ qc_transpiled = transpile(qc_example, basis_gates = ['cz', 'sx', 'rz'], coupling
### Executing your code on real quantum hardware

Qiskit provides an abstraction layer that lets users run quantum circuits on hardware from any vendor that provides a compatible interface.
The best way to use Qiskit is with a runtime environment that provides optimized implementations of `sampler` and `estimator` for a given hardware platform. This runtime may involve using pre- and post-processing, such as optimized transpiler passes with error suppression, error mitigation, and, eventually, error correction built in. A runtime implements `qiskit.primitives.BaseSampler` and `qiskit.primitives.BaseEstimator` interfaces. For example,
The best way to use Qiskit is with a runtime environment that provides optimized implementations of `sampler` and `estimator` for a given hardware platform. This runtime may involve using pre- and post-processing, such as optimized transpiler passes with error suppression, error mitigation, and, eventually, error correction built in. A runtime implements `qiskit.primitives.BaseSamplerV2` and `qiskit.primitives.BaseEstimatorV2` interfaces. For example,
some packages that provide implementations of a runtime primitive implementation are:

* https://github.com/Qiskit/qiskit-ibm-runtime
Expand Down Expand Up @@ -165,4 +165,4 @@ We acknowledge partial support for Qiskit development from the DOE Office of Sci

## License

[Apache License 2.0](LICENSE.txt)
[Apache License 2.0](LICENSE.txt)
9 changes: 4 additions & 5 deletions crates/accelerate/src/circuit_library/quantum_volume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use rayon::prelude::*;
use qiskit_circuit::circuit_data::CircuitData;
use qiskit_circuit::imports::UNITARY_GATE;
use qiskit_circuit::operations::Param;
use qiskit_circuit::operations::PyInstruction;
use qiskit_circuit::operations::PyGate;
use qiskit_circuit::packed_instruction::PackedOperation;
use qiskit_circuit::{Clbit, Qubit};
use smallvec::{smallvec, SmallVec};
Expand Down Expand Up @@ -127,17 +127,16 @@ pub fn quantum_volume(
let unitary_gate = UNITARY_GATE
.get_bound(py)
.call((unitary.clone(), py.None(), false), Some(&kwargs))?;
let instruction = PyInstruction {
let instruction = PyGate {
qubits: 2,
clbits: 0,
params: 1,
op_name: "unitary".to_string(),
control_flow: false,
instruction: unitary_gate.unbind(),
gate: unitary_gate.unbind(),
};
let qubit = layer_index * 2;
Ok((
PackedOperation::from_instruction(Box::new(instruction)),
PackedOperation::from_gate(Box::new(instruction)),
smallvec![Param::Obj(unitary.unbind().into())],
vec![permutation[qubit], permutation[qubit + 1]],
vec![],
Expand Down
5 changes: 3 additions & 2 deletions crates/accelerate/src/consolidate_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ const MAX_2Q_DEPTH: usize = 20;

#[allow(clippy::too_many_arguments)]
#[pyfunction]
#[pyo3(signature = (dag, decomposer, force_consolidate, target=None, basis_gates=None, blocks=None, runs=None))]
#[pyo3(signature = (dag, decomposer, basis_gate_name, force_consolidate, target=None, basis_gates=None, blocks=None, runs=None))]
pub(crate) fn consolidate_blocks(
py: Python,
dag: &mut DAGCircuit,
decomposer: &TwoQubitBasisDecomposer,
basis_gate_name: &str,
force_consolidate: bool,
target: Option<&Target>,
basis_gates: Option<HashSet<String>>,
Expand Down Expand Up @@ -125,7 +126,7 @@ pub(crate) fn consolidate_blocks(
let inst = dag.dag()[*node].unwrap_operation();
block_qargs.extend(dag.get_qargs(inst.qubits));
all_block_gates.insert(*node);
if inst.op.name() == decomposer.gate_name() {
if inst.op.name() == basis_gate_name {
basis_count += 1;
}
if !is_supported(
Expand Down
16 changes: 9 additions & 7 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1361,12 +1361,9 @@ impl CircuitData {
let Param::ParameterExpression(expr) = &params[parameter] else {
return Err(inconsistent());
};
params[parameter] = match bind_expr(
expr.bind_borrowed(py),
&param_ob,
value.as_ref(),
true,
)? {
let new_param =
bind_expr(expr.bind_borrowed(py), &param_ob, value.as_ref(), true)?;
params[parameter] = match new_param.clone_ref(py) {
Param::Obj(obj) => {
return Err(CircuitError::new_err(format!(
"bad type after binding for gate '{}': '{}'",
Expand All @@ -1382,8 +1379,13 @@ impl CircuitData {
#[cfg(feature = "cache_pygates")]
{
// Standard gates can all rebuild their definitions, so if the
// cached py_op exists, just clear out any existing cache.
// cached py_op exists, update the `params` attribute and clear out
// any existing cache.
if let Some(borrowed) = previous.py_op.get() {
borrowed
.bind(py)
.getattr(params_attr)?
.set_item(parameter, new_param)?;
borrowed.bind(py).setattr("_definition", py.None())?
}
}
Expand Down
13 changes: 10 additions & 3 deletions qiskit/transpiler/passes/optimization/consolidate_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

"""Replace each block of consecutive gates by a single Unitary node."""
from __future__ import annotations
from math import pi

from qiskit.synthesis.two_qubit import TwoQubitBasisDecomposer
from qiskit.circuit.library.standard_gates import CXGate, CZGate, iSwapGate, ECRGate
from qiskit.circuit.library.standard_gates import CXGate, CZGate, iSwapGate, ECRGate, RXXGate

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.passmanager import PassManager
from qiskit._accelerate.consolidate_blocks import consolidate_blocks
Expand All @@ -27,6 +29,7 @@
"cz": CZGate(),
"iswap": iSwapGate(),
"ecr": ECRGate(),
"rxx": RXXGate(pi / 2),
}


Expand Down Expand Up @@ -70,7 +73,6 @@ def __init__(
if basis_gates is not None:
self.basis_gates = set(basis_gates)
self.force_consolidate = force_consolidate

if kak_basis_gate is not None:
self.decomposer = TwoQubitBasisDecomposer(kak_basis_gate)
elif basis_gates is not None:
Expand All @@ -79,8 +81,12 @@ def __init__(
self.decomposer = TwoQubitBasisDecomposer(
KAK_GATE_NAMES[kak_gates.pop()], basis_fidelity=approximation_degree or 1.0
)
elif "rzx" in basis_gates:
self.decomposer = TwoQubitBasisDecomposer(
CXGate(), basis_fidelity=approximation_degree or 1.0
)
else:
self.decomposer = TwoQubitBasisDecomposer(CXGate())
self.decomposer = None
else:
self.decomposer = TwoQubitBasisDecomposer(CXGate())

Expand All @@ -103,6 +109,7 @@ def run(self, dag):
consolidate_blocks(
dag,
self.decomposer._inner_decomposer,
self.decomposer.gate.name,
self.force_consolidate,
target=self.target,
basis_gates=self.basis_gates,
Expand Down
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/optimization/inverse_cancellation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes.utils import control_flow

from qiskit._accelerate.inverse_cancellation import inverse_cancellation

Expand Down Expand Up @@ -74,6 +75,7 @@ def __init__(self, gates_to_cancel: List[Union[Gate, Tuple[Gate, Gate]]]):

super().__init__()

@control_flow.trivial_recurse
def run(self, dag: DAGCircuit):
"""Run the InverseCancellation pass on `dag`.
Expand Down
5 changes: 3 additions & 2 deletions qiskit/transpiler/passes/synthesis/hls_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,12 @@
- Targeted connectivity
* - ``"rustiq"``
- :class:`~.PauliEvolutionSynthesisRustiq`
- use a diagonalizing Clifford per Pauli term
- use the synthesis method from `Rustiq circuit synthesis library
<https://github.com/smartiel/rustiq-core>`_
- all-to-all
* - ``"default"``
- :class:`~.PauliEvolutionSynthesisDefault`
- use ``rustiq_core`` synthesis library
- use a diagonalizing Clifford per Pauli term
- all-to-all
.. autosummary::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,11 @@ def _parse_basis_gates(basis_gates, backend, inst_map, skip_target):
if inst not in standard_gates and inst not in default_gates:
warnings.warn(
category=DeprecationWarning,
message="Providing custom gates through the ``basis_gates`` argument is deprecated "
"for both ``transpile`` and ``generate_preset_pass_manager`` as of Qiskit 1.3.0. "
message=f"Providing non-standard gates ({inst}) through the ``basis_gates`` "
"argument is deprecated for both ``transpile`` and ``generate_preset_pass_manager`` "
"as of Qiskit 1.3.0. "
"It will be removed in Qiskit 2.0. The ``target`` parameter should be used instead. "
"You can build a target instance using ``Target.from_configuration()`` and provide"
"You can build a target instance using ``Target.from_configuration()`` and provide "
"custom gate definitions with the ``custom_name_mapping`` argument.",
)
skip_target = True
Expand Down
17 changes: 17 additions & 0 deletions releasenotes/notes/fix-cached-params-update-4d2814b698fa76b4.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
fixes:
- |
Fixed a bug in :meth:`.QuantumCircuit.assign_parameters`, occurring when assigning parameters
to standard gates whose definition has already been triggered. In this case, the new values
were not properly propagated to the gate instances. While the circuit itself was still
compiled as expected, inspecting the individual operations would still show the old parameter.
For example::
from qiskit.circuit.library import EfficientSU2
circuit = EfficientSU2(2, flatten=True)
circuit.assign_parameters([1.25] * circuit.num_parameters, inplace=True)
print(circuit.data[0].operation.params) # would print θ[0] instead of 1.25
Fixed `#13478 <https://github.com/Qiskit/qiskit/issues/13478>`__.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fixes:
- |
The transpilation pass :class`.InverseCancellation` now runs inside of flow controlled blocks. Previously, it ignores the pairs of gates in classical blocks that can be cancelled. Refer to `#13437 <https://github.com/Qiskit/qiskit/issues/13437>` for more details.
4 changes: 2 additions & 2 deletions test/benchmarks/mapping_passes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def setup(self, n_qubits, depth):
n_qubits, depth, measure=True, conditional=True, reset=True, seed=seed, max_operands=2
)
self.fresh_dag = circuit_to_dag(self.circuit)
self.basis_gates = ["u1", "u2", "u3", "cx", "iid"]
self.basis_gates = ["u1", "u2", "u3", "cx", "id"]
self.cmap = [
[0, 1],
[1, 0],
Expand Down Expand Up @@ -166,7 +166,7 @@ def setup(self, n_qubits, depth):
n_qubits, depth, measure=True, conditional=True, reset=True, seed=seed, max_operands=2
)
self.fresh_dag = circuit_to_dag(self.circuit)
self.basis_gates = ["u1", "u2", "u3", "cx", "iid"]
self.basis_gates = ["u1", "u2", "u3", "cx", "id"]
self.cmap = [
[0, 1],
[1, 0],
Expand Down
83 changes: 83 additions & 0 deletions test/benchmarks/qasm3_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2024
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=missing-docstring
# pylint: disable=attribute-defined-outside-init

from qiskit.circuit import Parameter
from qiskit import QuantumCircuit
from qiskit import qasm3

from .utils import random_circuit


class RandomBenchmarks:

params = ([20], [256, 1024], [0, 42])

param_names = ["n_qubits", "depth", "seed"]
timeout = 300

def setup(self, n_qubits, depth, seed):
self.circuit = random_circuit(
n_qubits,
depth,
measure=True,
conditional=True,
reset=True,
seed=seed,
max_operands=3,
)

def time_dumps(self, _, __, ___):
qasm3.dumps(self.circuit)


class CustomGateBenchmarks:

params = ([200], [100])

param_names = ["n_qubits", "depth"]
timeout = 300

def setup(self, n_qubits, depth):
custom_gate = QuantumCircuit(2, name="custom_gate")
custom_gate.h(0)
custom_gate.x(1)

qc = QuantumCircuit(n_qubits)
for _ in range(depth):
for i in range(n_qubits - 1):
qc.append(custom_gate.to_gate(), [i, i + 1])
self.circuit = qc

def time_dumps(self, _, __):
qasm3.dumps(self.circuit)


class ParameterizedBenchmarks:

params = ([20, 50], [1, 5, 10])

param_names = ["n_qubits", "n_params"]
timeout = 300

def setup(self, n_qubits, n_params):
qc = QuantumCircuit(n_qubits)
params = [Parameter(f"angle{i}") for i in range(n_params)]
for n in range(n_qubits - 1):
for i in params:
qc.rx(i, n)
self.circuit = qc

def time_dumps(self, _, __):
qasm3.dumps(self.circuit)
Loading

0 comments on commit d651919

Please sign in to comment.