Skip to content

Commit

Permalink
WIP v6
Browse files Browse the repository at this point in the history
  • Loading branch information
chemix-lunacy committed Oct 7, 2024
1 parent 28a57eb commit e4d7e5f
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 49 deletions.
7 changes: 7 additions & 0 deletions src/rasqal/src/analysis/projections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,13 @@ impl QuantumProjection {
return AnalysisResult::empty();
}

if self.trace_module.has(ActiveTracers::Solver) {
log!(Level::Info, "Solving circuit:");
for inst in self.instructions.iter() {
log!(Level::Info, "{}", inst.to_string());
}
}

let start = Instant::now();
let qsolver = QuantumSolver::with_trace(self.trace_module.clone());
for inst in self.instructions.iter() {
Expand Down
146 changes: 97 additions & 49 deletions src/rasqal/src/analysis/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,16 @@ impl AnalysisQubit {

pub fn is_entangled(&self) -> bool { !self.tangles.is_empty() }

pub fn entangle(&self, other: &Ptr<AnalysisQubit>) {
// TODO: Remove self_pointer and decide how to deal with this. Don't want to steal the
// borrow really.
pub fn entangle(&self, self_pointer: &Ptr<AnalysisQubit>, other: &Ptr<AnalysisQubit>) {
if self.is_entangled_with(&other.index) {
return;
}

let tangle = Ptr::from(Tangle::from_qubits(&Ptr::from(self), other));
with_mutable_self!(self.tangles.insert(other.index, tangle.clone()));
with_mutable_self!(other.tangles.insert(self.index, tangle));
let tangle = Ptr::from(Tangle::from_qubits(self_pointer, other));
with_mutable!(self_pointer.tangles.insert(other.index, tangle.clone()));
with_mutable!(other.tangles.insert(self.index, tangle));
}

/// Returns 0...x depending upon what this qubit would be measured as.
Expand Down Expand Up @@ -268,19 +270,33 @@ impl AnalysisQubit {
pub fn apply(&self, gate: &GateFragment) {
with_mutable_self!(self.state.apply(gate));
for tangle in self.tangles.values() {
// Depending which side of the matrix we're on, we need to expand it different ways.
// If we're a single-qubit gate, expand.
let expanded_gate = if gate.affected_qubits == 2 {
gate
} else if tangle.right.index == self.index {
&MatrixFragment::id().tensor(gate)
} else {
&gate.tensor(&MatrixFragment::id())
};

with_mutable!(tangle.state.apply(expanded_gate));
log!(Level::Info, "Applying gate: {}", gate);
log!(Level::Info, "Pre-apply: {}", self.to_string());

// If our actual target is inverted, invert the matrix too.
let application_result = if tangle.right.index == self.index {
log!(Level::Info, "Inverting.");
with_mutable!(tangle.state.apply(&gate.invert()))
} else {
log!(Level::Info, "No invert.");
with_mutable!(tangle.state.apply(expanded_gate))
};

if let Some(error) = application_result {
panic!("{}", error);
}

log!(Level::Info, "Post-apply: {}", self.to_string());

// If our rotation has removed entanglement, drop the tangle entirely.
if tangle.is_entangled() {
if !tangle.is_entangled() {
with_mutable!(tangle.left.tangles.remove(&tangle.right.index));
with_mutable!(tangle.right.tangles.remove(&tangle.left.index));
}
Expand All @@ -296,11 +312,17 @@ impl AnalysisQubit {
// TODO: Pretty sure these require target qubit alignment.
// (Aka now that left/right qubits are not garanteed, need to just re-align).

pub fn CX(&self, control: &i64, radians: &f64) { self.apply(&GateFragment::CX(radians)) }
pub fn CX(&self, control: &i64, radians: &f64) {
self.apply(&GateFragment::CX(radians))
}

pub fn CZ(&self, control: &i64, radians: &f64) { self.apply(&GateFragment::CZ(radians)) }
pub fn CZ(&self, control: &i64, radians: &f64) {
self.apply(&GateFragment::CZ(radians))
}

pub fn CY(&self, control: &i64, radians: &f64) { self.apply(&GateFragment::CY(radians)) }
pub fn CY(&self, control: &i64, radians: &f64) {
self.apply(&GateFragment::CY(radians))
}

pub fn stringify(&self, indent_level: i32) -> Vec<String> {
let mut result = Vec::new();
Expand All @@ -311,7 +333,7 @@ impl AnalysisQubit {
let indent = format!("{} ", base_indent);

result.push(format!("{}{{\n", base_indent));
result.push(format!("{}Q{}: {}\n", indent, self.index, self.measure()));
result.push(format!("{}Q{}: {}\n", indent, self.index, self.state.get((1, 1)).re));
for matrix_fragment in stringified_matrix_lines(&self.state.matrix_fragment.matrix) {
result.push(format!("{}{}\n", indent, matrix_fragment));
}
Expand All @@ -321,7 +343,7 @@ impl AnalysisQubit {
tangles.sort_by_key(|val| val.0);
for (index, state) in tangles {
result.push(format!("{}\n", indent));
result.push(format!("{}<{}~{}>:\n", indent, state.left, state.right));
result.push(format!("{}<{}~{}>:\n", indent, state.left.index, state.right.index));
for matrix_fragment in stringified_matrix_lines(&state.state.matrix_fragment.matrix) {
result.push(format!("{}{}\n", indent, matrix_fragment));
}
Expand Down Expand Up @@ -395,9 +417,9 @@ impl EntanglementCluster {

/// 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);
if let Some(rqubit) = self.qubits.get(right) {
if let Some(lqubit) = self.qubits.get(left) {
rqubit.entangle(rqubit, lqubit);
}
}
}
Expand Down Expand Up @@ -450,27 +472,43 @@ impl EntanglementCluster {
}

pub fn CX(&self, control: &i64, target: &i64, radians: &f64) {
self
let qubit = self
.qubits
.get(target)
.expect("Attempted CX on qubit which doesn't exist in cluster.")
.CX(control, radians);
.expect("Attempted CX on qubit which doesn't exist in cluster.");
log!(Level::Info, "Pre-CX qubit: {}", qubit.to_string());

qubit.CX(control, radians);

log!(Level::Info, "Post-CX qubit: {}", qubit.to_string());

if !qubit.is_entangled() {
self.remove(target);
}
}

pub fn CZ(&self, control: &i64, target: &i64, radians: &f64) {
self
let qubit = self
.qubits
.get(target)
.expect("Attempted CX on qubit which doesn't exist in cluster.")
.CZ(control, radians);
.expect("Attempted CX on qubit which doesn't exist in cluster.");
qubit.CZ(control, radians);

if !qubit.is_entangled() {
self.remove(target);
}
}

pub fn CY(&self, control: &i64, target: &i64, radians: &f64) {
self
let qubit = self
.qubits
.get(target)
.expect("Attempted CX on qubit which doesn't exist in cluster.")
.CY(control, radians);
.expect("Attempted CX on qubit which doesn't exist in cluster.");
qubit.CY(control, radians);

if !qubit.is_entangled() {
self.remove(target);
}
}

pub fn SWAP(&mut self, left: &i64, right: &i64) {
Expand Down Expand Up @@ -623,26 +661,26 @@ impl MatrixFragment {
}
}

// /// Flips the matrix reversing the value or which qubit the operation gets applied too.
// /// TODO: Check the latter.
// pub fn invert(&self) -> MatrixFragment {
// // TODO: Cache and re-use familiar transformations.
// if self.affected_qubits == 1 {
// Self::new(array![
// [*self.get((1, 1)), *self.get((1, 0))],
// [*self.get((0, 1)), *self.get((0, 0))],
// ])
// } else if self.affected_qubits == 2 {
// Self::new(array![
// [*self.get((3, 3)), *self.get((2, 3)), *self.get((1, 3)), *self.get((0, 3))],
// [*self.get((3, 2)), *self.get((2, 2)), *self.get((1, 2)), *self.get((0, 2))],
// [*self.get((3, 1)), *self.get((2, 1)), *self.get((1, 1)), *self.get((0, 1))],
// [*self.get((3, 0)), *self.get((2, 0)), *self.get((1, 0)), *self.get((0, 0))],
// ])
// } else {
// panic!("Can't transpose a matrix covering {} qubits.", self.affected_qubits);
// }
// }
/// Flips the matrix reversing the value or which qubit the operation gets applied too.
/// TODO: Check the latter.
pub fn invert(&self) -> MatrixFragment {
// TODO: Cache and re-use familiar transformations.
if self.affected_qubits == 1 {
Self::new(array![
[*self.get((1, 1)), *self.get((1, 0))],
[*self.get((0, 1)), *self.get((0, 0))],
])
} else if self.affected_qubits == 2 {
Self::new(array![
[*self.get((3, 3)), *self.get((2, 3)), *self.get((1, 3)), *self.get((0, 3))],
[*self.get((3, 2)), *self.get((2, 2)), *self.get((1, 2)), *self.get((0, 2))],
[*self.get((3, 1)), *self.get((2, 1)), *self.get((1, 1)), *self.get((0, 1))],
[*self.get((3, 0)), *self.get((2, 0)), *self.get((1, 0)), *self.get((0, 0))],
])
} else {
panic!("Can't transpose a matrix covering {} qubits.", self.affected_qubits);
}
}

#[rustfmt::skip]
pub fn X(radians: &f64) -> MatrixFragment {
Expand Down Expand Up @@ -1010,6 +1048,7 @@ impl SolverResult {
}
}
}

#[derive(Clone)]
pub struct ResultFragment {
/// Rolling probability of this whole fragment being applicable. Used for filtering.
Expand Down Expand Up @@ -1260,6 +1299,7 @@ impl QuantumSolver {

fn merge_clusters(&self, merger: Vec<&i64>, mergee: &i64) -> &Ptr<EntanglementCluster> {
let target_cluster = self.cluster_for(&mergee);
log!(Level::Info, "Mergee before: {}", target_cluster.to_string());
for index in merger {
if let Some(cluster) = self.clusters.get(index) {
// If clusters are different, merge, entangle our two qubits, then replace reference.
Expand All @@ -1276,6 +1316,8 @@ impl QuantumSolver {
target_cluster.add_then_entangle(qubit, mergee);
with_mutable_self!(self.clusters.insert(index.clone(), target_cluster.clone()));
}

log!(Level::Info, "Mergee after: {}", target_cluster.to_string());
}
target_cluster
}
Expand All @@ -1300,6 +1342,7 @@ impl QuantumSolver {

// We only record the last measure on a qubit as the valid one.
with_mutable_self!(self.measures.insert(qb.index, result));
log!(Level::Info, "{}", self.to_string());
}

pub fn X(&self, qb: &Qubit, radians: &f64) {
Expand Down Expand Up @@ -1496,10 +1539,15 @@ mod tests {
#[test]
fn bell_test() {
let solver = QuantumSolver::new();
let q1 = Qubit::new(0);
solver.Had(&q1);
solver.CX(&vec![Qubit::new(1)], &q1, &PI);
let (q0, q1) = (Qubit::new(0), Qubit::new(1));
solver.Had(&q0);
solver.CX(&vec![q1.clone()], &q0, &PI);
solver.measure(&q0);
solver.measure(&q1);
let solver_string = solver.to_string();
let result = solver.solve();
let mut dave = String::new();
dave = dave.to_string() + "dave";
}

#[test]
Expand Down

0 comments on commit e4d7e5f

Please # to comment.