microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/resource_estimator/src/system/optimization/distillation_units_map.rs
255lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | #[cfg(test)] |
| 5 | mod tests; |
| 6 | |
| 7 | use std::cmp::max; |
| 8 | |
| 9 | use std::rc::Rc; |
| 10 | |
| 11 | use crate::estimates::LogicalPatch; |
| 12 | use crate::system::modeling::{ |
| 13 | PhysicalQubit, Protocol, TFactoryDistillationUnit, TFactoryDistillationUnitTemplate, |
| 14 | TFactoryDistillationUnitType, TFactoryQubit, |
| 15 | }; |
| 16 | |
| 17 | pub struct DistillationUnitsMap<'a> { |
| 18 | physical_distillation_units: Vec<TFactoryDistillationUnit<'a>>, |
| 19 | is_valid_physical_distillation_units: Vec<bool>, |
| 20 | logical_distillation_units: Vec<Vec<TFactoryDistillationUnit<'a>>>, |
| 21 | num_combined_distillation_units: usize, |
| 22 | num_physical_distillation_units: usize, |
| 23 | num_logical_distillation_units: usize, |
| 24 | min_valid_code_distance_indexes: Vec<usize>, |
| 25 | num_code_distances: usize, |
| 26 | distances: Vec<u64>, |
| 27 | } |
| 28 | |
| 29 | impl<'a> DistillationUnitsMap<'a> { |
| 30 | pub fn create( |
| 31 | qubit: &PhysicalQubit, |
| 32 | qubits: &[Option<Rc<LogicalPatch<Protocol>>>], |
| 33 | distances: Vec<u64>, |
| 34 | distillation_unit_templates: &'a [TFactoryDistillationUnitTemplate], |
| 35 | ) -> Self { |
| 36 | let num_code_distances = distances.len(); |
| 37 | let combined_distillation_unit_templates = Self::get_templates_for_unit_type( |
| 38 | distillation_unit_templates, |
| 39 | TFactoryDistillationUnitType::Combined, |
| 40 | ); |
| 41 | let num_combined_distillation_units = combined_distillation_unit_templates.len(); |
| 42 | |
| 43 | let mut purely_logical_distillation_unit_templates = Self::get_templates_for_unit_type( |
| 44 | distillation_unit_templates, |
| 45 | TFactoryDistillationUnitType::Logical, |
| 46 | ); |
| 47 | let num_logical_distillation_units = purely_logical_distillation_unit_templates.len(); |
| 48 | |
| 49 | let mut physical_distillation_units: Vec<TFactoryDistillationUnit> = |
| 50 | combined_distillation_unit_templates |
| 51 | .iter() |
| 52 | .map(|x| TFactoryDistillationUnit::by_template(x, &TFactoryQubit::Physical(qubit))) |
| 53 | .collect(); |
| 54 | |
| 55 | let mut purely_physical_distillation_units: Vec<TFactoryDistillationUnit> = |
| 56 | distillation_unit_templates |
| 57 | .iter() |
| 58 | .filter(|x| x.unit_type == TFactoryDistillationUnitType::Physical) |
| 59 | .map(|x| TFactoryDistillationUnit::by_template(x, &TFactoryQubit::Physical(qubit))) |
| 60 | // exclude purely invalid physical distillation units from scratch |
| 61 | .filter(TFactoryDistillationUnit::is_valid) |
| 62 | .collect(); |
| 63 | |
| 64 | let num_physical_distillation_units = purely_physical_distillation_units.len(); |
| 65 | physical_distillation_units.append(&mut purely_physical_distillation_units); |
| 66 | |
| 67 | let mut is_valid_physical_distillation_units = |
| 68 | vec![true; num_combined_distillation_units + num_physical_distillation_units]; |
| 69 | |
| 70 | for idx in 0..num_combined_distillation_units { |
| 71 | if !physical_distillation_units[idx].is_valid() { |
| 72 | is_valid_physical_distillation_units[idx] = false; |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | let mut logical_distillation_unit_templates = combined_distillation_unit_templates; |
| 77 | logical_distillation_unit_templates.append(&mut purely_logical_distillation_unit_templates); |
| 78 | |
| 79 | let mut logical_distillation_units: Vec<Vec<TFactoryDistillationUnit>> = Vec::new(); |
| 80 | |
| 81 | for qubit in qubits { |
| 82 | if let Some(qubit) = qubit { |
| 83 | logical_distillation_units.push( |
| 84 | logical_distillation_unit_templates |
| 85 | .iter() |
| 86 | .map(|x| { |
| 87 | TFactoryDistillationUnit::by_template(x, &TFactoryQubit::Logical(qubit)) |
| 88 | }) |
| 89 | .collect(), |
| 90 | ); |
| 91 | } else { |
| 92 | logical_distillation_units.push(Vec::new()); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | let mut min_valid_code_distance_indexes = |
| 97 | vec![qubits.len(); num_logical_distillation_units + num_combined_distillation_units]; |
| 98 | for idx in 0..num_logical_distillation_units + num_combined_distillation_units { |
| 99 | if let Some(min_valid_code_distance_index) = logical_distillation_units |
| 100 | .iter() |
| 101 | .filter(|x| !x.is_empty()) |
| 102 | .enumerate() |
| 103 | .filter_map(|(distance_index, x)| { |
| 104 | if x[idx].is_valid() { |
| 105 | Some(distance_index) |
| 106 | } else { |
| 107 | None |
| 108 | } |
| 109 | }) |
| 110 | .min() |
| 111 | { |
| 112 | min_valid_code_distance_indexes[idx] = min_valid_code_distance_index; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | Self { |
| 117 | physical_distillation_units, |
| 118 | is_valid_physical_distillation_units, |
| 119 | logical_distillation_units, |
| 120 | num_combined_distillation_units, |
| 121 | num_physical_distillation_units, |
| 122 | num_logical_distillation_units, |
| 123 | min_valid_code_distance_indexes, |
| 124 | num_code_distances, |
| 125 | distances, |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | fn get_templates_for_unit_type( |
| 130 | distillation_unit_templates: &[TFactoryDistillationUnitTemplate], |
| 131 | unit_type: TFactoryDistillationUnitType, |
| 132 | ) -> Vec<&TFactoryDistillationUnitTemplate> { |
| 133 | distillation_unit_templates |
| 134 | .iter() |
| 135 | .filter(|x| x.unit_type == unit_type) |
| 136 | .collect() |
| 137 | } |
| 138 | |
| 139 | fn get(&self, position: usize, distance: u64, idx: usize) -> &TFactoryDistillationUnit { |
| 140 | // physical: combined, purely physical |
| 141 | // logical: combined, purely logical |
| 142 | // enumeration: combined, purely logical, purely physical |
| 143 | if distance == 1 && position == 0 { |
| 144 | let index = if idx < self.num_combined_distillation_units { |
| 145 | idx |
| 146 | } else { |
| 147 | idx - self.num_logical_distillation_units |
| 148 | }; |
| 149 | |
| 150 | &self.physical_distillation_units[index] |
| 151 | } else { |
| 152 | &self.logical_distillation_units[distance as usize][idx] |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | pub fn get_min_distance_indexes(&self, indexes: &[usize]) -> Vec<usize> { |
| 157 | indexes |
| 158 | .iter() |
| 159 | .enumerate() |
| 160 | .scan(0, |state, (position, &idx)| { |
| 161 | *state = max(*state, self.get_min_distance_index(position, idx)); |
| 162 | Some(*state) |
| 163 | }) |
| 164 | .collect() |
| 165 | } |
| 166 | |
| 167 | pub fn get_max_distance_indexes(&self, indexes: &[usize]) -> Vec<usize> { |
| 168 | indexes |
| 169 | .iter() |
| 170 | .enumerate() |
| 171 | .map(|(position, &idx)| self.get_max_distance_index(position, idx)) |
| 172 | .collect() |
| 173 | } |
| 174 | |
| 175 | fn get_min_distance_index(&self, position: usize, idx: usize) -> usize { |
| 176 | if position == 0 { |
| 177 | if idx >= self.num_logical_distillation_units + self.num_combined_distillation_units { |
| 178 | 0 |
| 179 | } else if idx < self.num_combined_distillation_units { |
| 180 | if self.is_valid_physical_distillation_units[idx] { |
| 181 | 0 |
| 182 | } else { |
| 183 | max(1, self.min_valid_code_distance_indexes[idx]) |
| 184 | } |
| 185 | } else { |
| 186 | // skip code distance = 1 for logical units at first round of distillation |
| 187 | max(1, self.min_valid_code_distance_indexes[idx]) |
| 188 | } |
| 189 | } else if idx >= self.num_logical_distillation_units + self.num_combined_distillation_units |
| 190 | { |
| 191 | panic!("Invalid position for physical unit: {position}") |
| 192 | } else { |
| 193 | self.min_valid_code_distance_indexes[idx] |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | fn get_max_distance_index(&self, position: usize, idx: usize) -> usize { |
| 198 | if idx >= self.num_logical_distillation_units + self.num_combined_distillation_units { |
| 199 | if position == 0 { |
| 200 | 0 |
| 201 | } else { |
| 202 | panic!("Invalid position for physical unit: {position}") |
| 203 | } |
| 204 | } else { |
| 205 | self.num_code_distances - 1 |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | pub fn get_many( |
| 210 | &self, |
| 211 | distance_indexes: &[usize], |
| 212 | indexes: &[usize], |
| 213 | ) -> Vec<&TFactoryDistillationUnit> { |
| 214 | indexes |
| 215 | .iter() |
| 216 | .zip(distance_indexes) |
| 217 | .enumerate() |
| 218 | .map(|(position, (&idx, &distance_index))| { |
| 219 | self.get(position, self.distances[distance_index], idx) |
| 220 | }) |
| 221 | .collect() |
| 222 | } |
| 223 | |
| 224 | pub fn iterate_for_all_distillation_units<F>(&self, num_rounds: usize, action: &mut F) |
| 225 | where |
| 226 | F: FnMut(&[usize]), |
| 227 | { |
| 228 | let mut a: Vec<usize> = vec![0; num_rounds + 1]; |
| 229 | loop { |
| 230 | action(&a[1..]); |
| 231 | let mut j = num_rounds; |
| 232 | while a[j] |
| 233 | == if j == 0 { |
| 234 | 1 |
| 235 | } else if j == 1 { |
| 236 | self.num_logical_distillation_units |
| 237 | + self.num_combined_distillation_units |
| 238 | + self.num_physical_distillation_units |
| 239 | - 1 |
| 240 | } else { |
| 241 | self.num_logical_distillation_units + self.num_combined_distillation_units - 1 |
| 242 | } |
| 243 | { |
| 244 | a[j] = 0; |
| 245 | j -= 1; |
| 246 | } |
| 247 | |
| 248 | if j == 0 { |
| 249 | break; |
| 250 | } |
| 251 | |
| 252 | a[j] += 1; |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |