Skip to main content

BasicBlock

Struct BasicBlock 

Source
pub struct BasicBlock {
    pub gates: Vec<GateInstruction>,
    pub qubits_op: BTreeMap<usize, QubitOp>,
    pub global: Option<f64>,
}
Expand description

A straight-line sequence of quantum gate instructions.

A BasicBlock is created empty and gates are appended one at a time via BasicBlock::append_gate or in bulk via BasicBlock::append_block. Each append operation attempts lightweight algebraic simplifications: adjacent inverse gate pairs are cancelled and consecutive same-axis rotation gates are merged into one.

The block also maintains a dependency index (qubits_op) consumed by the qubit-allocation and circuit-mapping passes.

Fields§

§gates: Vec<GateInstruction>

The ordered list of gate instructions in this block.

§qubits_op: BTreeMap<usize, QubitOp>

Per-qubit dependency records (propriety and read-control sets).

§global: Option<f64>

Accumulated global phase (in radians), or None if no global phase has been added to this block.

Implementations§

Source§

impl BasicBlock

Source

pub fn new() -> Self

Creates a new, empty BasicBlock.

Examples found in repository?
examples/qft.rs (line 19)
18fn qft(qubits: &[usize], do_swap: bool) -> Result<BasicBlock, KetError> {
19    let mut quantum_code = BasicBlock::new();
20    if qubits.len() == 1 {
21        quantum_code.append_gate(QuantumGate::Hadamard, qubits[0]);
22        return Ok(quantum_code);
23    }
24
25    let init = &qubits[..qubits.len() - 1];
26    let last = qubits[qubits.len() - 1];
27    quantum_code.append_gate(QuantumGate::Hadamard, last);
28    for (i, c) in init.iter().enumerate() {
29        quantum_code.append_block(
30            controlled_gate(
31                QuantumGate::Phase(Param::Value(PI / 2.0_f64.powi(i as i32 + 1))),
32                &[*c],
33                last,
34            )?,
35            None,
36        );
37    }
38    quantum_code.append_block(qft(init, false)?, None);
39
40    if do_swap {
41        for i in 0..qubits.len() / 2 {
42            quantum_code.append_block(swap_gate(qubits[i], qubits[qubits.len() - i - 1])?, None);
43        }
44    }
45
46    Ok(quantum_code)
47}
More examples
Hide additional examples
examples/grover.rs (line 25)
14fn main() -> Result<(), KetError> {
15    let num_qubits = 3;
16
17    let mut process = Process::new(QPUConfig {
18        num_qubits,
19        quantum_execution: None,
20    });
21
22    let qubits: Result<Vec<_>, _> = (0..num_qubits).map(|_| process.alloc()).collect();
23    let qubits = qubits?;
24
25    let mut quantum_code = BasicBlock::new();
26
27    for target in &qubits {
28        quantum_code.append_gate(QuantumGate::Hadamard, *target);
29    }
30
31    process.append_block(quantum_code)?;
32
33    let steps = ((FRAC_PI_4) * f64::sqrt((1 << num_qubits) as f64)) as i64;
34
35    for _ in 0..steps {
36        process.append_block(compute_action_uncompute_gate(
37            |mut compute| {
38                for target in &qubits {
39                    compute.append_gate(QuantumGate::PauliX, *target);
40                }
41                Ok(compute)
42            },
43            |mut action| {
44                action.append_gate(QuantumGate::PauliZ, qubits[0]);
45                Ok(action.control(&qubits[1..])?)
46            },
47        )?)?;
48
49        process.append_block(compute_action_uncompute_gate(
50            |mut compute| {
51                for target in &qubits {
52                    compute.append_gate(QuantumGate::Hadamard, *target);
53                    compute.append_gate(QuantumGate::PauliX, *target);
54                }
55                Ok(compute)
56            },
57            |mut action| {
58                action.append_gate(QuantumGate::PauliZ, qubits[0]);
59                Ok(action.control(&qubits[1..])?)
60            },
61        )?)?;
62    }
63
64    println!("{:#?}", process);
65
66    Ok(())
67}
Source

