diff --git a/Cargo.toml b/Cargo.toml index 6d98655..3c10cbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qip" -version = "0.16.0" +version = "1.0.0" authors = ["Sumner Hearth "] description = "A library for efficient quantum computing simulations." repository = "https://github.com/Renmusxd/RustQIP" @@ -19,6 +19,6 @@ optimization = [] num-rational = "^0.4" num-traits = "^0.2" num-complex = "^0.4" -rayon = {version = "^1.5", optional = true } +rayon = { version = "^1.5", optional = true } rand = "^0.8" -smallvec = "^1.7" \ No newline at end of file +smallvec = "^1.8" \ No newline at end of file diff --git a/examples/optimizer_example.rs b/examples/optimizer_example.rs index 48f34c6..382e90c 100644 --- a/examples/optimizer_example.rs +++ b/examples/optimizer_example.rs @@ -1,4 +1,3 @@ -use qip::builder::{BuilderCircuitObject, Qudit}; use qip::macros::program_ops::*; use qip::prelude::*; use rand::{thread_rng, Rng}; @@ -88,7 +87,7 @@ fn main() -> Result<(), CircuitError> { let mut b = LocalBuilder::::default(); let r = b.register(NonZeroUsize::new(3).unwrap()); - b.apply_optimizer_circuit(r, opt.get_ops()); + b.apply_optimizer_circuit(r, opt.get_ops())?; let (state, _) = b.calculate_state(); println!("{:?}", state); diff --git a/src/builder.rs b/src/builder.rs index 374d22f..a907c96 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -90,13 +90,13 @@ pub struct BuilderCircuitObject { object: BuilderCircuitObjectType

, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum BuilderCircuitObjectType { Unitary(UnitaryMatrixObject

), Measurement(MeasurementObject), } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum UnitaryMatrixObject { X, Y, @@ -114,7 +114,7 @@ pub enum UnitaryMatrixObject { GlobalPhase(RotationObject

), } -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone)] pub enum RotationObject { Floating(P), PiRational(Ratio), @@ -129,6 +129,110 @@ impl RotationObject

{ } } +impl PartialEq for UnitaryMatrixObject

{ + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::X, Self::X) + | (Self::Y, Self::Y) + | (Self::Z, Self::Z) + | (Self::H, Self::H) + | (Self::S, Self::S) + | (Self::T, Self::T) + | (Self::CNOT, Self::CNOT) + | (Self::SWAP, Self::SWAP) => true, + (Self::Rz(ra), Self::Rz(rb)) => ra.eq(rb), + (Self::MAT(ma), Self::MAT(mb)) => ma.eq(mb), + (Self::GlobalPhase(ra), Self::GlobalPhase(rb)) => ra.eq(rb), + (_, _) => false, + } + } +} + +impl PartialEq for BuilderCircuitObjectType

{ + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Unitary(ua), Self::Unitary(ub)) => ua.eq(ub), + (Self::Measurement(ma), Self::Measurement(mb)) => ma.eq(mb), + (_, _) => false, + } + } +} + +impl PartialEq for RotationObject

{ + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Floating(pa), Self::Floating(pb)) => pa.eq(pb), + (Self::PiRational(ra), Self::PiRational(rb)) => ra.eq(rb), + (_, _) => false, + } + } +} + +impl Eq for UnitaryMatrixObject

{} + +impl Eq for BuilderCircuitObjectType

{} + +impl Eq for RotationObject

{} + +fn hash_p(f: P, state: &mut H) { + format!("{}", f).hash(state) +} + +impl Hash for BuilderCircuitObjectType

{ + fn hash(&self, state: &mut H) { + match self { + BuilderCircuitObjectType::Measurement(m) => { + state.write_i8(0); + m.hash(state) + } + BuilderCircuitObjectType::Unitary(u) => { + state.write_i8(1); + u.hash(state) + } + } + } +} + +impl Hash for UnitaryMatrixObject

{ + fn hash(&self, state: &mut H) { + match self { + UnitaryMatrixObject::X => state.write_i8(0), + UnitaryMatrixObject::Y => state.write_i8(1), + UnitaryMatrixObject::Z => state.write_i8(2), + UnitaryMatrixObject::H => state.write_i8(3), + UnitaryMatrixObject::S => state.write_i8(4), + UnitaryMatrixObject::T => state.write_i8(5), + UnitaryMatrixObject::CNOT => state.write_i8(6), + UnitaryMatrixObject::SWAP => state.write_i8(7), + UnitaryMatrixObject::Rz(rot) => { + state.write_i8(8); + rot.hash(state); + } + UnitaryMatrixObject::GlobalPhase(rot) => { + state.write_i8(9); + rot.hash(state); + } + UnitaryMatrixObject::MAT(data) => { + state.write_i8(10); + data.iter().for_each(|c| { + hash_p(c.re, state); + hash_p(c.im, state); + }) + } + } + } +} + +impl Hash for RotationObject

