From 8fade6551b66981f0065c9819e4a4d7f4d8b1006 Mon Sep 17 00:00:00 2001 From: Tdxdxoz Date: Fri, 1 Mar 2024 16:20:23 +0800 Subject: [PATCH 1/5] basic circuit with apply and run --- qip/src/builder_traits.rs | 12 +++-- qip/src/circuit.rs | 105 ++++++++++++++++++++++++++++++++++++++ qip/src/lib.rs | 2 + 3 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 qip/src/circuit.rs diff --git a/qip/src/builder_traits.rs b/qip/src/builder_traits.rs index 724b042..3e0b4a4 100644 --- a/qip/src/builder_traits.rs +++ b/qip/src/builder_traits.rs @@ -1,10 +1,12 @@ -use crate::errors::{CircuitError, CircuitResult}; -use crate::types::Precision; +use std::fmt::Debug; +use std::num::NonZeroUsize; + use num_complex::Complex; use num_rational::Rational64; use num_traits::{One, Zero}; -use std::fmt::Debug; -use std::num::NonZeroUsize; + +use crate::errors::{CircuitError, CircuitResult}; +use crate::types::Precision; /// Standard functions needed by registers containing multiple qubits. pub trait QubitRegister { @@ -60,7 +62,7 @@ impl SplitManyResult { /// and end-result quantum state. pub trait CircuitBuilder { /// The register type used for the circuit. - type Register: QubitRegister + Debug; + type Register: QubitRegister + Debug + 'static; /// The struct used to represent circuit objects. type CircuitObject; /// Return type for state calculations. diff --git a/qip/src/circuit.rs b/qip/src/circuit.rs new file mode 100644 index 0000000..6fcab15 --- /dev/null +++ b/qip/src/circuit.rs @@ -0,0 +1,105 @@ +use std::fmt; +use std::iter; +use std::mem; +use std::rc::Rc; + +use crate::builder_traits::CircuitBuilder; +use crate::errors::CircuitResult; + +/// A circuit described by a function +#[derive(Clone)] +pub struct Circuit { + func: Rc CircuitResult<[CB::Register; N]>>, +} + +impl std::fmt::Debug for Circuit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Circuit").field("N", &N).finish() + } +} + +impl Circuit { + /// Init a empty circuit + pub fn new() -> Self { + Self { + func: Rc::new(|_, rs| Ok(rs)), + } + } + + /// Apply a function to part of this circuit + pub fn apply(self, f: F, indices: [usize; L]) -> Self + where + F: Fn(&mut CB, [CB::Register; L]) -> CircuitResult<[CB::Register; L]> + 'static, + { + let func = self.func.clone(); + Self { + func: Rc::new(move |cb, rs| { + let out = (*func)(cb, rs)?; + let mut out = out.map(Some); + let f_input = indices.map(|idx| mem::take(&mut out[idx]).unwrap()); + let f_out = f(cb, f_input)?; + + iter::zip(indices, f_out).for_each(|(idx, r)| out[idx] = Some(r)); + + Ok(out.map(|r| r.unwrap())) + }), + } + } + + /// Set input for circuit + pub fn input(self, input: [CB::Register; N]) -> CircuitWithInput { + CircuitWithInput { + circuit: self, + input, + } + } +} + +/// Circuit with input +#[derive(Debug)] +pub struct CircuitWithInput { + circuit: Circuit, + input: [CB::Register; N], +} + +impl CircuitWithInput { + /// Run the circuit + pub fn run(self, cb: &mut CB) -> CircuitResult<[CB::Register; N]> { + (*self.circuit.func)(cb, self.input) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::*; + + fn gamma(b: &mut B, rs: [B::Register; 2]) -> CircuitResult<[B::Register; 2]> + where + B: AdvancedCircuitBuilder, + { + let [ra, rb] = rs; + let (ra, rb) = b.toffoli(ra, rb)?; + let (rb, ra) = b.toffoli(rb, ra)?; + Ok([ra, rb]) + } + + #[test] + fn test_chain_circuit() -> CircuitResult<()> { + let mut b = LocalBuilder::default(); + let ra = b.try_register(3).unwrap(); + let rb = b.try_register(3).unwrap(); + + let [ra, rb] = Circuit::new() + .apply(gamma, [0, 1]) + .input([ra, rb]) + .run(&mut b)?; + + let r = b.merge_two_registers(ra, rb); + let (_, _) = b.measure_stochastic(r); + + let (_, _) = b.calculate_state(); + + Ok(()) + } +} diff --git a/qip/src/lib.rs b/qip/src/lib.rs index a58ec4a..ac7cd31 100644 --- a/qip/src/lib.rs +++ b/qip/src/lib.rs @@ -240,6 +240,8 @@ pub mod boolean_circuits; pub mod builder; /// Standard traits for circuit builders. pub mod builder_traits; +/// Circuit +pub mod circuit; /// Traits for constructing conditioned circuit builders. pub mod conditioning; /// Circuit builder error types. From c2ccd07418120223bfb4507f6529c778b2501eeb Mon Sep 17 00:00:00 2001 From: Tdxdxoz Date: Fri, 1 Mar 2024 18:40:21 +0800 Subject: [PATCH 2/5] support under_new_indices --- qip/src/circuit.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/qip/src/circuit.rs b/qip/src/circuit.rs index 6fcab15..9398105 100644 --- a/qip/src/circuit.rs +++ b/qip/src/circuit.rs @@ -18,14 +18,16 @@ impl std::fmt::Debug for Circuit { } } -impl Circuit { +impl Default for Circuit { /// Init a empty circuit - pub fn new() -> Self { + fn default() -> Self { Self { func: Rc::new(|_, rs| Ok(rs)), } } +} +impl Circuit { /// Apply a function to part of this circuit pub fn apply(self, f: F, indices: [usize; L]) -> Self where @@ -36,7 +38,7 @@ impl Circuit { func: Rc::new(move |cb, rs| { let out = (*func)(cb, rs)?; let mut out = out.map(Some); - let f_input = indices.map(|idx| mem::take(&mut out[idx]).unwrap()); + let f_input = indices.map(|idx| out[idx].take().unwrap()); let f_out = f(cb, f_input)?; iter::zip(indices, f_out).for_each(|(idx, r)| out[idx] = Some(r)); @@ -46,6 +48,64 @@ impl Circuit { } } + /// Apply a sub circuit for specific qubits under some new indices combine + pub fn under_new_indices( + self, + new_indices: [&[(usize, usize)]; L], + sub_circuit: Circuit, + ) -> Self { + let func = self.func.clone(); + let new_indices = new_indices.map(|idx_pairs| idx_pairs.to_vec()); + + Self { + func: Rc::new(move |cb, rs| { + let out = (*func)(cb, rs)?; + let mut out = out.map(RegisterRepr::Origin); + + // combine to new registers + let f_input = new_indices.clone().map(|idx_pairs| { + let rs = idx_pairs + .iter() + .map(|&(reg_idx, idx)| { + if matches!(out[reg_idx], RegisterRepr::Origin(_)) { + let r = + mem::replace(out.get_mut(reg_idx).unwrap(), RegisterRepr::Tmp); + let RegisterRepr::Origin(r) = r else { + unreachable!() + }; + let qubits = cb.split_all_register(r); + out[reg_idx] = + RegisterRepr::Splited(qubits.into_iter().map(Some).collect()); + } + + let RegisterRepr::Splited(rs) = out.get_mut(reg_idx).unwrap() else { + unreachable!() + }; + rs[idx].take().unwrap() + }) + .collect::>(); + + cb.merge_registers(rs).unwrap() + }); + + let f_output = (*sub_circuit.func)(cb, f_input)?; + let f_output_qubits = f_output.map(|r| cb.split_all_register(r)); + + // restore + iter::zip(new_indices.clone(), f_output_qubits).for_each(|(idx_pairs, qubits)| { + iter::zip(idx_pairs, qubits).for_each(|((reg_idx, idx), qubit)| { + let RegisterRepr::Splited(rs) = out.get_mut(reg_idx).unwrap() else { + unreachable!() + }; + rs[idx] = Some(qubit); + }); + }); + + Ok(out.map(|rr| rr.into_origin(cb))) + }), + } + } + /// Set input for circuit pub fn input(self, input: [CB::Register; N]) -> CircuitWithInput { CircuitWithInput { @@ -55,6 +115,24 @@ impl Circuit { } } +enum RegisterRepr { + Origin(CB::Register), + Splited(Vec>), + Tmp, +} + +impl RegisterRepr { + fn into_origin(self, cb: &mut CB) -> CB::Register { + match self { + Self::Origin(r) => r, + Self::Splited(qubits) => cb + .merge_registers(qubits.into_iter().map(|r| r.unwrap())) + .unwrap(), + Self::Tmp => unreachable!(), + } + } +} + /// Circuit with input #[derive(Debug)] pub struct CircuitWithInput { @@ -90,8 +168,14 @@ mod tests { let ra = b.try_register(3).unwrap(); let rb = b.try_register(3).unwrap(); - let [ra, rb] = Circuit::new() + let [ra, rb] = Circuit::default() + // Applies gamma to |ra>|rb> .apply(gamma, [0, 1]) + // Applies gamma to |ra[0] ra[1]>|ra[2]> + .under_new_indices( + [&[(0, 0), (0, 1)], &[(0, 2)]], + Circuit::default().apply(gamma, [0, 1]), + ) .input([ra, rb]) .run(&mut b)?; From 5b5fe2db0d524f7a8ac35e0c09b9a7152861baa4 Mon Sep 17 00:00:00 2001 From: Tdxdxoz Date: Mon, 4 Mar 2024 18:23:32 +0800 Subject: [PATCH 3/5] rewrite apply_subcircuit to use mapping function --- qip/src/circuit.rs | 145 +++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/qip/src/circuit.rs b/qip/src/circuit.rs index 9398105..95c0c6a 100644 --- a/qip/src/circuit.rs +++ b/qip/src/circuit.rs @@ -1,15 +1,18 @@ use std::fmt; use std::iter; -use std::mem; use std::rc::Rc; use crate::builder_traits::CircuitBuilder; use crate::errors::CircuitResult; +type Registers = [::Register; N]; +type CircuitFunction = + dyn Fn(&mut CB, Registers) -> CircuitResult>; + /// A circuit described by a function #[derive(Clone)] pub struct Circuit { - func: Rc CircuitResult<[CB::Register; N]>>, + func: Rc>, } impl std::fmt::Debug for Circuit { @@ -28,10 +31,18 @@ impl Default for Circuit { } impl Circuit { + /// From a function + pub fn from(f: F) -> Self + where + F: Fn(&mut CB, Registers) -> CircuitResult> + 'static, + { + Self::default().apply(f, core::array::from_fn(|i| i)) + } + /// Apply a function to part of this circuit pub fn apply(self, f: F, indices: [usize; L]) -> Self where - F: Fn(&mut CB, [CB::Register; L]) -> CircuitResult<[CB::Register; L]> + 'static, + F: Fn(&mut CB, Registers) -> CircuitResult> + 'static, { let func = self.func.clone(); Self { @@ -49,65 +60,74 @@ impl Circuit { } /// Apply a sub circuit for specific qubits under some new indices combine - pub fn under_new_indices( + pub fn apply_subcircuit( self, - new_indices: [&[(usize, usize)]; L], - sub_circuit: Circuit, - ) -> Self { + indices_map: MAP, + sub_circuit: &Circuit, + ) -> Self + where + MAP: Fn([Vec<(usize, usize)>; N]) -> [Vec<(usize, usize)>; L] + 'static, + { let func = self.func.clone(); - let new_indices = new_indices.map(|idx_pairs| idx_pairs.to_vec()); + let sub_func = sub_circuit.func.clone(); Self { func: Rc::new(move |cb, rs| { let out = (*func)(cb, rs)?; - let mut out = out.map(RegisterRepr::Origin); - - // combine to new registers - let f_input = new_indices.clone().map(|idx_pairs| { - let rs = idx_pairs - .iter() - .map(|&(reg_idx, idx)| { - if matches!(out[reg_idx], RegisterRepr::Origin(_)) { - let r = - mem::replace(out.get_mut(reg_idx).unwrap(), RegisterRepr::Tmp); - let RegisterRepr::Origin(r) = r else { - unreachable!() - }; - let qubits = cb.split_all_register(r); - out[reg_idx] = - RegisterRepr::Splited(qubits.into_iter().map(Some).collect()); - } - - let RegisterRepr::Splited(rs) = out.get_mut(reg_idx).unwrap() else { - unreachable!() - }; - rs[idx].take().unwrap() - }) - .collect::>(); - - cb.merge_registers(rs).unwrap() + + //split + let mut out = out.map(|r| { + let qubits = cb.split_all_register(r); + qubits.into_iter().map(Some).collect::>() + }); + + // combine to new registers + let init_indices: [Vec<(usize, usize)>; N] = out + .iter() + .enumerate() + .map(|(reg_idx, qubits)| { + (0..qubits.len()) + .map(|idx| (reg_idx, idx)) + .collect::>() + }) + .collect::>() + .try_into() + .unwrap(); + let new_indices = indices_map(init_indices); + + let f_input = new_indices.clone().map(|qubit_positions| { + cb.merge_registers( + qubit_positions + .iter() + .map(|&(reg_idx, idx)| out[reg_idx][idx].take().unwrap()), + ) + .unwrap() }); - let f_output = (*sub_circuit.func)(cb, f_input)?; + let f_output = (*sub_func)(cb, f_input)?; let f_output_qubits = f_output.map(|r| cb.split_all_register(r)); // restore - iter::zip(new_indices.clone(), f_output_qubits).for_each(|(idx_pairs, qubits)| { - iter::zip(idx_pairs, qubits).for_each(|((reg_idx, idx), qubit)| { - let RegisterRepr::Splited(rs) = out.get_mut(reg_idx).unwrap() else { - unreachable!() - }; - rs[idx] = Some(qubit); - }); - }); - - Ok(out.map(|rr| rr.into_origin(cb))) + iter::zip(new_indices, f_output_qubits).for_each( + |(qubit_positions, out_qubits)| { + iter::zip(qubit_positions, out_qubits).for_each( + |((reg_idx, idx), qubit)| { + out[reg_idx][idx] = Some(qubit); + }, + ); + }, + ); + + Ok(out.map(|qubits| { + cb.merge_registers(qubits.into_iter().map(|qubit| qubit.unwrap())) + .unwrap() + })) }), } } /// Set input for circuit - pub fn input(self, input: [CB::Register; N]) -> CircuitWithInput { + pub fn input(self, input: Registers) -> CircuitWithInput { CircuitWithInput { circuit: self, input, @@ -115,34 +135,16 @@ impl Circuit { } } -enum RegisterRepr { - Origin(CB::Register), - Splited(Vec>), - Tmp, -} - -impl RegisterRepr { - fn into_origin(self, cb: &mut CB) -> CB::Register { - match self { - Self::Origin(r) => r, - Self::Splited(qubits) => cb - .merge_registers(qubits.into_iter().map(|r| r.unwrap())) - .unwrap(), - Self::Tmp => unreachable!(), - } - } -} - /// Circuit with input #[derive(Debug)] pub struct CircuitWithInput { circuit: Circuit, - input: [CB::Register; N], + input: Registers, } impl CircuitWithInput { /// Run the circuit - pub fn run(self, cb: &mut CB) -> CircuitResult<[CB::Register; N]> { + pub fn run(self, cb: &mut CB) -> CircuitResult> { (*self.circuit.func)(cb, self.input) } } @@ -168,14 +170,17 @@ mod tests { let ra = b.try_register(3).unwrap(); let rb = b.try_register(3).unwrap(); + let gamma_circuit = Circuit::from(gamma); + let [ra, rb] = Circuit::default() // Applies gamma to |ra>|rb> .apply(gamma, [0, 1]) // Applies gamma to |ra[0] ra[1]>|ra[2]> - .under_new_indices( - [&[(0, 0), (0, 1)], &[(0, 2)]], - Circuit::default().apply(gamma, [0, 1]), - ) + .apply_subcircuit(|[ra, _]| [ra[0..=1].to_vec(), vec![ra[2]]], &gamma_circuit) + // Applies gamma to |ra[0] rb[0]>|ra[2]> + .apply_subcircuit(|[ra, rb]| [vec![ra[0], rb[0]], vec![ra[2]]], &gamma_circuit) + // Applies gamma to |ra[0]>|rb[0] ra[2]> + .apply_subcircuit(|[ra, rb]| [vec![ra[0]], vec![rb[0], ra[2]]], &gamma_circuit) .input([ra, rb]) .run(&mut b)?; From 30b4adb55f56f40f7d4dcf2fffd890c730368c99 Mon Sep 17 00:00:00 2001 From: Tdxdxoz Date: Tue, 5 Mar 2024 14:22:18 +0800 Subject: [PATCH 4/5] accept AsSubcircuit and IndicesInfo for apply --- qip/src/circuit.rs | 264 ++++++++++++++++++++++++++++++-------------- rust-toolchain.toml | 3 + 2 files changed, 187 insertions(+), 80 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/qip/src/circuit.rs b/qip/src/circuit.rs index 95c0c6a..d1ed6c2 100644 --- a/qip/src/circuit.rs +++ b/qip/src/circuit.rs @@ -9,6 +9,143 @@ type Registers = [::Register; N]; type CircuitFunction = dyn Fn(&mut CB, Registers) -> CircuitResult>; +/// indices for N registers +pub type Idx = [Vec<(usize, usize)>; N]; + +/// A subcircuit that you can apply +pub trait AsSubcircuit { + /// The innner function of this circuit + fn circuit_func(self) -> Rc>; +} + +impl AsSubcircuit for F +where + F: Fn(&mut CB, Registers) -> CircuitResult> + 'static, +{ + fn circuit_func(self) -> Rc> { + Rc::new(self) + } +} + +/// Provide indices information when apply a subcircuit +pub trait IndicesInfo: 'static { + /// Temporary intermediate type for storing other registers + type IntermediateRegisters; + + /// Get new registers + fn get_new_registers( + &self, + cb: &mut CB, + orig_rs: Registers, + ) -> (Self::IntermediateRegisters, Registers); + + /// Restore original registers + fn restore_original_registers( + &self, + cb: &mut CB, + itm_rs: Self::IntermediateRegisters, + sub_rs: Registers, + ) -> Registers; +} + +impl IndicesInfo for [usize; L] { + type IntermediateRegisters = [Option; N]; + + fn get_new_registers( + &self, + _cb: &mut CB, + orig_rs: Registers, + ) -> (Self::IntermediateRegisters, Registers) { + let mut itm_rs = orig_rs.map(Some); + let sub_rs = self.map(|idx| itm_rs[idx].take().unwrap()); + (itm_rs, sub_rs) + } + + fn restore_original_registers( + &self, + _cb: &mut CB, + mut itm_rs: Self::IntermediateRegisters, + sub_rs: Registers, + ) -> Registers { + iter::zip(self, sub_rs).for_each(|(&idx, r)| itm_rs[idx] = Some(r)); + itm_rs.map(|r| r.unwrap()) + } +} + +impl IndicesInfo for MAP +where + MAP: Fn(Idx) -> Idx + 'static, +{ + type IntermediateRegisters = [Vec>; N]; + + fn get_new_registers( + &self, + cb: &mut CB, + orig_rs: Registers, + ) -> (Self::IntermediateRegisters, Registers) { + let mut itm_rs = orig_rs.map(|r| { + let qubits = cb.split_all_register(r); + qubits.into_iter().map(Some).collect::>() + }); + + let init_indices: [Vec<(usize, usize)>; N] = itm_rs + .iter() + .enumerate() + .map(|(reg_idx, qubits)| { + (0..qubits.len()) + .map(|idx| (reg_idx, idx)) + .collect::>() + }) + .collect::>() + .try_into() + .unwrap(); + let new_indices = self(init_indices); + + let sub_rs = new_indices.map(|qubit_positions| { + cb.merge_registers( + qubit_positions + .iter() + .map(|&(reg_idx, idx)| itm_rs[reg_idx][idx].take().unwrap()), + ) + .unwrap() + }); + (itm_rs, sub_rs) + } + + fn restore_original_registers( + &self, + cb: &mut CB, + mut itm_rs: Self::IntermediateRegisters, + sub_rs: Registers, + ) -> Registers { + let init_indices: [Vec<(usize, usize)>; N] = itm_rs + .iter() + .enumerate() + .map(|(reg_idx, qubits)| { + (0..qubits.len()) + .map(|idx| (reg_idx, idx)) + .collect::>() + }) + .collect::>() + .try_into() + .unwrap(); + let new_indices = self(init_indices); + + iter::zip(new_indices, sub_rs.map(|r| cb.split_all_register(r))).for_each( + |(qubit_positions, out_qubits)| { + iter::zip(qubit_positions, out_qubits).for_each(|((reg_idx, idx), qubit)| { + itm_rs[reg_idx][idx] = Some(qubit); + }); + }, + ); + + itm_rs.map(|qubits| { + cb.merge_registers(qubits.into_iter().map(|qubit| qubit.unwrap())) + .unwrap() + }) + } +} + /// A circuit described by a function #[derive(Clone)] pub struct Circuit { @@ -30,6 +167,18 @@ impl Default for Circuit { } } +impl AsSubcircuit for Circuit { + fn circuit_func(self) -> Rc> { + self.func.clone() + } +} + +impl AsSubcircuit for &Circuit { + fn circuit_func(self) -> Rc> { + self.func.clone() + } +} + impl Circuit { /// From a function pub fn from(f: F) -> Self @@ -40,88 +189,20 @@ impl Circuit { } /// Apply a function to part of this circuit - pub fn apply(self, f: F, indices: [usize; L]) -> Self - where - F: Fn(&mut CB, Registers) -> CircuitResult> + 'static, - { - let func = self.func.clone(); - Self { - func: Rc::new(move |cb, rs| { - let out = (*func)(cb, rs)?; - let mut out = out.map(Some); - let f_input = indices.map(|idx| out[idx].take().unwrap()); - let f_out = f(cb, f_input)?; - - iter::zip(indices, f_out).for_each(|(idx, r)| out[idx] = Some(r)); - - Ok(out.map(|r| r.unwrap())) - }), - } - } - - /// Apply a sub circuit for specific qubits under some new indices combine - pub fn apply_subcircuit( + pub fn apply( self, - indices_map: MAP, - sub_circuit: &Circuit, - ) -> Self - where - MAP: Fn([Vec<(usize, usize)>; N]) -> [Vec<(usize, usize)>; L] + 'static, - { + subcircuit: impl AsSubcircuit, + indices: impl IndicesInfo, + ) -> Self { let func = self.func.clone(); - let sub_func = sub_circuit.func.clone(); - + let sub_func = subcircuit.circuit_func(); Self { func: Rc::new(move |cb, rs| { let out = (*func)(cb, rs)?; + let (itm, f_input) = indices.get_new_registers(cb, out); + let f_out = sub_func(cb, f_input)?; - //split - let mut out = out.map(|r| { - let qubits = cb.split_all_register(r); - qubits.into_iter().map(Some).collect::>() - }); - - // combine to new registers - let init_indices: [Vec<(usize, usize)>; N] = out - .iter() - .enumerate() - .map(|(reg_idx, qubits)| { - (0..qubits.len()) - .map(|idx| (reg_idx, idx)) - .collect::>() - }) - .collect::>() - .try_into() - .unwrap(); - let new_indices = indices_map(init_indices); - - let f_input = new_indices.clone().map(|qubit_positions| { - cb.merge_registers( - qubit_positions - .iter() - .map(|&(reg_idx, idx)| out[reg_idx][idx].take().unwrap()), - ) - .unwrap() - }); - - let f_output = (*sub_func)(cb, f_input)?; - let f_output_qubits = f_output.map(|r| cb.split_all_register(r)); - - // restore - iter::zip(new_indices, f_output_qubits).for_each( - |(qubit_positions, out_qubits)| { - iter::zip(qubit_positions, out_qubits).for_each( - |((reg_idx, idx), qubit)| { - out[reg_idx][idx] = Some(qubit); - }, - ); - }, - ); - - Ok(out.map(|qubits| { - cb.merge_registers(qubits.into_iter().map(|qubit| qubit.unwrap())) - .unwrap() - })) + Ok(indices.restore_original_registers(cb, itm, f_out)) }), } } @@ -154,6 +235,8 @@ mod tests { use super::*; use crate::prelude::*; + type CurrentBuilderType = LocalBuilder; + fn gamma(b: &mut B, rs: [B::Register; 2]) -> CircuitResult<[B::Register; 2]> where B: AdvancedCircuitBuilder, @@ -166,7 +249,7 @@ mod tests { #[test] fn test_chain_circuit() -> CircuitResult<()> { - let mut b = LocalBuilder::default(); + let mut b = CurrentBuilderType::default(); let ra = b.try_register(3).unwrap(); let rb = b.try_register(3).unwrap(); @@ -176,11 +259,32 @@ mod tests { // Applies gamma to |ra>|rb> .apply(gamma, [0, 1]) // Applies gamma to |ra[0] ra[1]>|ra[2]> - .apply_subcircuit(|[ra, _]| [ra[0..=1].to_vec(), vec![ra[2]]], &gamma_circuit) + .apply(gamma, |[ra, _]: Idx<2>| { + [ra[0..=1].to_vec(), vec![ra[2]]] + }) // Applies gamma to |ra[0] rb[0]>|ra[2]> - .apply_subcircuit(|[ra, rb]| [vec![ra[0], rb[0]], vec![ra[2]]], &gamma_circuit) + .apply(&gamma_circuit, |[ra, rb]: Idx<2>| { + [vec![ra[0], rb[0]], vec![ra[2]]] + }) // Applies gamma to |ra[0]>|rb[0] ra[2]> - .apply_subcircuit(|[ra, rb]| [vec![ra[0]], vec![rb[0], ra[2]]], &gamma_circuit) + .apply(gamma_circuit, |[ra, rb]: Idx<2>| { + [vec![ra[0]], vec![rb[0], ra[2]]] + }) + // Applies a more complex subcircuit to |ra[1]>|ra[2]>|rb> + .apply( + Circuit::default() + .apply(gamma, [0, 1]) + .apply(gamma, [1, 2]) + .apply( + |b: &mut CurrentBuilderType, rs| { + let [ra, rb] = rs; + let (ra, rb) = b.cnot(ra, rb)?; + Ok([ra, rb]) + }, + |[_, r2, r3]: Idx<3>| [r2, vec![r3[0]]], + ), + |[ra, rb]: Idx<2>| [vec![ra[1]], vec![ra[2]], rb], + ) .input([ra, rb]) .run(&mut b)?; diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..86136c4 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "beta" +components = [ "rustfmt", "rustc-dev" ] From 62bf9987d5904f4b9bc42aa243f6e2bae17bf10c Mon Sep 17 00:00:00 2001 From: Tdxdxoz Date: Tue, 5 Mar 2024 14:44:33 +0800 Subject: [PATCH 5/5] apply_when --- qip/src/circuit.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/qip/src/circuit.rs b/qip/src/circuit.rs index d1ed6c2..9bebe61 100644 --- a/qip/src/circuit.rs +++ b/qip/src/circuit.rs @@ -207,6 +207,20 @@ impl Circuit { } } + /// Apply a function to part of this circuit when flag is true + pub fn apply_when( + self, + flag: bool, + subcircuit: impl AsSubcircuit, + indices: impl IndicesInfo, + ) -> Self { + if flag { + self.apply(subcircuit, indices) + } else { + self + } + } + /// Set input for circuit pub fn input(self, input: Registers) -> CircuitWithInput { CircuitWithInput { @@ -255,13 +269,15 @@ mod tests { let gamma_circuit = Circuit::from(gamma); + let measure_result_0 = 1; + let [ra, rb] = Circuit::default() // Applies gamma to |ra>|rb> .apply(gamma, [0, 1]) + // Applies gamma to |rb>|ra> when measure_result_0 = 1 + .apply_when(measure_result_0 == 1, gamma, [1, 0]) // Applies gamma to |ra[0] ra[1]>|ra[2]> - .apply(gamma, |[ra, _]: Idx<2>| { - [ra[0..=1].to_vec(), vec![ra[2]]] - }) + .apply(gamma, |[ra, _]: Idx<2>| [ra[0..=1].to_vec(), vec![ra[2]]]) // Applies gamma to |ra[0] rb[0]>|ra[2]> .apply(&gamma_circuit, |[ra, rb]: Idx<2>| { [vec![ra[0], rb[0]], vec![ra[2]]]