pub fn append_gate(&mut self, gate: QuantumGate, target: usize)

Appends an uncontrolled gate to the block.

Examples found in repository?
examples/qft.rs (line 21)
18fn qft(qubits: &[usize], do_swap: bool) -> Result<BasicBlock, KetError> {
19    let mut quantum_code = BasicBlock::new();
20    if qubits.len() == 1 {
21        quantum_code.append_gate(QuantumGate::Hadamard, qubits[0]);
22        return Ok(quantum_code);
23    }
24
25    let init = &qubits[..qubits.len() - 1];
26    let last = qubits[qubits.len() - 1];
27    quantum_code.append_gate(QuantumGate::Hadamard, last);
28    for (i, c) in init.iter().enumerate() {
29        quantum_code.append_block(
30            controlled_gate(
31                QuantumGate::Phase(Param::Value(PI / 2.0_f64.powi(i as i32 + 1))),
32                &[*c],
33                last,
34            )?,
35            None,
36        );
37    }
38    quantum_code.append_block(qft(init, false)?, None);
39
40    if do_swap {
41        for i in 0..qubits.len() / 2 {
42            quantum_code.append_block(swap_gate(qubits[i], qubits[qubits.len() - i - 1])?, None);
43        }
44    }
45
46    Ok(quantum_code)
47}
More examples
Hide additional examples
examples/grover.rs (line 28)
14fn main() -> Result<(), KetError> {
15    let num_qubits = 3;
16
17    let mut process = Process::new(QPUConfig {
18        num_qubits,
19        quantum_execution: None,
20    });
21
22    let qubits: Result<Vec<_>, _> = (0..num_qubits).map(|_| process.alloc()).collect();
23    let qubits = qubits?;
24
25    let mut quantum_code = BasicBlock::new();
26
27    for target in &qubits {
28        quantum_code.append_gate(QuantumGate::Hadamard, *target);
29    }
30
31    process.append_block(quantum_code)?;
32
33    let steps = ((FRAC_PI_4) * f64::sqrt((1 << num_qubits) as f64)) as i64;
34
35    for _ in 0..steps {
36        process.append_block(compute_action_uncompute_gate(
37            |mut compute| {
38                for target in &qubits {
39                    compute.append_gate(QuantumGate::PauliX, *target);
40                }
41                Ok(compute)
42            },
43            |mut action| {
44                action.append_gate(QuantumGate::PauliZ, qubits[0]);
45                Ok(action.control(&qubits[1..])?)
46            },
47        )?)?;
48
49        process.append_block(compute_action_uncompute_gate(
50            |mut compute| {
51                for target in &qubits {
52                    compute.append_gate(QuantumGate::Hadamard, *target);
53                    compute.append_gate(QuantumGate::PauliX, *target);
54                }
55                Ok(compute)
56            },
57            |mut action| {
58                action.append_gate(QuantumGate::PauliZ, qubits[0]);
59                Ok(action.control(&qubits[1..])?)
60            },
61        )?)?;
62    }
63
64    println!("{:#?}", process);
65
66    Ok(())
67}
Source

pub fn inverse(&self) -> Self

Return a new basic block with the inverse of the circuit.

Source

pub fn control(&self, control_qubits: &[usize]) -> Result<Self, KetError>

Returns a copy of this block with control_qubits added to every gate.

§Errors

Propagates any KetError returned by GateInstruction::control.