{ + fn hash(&self, state: &mut H) { + match self { + // Grossly inefficient but also don't hash floats. + RotationObject::Floating(f) => hash_p(*f, state), + RotationObject::PiRational(r) => r.hash(state), + } + } +} + #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub enum MeasurementObject { Measurement, @@ -247,15 +351,15 @@ impl CircuitBuilder for LocalBuilder

{ let mut initial_index = 0; it.into_iter() - .map(|(r, x)| { + .flat_map(|(r, x)| { + let rn = r.n(); r.indices .iter() .rev() .cloned() .enumerate() - .map(move |(ri, i)| (r.n() - 1 - i, (x >> ri) & 1)) + .map(move |(ri, i)| (n - 1 - i, (x >> (rn - 1 - ri)) & 1)) }) - .flatten() .for_each(|(index, bit)| initial_index |= bit << index); state[initial_index] = Complex::one(); @@ -707,8 +811,7 @@ where let mut rs = cb.split_all_register(r); let max_r_index = sc .iter() - .map(|(indices, _)| indices.iter().cloned().max()) - .flatten() + .flat_map(|(indices, _)| indices.iter().cloned().max()) .max() .unwrap(); // Need temp qubits for the max_r_index - rn @@ -797,68 +900,6 @@ pub mod optimizers { use crate::optimizer::mc_optimizer::MonteCarloOptimizer; use std::path::Path; - impl Eq for UnitaryMatrixObject

{} - impl Eq for BuilderCircuitObjectType

{} - - impl Hash for BuilderCircuitObjectType

{ - fn hash(&self, state: &mut H) { - match self { - BuilderCircuitObjectType::Measurement(m) => { - state.write_i8(0); - m.hash(state) - } - BuilderCircuitObjectType::Unitary(u) => { - state.write_i8(1); - u.hash(state) - } - } - } - } - - impl Hash for UnitaryMatrixObject

{ - fn hash(&self, state: &mut H) { - match self { - UnitaryMatrixObject::X => state.write_i8(0), - UnitaryMatrixObject::Y => state.write_i8(1), - UnitaryMatrixObject::Z => state.write_i8(2), - UnitaryMatrixObject::H => state.write_i8(3), - UnitaryMatrixObject::S => state.write_i8(4), - UnitaryMatrixObject::T => state.write_i8(5), - UnitaryMatrixObject::CNOT => state.write_i8(6), - UnitaryMatrixObject::SWAP => state.write_i8(7), - UnitaryMatrixObject::Rz(rot) => { - state.write_i8(8); - rot.hash(state); - } - UnitaryMatrixObject::GlobalPhase(rot) => { - state.write_i8(9); - rot.hash(state); - } - UnitaryMatrixObject::MAT(data) => { - state.write_i8(10); - data.iter().for_each(|c| { - hash_p(c.re, state); - hash_p(c.im, state); - }) - } - } - } - } - - impl Hash for RotationObject

{ - fn hash(&self, state: &mut H) { - match self { - // Grossly inefficient but also don't hash floats. - RotationObject::Floating(f) => hash_p(*f, state), - RotationObject::PiRational(r) => r.hash(state), - } - } - } - - fn hash_p(f: P, state: &mut H) { - format!("{}", f).hash(state) - } - pub type OptimizerTrie

= IndexTrie< (Vec, BuilderCircuitObjectType

), Vec<(Vec, BuilderCircuitObjectType

)>, diff --git a/src/builder_traits.rs b/src/builder_traits.rs index 1dd3075..903d34d 100644 --- a/src/builder_traits.rs +++ b/src/builder_traits.rs @@ -37,7 +37,9 @@ pub trait CircuitBuilder { fn qubit(&mut self) -> Self::Register { self.register(NonZeroUsize::new(1).unwrap()) } - + fn qudit(&mut self, n: usize) -> Option { + NonZeroUsize::new(n).map(|n| self.register(n)) + } fn register(&mut self, n: NonZeroUsize) -> Self::Register; fn try_register(&mut self, n: usize) -> Option { @@ -115,11 +117,10 @@ pub trait CircuitBuilder { .collect::>(); let selected_rs = indices .into_iter() - .map(|is| { + .flat_map(|is| { let subrs = is.into_iter().map(|i| rs[i].take().unwrap()); self.merge_registers(subrs) }) - .flatten() .collect(); let remaining_rs = self.merge_registers(rs.into_iter().flatten()); match remaining_rs { diff --git a/src/lib.rs b/src/lib.rs index 2d3a7db..739d9ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,105 @@ unused_qualifications )] -pub mod boolean_circuits; +//! Quantum Computing library leveraging graph building to build efficient quantum circuit +//! simulations. +//! Rust is a great language for quantum computing with gate models because the borrow checker +//! is very similar to the [No-cloning theorem](https://wikipedia.org/wiki/No-cloning_theorem). +//! +//! See all the examples in the [examples directory](https://github.com/Renmusxd/RustQIP/tree/master/examples) of the Github repository. +//! +//! # Example (CSWAP) +//! Here's an example of a small circuit where two groups of Registers are swapped conditioned on a +//! third. This circuit is very small, only three operations plus a measurement, so the boilerplate +//! can look quite large in comparison, but that setup provides the ability to construct circuits +//! easily and safely when they do get larger. +//! ``` +//! use qip::prelude::*; +//! use std::num::NonZeroUsize; +//! +//! # fn main() -> CircuitResult<()> { +//! // Make a new circuit builder. +//! let mut b = LocalBuilder::::default(); +//! let n = NonZeroUsize::new(3).unwrap(); +//! +//! // Make three registers of sizes 1, 3, 3 (7 qubits total). +//! let q = b.qubit(); // Same as b.register(1)?; +//! let ra = b.register(n); +//! let rb = b.register(n); +//! +//! // Define circuit +//! // First apply an H to q +//! let q = b.h(q); +//! // Then swap ra and rb, conditioned on q. +//! let mut cb = b.condition_with(q); +//! let (ra, rb) = cb.swap(ra, rb)?; +//! let q = cb.dissolve(); +//! // Finally apply H to q again. +//! let q = b.h(q); +//! +//! // Add a measurement to the first qubit, save a reference so we can get the result later. +//! let (q, m_handle) = b.measure(q); +//! +//! // Now q is the end result of the above circuit, and we can run the circuit by referencing it. +//! +//! // Run circuit with a given precision. +//! let (_, measured) = b.calculate_state_with_init([(&ra, 0b000), (&rb, 0b001)]); +//! +//! // Lookup the result of the measurement we performed using the handle, and the probability +//! // of getting that measurement. +//! let (result, p) = measured.get_measurement(m_handle); +//! +//! // Print the measured result +//! println!("Measured: {:?} (with chance {:?})", result, p); +//! # Ok(()) +//! # } +//! ``` +//! +//! # The Program Macro +//! While the borrow checker included in rust is a wonderful tool for checking that our registers +//! are behaving, it can be cumbersome. For that reason qip also includes a macro which provides an +//! API similar to that which you would see in quantum computing textbooks. +//! *Notice that due to a design choice in rust's `macro_rules!` we use vertical bars to group qubits +//! and a comma must appear before the closing bar. This may be fixed in the future using procedural +//! macros.* +//! ``` +//! use qip::prelude::*; +//! use std::num::NonZeroUsize; +//! # fn main() -> CircuitResult<()> { +//! +//! let n = NonZeroUsize::new(3).unwrap(); +//! let mut b = LocalBuilder::default(); +//! let ra = b.register(n); +//! let rb = b.register(n); +//! +//! fn gamma(b: &mut B, mut rs: Vec) -> CircuitResult> +//! where B: AdvancedCircuitBuilder +//! { +//! let rb = rs.pop().ok_or(CircuitError::new("No rb provided"))?; +//! let ra = rs.pop().ok_or(CircuitError::new("No ra provided"))?; +//! let (ra, rb) = b.toffoli(ra, rb)?; +//! let (rb, ra) = b.toffoli(rb, ra)?; +//! Ok(vec![ra, rb]) +//! } +//! +//! let (ra, rb) = program!(&mut b, ra, rb; +//! // Applies gamma to |ra[0] ra[1]>|ra[2]> +//! gamma ra[0..2], ra[2]; +//! // Applies gamma to |ra[0] rb[0]>|ra[2]> +//! gamma |ra[0], rb[0],| ra[2]; +//! // Applies gamma to |ra[0]>|rb[0] ra[2]> +//! gamma ra[0], |rb[0], ra[2],|; +//! // Applies gamma to |ra[0] ra[1]>|ra[2]> if rb == |111> +//! control gamma rb, ra[0..2], ra[2]; +//! // Applies gamma to |ra[0] ra[1]>|ra[2]> if rb == |110> (rb[0] == |0>, rb[1] == 1, ...) +//! control(0b110) gamma rb, ra[0..2], ra[2]; +//! )?; +//! let r = b.merge_two_registers(ra, rb); +//! +//! # Ok(()) +//! # } +//! ``` + pub mod builder; pub mod builder_traits; pub mod conditioning; @@ -25,6 +123,7 @@ pub mod types; pub mod utils; pub use num_complex::Complex; +pub use rand; pub use types::Precision; pub mod prelude { diff --git a/src/macros/inverter.rs b/src/macros/inverter.rs index 112d233..3d84b6d 100644 --- a/src/macros/inverter.rs +++ b/src/macros/inverter.rs @@ -37,13 +37,15 @@ where .collect::<_>(); let _ = f(&mut sub_cb, sub_rs, t)?; let subcircuit = sub_cb.make_subcircuit()?; - let rns = rs.iter().map(|r| r.n()).collect::>(); + let (_, ranges) = rs + .iter() + .map(|r| r.n()) + .fold((0, vec![]), |(n, mut acc), rn| { + acc.push(n..n + rn); + (n + rn, acc) + }); let r = cb.merge_registers(rs).unwrap(); let r = cb.apply_inverted_subcircuit(subcircuit, r)?; - let (_, ranges) = rns.into_iter().fold((0, vec![]), |(n, mut acc), rn| { - acc.push(n..n + rn); - (n + rn, acc) - }); match cb.split_relative_index_groups(r, ranges) { SplitManyResult::AllSelected(rs) => Ok(rs), SplitManyResult::Remaining(_, _) => unreachable!(), @@ -175,3 +177,132 @@ macro_rules! wrap_and_invert { invert_fn!($newinvert, $newfunc); } } + +#[cfg(test)] +mod inverter_test { + // use super::*; + // use crate::builder::Qudit; + // use crate::macros::program_ops::*; + // use crate::prelude::*; + // use num_traits::One; + // + // fn test_inversion(b: &mut LocalBuilder

, rs: Vec, f: F) -> CircuitResult<()> + // where + // P: Precision, + // F: Fn(&mut LocalBuilder

, Vec) -> CircuitResult>, + // { + // let rs = f(b, rs)?; + // let rs = inverter(b, rs, f)?; + // let r = b + // .merge_registers(rs) + // .ok_or(CircuitError::new("No registers returned."))?; + // let indices = (0..b.n()).collect::>(); + // (0..1 << b.n()).for_each(|indx| { + // let (state, _) = b.calculate_state_with_init([(&r, indx)]); + // let pos = state.into_iter().position(|v| v == Complex::one()); + // // .map(|pos| flip_bits(n as usize, pos as u64)); + // assert!(pos.is_some()); + // assert_eq!(pos.unwrap(), indx); + // }); + // Ok(()) + // } + // + // #[test] + // fn test_invert_x() -> Result<(), CircuitError> { + // wrap_fn!(x_op, x, r); + // let mut b = LocalBuilder::::default(); + // let r = b.qubit(); + // test_inversion(&mut b, vec![r], x_op) + // } + // + // #[test] + // fn test_invert_y() -> Result<(), CircuitError> { + // wrap_fn!(y_op, y, r); + // let mut b = LocalBuilder::::default(); + // let r = b.qubit(); + // test_inversion(&mut b, vec![r], y_op) + // } + // + // #[test] + // fn test_invert_multi() -> Result<(), CircuitError> { + // fn gamma( + // b: &mut dyn UnitaryBuilder, + // ra: Register, + // rb: Register, + // ) -> Result<(Register, Register), CircuitError> { + // let (ra, rb) = b.cy(ra, rb); + // b.swap(ra, rb) + // } + // wrap_fn!(wrap_gamma, (gamma), ra, rb); + // let mut b = LocalBuilder::::default(); + // let ra = b.qubit(); + // let rb = b.qubit(); + // test_inversion(&mut b, vec![ra, rb], wrap_gamma) + // } + // + // #[test] + // fn test_invert_add() -> Result<(), CircuitError> { + // let mut b = LocalBuilder::::default(); + // let rc = b.qubit(); + // let ra = b.qubit(); + // let rb = b.register(2)?; + // test_inversion(&mut b, vec![rc, ra, rb], add_op) + // } + // + // #[test] + // fn test_invert_add_larger() -> Result<(), CircuitError> { + // let mut b = LocalBuilder::::default(); + // let rc = b.register(2)?; + // let ra = b.register(2)?; + // let rb = b.register(3)?; + // test_inversion(&mut b, vec![rc, ra, rb], add_op) + // } + // + // #[test] + // fn test_invert_and_wrap_add() -> Result<(), CircuitError> { + // wrap_and_invert!(add_op, inv_add, (add), rc, ra, rb); + // let mut b = LocalBuilder::::default(); + // let rc = b.register(2)?; + // let ra = b.register(2)?; + // let rb = b.register(3)?; + // let rs = add_op(&mut b, vec![rc, ra, rb])?; + // let rs = inv_add(&mut b, rs)?; + // let _r = b.merge(rs)?; + // Ok(()) + // } + // + // #[test] + // fn test_invert_add_larger_macro() -> Result<(), CircuitError> { + // invert_fn!(inv_add, add_op); + // let mut b = LocalBuilder::::default(); + // let rc = b.register(2)?; + // let ra = b.register(2)?; + // let rb = b.register(3)?; + // let rs = inv_add(&mut b, vec![rc, ra, rb])?; + // let _r = b.merge(rs)?; + // Ok(()) + // } + // + // #[test] + // fn test_invert_and_wrap_rz() -> Result<(), CircuitError> { + // wrap_and_invert!(rz_op(theta: f64), inv_rz, UnitaryBuilder::rz, r); + // let mut b = LocalBuilder::::default(); + // let r = b.qubit(); + // let rs = rz_op(&mut b, vec![r], 1.0)?; + // let _rs = inv_rz(&mut b, rs, 1.0)?; + // Ok(()) + // } + // + // #[test] + // fn test_invert_and_wrap_rz_generic() -> Result<(), CircuitError> { + // fn rz>(b: &mut dyn UnitaryBuilder, r: Register, theta: T) -> Register { + // b.rz(r, theta.into()) + // } + // wrap_and_invert!(rz_op[T: Into](theta: T), inv_rz, rz, r); + // let mut b = LocalBuilder::::default(); + // let r = b.qubit(); + // let rs = rz_op(&mut b, vec![r], 1.0)?; + // let _rs = inv_rz(&mut b, rs, 1.0)?; + // Ok(()) + // } +} diff --git a/src/macros/program.rs b/src/macros/program.rs index 6822954..d8615df 100644 --- a/src/macros/program.rs +++ b/src/macros/program.rs @@ -416,8 +416,7 @@ impl RegisterManager { /// Give qubits back to any registers. pub fn return_registers>(&mut self, b: &mut CB, rs: Vec) { rs.into_iter() - .map(|r| b.split_all_register(r)) - .flatten() + .flat_map(|r| b.split_all_register(r)) .for_each(|r: R| { let (reg_indx, rel_indx) = self.reverse_lookup[r.indices()[0]].unwrap(); let (_, qubits) = &mut self.registers[reg_indx]; @@ -445,109 +444,128 @@ impl From> for QubitIndices { indices.into() } } + impl From> for QubitIndices { fn from(indices: Range) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: Range) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: Range) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: Range) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From<&Range> for QubitIndices { fn from(indices: &Range) -> Self { (indices.start..indices.end).into() } } + impl From<&Range> for QubitIndices { fn from(indices: &Range) -> Self { (indices.start..indices.end).into() } } + impl From<&Range> for QubitIndices { fn from(indices: &Range) -> Self { (indices.start..indices.end).into() } } + impl From<&Range> for QubitIndices { fn from(indices: &Range) -> Self { (indices.start..indices.end).into() } } + impl From<&Range> for QubitIndices { fn from(indices: &Range) -> Self { (indices.start..indices.end).into() } } + impl From> for QubitIndices { fn from(indices: RangeInclusive) -> Self { let indices: Vec<_> = indices.collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: RangeInclusive) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: RangeInclusive) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: RangeInclusive) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From> for QubitIndices { fn from(indices: RangeInclusive) -> Self { let indices: Vec<_> = indices.map(|indx| indx as usize).collect(); indices.into() } } + impl From<&RangeInclusive> for QubitIndices { fn from(indices: &RangeInclusive) -> Self { let indices: Vec<_> = indices.clone().collect(); indices.into() } } + impl From<&RangeInclusive> for QubitIndices { fn from(indices: &RangeInclusive) -> Self { let indices: Vec<_> = indices.clone().map(|indx| indx as usize).collect(); indices.into() } } + impl From<&RangeInclusive> for QubitIndices { fn from(indices: &RangeInclusive) -> Self { let indices: Vec<_> = indices.clone().map(|indx| indx as usize).collect(); indices.into() } } + impl From<&RangeInclusive> for QubitIndices { fn from(indices: &RangeInclusive) -> Self { let indices: Vec<_> = indices.clone().map(|indx| indx as usize).collect(); indices.into() } } + impl From<&RangeInclusive> for QubitIndices { fn from(indices: &RangeInclusive) -> Self { let indices: Vec<_> = indices.clone().map(|indx| indx as usize).collect(); @@ -569,6 +587,7 @@ impl From for QubitIndices { } } } + impl From<&usize> for QubitIndices { fn from(item: &usize) -> Self { (*item).into() @@ -582,6 +601,7 @@ impl From for QubitIndices { } } } + impl From<&u64> for QubitIndices { fn from(item: &u64) -> Self { (*item).into() @@ -595,6 +615,7 @@ impl From for QubitIndices { } } } + impl From<&u32> for QubitIndices { fn from(item: &u32) -> Self { (*item).into() @@ -608,6 +629,7 @@ impl From for QubitIndices { } } } + impl From<&i64> for QubitIndices { fn from(item: &i64) -> Self { (*item).into() @@ -621,6 +643,7 @@ impl From for QubitIndices { } } } + impl From<&i32> for QubitIndices { fn from(item: &i32) -> Self { (*item).into() diff --git a/src/optimizer/index_trie.rs b/src/optimizer/index_trie.rs index 08a7897..b300ce8 100644 --- a/src/optimizer/index_trie.rs +++ b/src/optimizer/index_trie.rs @@ -14,6 +14,7 @@ struct Node { values: Vec, children: HashMap>, } + impl Default for Node { fn default() -> Self { Self { @@ -91,7 +92,7 @@ where }; if !line.is_empty() { let (lhs, rhs) = line - .split_once("=") + .split_once('=') .ok_or_else(|| CircuitError::new("Line missing '='"))?; let lhs = parse_str(lhs, &f)?; let rhs = parse_str(rhs, &f)?; @@ -108,8 +109,7 @@ where rhs.reverse(); (lhs, rhs) }) - .map(|(lhs, rhs)| [(lhs.clone(), rhs.clone()), (rhs, lhs)]) - .flatten(); + .flat_map(|(lhs, rhs)| [(lhs.clone(), rhs.clone()), (rhs, lhs)]); Ok(Self::new(iter)) } diff --git a/src/optimizer/mc_optimizer.rs b/src/optimizer/mc_optimizer.rs index 92109aa..b391f07 100644 --- a/src/optimizer/mc_optimizer.rs +++ b/src/optimizer/mc_optimizer.rs @@ -343,8 +343,7 @@ where } let max_index_needed = attempt .iter() - .map(|(indices, _)| indices.iter()) - .flatten() + .flat_map(|(indices, _)| indices.iter()) .cloned() .max() .unwrap(); @@ -415,12 +414,11 @@ where |(node, energy, count), co| (node, energy + energy_function(co), count + 1), ) .into_iter() - .map(|((starting_node, energy, count), indices, exchanges)| { + .flat_map(|((starting_node, energy, count), indices, exchanges)| { exchanges.into_iter().map(move |exchange| { (starting_node, energy, count, indices.clone(), exchange) }) }) - .flatten() .collect::>(); res.shuffle(rng); diff --git a/src/state_ops/matrix_ops.rs b/src/state_ops/matrix_ops.rs index b7c6269..25f1c38 100644 --- a/src/state_ops/matrix_ops.rs +++ b/src/state_ops/matrix_ops.rs @@ -367,10 +367,9 @@ pub fn apply_ops( _ => { let mat_indices: Vec = ops .iter() - .map(|op| -> Vec { + .flat_map(|op| -> Vec { (0..num_indices(op)).map(|i| get_index(op, i)).collect() }) - .flatten() .collect(); let row_fn = |(outputrow, outputloc): (usize, &mut Complex

)| {