Skip to content

Commit

Permalink
Simplify Relabel logic (#2085)
Browse files Browse the repository at this point in the history
This simplifies the relabeling logic to collapse the four match cases
down into one for easier readibility. It avoids trying to optimize
special cases for qubits that may not have been mapped and treats all
labels as having an intial mapping to themselves.

Based on feedback from @DmitryVasilevsky in
#2082 (comment)
  • Loading branch information
swernli authored Jan 9, 2025
1 parent 189523e commit 2423da6
Showing 1 changed file with 19 additions and 45 deletions.
64 changes: 19 additions & 45 deletions compiler/qsc_eval/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,56 +377,30 @@ pub fn qubit_relabel(
return Err(Error::RelabelingMismatch(arg_span));
}

let mut map = FxHashMap::default();
map.reserve(left.len());
// Start with a mapping of each qubit to itself.
let mut mappings: FxHashMap<usize, usize> =
left.iter().copied().zip(left.iter().copied()).collect();
for (l, r) in left.into_iter().zip(right.into_iter()) {
// Trivial case where the qubit is already mapped to itself in the relabel, which can be short circuited.
if l == r {
continue;
}
match (map.contains_key(&l), map.contains_key(&r)) {
(false, false) => {
// Neither qubit has been relabeled yet.
swap(l, r);
map.insert(l, r);
map.insert(r, l);
}
(false, true) => {
// The right qubit has been relabeled, so we need to swap the left qubit with the
// new label for the right qubit.
let label = *map
.keys()
.find(|k| map[*k] == r)
.expect("mapped qubit should be present as both key and value");
swap(l, label);
map.insert(l, r);
map.insert(label, l);
}
(true, false) => {
// The left qubit has been relabeled, so we swap the qubits as normal but
// remember the new mapping of the right qubit.
let mapped = *map.get(&l).expect("mapped qubit should be present");
swap(l, r);
map.insert(l, r);
map.insert(r, mapped);
}
(true, true) => {
// Both qubits have been relabeled, so we need to swap new label for the right qubit with
// the left qubit and remember the new mapping of both qubits.
// This is effectively a combination of the second and third cases above.
let label_r = *map
.keys()
.find(|k| map[*k] == r)
.expect("mapped qubit should be present as both key and value");
let mapped_l = *map.get(&l).expect("mapped qubit should be present");
let mapped_r = *map.get(&r).expect("mapped qubit should be present");

// This swap is only necessary if the labels don't already point to each other.
if mapped_l != r && mapped_r != l {
swap(label_r, l);
map.insert(label_r, mapped_l);
map.insert(l, mapped_r);
}
}
// Check what each label currently maps to.
let mapped_l = *mappings.get(&l).expect("mapped qubit should be present");
let mapped_r = *mappings.get(&r).expect("mapped qubit should be present");

// We only need to swap if the label is not pointing to the correct qubit.
if mapped_l != r && mapped_r != l {
// Do a reverse lookup to find which label is currently mapped to the desired right qubit.
// This tells us which label to use in the swap, which we will use in the update of the mappings too.
let label_r = *mappings
.keys()
.find(|k| mappings[*k] == r)
.expect("mapped qubit should be present as both key and value");
swap(l, label_r);
mappings.insert(label_r, mapped_l);
mappings.insert(l, mapped_r);
}
}

Expand Down

0 comments on commit 2423da6

Please sign in to comment.