Examples found in repository?
examples/grover.rs (line 45)
14fn main() -> Result<(), KetError> {
15    let num_qubits = 3;
16
17    let mut process = Process::new(QPUConfig {
18        num_qubits,
19        quantum_execution: None,
20    });
21
22    let qubits: Result<Vec<_>, _> = (0..num_qubits).map(|_| process.alloc()).collect();
23    let qubits = qubits?;
24
25    let mut quantum_code = BasicBlock::new();
26
27    for target in &qubits {
28        quantum_code.append_gate(QuantumGate::Hadamard, *target);
29    }
30
31    process.append_block(quantum_code)?;
32
33    let steps = ((FRAC_PI_4) * f64::sqrt((1 << num_qubits) as f64)) as i64;
34
35    for _ in 0..steps {
36        process.append_block(compute_action_uncompute_gate(
37            |mut compute| {
38                for target in &qubits {
39                    compute.append_gate(QuantumGate::PauliX, *target);
40                }
41                Ok(compute)
42            },
43            |mut action| {
44                action.append_gate(QuantumGate::PauliZ, qubits[0]);
45                Ok(action.control(&qubits[1..])?)
46            },
47        )?)?;
48
49        process.append_block(compute_action_uncompute_gate(
50            |mut compute| {
51                for target in &qubits {
52                    compute.append_gate(QuantumGate::Hadamard, *target);
53                    compute.append_gate(QuantumGate::PauliX, *target);
54                }
55                Ok(compute)
56            },
57            |mut action| {
58                action.append_gate(QuantumGate::PauliZ, qubits[0]);
59                Ok(action.control(&qubits[1..])?)
60            },
61        )?)?;
62    }
63
64    println!("{:#?}", process);
65
66    Ok(())
67}
Source

pub fn enable_approximated_decomposition(&mut self)

Enable approximate decomposition enabled on all gate instructions.

Source

pub fn lock_control(&mut self)

Lock control sets locked on all gates.

Source

pub fn append_block(&mut self, block: Self, epsilon: Option<f64>)

Appends all instructions from block into self, applying algebraic simplifications using the given epsilon tolerance.

After draining block.gates, the global phases are summed and the qubits_op dependency maps are merged (restricting propriety to the more-general of the two).

Examples found in repository?
examples/qft.rs (lines 29-36)
18fn qft(qubits: &[usize], do_swap: bool) -> Result<BasicBlock, KetError> {
19    let mut quantum_code = BasicBlock::new();
20    if qubits.len() == 1 {
21        quantum_code.append_gate(QuantumGate::Hadamard, qubits[0]);
22        return Ok(quantum_code);
23    }
24
25    let init = &qubits[..qubits.len() - 1];
26    let last = qubits[qubits.len() - 1];
27    quantum_code.append_gate(QuantumGate::Hadamard, last);
28    for (i, c) in init.iter().enumerate() {
29        quantum_code.append_block(
30            controlled_gate(
31                QuantumGate::Phase(Param::Value(PI / 2.0_f64.powi(i as i32 + 1))),
32                &[*c],
33                last,
34            )?,
35            None,
36        );
37    }
38    quantum_code.append_block(qft(init, false)?, None);
39
40    if do_swap {
41        for i in 0..qubits.len() / 2 {
42            quantum_code.append_block(swap_gate(qubits[i], qubits[qubits.len() - i - 1])?, None);
43        }
44    }
45
46    Ok(quantum_code)
47}
Source

pub fn add_global_phase(&mut self, phase: f64)

Sum a global phase in the block.

Source

pub fn max_qubit_index(&self) -> Option<usize>

Returns the highest qubit index referenced by any gate in this block, or None if the block is empty.

Source

pub fn flat_gates( &self, parameters: Option<&[f64]>, shift: Option<(usize, usize, f64)>, ) -> Vec<DecomposedGate>

Flattens decomposed multi-qubit gates into a single sequence of DecomposedGate.

When parameters is provided, any symbolic Param::Ref values are resolved to their concrete floating-point equivalents before output.

Source

pub fn set_as_diagonal(&mut self)

Forces the propriety classification of every qubit entry to at most GatePropriety::Diagonal, even if the actual gate sequence is more general. Used when an externally-verified analysis guarantees the circuit is diagonal.

Source

pub fn set_as_permutation(&mut self)

Forces the propriety classification of every qubit entry to at most GatePropriety::Permutation, even if the actual gate sequence is more general. Used when an externally-verified analysis guarantees the circuit only permutes basis states.

Trait Implementations§

Source§

impl Clone for BasicBlock

Source§

fn clone(&self) -> BasicBlock

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for BasicBlock

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for BasicBlock

Source§

fn default() -> BasicBlock

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.