Skip to content
Open
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
156 changes: 41 additions & 115 deletions source/compiler/qsc/src/interpret/circuit_classical_ctl_tests.rs

Large diffs are not rendered by default.

64 changes: 53 additions & 11 deletions source/compiler/qsc_circuit/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pub(crate) mod tests;
use crate::{
angle_format::format_angle,
circuit::{
Circuit, ComponentColumn, Ket, Measurement, Metadata, Operation, Qubit, Register,
SourceLocation, Unitary, operation_list_to_grid,
Circuit, ComponentColumn, ControlRegister, Ket, Measurement, Metadata, Operation, Qubit,
Register, SourceLocation, Unitary, operation_list_to_grid,
},
operations::QubitParam,
};
Expand Down Expand Up @@ -86,7 +86,11 @@ impl Tracer for CircuitTracer {
self.wire_map_builder.current(),
name,
is_adjoint,
&GateInputs { targets, controls },
&GateInputs {
targets,
controls,
classical_controls: &[],
},
display_args,
called_at,
);
Expand Down Expand Up @@ -145,6 +149,7 @@ impl Tracer for CircuitTracer {
&GateInputs {
targets: &qubit_args,
controls: &[],
classical_controls: &[],
},
if classical_args.is_empty() {
vec![]
Expand Down Expand Up @@ -1236,6 +1241,7 @@ impl OperationOrGroup {
is_adjoint: bool,
targets: &[QubitWire],
controls: &[QubitWire],
classical_controls: Vec<ControlRegister>,
args: Vec<String>,
) -> Self {
Self::new_single(Operation::Unitary(Unitary {
Expand All @@ -1251,10 +1257,14 @@ impl OperationOrGroup {
.collect(),
controls: controls
.iter()
.map(|q| Register {
qubit: q.0,
result: None,
.map(|q| ControlRegister {
register: Register {
qubit: q.0,
result: None,
},
inverted: false,
})
.chain(classical_controls)
.collect(),
is_adjoint,
is_conditional: false,
Expand Down Expand Up @@ -1298,7 +1308,7 @@ impl OperationOrGroup {
Operation::Unitary(unitary) => unitary
.targets
.iter()
.chain(unitary.controls.iter())
.chain(unitary.controls.iter().map(|control| &control.register))
.filter(|r| r.result.is_none())
.cloned()
.collect(),
Expand Down Expand Up @@ -1334,7 +1344,12 @@ impl OperationOrGroup {
Operation::Unitary(unitary) => unitary
.controls
.iter()
.filter_map(|r| r.result.map(|res| ResultWire(r.qubit, res)))
.filter_map(|control| {
control
.register
.result
.map(|res| ResultWire(control.register.qubit, res))
})
.collect(),
Operation::Measurement(_) | Operation::Ket(_) => vec![],
}
Expand Down Expand Up @@ -1381,7 +1396,7 @@ impl OperationOrGroup {
result: Some(result_wire.1),
};
control_result_ids_map.push((register.clone(), *result_id));
control_result_registers.push(register);
control_result_registers.push(ControlRegister::from(register));
}

metadata = Some(Metadata {
Expand All @@ -1400,7 +1415,10 @@ impl OperationOrGroup {
gate: String::new(),
args: vec![],
children: vec![],
targets: control_result_registers.clone(),
targets: control_result_registers
.iter()
.map(|control| control.register.clone())
.collect(),
controls: control_result_registers,
is_adjoint: false,
metadata,
Expand Down Expand Up @@ -1624,6 +1642,12 @@ impl OperationListBuilder {
pub(crate) struct GateInputs<'a> {
pub(crate) targets: &'a [usize],
pub(crate) controls: &'a [usize],
pub(crate) classical_controls: &'a [ClassicalControlInput],
}

pub(crate) struct ClassicalControlInput {
pub(crate) result_id: usize,
pub(crate) inverted: bool,
}

/// Trait representing a receiver of circuit operations that can accept
Expand Down Expand Up @@ -1671,8 +1695,26 @@ impl OperationReceiver for OperationListBuilder {
.iter()
.map(|q| wire_map.qubit_wire(*q))
.collect::<Vec<_>>();
let classical_controls = inputs
.classical_controls
.iter()
.map(|control| {
let result = wire_map.result_wire(control.result_id);
ControlRegister {
register: Register::classical(result.0, result.1),
inverted: control.inverted,
}
})
.collect();
self.push_op(
OperationOrGroup::new_unitary(name, is_adjoint, &targets, &controls, args),
OperationOrGroup::new_unitary(
name,
is_adjoint,
&targets,
&controls,
classical_controls,
args,
),
call_stack,
wire_map,
);
Expand Down
131 changes: 93 additions & 38 deletions source/compiler/qsc_circuit/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ mod tests;

use indenter::indented;
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{Deserialize, Serialize};
use serde::{
Deserialize, Serialize,
ser::{SerializeStruct, Serializer},
};
use std::{
cmp::max,
fmt::{Display, Write},
Expand Down Expand Up @@ -274,7 +277,7 @@ pub struct Unitary {
pub targets: Vec<Register>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub controls: Vec<Register>,
pub controls: Vec<ControlRegister>,
#[serde(rename = "isAdjoint")]
#[serde(skip_serializing_if = "Not::not")]
#[serde(default)]
Expand All @@ -287,6 +290,43 @@ pub struct Unitary {
pub metadata: Option<Metadata>,
}

#[derive(Clone, Deserialize, Debug, Eq, PartialEq)]
pub struct ControlRegister {
#[serde(flatten)]
pub register: Register,
#[serde(default)]
pub inverted: bool,
}

// Custom serialization emits a plain JavaScript object; deriving it with `flatten` emits a Map.
impl Serialize for ControlRegister {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let field_count =
1 + usize::from(self.register.result.is_some()) + usize::from(self.inverted);
let mut state = serializer.serialize_struct("ControlRegister", field_count)?;
state.serialize_field("qubit", &self.register.qubit)?;
if let Some(result) = self.register.result {
state.serialize_field("result", &result)?;
}
if self.inverted {
state.serialize_field("inverted", &true)?;
}
state.end()
}
}

impl From<Register> for ControlRegister {
fn from(register: Register) -> Self {
Self {
register,
inverted: false,
}
}
}

/// Representation of a gate that will set the target to a specific state.
#[derive(Clone, Serialize, Deserialize, Default, Debug)]
pub struct Ket {
Expand Down Expand Up @@ -842,11 +882,11 @@ impl CircuitDisplay<'_> {
) -> usize {
let mut col_width = 0;
for op in &col.components {
let target_rows = get_row_indexes(op, register_to_row, true);
let control_rows = get_row_indexes(op, register_to_row, false);
let target_rows = get_target_rows(op, register_to_row);
let control_rows = get_control_rows(op, register_to_row);

let mut all_rows = target_rows.clone();
all_rows.extend(control_rows.iter());
all_rows.extend(control_rows.iter().map(|(row, _)| row));
all_rows.sort_unstable();

// We'll need to know the entire range of rows for this operation so we can
Expand Down Expand Up @@ -970,7 +1010,7 @@ fn add_operation_to_rows(
operation: &Operation,
rows: &mut [Row],
targets: &[usize],
controls: &[usize],
controls: &[(usize, bool)],
column: usize,
begin: usize,
end: usize,
Expand All @@ -986,12 +1026,12 @@ fn add_operation_to_rows(
}

if operation.is_controlled() || operation.is_measurement() {
for i in controls {
for (i, inverted) in controls {
let row = &mut rows[*i];
if matches!(row.wire, Wire::Qubit { .. }) && operation.is_measurement() {
row.add_measurement(column, operation.source_location());
} else {
row.add_object(column, "●");
row.add_object(column, if *inverted { "○" } else { "●" });
}
}

Expand Down Expand Up @@ -1081,34 +1121,15 @@ fn finalize_columns(rows: &[Row]) -> Vec<Column> {
.collect()
}

/// Gets the row indexes for the targets or controls of an operation.
fn get_row_indexes(
/// Gets the row indexes for the targets of an operation.
fn get_target_rows(
operation: &Operation,
register_to_row: &FxHashMap<(usize, Option<usize>), usize>,
is_target: bool,
) -> Vec<usize> {
let registers = match operation {
Operation::Measurement(m) => {
if is_target {
&m.results
} else {
&m.qubits
}
}
Operation::Unitary(u) => {
if is_target {
&u.targets
} else {
&u.controls
}
}
Operation::Ket(k) => {
if is_target {
&k.targets
} else {
&vec![]
}
}
Operation::Measurement(measurement) => &measurement.results,
Operation::Unitary(unitary) => &unitary.targets,
Operation::Ket(ket) => &ket.targets,
};

registers
Expand All @@ -1120,6 +1141,33 @@ fn get_row_indexes(
.collect()
}

fn get_control_rows(
operation: &Operation,
register_to_row: &FxHashMap<(usize, Option<usize>), usize>,
) -> Vec<(usize, bool)> {
match operation {
Operation::Measurement(measurement) => measurement
.qubits
.iter()
.filter_map(|register| {
register_to_row
.get(&(register.qubit, register.result))
.map(|row| (*row, false))
})
.collect(),
Operation::Unitary(unitary) => unitary
.controls
.iter()
.filter_map(|control| {
register_to_row
.get(&(control.register.qubit, control.register.result))
.map(|row| (*row, control.inverted))
})
.collect(),
Operation::Ket(_) => vec![],
}
}

/// Converts a list of operations into a 2D grid of operations in col-row format.
/// Operations will be left-justified as much as possible in the resulting grid.
/// Children operations are recursively converted into a grid.
Expand Down Expand Up @@ -1205,15 +1253,22 @@ fn operation_list_to_grid_base(
Operation::Unitary(u) => &u.targets,
Operation::Ket(k) => &k.targets,
};
let controls = match &op {
Operation::Measurement(m) => &m.results,
Operation::Unitary(u) => &u.controls,
Operation::Ket(_) => &vec![],
};
let mut all_rows = targets
.iter()
.chain(controls.iter())
.map(|r| get_row_for_register(r, &rows))
.chain(match &op {
Operation::Measurement(measurement) => measurement
.results
.iter()
.map(|register| get_row_for_register(register, &rows))
.collect::<Vec<_>>(),
Operation::Unitary(unitary) => unitary
.controls
.iter()
.map(|control| get_row_for_register(&control.register, &rows))
.collect(),
Operation::Ket(_) => vec![],
})
.collect::<Vec<_>>();
all_rows.sort_unstable();
let (begin, end) = all_rows.split_first().map_or((0, 0), |(first, tail)| {
Expand Down
4 changes: 2 additions & 2 deletions source/compiler/qsc_circuit/src/circuit/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn ctl_unitary(gate: &str, targets: Vec<Register>, controls: Vec<Register>) -> O
gate: gate.to_string(),
args: vec![],
is_adjoint: false,
controls,
controls: controls.into_iter().map(Into::into).collect(),
targets,
children: vec![],
metadata: None,
Expand Down Expand Up @@ -106,7 +106,7 @@ fn ctl_unitary_with_children(
gate: gate.to_string(),
args: vec![],
is_adjoint: false,
controls,
controls: controls.into_iter().map(Into::into).collect(),
targets,
children,
metadata: None,
Expand Down
Loading