Skip to content

Commit

Permalink
Merge branch 'Qiskit:main' into move-basis-translator
Browse files Browse the repository at this point in the history
  • Loading branch information
raynelfss authored Sep 19, 2024
2 parents b9668c1 + 4c64a64 commit d233aa9
Show file tree
Hide file tree
Showing 31 changed files with 249 additions and 66 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 crates/accelerate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ itertools.workspace = true
qiskit-circuit.workspace = true
thiserror.workspace = true
ndarray_einsum_beta = "0.7"
once_cell = "1.19.0"
once_cell = "1.20.0"

[dependencies.smallvec]
workspace = true
Expand Down
53 changes: 47 additions & 6 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use approx::relative_eq;
use std::f64::consts::PI;
use std::vec;

use crate::circuit_data::CircuitData;
use crate::circuit_instruction::ExtraInstructionAttributes;
Expand Down Expand Up @@ -163,6 +164,7 @@ pub trait Operation {
fn num_clbits(&self) -> u32;
fn num_params(&self) -> u32;
fn control_flow(&self) -> bool;
fn blocks(&self) -> Vec<CircuitData>;
fn matrix(&self, params: &[Param]) -> Option<Array2<Complex64>>;
fn definition(&self, params: &[Param]) -> Option<CircuitData>;
fn standard_gate(&self) -> Option<StandardGate>;
Expand Down Expand Up @@ -228,6 +230,15 @@ impl<'a> Operation for OperationRef<'a> {
}
}
#[inline]
fn blocks(&self) -> Vec<CircuitData> {
match self {
OperationRef::Standard(standard) => standard.blocks(),
OperationRef::Gate(gate) => gate.blocks(),
OperationRef::Instruction(instruction) => instruction.blocks(),
OperationRef::Operation(operation) => operation.blocks(),
}
}
#[inline]
fn matrix(&self, params: &[Param]) -> Option<Array2<Complex64>> {
match self {
Self::Standard(standard) => standard.matrix(params),
Expand Down Expand Up @@ -530,20 +541,20 @@ impl Operation for StandardGate {
STANDARD_GATE_NUM_QUBITS[*self as usize]
}

fn num_params(&self) -> u32 {
STANDARD_GATE_NUM_PARAMS[*self as usize]
}

fn num_clbits(&self) -> u32 {
0
}

fn num_params(&self) -> u32 {
STANDARD_GATE_NUM_PARAMS[*self as usize]
}

fn control_flow(&self) -> bool {
false
}

fn directive(&self) -> bool {
false
fn blocks(&self) -> Vec<CircuitData> {
vec![]
}

fn matrix(&self, params: &[Param]) -> Option<Array2<Complex64>> {
Expand Down Expand Up @@ -2043,6 +2054,10 @@ impl Operation for StandardGate {
fn standard_gate(&self) -> Option<StandardGate> {
Some(*self)
}

fn directive(&self) -> bool {
false
}
}

const FLOAT_ZERO: Param = Param::Float(0.0);
Expand Down Expand Up @@ -2145,6 +2160,26 @@ impl Operation for PyInstruction {
fn control_flow(&self) -> bool {
self.control_flow
}
fn blocks(&self) -> Vec<CircuitData> {
if !self.control_flow {
return vec![];
}
Python::with_gil(|py| -> Vec<CircuitData> {
// We expect that if PyInstruction::control_flow is true then the operation WILL
// have a 'blocks' attribute which is a tuple of the Python QuantumCircuit.
let raw_blocks = self.instruction.getattr(py, "blocks").unwrap();
let blocks: &Bound<PyTuple> = raw_blocks.downcast_bound::<PyTuple>(py).unwrap();
blocks
.iter()
.map(|b| {
b.getattr(intern!(py, "_data"))
.unwrap()
.extract::<CircuitData>()
.unwrap()
})
.collect()
})
}
fn matrix(&self, _params: &[Param]) -> Option<Array2<Complex64>> {
None
}
Expand Down Expand Up @@ -2211,6 +2246,9 @@ impl Operation for PyGate {
fn control_flow(&self) -> bool {
false
}
fn blocks(&self) -> Vec<CircuitData> {
vec![]
}
fn matrix(&self, _params: &[Param]) -> Option<Array2<Complex64>> {
Python::with_gil(|py| -> Option<Array2<Complex64>> {
match self.gate.getattr(py, intern!(py, "to_matrix")) {
Expand Down Expand Up @@ -2287,6 +2325,9 @@ impl Operation for PyOperation {
fn control_flow(&self) -> bool {
false
}
fn blocks(&self) -> Vec<CircuitData> {
vec![]
}
fn matrix(&self, _params: &[Param]) -> Option<Array2<Complex64>> {
None
}
Expand Down
4 changes: 4 additions & 0 deletions crates/circuit/src/packed_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@ impl Operation for PackedOperation {
self.view().control_flow()
}
#[inline]
fn blocks(&self) -> Vec<CircuitData> {
self.view().blocks()
}
#[inline]
fn matrix(&self, params: &[Param]) -> Option<Array2<Complex64>> {
self.view().matrix(params)
}
Expand Down
13 changes: 12 additions & 1 deletion crates/qasm3/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,23 @@ register_type!(PyClassicalRegister);

/// Information received from Python space about how to construct a Python-space object to
/// represent a given gate that might be declared.
#[pyclass(module = "qiskit._accelerate.qasm3", frozen, name = "CustomGate")]
#[pyclass(
module = "qiskit._accelerate.qasm3",
frozen,
name = "CustomGate",
get_all
)]
#[derive(Clone, Debug)]
pub struct PyGate {
/// A callable Python object that takes ``num_params`` angles as positional arguments, and
/// returns a :class:`~.circuit.Gate` object representing the gate.
constructor: Py<PyAny>,
/// The name of the gate as it appears in the OpenQASM 3 program. This is not necessarily
/// identical to the name that Qiskit gives the gate.
name: String,
/// The number of angle-like parameters the gate requires.
num_params: usize,
/// The number of qubits the gate acts on.
num_qubits: usize,
}

Expand Down
4 changes: 2 additions & 2 deletions crates/qasm3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use crate::error::QASM3ImporterError;
/// :class:`.QuantumCircuit`: the constructed circuit object.
///
/// Raises:
/// :class:`.QASM3ImporterError`: if an error occurred during parsing or semantic analysis.
/// :exc:`.QASM3ImporterError`: if an error occurred during parsing or semantic analysis.
/// In the case of a parsing error, most of the error messages are printed to the terminal
/// and formatted, for better legibility.
#[pyfunction]
Expand Down Expand Up @@ -120,7 +120,7 @@ pub fn loads(
/// :class:`.QuantumCircuit`: the constructed circuit object.
///
/// Raises:
/// :class:`.QASM3ImporterError`: if an error occurred during parsing or semantic analysis.
/// :exc:`.QASM3ImporterError`: if an error occurred during parsing or semantic analysis.
/// In the case of a parsing error, most of the error messages are printed to the terminal
/// and formatted, for better legibility.
#[pyfunction]
Expand Down
1 change: 1 addition & 0 deletions qiskit/circuit/library/pauli_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class PauliEvolutionGate(Gate):
X = SparsePauliOp("X")
Z = SparsePauliOp("Z")
I = SparsePauliOp("I")
# build the evolution gate
operator = (Z ^ Z) - 0.1 * (X ^ I)
Expand Down
4 changes: 2 additions & 2 deletions qiskit/primitives/base/base_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class BaseEstimatorV1(BasePrimitive, Generic[T]):
# calculate [ <psi1(theta1)|H1|psi1(theta1)> ]
job = estimator.run([psi1], [H1], [theta1])
job_result = job.result() # It will block until the job finishes.
print(f"The primitive-job finished with result {job_result}"))
print(f"The primitive-job finished with result {job_result}")
# calculate [ <psi1(theta1)|H1|psi1(theta1)>,
# <psi2(theta2)|H2|psi2(theta2)>,
Expand Down Expand Up @@ -144,7 +144,7 @@ def run(
.. code-block:: python
values = parameter_values[i].
values = parameter_values[i]
Args:
circuits: one or more circuit objects.
Expand Down
10 changes: 9 additions & 1 deletion qiskit/primitives/containers/data_bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,22 @@ class DataBin(ShapedMixin):
.. code-block:: python
import numpy as np
from qiskit.primitives import DataBin, BitArray
data = DataBin(
alpha=BitArray.from_bitstrings(["0010"]),
alpha=BitArray.from_samples(["0010"]),
beta=np.array([1.2])
)
print("alpha data:", data.alpha)
print("beta data:", data.beta)
.. code-block::
alpha data: BitArray(<shape=(), num_shots=1, num_bits=2>)
beta data: [1.2]
"""

__slots__ = ("_data", "_shape")
Expand Down
23 changes: 16 additions & 7 deletions qiskit/pulse/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@
from qiskit.compiler import schedule
from qiskit import pulse, QuantumCircuit
from qiskit.pulse import library
from qiskit.providers.fake_provider import FakeOpenPulse2Q
backend = FakeOpenPulse2Q()
Expand All @@ -147,7 +146,7 @@
with pulse.build(backend) as pulse_prog:
# Create a pulse.
gaussian_pulse = library.gaussian(10, 1.0, 2)
gaussian_pulse = pulse.Gaussian(10, 1.0, 2)
# Get the qubit's corresponding drive channel from the backend.
d0 = pulse.drive_channel(0)
d1 = pulse.drive_channel(1)
Expand Down Expand Up @@ -285,16 +284,16 @@
d0 = pulse.drive_channel(0)
a0 = pulse.acquire_channel(0)
pulse.play(pulse.library.Constant(10, 1.0), d0)
pulse.play(pulse.Constant(10, 1.0), d0)
pulse.delay(20, d0)
pulse.shift_phase(3.14/2, d0)
pulse.set_phase(3.14, d0)
pulse.shift_frequency(1e7, d0)
pulse.set_frequency(5e9, d0)
with pulse.build() as temp_sched:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), d0)
pulse.play(pulse.library.Gaussian(20, -1.0, 3.0), d0)
pulse.play(pulse.Gaussian(20, 1.0, 3.0), d0)
pulse.play(pulse.Gaussian(20, -1.0, 3.0), d0)
pulse.call(temp_sched)
pulse.acquire(30, a0, pulse.MemorySlot(0))
Expand Down Expand Up @@ -1327,7 +1326,9 @@ def frequency_offset(
:emphasize-lines: 7, 16
from qiskit import pulse
from qiskit.providers.fake_provider import FakeOpenPulse2Q
backend = FakeOpenPulse2Q()
d0 = pulse.DriveChannel(0)
with pulse.build(backend) as pulse_prog:
Expand Down Expand Up @@ -1969,19 +1970,23 @@ def barrier(*channels_or_qubits: chans.Channel | int, name: str | None = None):
.. code-block::
import math
from qiskit import pulse
from qiskit.providers.fake_provider import FakeOpenPulse2Q
backend = FakeOpenPulse2Q()
d0 = pulse.DriveChannel(0)
with pulse.build(backend) as pulse_prog:
with pulse.align_right():
pulse.call(backend.defaults.instruction_schedule_map.get('x', (1,)))
pulse.call(backend.defaults().instruction_schedule_map.get('u1', (1,)))
# Barrier qubit 1 and d0.
pulse.barrier(1, d0)
# Due to barrier this will play before the gate on qubit 1.
pulse.play(pulse.Constant(10, 1.0), d0)
# This will end at the same time as the pulse above due to
# the barrier.
pulse.call(backend.defaults.instruction_schedule_map.get('x', (1,)))
pulse.call(backend.defaults().instruction_schedule_map.get('u1', (1,)))
.. note:: Requires the active builder context to have a backend set if
qubits are barriered on.
Expand Down Expand Up @@ -2012,6 +2017,7 @@ def macro(func: Callable):
:include-source:
from qiskit import pulse
from qiskit.providers.fake_provider import FakeOpenPulse2Q
@pulse.macro
def measure(qubit: int):
Expand All @@ -2021,6 +2027,9 @@ def measure(qubit: int):
return mem_slot
backend = FakeOpenPulse2Q()
with pulse.build(backend=backend) as sched:
mem_slot = measure(0)
print(f"Qubit measured into {mem_slot}")
Expand Down
5 changes: 5 additions & 0 deletions qiskit/pulse/instructions/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,18 @@ class TimeBlockade(Directive):
.. code-block:: python
from qiskit.pulse import Schedule, Play, Constant, DriveChannel
schedule = Schedule()
schedule.insert(120, Play(Constant(10, 0.1), DriveChannel(0)))
This schedule block is expected to be identical to above at a time of execution.
.. code-block:: python
from qiskit.pulse import ScheduleBlock, Play, Constant, DriveChannel
from qiskit.pulse.instructions import TimeBlockade
block = ScheduleBlock()
block.append(TimeBlockade(120, DriveChannel(0)))
block.append(Play(Constant(10, 0.1), DriveChannel(0)))
Expand Down
Loading

0 comments on commit d233aa9

Please sign in to comment.