From 55c7e4ee9af2c0032aea80d703264dd7204a4701 Mon Sep 17 00:00:00 2001 From: John Dumbell Date: Wed, 4 Sep 2024 16:07:38 +0100 Subject: [PATCH] WIP v4 --- src/rasqal/src/analysis/solver.rs | 1301 +++++++++++++++-------------- src/rasqal/src/builders.rs | 1 + 2 files changed, 655 insertions(+), 647 deletions(-) diff --git a/src/rasqal/src/analysis/solver.rs b/src/rasqal/src/analysis/solver.rs index 739a5d6..2f91eb4 100644 --- a/src/rasqal/src/analysis/solver.rs +++ b/src/rasqal/src/analysis/solver.rs @@ -33,394 +33,394 @@ macro_rules! C { /// Construct which holds the entanglement information between two qubits, shared between both. #[derive(Clone)] pub struct Tangle { - upper_left: i64, - state: Ptr, - bottom_right: i64 + upper_left: i64, + state: Ptr, + bottom_right: i64 } impl Tangle { - pub fn from_qubits( - left: (&i64, &Ptr), right: (&i64, &Ptr) - ) -> Tangle { - let fragment = Ptr::from(EntangledFragment::EntangledFromExisting(left.1, right.1)); - Tangle { - upper_left: *left.0, - state: fragment, - bottom_right: *right.0 - } + pub fn from_qubits( + left: (&i64, &Ptr), right: (&i64, &Ptr) + ) -> Tangle { + let fragment = Ptr::from(EntangledFragment::EntangledFromExisting(left.1, right.1)); + Tangle { + upper_left: *left.0, + state: fragment, + bottom_right: *right.0 } + } } #[derive(Clone)] pub struct EntangledWith { - qubit: i64, + qubit: i64, - /// Entanglement ratio with this particular qubit. - ratio: f64 + /// Entanglement ratio with this particular qubit. + ratio: f64 } impl EntangledWith { - pub fn new(qubit: i64, ratio: f64) -> EntangledWith { - EntangledWith { - qubit, ratio - } + pub fn new(qubit: i64, ratio: f64) -> EntangledWith { + EntangledWith { + qubit, ratio } + } } #[derive(Clone)] pub struct MeasureAnalysis { - result: f64, - entangled_with: Vec + result: f64, + entangled_with: Vec } impl MeasureAnalysis { - pub fn qubit(result: f64) -> MeasureAnalysis { - MeasureAnalysis { - result, - entangled_with: Vec::new() - } + pub fn qubit(result: f64) -> MeasureAnalysis { + MeasureAnalysis { + result, + entangled_with: Vec::new() } + } - pub fn entangled_qubit( - result: f64, entangled_with: Vec - ) -> MeasureAnalysis { - MeasureAnalysis { - result, - entangled_with - } + pub fn entangled_qubit( + result: f64, entangled_with: Vec + ) -> MeasureAnalysis { + MeasureAnalysis { + result, + entangled_with } + } } impl Display for MeasureAnalysis { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("1 = {}%", self.result * 100.)) - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("1 = {}%", self.result * 100.)) + } } #[derive(Clone)] pub struct AnalysisQubit { - index: i64, + index: i64, - /// Record of the raw qubit sans entanglement. - state: Ptr, + /// Record of the raw qubit sans entanglement. + state: Ptr, - /// All the tangles between this qubit and others. Key is the index of the other qubit, along - /// with a 4x4 density matrix. - tangles: Ptr>> + /// All the tangles between this qubit and others. Key is the index of the other qubit, along + /// with a 4x4 density matrix. + tangles: Ptr>> } impl AnalysisQubit { - pub fn new( - index: &i64, qubit: &Ptr, tangles: &HashMap> - ) -> AnalysisQubit { - AnalysisQubit { - index: *index, - state: qubit.clone(), - tangles: Ptr::from(tangles.clone()) - } + pub fn new( + index: &i64, qubit: &Ptr, tangles: &HashMap> + ) -> AnalysisQubit { + AnalysisQubit { + index: *index, + state: qubit.clone(), + tangles: Ptr::from(tangles.clone()) } + } - pub fn with_index(index: &i64) -> AnalysisQubit { - AnalysisQubit { - index: *index, - state: Ptr::from(QubitFragment::DefaultQubit()), - tangles: Ptr::from(HashMap::default()) - } + pub fn with_index(index: &i64) -> AnalysisQubit { + AnalysisQubit { + index: *index, + state: Ptr::from(QubitFragment::DefaultQubit()), + tangles: Ptr::from(HashMap::default()) } + } - pub fn with_fragment(index: &i64, qubit: &Ptr) -> AnalysisQubit { - AnalysisQubit { - index: *index, - state: qubit.clone(), - tangles: Ptr::from(HashMap::default()) - } + pub fn with_fragment(index: &i64, qubit: &Ptr) -> AnalysisQubit { + AnalysisQubit { + index: *index, + state: qubit.clone(), + tangles: Ptr::from(HashMap::default()) } + } - pub fn entangled_with(&self) -> Keys<'_, i64, Ptr> { self.tangles.keys() } - - pub fn is_entangled_with(&self, index: &i64) -> bool { self.tangles.contains_key(index) } + pub fn entangled_with(&self) -> Keys<'_, i64, Ptr> { self.tangles.keys() } - pub fn entangle(&self, other: &Ptr) { - if self.is_entangled_with(&other.index) { - return; - } + pub fn is_entangled_with(&self, index: &i64) -> bool { self.tangles.contains_key(index) } - let tangle = Ptr::from(Tangle::from_qubits( - (&self.index, &self.state), - (&other.index, &other.state) - )); - with_mutable_self!(self.tangles.insert(other.index, tangle.clone())); - with_mutable_self!(other.tangles.insert(self.index, tangle)); + pub fn entangle(&self, other: &Ptr) { + if self.is_entangled_with(&other.index) { + return; } - /// Returns 0...x depending upon what this qubit would be measured as. - pub fn measure(&self) -> MeasureAnalysis { - if self.tangles.is_empty() { - MeasureAnalysis::qubit(self.state.get((1, 1)).re) - } else { - let mut results = Vec::new(); - for (key, tangle) in self.tangles.iter() { - let entangled = tangle.state.get((3, 0)).re; - let mut max = tangle.state.get((0, 0)).re; - let mut next = tangle.state.get((1, 1)).re; - if next >= max { - max = next; - } - - next = tangle.state.get((2, 2)).re;; - if next >= max { - max = next; - } - - next = tangle.state.get((3, 3)).re; - if next >= max { - max = next; - } - - results.push(EntangledWith::new(*key, (max / entangled) * 100.)); - } - - MeasureAnalysis::entangled_qubit(self.state.get((1, 1)).re, results) + let tangle = Ptr::from(Tangle::from_qubits( + (&self.index, &self.state), + (&other.index, &other.state) + )); + with_mutable_self!(self.tangles.insert(other.index, tangle.clone())); + with_mutable_self!(other.tangles.insert(self.index, tangle)); + } + + /// Returns 0...x depending upon what this qubit would be measured as. + pub fn measure(&self) -> MeasureAnalysis { + if self.tangles.is_empty() { + MeasureAnalysis::qubit(self.state.get((1, 1)).re) + } else { + let mut results = Vec::new(); + for (key, tangle) in self.tangles.iter() { + let entangled = tangle.state.get((3, 0)).re; + let mut max = tangle.state.get((0, 0)).re; + let mut next = tangle.state.get((1, 1)).re; + if next >= max { + max = next; } - } - /// Applies this gate to this qubit and all tangles. - pub fn apply(&self, gate: &GateFragment) { - with_mutable_self!(self.state.apply(gate)); - for tangle in self.tangles.values() { - let expanded_gate = if gate.affected_qubits == 2 { - gate - } else if tangle.bottom_right == self.index { - &MatrixFragment::id().tensor(gate) - } else { - &gate.tensor(&MatrixFragment::id()) - }; - - with_mutable!(tangle.state.apply(expanded_gate)); + next = tangle.state.get((2, 2)).re;; + if next >= max { + max = next; } + + next = tangle.state.get((3, 3)).re; + if next >= max { + max = next; + } + + results.push(EntangledWith::new(*key, (max / entangled) * 100.)); + } + + MeasureAnalysis::entangled_qubit(self.state.get((1, 1)).re, results) } + } - pub fn X(&self, radians: &f64) { self.apply(&GateFragment::X(radians)); } + /// Applies this gate to this qubit and all tangles. + pub fn apply(&self, gate: &GateFragment) { + with_mutable_self!(self.state.apply(gate)); + for tangle in self.tangles.values() { + let expanded_gate = if gate.affected_qubits == 2 { + gate + } else if tangle.bottom_right == self.index { + &MatrixFragment::id().tensor(gate) + } else { + &gate.tensor(&MatrixFragment::id()) + }; - pub fn Y(&self, radians: &f64) { self.apply(&GateFragment::Y(radians)); } + with_mutable!(tangle.state.apply(expanded_gate)); + } + } - pub fn Z(&self, radians: &f64) { self.apply(&GateFragment::Z(radians)) } + pub fn X(&self, radians: &f64) { self.apply(&GateFragment::X(radians)); } - // TODO: Pretty sure these require target qubit alignment. - // (Aka now that left/right qubits are not garanteed, need to just re-align). + pub fn Y(&self, radians: &f64) { self.apply(&GateFragment::Y(radians)); } - pub fn CX(&self, control: &i64, radians: &f64) { self.apply(&GateFragment::CX(radians)) } + pub fn Z(&self, radians: &f64) { self.apply(&GateFragment::Z(radians)) } - pub fn CZ(&mut self, control: &i64, radians: &f64) { self.apply(&GateFragment::CZ(radians)) } + // TODO: Pretty sure these require target qubit alignment. + // (Aka now that left/right qubits are not garanteed, need to just re-align). - pub fn CY(&mut self, control: &i64, radians: &f64) { self.apply(&GateFragment::CY(radians)) } + pub fn CX(&self, control: &i64, radians: &f64) { self.apply(&GateFragment::CX(radians)) } - pub fn stringify(&self, indent_level: i32) -> Vec { - let mut result = Vec::new(); - let mut base_indent = String::new(); - for multiplier in 0..indent_level { - base_indent = format!("{} ", base_indent); - } - let indent = format!("{} ", base_indent); + pub fn CZ(&mut self, control: &i64, radians: &f64) { self.apply(&GateFragment::CZ(radians)) } - result.push(format!("{}{{\n", base_indent)); + pub fn CY(&mut self, control: &i64, radians: &f64) { self.apply(&GateFragment::CY(radians)) } + + pub fn stringify(&self, indent_level: i32) -> Vec { + let mut result = Vec::new(); + let mut base_indent = String::new(); + for multiplier in 0..indent_level { + base_indent = format!("{} ", base_indent); + } + let indent = format!("{} ", base_indent); + + result.push(format!("{}{{\n", base_indent)); + result.push(format!( + "{}Q{}: {}\n", + indent, + self.index, + self.measure() + )); + for matrix_fragment in stringified_matrix_lines(&self.state.matrix_fragment.matrix) { + result.push(format!("{}{}\n", indent, matrix_fragment)); + } + + if !self.tangles.is_empty() { + let mut tangles = self.tangles.iter().collect::>(); + tangles.sort_by_key(|val| val.0); + for (index, state) in tangles { + result.push(format!("{}\n", indent)); result.push(format!( - "{}Q{}: {}\n", - indent, - self.index, - self.measure() + "{}<{}~{}>:\n", + indent, state.upper_left, state.bottom_right )); - for matrix_fragment in stringified_matrix_lines(&self.state.matrix_fragment.matrix) { - result.push(format!("{}{}\n", indent, matrix_fragment)); + for matrix_fragment in stringified_matrix_lines(&state.state.matrix_fragment.matrix) { + result.push(format!("{}{}\n", indent, matrix_fragment)); } - - if !self.tangles.is_empty() { - let mut tangles = self.tangles.iter().collect::>(); - tangles.sort_by_key(|val| val.0); - for (index, state) in tangles { - result.push(format!("{}\n", indent)); - result.push(format!( - "{}<{}~{}>:\n", - indent, state.upper_left, state.bottom_right - )); - for matrix_fragment in stringified_matrix_lines(&state.state.matrix_fragment.matrix) { - result.push(format!("{}{}\n", indent, matrix_fragment)); - } - } - } - - result.push(format!("{}}},\n", base_indent)); - result + } } + + result.push(format!("{}}},\n", base_indent)); + result + } } impl Display for AnalysisQubit { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - for line in self.stringify(0) { - f.write_str(&line); - } - f.write_str("") + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + for line in self.stringify(0) { + f.write_str(&line); } + f.write_str("") + } } /// A cluster of entangled states that should be treated as an individual cohesive state. #[derive(Clone)] pub struct EntanglementCluster { - qubits: Ptr>> + qubits: Ptr>> } impl EntanglementCluster { - pub fn new() -> EntanglementCluster { - EntanglementCluster { - qubits: Ptr::from(HashMap::default()) - } + pub fn new() -> EntanglementCluster { + EntanglementCluster { + qubits: Ptr::from(HashMap::default()) } + } - pub fn spans(&self) -> Keys<'_, i64, Ptr> { self.qubits.keys() } + pub fn spans(&self) -> Keys<'_, i64, Ptr> { self.qubits.keys() } - /// Disentangles this qubit if it no longer is part of this cluster. Returns whether - /// this operation was needed or not, combining the check of 'disentangle' and 'can_disentangle'. - pub fn disentangle(&self, index: &i64) -> bool { false } + /// Disentangles this qubit if it no longer is part of this cluster. Returns whether + /// this operation was needed or not, combining the check of 'disentangle' and 'can_disentangle'. + pub fn disentangle(&self, index: &i64) -> bool { false } - fn contains_qubit(&self, index: &i64) -> bool { self.qubits.contains_key(index) } + fn contains_qubit(&self, index: &i64) -> bool { self.qubits.contains_key(index) } - /// Gets the qubit at this index crom this cluster. Assumes existence. - pub fn qubit_for(&self, index: &i64) -> &Ptr { self.qubits.get(&index).unwrap() } + /// Gets the qubit at this index crom this cluster. Assumes existence. + pub fn qubit_for(&self, index: &i64) -> &Ptr { self.qubits.get(&index).unwrap() } - pub fn merge(&self, other: &Ptr) { - // No need to check for existence since if a qubit is related it will already be in a - // cluster together. - // TODO: These lines makes the below not add infinite ints to the hashset. - // Definitely not what it should be doing. - let other_length = other.qubits.len(); - let our_length = self.qubits.len(); + pub fn merge(&self, other: &Ptr) { + // No need to check for existence since if a qubit is related it will already be in a + // cluster together. + // TODO: These lines makes the below not add infinite ints to the hashset. + // Definitely not what it should be doing. + let other_length = other.qubits.len(); + let our_length = self.qubits.len(); - for (index, qubit) in other.qubits.iter() { - with_mutable_self!(self.qubits.insert(index.clone(), qubit.clone())); - } + for (index, qubit) in other.qubits.iter() { + with_mutable_self!(self.qubits.insert(index.clone(), qubit.clone())); } + } - /// Adds these qubits to the cluster then entangles them. - pub fn add_then_entangle(&self, qubit_one: &Ptr, qubit_two: &Ptr) { - self.add(qubit_one); - self.add(qubit_two); - self.entangle(&qubit_one.index, &qubit_two.index); - } + /// Adds these qubits to the cluster then entangles them. + pub fn add_then_entangle(&self, qubit_one: &Ptr, qubit_two: &Ptr) { + self.add(qubit_one); + self.add(qubit_two); + self.entangle(&qubit_one.index, &qubit_two.index); + } - /// Adds this qubit to the cluster. - pub fn add(&self, qubit: &Ptr) { - if !self.qubits.contains_key(&qubit.index) { - with_mutable_self!(self.qubits.insert(qubit.index, qubit.clone())); - } + /// Adds this qubit to the cluster. + pub fn add(&self, qubit: &Ptr) { + if !self.qubits.contains_key(&qubit.index) { + with_mutable_self!(self.qubits.insert(qubit.index, qubit.clone())); } + } - /// Entangles these two qubits if they exist. Does not entangle if not. - pub fn entangle(&self, left: &i64, right: &i64) { - if let Some(rtangle) = self.qubits.get(right) { - if let Some(ltangle) = self.qubits.get(left) { - rtangle.entangle(ltangle); - } - } + /// Entangles these two qubits if they exist. Does not entangle if not. + pub fn entangle(&self, left: &i64, right: &i64) { + if let Some(rtangle) = self.qubits.get(right) { + if let Some(ltangle) = self.qubits.get(left) { + rtangle.entangle(ltangle); + } } + } - pub fn X(&self, qubit: &Ptr, radians: &f64) { - self.add(qubit); - with_mutable!(qubit.X(radians)) - } + pub fn X(&self, qubit: &Ptr, radians: &f64) { + self.add(qubit); + with_mutable!(qubit.X(radians)) + } - pub fn Y(&self, qubit: &Ptr, radians: &f64) { - self.add(qubit); - with_mutable!(qubit.Y(radians)) - } + pub fn Y(&self, qubit: &Ptr, radians: &f64) { + self.add(qubit); + with_mutable!(qubit.Y(radians)) + } - pub fn Z(&self, qubit: &Ptr, radians: &f64) { - self.add(qubit); - with_mutable!(qubit.Z(radians)) - } + pub fn Z(&self, qubit: &Ptr, radians: &f64) { + self.add(qubit); + with_mutable!(qubit.Z(radians)) + } - pub fn CX(&self, control: &Ptr, target: &Ptr, radians: &f64) { - self.add_then_entangle(control, target); - with_mutable!(target.CX(&control.index, radians)) - } + pub fn CX(&self, control: &Ptr, target: &Ptr, radians: &f64) { + self.add_then_entangle(control, target); + with_mutable!(target.CX(&control.index, radians)) + } - pub fn CZ(&self, control: &Ptr, target: &Ptr, radians: &f64) { - self.add_then_entangle(control, target); - with_mutable!(target.CZ(&control.index, radians)) - } + pub fn CZ(&self, control: &Ptr, target: &Ptr, radians: &f64) { + self.add_then_entangle(control, target); + with_mutable!(target.CZ(&control.index, radians)) + } - pub fn CY(&self, control: &Ptr, target: &Ptr, radians: &f64) { - self.add_then_entangle(control, target); - with_mutable!(target.CY(&control.index, radians)) - } + pub fn CY(&self, control: &Ptr, target: &Ptr, radians: &f64) { + self.add_then_entangle(control, target); + with_mutable!(target.CY(&control.index, radians)) + } + + pub fn SWAP(&mut self, left: &i64, right: &i64) { + if self.qubits.contains_key(left) && self.qubits.contains_key(right) { + let mut qubit_one = self.qubits.remove(left).unwrap(); + let mut qubit_two = self.qubits.remove(left).unwrap(); + let first_index = qubit_one.index; + let second_index = qubit_two.index; + + // Just merge indexes so we can deal with the point where entangled qubits reference both + // our swapped qubits. + let mut entanglements = qubit_one.entangled_with().collect::>(); + for index in qubit_two.entangled_with() { + entanglements.insert(index); + } + + // Go through each tangle and swap target indexes. + for index in entanglements { + let target_qubit = self.qubits.get(index).unwrap(); + let first_tangle = with_mutable!(target_qubit.tangles.remove(&first_index)); + let second_tangle = with_mutable!(target_qubit.tangles.remove(&second_index)); - pub fn SWAP(&mut self, left: &i64, right: &i64) { - if self.qubits.contains_key(left) && self.qubits.contains_key(right) { - let mut qubit_one = self.qubits.remove(left).unwrap(); - let mut qubit_two = self.qubits.remove(left).unwrap(); - let first_index = qubit_one.index; - let second_index = qubit_two.index; - - // Just merge indexes so we can deal with the point where entangled qubits reference both - // our swapped qubits. - let mut entanglements = qubit_one.entangled_with().collect::>(); - for index in qubit_two.entangled_with() { - entanglements.insert(index); - } - - // Go through each tangle and swap target indexes. - for index in entanglements { - let target_qubit = self.qubits.get(index).unwrap(); - let first_tangle = with_mutable!(target_qubit.tangles.remove(&first_index)); - let second_tangle = with_mutable!(target_qubit.tangles.remove(&second_index)); - - if let Some(mut tangle) = first_tangle { - if tangle.bottom_right == first_index { - tangle.bottom_right = second_index; - } else { - tangle.upper_left = second_index; - } - - with_mutable!(target_qubit.tangles.insert(second_index, tangle)); - } - - if let Some(mut tangle) = second_tangle { - if tangle.bottom_right == second_index { - tangle.bottom_right = first_index; - } else { - tangle.upper_left = first_index; - } - - with_mutable!(target_qubit.tangles.insert(first_index, tangle)); - } - } - - // Then just swap the designation around. - qubit_one.index = second_index; - qubit_two.index = first_index; - self.qubits.insert(qubit_one.index, qubit_one); - self.qubits.insert(qubit_two.index, qubit_two); + if let Some(mut tangle) = first_tangle { + if tangle.bottom_right == first_index { + tangle.bottom_right = second_index; + } else { + tangle.upper_left = second_index; + } + + with_mutable!(target_qubit.tangles.insert(second_index, tangle)); + } + + if let Some(mut tangle) = second_tangle { + if tangle.bottom_right == second_index { + tangle.bottom_right = first_index; + } else { + tangle.upper_left = first_index; + } + + with_mutable!(target_qubit.tangles.insert(first_index, tangle)); } + } + + // Then just swap the designation around. + qubit_one.index = second_index; + qubit_two.index = first_index; + self.qubits.insert(qubit_one.index, qubit_one); + self.qubits.insert(qubit_two.index, qubit_two); } + } } impl Display for EntanglementCluster { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("((\n"); - - let mut sorted_qubits = - self.qubits - .values() - .collect::>(); - - sorted_qubits.sort_by_key(|val| val.index); - f.write_str( - &sorted_qubits.iter() - .map(|val| val.stringify(1).join("")) - .collect::>() - .join("") - ); - f.write_str(")),\n") - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("((\n"); + + let mut sorted_qubits = + self.qubits + .values() + .collect::>(); + + sorted_qubits.sort_by_key(|val| val.index); + f.write_str( + &sorted_qubits.iter() + .map(|val| val.stringify(1).join("")) + .collect::>() + .join("") + ); + f.write_str(")),\n") + } } type GateFragment = MatrixFragment; @@ -428,19 +428,19 @@ type GateFragment = MatrixFragment; /// Matrix which can be applied to a state fragment. #[derive(Clone)] pub struct MatrixFragment { - matrix: Array2>, - affected_qubits: i32 + matrix: Array2>, + affected_qubits: i32 } impl MatrixFragment { - pub fn new(matrix: Array2>, affected_qubits: i32) -> MatrixFragment { - MatrixFragment { - matrix, - affected_qubits - } + pub fn new(matrix: Array2>, affected_qubits: i32) -> MatrixFragment { + MatrixFragment { + matrix, + affected_qubits } + } - #[rustfmt::skip] + #[rustfmt::skip] pub fn id() -> MatrixFragment { MatrixFragment { matrix: array![ @@ -451,56 +451,56 @@ impl MatrixFragment { } } - pub fn get(&self, key: (usize, usize)) -> &Complex { - self.matrix.get(key).unwrap() + pub fn get(&self, key: (usize, usize)) -> &Complex { + self.matrix.get(key).unwrap() + } + + pub fn tensor(&self, other: &MatrixFragment) -> MatrixFragment { + // We don't expand past 2 qubits. + if self.affected_qubits == 2 { + panic!("Attempted to tensor a 4x4 matrix.") } - pub fn tensor(&self, other: &MatrixFragment) -> MatrixFragment { - // We don't expand past 2 qubits. - if self.affected_qubits == 2 { - panic!("Attempted to tensor a 4x4 matrix.") - } + let one = self.get((0, 0)); + let two = self.get((0, 1)); + let three = self.get((1, 0)); + let four = self.get((1, 1)); - let one = self.get((0, 0)); - let two = self.get((0, 1)); - let three = self.get((1, 0)); - let four = self.get((1, 1)); - - let other_one = other.get((0, 0)); - let other_two = other.get((0, 1)); - let other_three = other.get((1, 0)); - let other_four = other.get((1, 1)); - - Self::new(array![ - [one*other_one, one*other_two, two*other_one, two*other_two], - [one*other_three, one*other_four, two*other_three, two*other_four], - [three*other_one, three*other_two, four*other_one, four*other_two], - [three*other_three, three*other_four, four*other_three, four*other_four], - ], 2) - } + let other_one = other.get((0, 0)); + let other_two = other.get((0, 1)); + let other_three = other.get((1, 0)); + let other_four = other.get((1, 1)); - pub fn transpose_conjugate(&self) -> MatrixFragment { - // TODO: Cache and re-use familiar transformations. - if self.affected_qubits == 1 { - Self::new(array![ - [self.get((0, 0)).conj(), self.get((1, 0)).conj()], - [self.get((0, 1)).conj(), self.get((1, 1)).conj()], - ], self.affected_qubits) - } else if self.affected_qubits == 2 { - Self::new(array![ - [self.get((0, 0)).conj(), self.get((1, 0)).conj(), self.get((2, 0)).conj(), self.get((3, 0)).conj()], - [self.get((0, 1)).conj(), self.get((1, 1)).conj(), self.get((2, 1)).conj(), self.get((3, 1)).conj()], - [self.get((0, 2)).conj(), self.get((1, 2)).conj(), self.get((2, 2)).conj(), self.get((3, 2)).conj()], - [self.get((0, 3)).conj(), self.get((1, 3)).conj(), self.get((2, 3)).conj(), self.get((3, 3)).conj()], - ], self.affected_qubits) - } else { - panic!("Can't transpose a matrix covering {} qubits.", self.affected_qubits); - } + Self::new(array![ + [one*other_one, one*other_two, two*other_one, two*other_two], + [one*other_three, one*other_four, two*other_three, two*other_four], + [three*other_one, three*other_two, four*other_one, four*other_two], + [three*other_three, three*other_four, four*other_three, four*other_four], + ], 2) + } + + pub fn transpose_conjugate(&self) -> MatrixFragment { + // TODO: Cache and re-use familiar transformations. + if self.affected_qubits == 1 { + Self::new(array![ + [self.get((0, 0)).conj(), self.get((1, 0)).conj()], + [self.get((0, 1)).conj(), self.get((1, 1)).conj()], + ], self.affected_qubits) + } else if self.affected_qubits == 2 { + Self::new(array![ + [self.get((0, 0)).conj(), self.get((1, 0)).conj(), self.get((2, 0)).conj(), self.get((3, 0)).conj()], + [self.get((0, 1)).conj(), self.get((1, 1)).conj(), self.get((2, 1)).conj(), self.get((3, 1)).conj()], + [self.get((0, 2)).conj(), self.get((1, 2)).conj(), self.get((2, 2)).conj(), self.get((3, 2)).conj()], + [self.get((0, 3)).conj(), self.get((1, 3)).conj(), self.get((2, 3)).conj(), self.get((3, 3)).conj()], + ], self.affected_qubits) + } else { + panic!("Can't transpose a matrix covering {} qubits.", self.affected_qubits); } + } - // TODO: Fix all rotations to allow variable rotations. + // TODO: Fix all rotations to allow variable rotations. - #[rustfmt::skip] + #[rustfmt::skip] pub fn X(radians: &f64) -> MatrixFragment { if radians == &PI { MatrixFragment { @@ -514,15 +514,15 @@ impl MatrixFragment { let radians_halved = radians/2.; MatrixFragment { matrix: array![ - [C!(radians_halved.cos(), 0.), C!(1.0, -radians_halved.sin())], - [C!(1.0, -radians_halved.sin()), C!(radians_halved.cos(), 0.)] + [C!(radians_halved.cos(), 0.), C!(0.0, -radians_halved.sin())], + [C!(0.0, -radians_halved.sin()), C!(radians_halved.cos(), 0.)] ], affected_qubits: 1 } } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn Y(radians: &f64) -> MatrixFragment { if radians == &PI { MatrixFragment { @@ -544,7 +544,7 @@ impl MatrixFragment { } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn Z(radians: &f64) -> MatrixFragment { if radians == &PI { MatrixFragment { @@ -559,14 +559,14 @@ impl MatrixFragment { MatrixFragment { matrix: array![ [f64::E().powc(C!(0., -radians_halved)), C!(0., 0.)], - [C!(0., 0.), f64::E().powc(C!(0., radians_halved))] + [C!(0., 0.), f64::E().powc(C!(0., radians_halved))] ], affected_qubits: 1 } } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn Had() -> MatrixFragment { let one_sq2 = C!(1. / 2.0f64.sqrt(), 0.); MatrixFragment { @@ -578,7 +578,7 @@ impl MatrixFragment { } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn CX(radians: &f64) -> MatrixFragment { MatrixFragment { matrix: array![ @@ -591,7 +591,7 @@ impl MatrixFragment { } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn CZ(radians: &f64) -> MatrixFragment { MatrixFragment { matrix: array![ @@ -604,7 +604,7 @@ impl MatrixFragment { } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn CY(radians: &f64) -> MatrixFragment { MatrixFragment { matrix: array![ @@ -617,7 +617,7 @@ impl MatrixFragment { } } - #[rustfmt::skip] + #[rustfmt::skip] pub fn SWAP() -> MatrixFragment { MatrixFragment { matrix: array![ @@ -632,74 +632,74 @@ impl MatrixFragment { } fn stringified_matrix_lines(matrix: &Array2>) -> Vec { - let mut result = Vec::new(); - let dimensions = matrix.dim(); - - // Restrict precision, but trim off any which are fully zero to make the output less verbose. - if dimensions == (2, 2) { - result.push(format!( - "[{:.2}, {:.2}]", - matrix.get((0, 0)).unwrap(), - matrix.get((0, 1)).unwrap() - ).replace(".00", "")); - result.push(format!( - "[{:.2}, {:.2}]", - matrix.get((1, 0)).unwrap(), - matrix.get((1, 1)).unwrap() - ).replace(".00", "")); - } else if dimensions == (4, 4) { - result.push(format!( - "[{:.2}, {:.2}, {:.2}, {:.2}]", - matrix.get((0, 0)).unwrap(), - matrix.get((0, 1)).unwrap(), - matrix.get((0, 2)).unwrap(), - matrix.get((0, 3)).unwrap() - ).replace(".00", "")); - result.push(format!( - "[{:.2}, {:.2}, {:.2}, {:.2}]", - matrix.get((1, 0)).unwrap(), - matrix.get((1, 1)).unwrap(), - matrix.get((1, 2)).unwrap(), - matrix.get((1, 3)).unwrap() - ).replace(".00", "")); - result.push(format!( - "[{:.2}, {:.2}, {:.2}, {:.2}]", - matrix.get((2, 0)).unwrap(), - matrix.get((2, 1)).unwrap(), - matrix.get((2, 2)).unwrap(), - matrix.get((2, 3)).unwrap() - ).replace(".00", "")); - result.push(format!( - "[{:.2}, {:.2}, {:.2}, {:.2}]", - matrix.get((3, 0)).unwrap(), - matrix.get((3, 1)).unwrap(), - matrix.get((3, 2)).unwrap(), - matrix.get((3, 3)).unwrap() - ).replace(".00", "")); - } else { - panic!("Attempted to print matrix of irregular dimensions.") - } - result + let mut result = Vec::new(); + let dimensions = matrix.dim(); + + // Restrict precision, but trim off any which are fully zero to make the output less verbose. + if dimensions == (2, 2) { + result.push(format!( + "[{:.2}, {:.2}]", + matrix.get((0, 0)).unwrap(), + matrix.get((0, 1)).unwrap() + ).replace(".00", "")); + result.push(format!( + "[{:.2}, {:.2}]", + matrix.get((1, 0)).unwrap(), + matrix.get((1, 1)).unwrap() + ).replace(".00", "")); + } else if dimensions == (4, 4) { + result.push(format!( + "[{:.2}, {:.2}, {:.2}, {:.2}]", + matrix.get((0, 0)).unwrap(), + matrix.get((0, 1)).unwrap(), + matrix.get((0, 2)).unwrap(), + matrix.get((0, 3)).unwrap() + ).replace(".00", "")); + result.push(format!( + "[{:.2}, {:.2}, {:.2}, {:.2}]", + matrix.get((1, 0)).unwrap(), + matrix.get((1, 1)).unwrap(), + matrix.get((1, 2)).unwrap(), + matrix.get((1, 3)).unwrap() + ).replace(".00", "")); + result.push(format!( + "[{:.2}, {:.2}, {:.2}, {:.2}]", + matrix.get((2, 0)).unwrap(), + matrix.get((2, 1)).unwrap(), + matrix.get((2, 2)).unwrap(), + matrix.get((2, 3)).unwrap() + ).replace(".00", "")); + result.push(format!( + "[{:.2}, {:.2}, {:.2}, {:.2}]", + matrix.get((3, 0)).unwrap(), + matrix.get((3, 1)).unwrap(), + matrix.get((3, 2)).unwrap(), + matrix.get((3, 3)).unwrap() + ).replace(".00", "")); + } else { + panic!("Attempted to print matrix of irregular dimensions.") + } + result } fn stringified_matrix(matrix: &Array2>, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&stringified_matrix_lines(matrix).join("\n")) + f.write_str(&stringified_matrix_lines(matrix).join("\n")) } impl Display for MatrixFragment { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { stringified_matrix(&self.matrix, f) } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { stringified_matrix(&self.matrix, f) } } /// Multiply both matrix fragments together. Dimensions are expected to be equal. fn multiply(left: &MatrixFragment, right: &MatrixFragment) -> MatrixFragment { - let mult = |left_index, right_index| -> Complex64 { - left.get(left_index) * right.get(right_index) - }; + let mult = |left_index, right_index| -> Complex64 { + left.get(left_index) * right.get(right_index) + }; - // TODO: Find specialized MM library or algorithm, current one not seemingly working correctly. - // This is just a brute-force implementation for testing. - if left.affected_qubits == 1 { - MatrixFragment::new(array![ + // TODO: Find specialized MM library or algorithm, current one not seemingly working correctly. + // This is just a brute-force implementation for testing. + if left.affected_qubits == 1 { + MatrixFragment::new(array![ [ mult((0, 0), (0, 0)) + mult((0, 1), (1, 0)), mult((0, 0), (1, 0)) + mult((0, 1), (1, 1)) @@ -709,8 +709,8 @@ fn multiply(left: &MatrixFragment, right: &MatrixFragment) -> MatrixFragment { mult((1, 0), (1, 0)) + mult((1, 1), (1, 1)) ] ], left.affected_qubits) - } else if left.affected_qubits == 2 { - MatrixFragment::new(array![ + } else if left.affected_qubits == 2 { + MatrixFragment::new(array![ [ mult((0, 0), (0, 0)) + mult((0, 1), (1, 0)) + mult((0, 2), (2, 0)) + mult((0, 3), (3, 0)), mult((0, 0), (0, 1)) + mult((0, 1), (1, 1)) + mult((0, 2), (2, 1)) + mult((0, 3), (3, 1)), @@ -737,36 +737,36 @@ fn multiply(left: &MatrixFragment, right: &MatrixFragment) -> MatrixFragment { ], ], left.affected_qubits) - } else { - panic!("Attempted multiplication on irregular size of matrix.") - } + } else { + panic!("Attempted multiplication on irregular size of matrix.") + } } impl Mul for MatrixFragment { - type Output = MatrixFragment; + type Output = MatrixFragment; - fn mul(self, rhs: Self) -> Self::Output { - multiply(&self, &rhs) - } + fn mul(self, rhs: Self) -> Self::Output { + multiply(&self, &rhs) + } } impl Mul for &MatrixFragment { - type Output = MatrixFragment; + type Output = MatrixFragment; - fn mul(self, rhs: Self) -> Self::Output { - multiply(&self, &rhs) - } + fn mul(self, rhs: Self) -> Self::Output { + multiply(&self, &rhs) + } } impl Mul for &mut MatrixFragment { - type Output = MatrixFragment; + type Output = MatrixFragment; - fn mul(self, rhs: Self) -> Self::Output { - MatrixFragment::new( - &self.matrix * &rhs.matrix, - self.affected_qubits + rhs.affected_qubits - ) - } + fn mul(self, rhs: Self) -> Self::Output { + MatrixFragment::new( + &self.matrix * &rhs.matrix, + self.affected_qubits + rhs.affected_qubits + ) + } } // While there is no distinction it's better to define what the types mean, even if the generic @@ -778,15 +778,15 @@ type EntangledFragment = StateFragment; /// smaller ones. #[derive(Clone)] pub struct StateFragment { - matrix_fragment: MatrixFragment, + matrix_fragment: MatrixFragment, } impl StateFragment { - pub fn new(matrix_fragment: MatrixFragment) -> StateFragment { - StateFragment { matrix_fragment } - } + pub fn new(matrix_fragment: MatrixFragment) -> StateFragment { + StateFragment { matrix_fragment } + } - #[rustfmt::skip] + #[rustfmt::skip] pub fn DefaultQubit() -> QubitFragment { StateFragment { matrix_fragment: @@ -797,9 +797,9 @@ impl StateFragment { } } - /// Creates a state fragment to represent entanglement between 2 qubits. Needs setup with - /// initial qubit values. - #[rustfmt::skip] + /// Creates a state fragment to represent entanglement between 2 qubits. Needs setup with + /// initial qubit values. + #[rustfmt::skip] pub fn DefaultEntangled() -> EntangledFragment { StateFragment { matrix_fragment: @@ -812,15 +812,15 @@ impl StateFragment { } } - pub fn get(&self, key: (usize, usize)) -> &Complex { - self.matrix_fragment.get(key) - } + pub fn get(&self, key: (usize, usize)) -> &Complex { + self.matrix_fragment.get(key) + } - pub fn represented_qubits(&self) -> i32 { - self.matrix_fragment.affected_qubits - } + pub fn represented_qubits(&self) -> i32 { + self.matrix_fragment.affected_qubits + } - #[rustfmt::skip] + #[rustfmt::skip] pub fn EntangledFromExisting( top_left: &Ptr, bottom_right: &Ptr ) -> EntangledFragment { @@ -839,289 +839,296 @@ impl StateFragment { } } - pub fn apply(&mut self, gate: &MatrixFragment) -> Option { - if self.represented_qubits() != gate.affected_qubits { - return Some(String::from("Can't apply to fragments of differing sizes.")); - } - - let mut result = gate * &self.matrix_fragment; - self.matrix_fragment = result * gate.transpose_conjugate(); - None + pub fn apply(&mut self, gate: &MatrixFragment) -> Option { + if self.represented_qubits() != gate.affected_qubits { + return Some(String::from("Can't apply to fragments of differing sizes.")); } + + let mut result = gate * &self.matrix_fragment; + self.matrix_fragment = result * gate.transpose_conjugate(); + None + } } impl Display for StateFragment { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { stringified_matrix(&self.matrix_fragment.matrix, f) } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { stringified_matrix(&self.matrix_fragment.matrix, f) } } impl Mul for StateFragment { - type Output = StateFragment; + type Output = StateFragment; - fn mul(self, rhs: Self) -> Self::Output { - StateFragment::new(self.matrix_fragment * rhs.matrix_fragment) - } + fn mul(self, rhs: Self) -> Self::Output { + StateFragment::new(self.matrix_fragment * rhs.matrix_fragment) + } } #[derive(Clone)] pub struct SolverConfig { - pub active: bool + pub active: bool } impl SolverConfig { - pub fn new(active: bool) -> SolverConfig { SolverConfig { active } } + pub fn new(active: bool) -> SolverConfig { SolverConfig { active } } - pub fn off() -> SolverConfig { SolverConfig::new(false) } + pub fn off() -> SolverConfig { SolverConfig::new(false) } - pub fn on() -> SolverConfig { SolverConfig::new(true) } + pub fn on() -> SolverConfig { SolverConfig::new(true) } - pub fn with_config(config: &Ptr) -> SolverConfig { - SolverConfig::new(config.solver_active) - } + pub fn with_config(config: &Ptr) -> SolverConfig { + SolverConfig::new(config.solver_active) + } } #[derive(Clone)] pub struct SolverResult {} impl SolverResult { - pub fn new() -> SolverResult { SolverResult {} } + pub fn new() -> SolverResult { SolverResult {} } } /// Acts as a pseudo-state that allows for partial circuit solving and value introspection. pub struct QuantumSolver { - qubits: Ptr>>, - clusters: Ptr>>, - trace_module: Ptr + qubits: Ptr>>, + clusters: Ptr>>, + trace_module: Ptr } impl QuantumSolver { - pub fn new() -> QuantumSolver { - QuantumSolver { - qubits: Ptr::from(HashMap::default()), - clusters: Ptr::from(HashMap::default()), - trace_module: Ptr::from(TracingModule::default()) - } + pub fn new() -> QuantumSolver { + QuantumSolver { + qubits: Ptr::from(HashMap::default()), + clusters: Ptr::from(HashMap::default()), + trace_module: Ptr::from(TracingModule::default()) } + } - fn is_tracing(&self) -> bool { self.trace_module.has(ActiveTracers::Solver) } + fn is_tracing(&self) -> bool { self.trace_module.has(ActiveTracers::Solver) } - pub fn with_trace(trace_module: Ptr) -> QuantumSolver { - QuantumSolver { - qubits: Ptr::from(HashMap::default()), - clusters: Ptr::from(HashMap::default()), - trace_module - } + pub fn with_trace(trace_module: Ptr) -> QuantumSolver { + QuantumSolver { + qubits: Ptr::from(HashMap::default()), + clusters: Ptr::from(HashMap::default()), + trace_module } + } - /// Gets a qubit, or adds a default one at this index if it doesn't exist. - fn qubit_for(&self, index: &i64) -> &Ptr { - if let Some(qubit) = self.qubits.get(index) { - qubit - } else { - with_mutable_self!(self + /// Gets a qubit, or adds a default one at this index if it doesn't exist. + fn qubit_for(&self, index: &i64) -> &Ptr { + if let Some(qubit) = self.qubits.get(index) { + qubit + } else { + with_mutable_self!(self .qubits .insert(index.clone(), Ptr::from(AnalysisQubit::with_index(index)))); - self.qubits.get(&index).unwrap() - } + self.qubits.get(&index).unwrap() } + } - /// Gets the cluster for this index. Inserts the qubit into both solver and cluster, creating a - /// new cluster if required. Don't use this if you only want to fetch a cluster without modifying - /// it. - fn cluster_for(&self, index: &i64) -> &Ptr { - let cluster = if let Some(cluster) = with_mutable_self!(self.clusters.get_mut(index)) { - cluster - } else { - with_mutable_self!(self + /// Gets the cluster for this index. Inserts the qubit into both solver and cluster, creating a + /// new cluster if required. Don't use this if you only want to fetch a cluster without modifying + /// it. + fn cluster_for(&self, index: &i64) -> &Ptr { + let cluster = if let Some(cluster) = with_mutable_self!(self.clusters.get_mut(index)) { + cluster + } else { + with_mutable_self!(self .clusters .insert(index.clone(), Ptr::from(EntanglementCluster::new()))); - self.clusters.get(&index).unwrap() - }; - - if !cluster.contains_qubit(index) { - cluster.add(self.qubit_for(index)); - } + self.clusters.get(&index).unwrap() + }; - cluster + if !cluster.contains_qubit(index) { + cluster.add(self.qubit_for(index)); } - fn merge_clusters(&self, merger: Vec<&i64>, mergee: &i64) -> &Ptr { - let target_cluster = self.cluster_for(&mergee); - for index in merger { - let cluster = self.cluster_for(&index); - if !cluster.contains_qubit(&mergee) { - target_cluster.merge(cluster); - target_cluster.add_then_entangle(self.qubit_for(&index), target_cluster.qubit_for(&mergee)); - - // Remove the previous cluster, it's no longer needed, replace with new merged one. - with_mutable_self!(self.clusters.insert(index.clone(), target_cluster.clone())); - } - } - target_cluster + cluster + } + + fn merge_clusters(&self, merger: Vec<&i64>, mergee: &i64) -> &Ptr { + let target_cluster = self.cluster_for(&mergee); + for index in merger { + let cluster = self.cluster_for(&index); + if !cluster.contains_qubit(&mergee) { + target_cluster.merge(cluster); + target_cluster.add_then_entangle(self.qubit_for(&index), target_cluster.qubit_for(&mergee)); + + // Remove the previous cluster, it's no longer needed, replace with new merged one. + with_mutable_self!(self.clusters.insert(index.clone(), target_cluster.clone())); + } } + target_cluster + } - pub fn reset(&self, qb: &Qubit) {} + pub fn reset(&self, qb: &Qubit) {} - pub fn measure(&self, qbs: &Qubit) {} + pub fn measure(&self, qbs: &Qubit) {} - pub fn X(&self, qb: &Qubit, radians: &f64) { self.qubit_for(&qb.index).X(radians) } + pub fn X(&self, qb: &Qubit, radians: &f64) { self.qubit_for(&qb.index).X(radians) } - pub fn Y(&self, qb: &Qubit, radians: &f64) { self.qubit_for(&qb.index).Y(radians) } + pub fn Y(&self, qb: &Qubit, radians: &f64) { self.qubit_for(&qb.index).Y(radians) } - pub fn Z(&self, qb: &Qubit, radians: &f64) { self.qubit_for(&qb.index).Z(radians) } + pub fn Z(&self, qb: &Qubit, radians: &f64) { self.qubit_for(&qb.index).Z(radians) } - pub fn CX(&self, controls: &Vec, target: &Qubit, radians: &f64) { - let target_cluster = self.merge_clusters( - controls.iter().map(|val| &val.index).collect::>(), - &target.index - ); + pub fn CX(&self, controls: &Vec, target: &Qubit, radians: &f64) { + let target_cluster = self.merge_clusters( + controls.iter().map(|val| &val.index).collect::>(), + &target.index + ); - let spans = target_cluster.spans().collect::>(); - for qb in controls { - target_cluster.CX( - target_cluster.qubit_for(&qb.index), - target_cluster.qubit_for(&target.index), - radians - ); - } + let spans = target_cluster.spans().collect::>(); + for qb in controls { + target_cluster.CX( + target_cluster.qubit_for(&qb.index), + target_cluster.qubit_for(&target.index), + radians + ); + } - if target_cluster.disentangle(&target.index) { - with_mutable_self!(self.clusters.remove(&target.index)); - } + if target_cluster.disentangle(&target.index) { + with_mutable_self!(self.clusters.remove(&target.index)); } + } - pub fn CY(&self, controls: &Vec, target: &Qubit, radians: &f64) { - let target_cluster = self.merge_clusters( - controls.iter().map(|val| &val.index).collect::>(), - &target.index - ); - for qb in controls { - target_cluster.CY( - target_cluster.qubit_for(&qb.index), - target_cluster.qubit_for(&target.index), - radians - ); - } + pub fn CY(&self, controls: &Vec, target: &Qubit, radians: &f64) { + let target_cluster = self.merge_clusters( + controls.iter().map(|val| &val.index).collect::>(), + &target.index + ); + for qb in controls { + target_cluster.CY( + target_cluster.qubit_for(&qb.index), + target_cluster.qubit_for(&target.index), + radians + ); + } - if target_cluster.disentangle(&target.index) { - with_mutable_self!(self.clusters.remove(&target.index)); - } + if target_cluster.disentangle(&target.index) { + with_mutable_self!(self.clusters.remove(&target.index)); } + } - pub fn CZ(&self, controls: &Vec, target: &Qubit, radians: &f64) { - let target_cluster = self.cluster_for(&target.index); - for qb in controls { - let target_cluster = self.merge_clusters( - controls.iter().map(|val| &val.index).collect::>(), - &target.index - ); - - target_cluster.CZ( - target_cluster.qubit_for(&qb.index), - target_cluster.qubit_for(&target.index), - radians - ); - } + pub fn CZ(&self, controls: &Vec, target: &Qubit, radians: &f64) { + let target_cluster = self.cluster_for(&target.index); + for qb in controls { + let target_cluster = self.merge_clusters( + controls.iter().map(|val| &val.index).collect::>(), + &target.index + ); - if target_cluster.disentangle(&target.index) { - with_mutable_self!(self.clusters.remove(&target.index)); - } + target_cluster.CZ( + target_cluster.qubit_for(&qb.index), + target_cluster.qubit_for(&target.index), + radians + ); } - pub fn solve(&self) -> SolverResult { - // We don't worry about printing if we're utterly empty. - if self.is_tracing() { - if self.qubits.is_empty() { - log!(Level::Info, "Nothing to solve."); - } else { - log!(Level::Info, "{}", self.to_string()); - } - } - SolverResult::new() + if target_cluster.disentangle(&target.index) { + with_mutable_self!(self.clusters.remove(&target.index)); } + } + + pub fn solve(&self) -> SolverResult { + // We don't worry about printing if we're utterly empty. + if self.is_tracing() { + if self.qubits.is_empty() { + log!(Level::Info, "Nothing to solve."); + } else { + log!(Level::Info, "{}", self.to_string()); + } + } + SolverResult::new() + } } impl Display for QuantumSolver { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("\nSolver:\n"); - - let mut covered_qubits = HashSet::new(); - let mut qubits = self.qubits.iter().collect::>(); - qubits.sort_by_key(|val| val.0); - for (key, value) in qubits { - if covered_qubits.contains(key) { - continue; - } - - if let Some(cluster) = self.clusters.get(key) { - for index in cluster.spans() { - covered_qubits.insert(index); - } - - f.write_str("[Cluster]\n"); - f.write_str(&cluster.to_string()); - } else { - covered_qubits.insert(key); - f.write_str("[Qubit]\n"); - f.write_str(&value.to_string()); - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("\nSolver:\n"); + + let mut covered_qubits = HashSet::new(); + let mut qubits = self.qubits.iter().collect::>(); + qubits.sort_by_key(|val| val.0); + for (key, value) in qubits { + if covered_qubits.contains(key) { + continue; + } + + if let Some(cluster) = self.clusters.get(key) { + for index in cluster.spans() { + covered_qubits.insert(index); } - f.write_str("") + f.write_str("[Cluster]\n"); + f.write_str(&cluster.to_string()); + } else { + covered_qubits.insert(key); + f.write_str("[Qubit]\n"); + f.write_str(&value.to_string()); + } } + + f.write_str("") + } } #[cfg(test)] mod tests { - use std::borrow::Borrow; - use std::fmt::{Display, Formatter}; - use crate::analysis::solver::{GateFragment, QubitFragment}; - - #[test] - fn X() { - let mut qubit = QubitFragment::DefaultQubit(); - let result = qubit.apply(&GateFragment::X(&90.0)); - assert!(result.is_none()); - - assert_eq!(qubit.get((0, 0)).re, 0.); - assert_eq!(qubit.get((0, 1)).re, 0.); - assert_eq!(qubit.get((1, 0)).re, 0.); - assert_eq!(qubit.get((1, 1)).re, 1.); - } + use std::borrow::Borrow; + use std::f64::consts::PI; + use std::fmt::{Display, Formatter}; + use crate::analysis::solver::{GateFragment, QubitFragment}; - #[test] - fn Z() { - let mut qubit = QubitFragment::DefaultQubit(); - let result = qubit.apply(&GateFragment::Z(&90.0)); - assert!(result.is_none()); + #[test] + fn X() { + let mut qubit = QubitFragment::DefaultQubit(); + let result = qubit.apply(&GateFragment::X(&(PI/2.))); + assert!(result.is_none()); - assert_eq!(qubit.get((0, 0)).re, 1.); - assert_eq!(qubit.get((0, 1)).re, 0.); - assert_eq!(qubit.get((1, 0)).re, 0.); - assert_eq!(qubit.get((1, 1)).re, 0.); - } + let stuff = qubit.to_string(); - #[test] - fn Y() { - let mut qubit = QubitFragment::DefaultQubit(); - let result = qubit.apply(&GateFragment::Y(&90.0)); - assert!(result.is_none()); + let zero = qubit.get((0, 0)).re; + assert!(zero >= 0.48 && zero <= 0.52); - assert_eq!(qubit.get((0, 0)).re, 0.); - assert_eq!(qubit.get((0, 1)).re, 0.); - assert_eq!(qubit.get((1, 0)).re, 0.); - assert_eq!(qubit.get((1, 1)).re, -1.); - } + assert_eq!(qubit.get((0, 1)).re, 0.); + assert_eq!(qubit.get((1, 0)).re, 0.); - #[test] - fn Had() { - let mut qubit = QubitFragment::DefaultQubit(); - let result = qubit.apply(&GateFragment::Had()); - assert!(result.is_none()); + let one = qubit.get((1, 1)).re; + assert!(one >= 0.48 && one <= 0.52); + } - assert!(qubit.get((0, 0)).re > 0.22); - assert!(qubit.get((0, 1)).re > 0.22); - assert!(qubit.get((1, 0)).re > 0.22); - assert!(qubit.get((1, 1)).re > 0.22); - } + #[test] + fn Z() { + let mut qubit = QubitFragment::DefaultQubit(); + let result = qubit.apply(&GateFragment::Z(&(PI/2.))); + assert!(result.is_none()); + + assert_eq!(qubit.get((0, 0)).re, 1.); + assert_eq!(qubit.get((0, 1)).re, 0.); + assert_eq!(qubit.get((1, 0)).re, 0.); + assert_eq!(qubit.get((1, 1)).re, 0.); + } + + #[test] + fn Y() { + let mut qubit = QubitFragment::DefaultQubit(); + let result = qubit.apply(&GateFragment::Y(&(PI/2.))); + assert!(result.is_none()); + + assert_eq!(qubit.get((0, 0)).re, 0.); + assert_eq!(qubit.get((0, 1)).re, 0.); + assert_eq!(qubit.get((1, 0)).re, 0.); + assert_eq!(qubit.get((1, 1)).re, -1.); + } + + #[test] + fn Had() { + let mut qubit = QubitFragment::DefaultQubit(); + let result = qubit.apply(&GateFragment::Had()); + assert!(result.is_none()); + + assert!(qubit.get((0, 0)).re > 0.22); + assert!(qubit.get((0, 1)).re > 0.22); + assert!(qubit.get((1, 0)).re > 0.22); + assert!(qubit.get((1, 1)).re > 0.22); + } } \ No newline at end of file diff --git a/src/rasqal/src/builders.rs b/src/rasqal/src/builders.rs index 9a880c0..08603ef 100644 --- a/src/rasqal/src/builders.rs +++ b/src/rasqal/src/builders.rs @@ -10,6 +10,7 @@ use std::borrow::Borrow; use std::f64::consts::PI; use std::ops::{Deref, DerefMut}; use crate::analysis::projections::AnalysisResult; + // TODO: Use macros to reduce duplication across both integration objects. pub enum IntegrationRuntime {