Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions crates/cext/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,8 @@ pub enum CDelayUnit {
NS = 3,
/// Picoseconds.
PS = 4,
/// QPU clock cycles.
DT = 5,
}

impl From<CDelayUnit> for DelayUnit {
Expand All @@ -1919,6 +1921,7 @@ impl From<CDelayUnit> for DelayUnit {
CDelayUnit::US => DelayUnit::US,
CDelayUnit::NS => DelayUnit::NS,
CDelayUnit::PS => DelayUnit::PS,
CDelayUnit::DT => DelayUnit::DT,
}
}
}
Expand Down Expand Up @@ -1970,6 +1973,46 @@ pub unsafe extern "C" fn qk_circuit_delay(
ExitCode::Success
}

/// @ingroup QkCircuit
/// Append a delay instruction with unit ``dt`` to the circuit.
///
/// The duration is stored internally as a floating-point value; values larger
/// than ``2^53`` lose precision.
///
/// @param circuit A pointer to the circuit to add the delay to.
/// @param qubit The ``uint32_t`` index of the qubit to apply the delay to.
/// @param duration The duration of the delay in clock cycles.
///
/// @return An exit code.
///
/// # Safety
///
/// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn qk_circuit_delay_dt(
circuit: *mut CircuitData,
qubit: u32,
duration: u64,
) -> ExitCode {
// SAFETY: Per documentation, the pointer is non-null and aligned.
let circuit = unsafe { mut_ptr_as_ref(circuit) };

let duration_param: Param = (duration as f64).into();
let delay_instruction = StandardInstruction::Delay(DelayUnit::DT);

let params = Parameters::Params(smallvec![duration_param]);
circuit
.push_packed_operation(
PackedOperation::from_standard_instruction(delay_instruction),
Some(params),
&[Qubit(qubit)],
&[],
)
.unwrap();

ExitCode::Success
}

/// The configuration options for the ``qk_circuit_draw`` function.
#[repr(C)]
pub struct CircuitDrawerConfig {
Expand Down
13 changes: 5 additions & 8 deletions crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1023,14 +1023,11 @@ pub fn extract_params<T: CircuitBlock>(
match &i {
StandardInstruction::Barrier(_) => None,
StandardInstruction::Delay(_) => {
// If the delay's duration is a Python int, we preserve it rather than
// coercing it to a float (e.g. when unit is 'dt').
Some(Parameters::Params(
params
.try_iter()?
.map(|p| Param::extract_no_coerce(p?.as_borrowed()))
.collect::<PyResult<_>>()?,
))
// Coerce the duration to `Param::Float`. Python `int` durations (used for
// the 'dt' unit) round-trip back to `int` on the Python side via
// `Delay.validate_parameter`.
let params: SmallVec<[Param; 3]> = params.extract()?;
Some(Parameters::Params(params))
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woah boy that's one long range assumption.

StandardInstruction::Measure => None,
StandardInstruction::Reset => None,
Expand Down
3 changes: 1 addition & 2 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,7 @@ impl Param {
if coerce_to_float {
Ok(Self::Float(i as f64)) // coerce integer to float
} else {
// Int is not a param type and only comes from Python so dump it in
// there until we support DT unit delay from C
// Int is not a `Param` variant; store as a Python object.
Python::attach(|py| Ok(Self::Obj(i.into_py_any(py)?)))
}
}
Expand Down
13 changes: 13 additions & 0 deletions releasenotes/notes/c-api-dt-delay-2d0bc1fcf6b7214c.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
features_c:
- |
Added support for ``dt``-unit delays to the C API. The ``QkDelayUnit`` enum
now includes ``QkDelayUnit_DT``, and a new function
``qk_circuit_delay_dt(circuit, qubit, duration)`` constructs a delay whose
duration is given in integer clock cycles (``uint64_t``).
fixes:
- |
Fixed a crash where reading a ``QkCircuit`` containing a ``Delay`` with
unit ``"dt"`` (built from Python) via the C API would panic. The integer
duration is now stored as a floating-point value internally, so the
instruction is readable by the C-API instruction-readback path.
22 changes: 22 additions & 0 deletions test/c/test_circuit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,28 @@ static int test_delay_instruction(void) {
goto cleanup;
}

QkExitCode delay_dt_code = qk_circuit_delay_dt(qc, 1, 100);
if (delay_dt_code != QkExitCode_Success) {
result = RuntimeError;
goto cleanup;
}

// Read back the dt-delay and verify the duration.
QkCircuitInstruction inst;
qk_circuit_get_instruction(qc, 1, &inst);
if (inst.num_params != 1) {
printf("Expected 1 parameter in dt-delay, got %u\n", inst.num_params);
result = EqualityError;
qk_circuit_instruction_clear(&inst);
goto cleanup;
}
double duration = qk_param_as_real(inst.params[0]);
if (fabs(duration - 100.0) > 1e-10) {
printf("Unexpected dt-delay duration: %f\n", duration);
result = EqualityError;
}
qk_circuit_instruction_clear(&inst);

cleanup:
qk_circuit_free(qc);
return result;
Expand Down
8 changes: 8 additions & 0 deletions test/python/circuit/test_delay.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@


"""Test delay instruction for quantum circuits."""

import copy
import pickle

Expand Down Expand Up @@ -40,6 +41,13 @@ def test_keep_units_after_adding_delays_to_circuit(self):
self.assertEqual(qc.data[3].operation.unit, "ns")
self.assertEqual(qc.data[4].operation.unit, "dt")

def test_dt_duration_stays_int_after_circuit_roundtrip(self):
qc = QuantumCircuit(1)
qc.delay(100, 0, unit="dt")
duration = qc.data[0].operation.duration
self.assertIsInstance(duration, int)
self.assertEqual(duration, 100)

def test_fail_if_non_integer_duration_with_dt_unit_is_supplied(self):
qc = QuantumCircuit(1)
with self.assertRaises(CircuitError):
Expand Down
Loading