microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/paulimer/src/clifford/generic_algos.rs

576lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::{Clifford, CliffordMutable, MutablePreImages, PreimageViews};
5use crate::pauli::{PauliMutable, PauliMutableBits, PauliNeutralElement};
6use crate::{
7 bits::{
8 bitmatrix::{kernel_basis_matrix, MutableRow},
9 BitMatrix, Bitwise, BitwiseBinaryOps,
10 },
11 pauli::{anti_commutes_with, commutes_with, Pauli, PauliBinaryOps, PauliBits},
12 setwise::complement,
13 NeutralElement,
14};
15
16pub fn mul_assign_right_clifford_preimage_x_bits<'life, Target, PreImageUnder: PreimageViews>(
17 target: &mut Target,
18 clifford: &'life PreImageUnder,
19 bits: &impl Bitwise,
20) where
21 Target: PauliBinaryOps<PreImageUnder::PreImageView<'life>>,
22{
23 for id in bits.support() {
24 target.mul_assign_right(&clifford.preimage_x_view(id));
25 }
26}
27
28pub fn mul_assign_right_clifford_preimage_z_bits<'life, Target, PreImageUnder: PreimageViews>(
29 target: &mut Target,
30 clifford: &'life PreImageUnder,
31 bits: &impl Bitwise,
32) where
33 Target: PauliBinaryOps<PreImageUnder::PreImageView<'life>>,
34{
35 for id in bits.support() {
36 target.mul_assign_right(&clifford.preimage_z_view(id));
37 }
38}
39
40pub fn mul_assign_right_clifford_preimage<
41 'life,
42 Target,
43 PreImageUnder: PreimageViews,
44 PreImageOf: Pauli,
45>(
46 target: &mut Target,
47 clifford: &'life PreImageUnder,
48 pauli: &PreImageOf,
49) where
50 Target: PauliBinaryOps<PreImageUnder::PreImageView<'life>>
51 + Pauli<PhaseExponentValue = PreImageOf::PhaseExponentValue>,
52{
53 mul_assign_right_clifford_preimage_x_bits(target, clifford, pauli.x_bits());
54 mul_assign_right_clifford_preimage_z_bits(target, clifford, pauli.z_bits());
55 target.mul_assign_phase_from(pauli);
56}
57
58pub fn mul_assign_right_clifford_image_x_bits_up_to_phase<
59 'life,
60 Target,
61 ImageUnder: Clifford + PreimageViews,
62>(
63 target: &mut Target,
64 clifford: &'life ImageUnder,
65 bits: &impl Bitwise,
66) where
67 Target: PauliBinaryOps<ImageUnder::ImageViewUpToPhase<'life>>,
68{
69 for id in bits.support() {
70 target.mul_assign_right(&clifford.x_image_view_up_to_phase(id));
71 }
72}
73
74pub fn mul_assign_right_clifford_image_z_bits_up_to_phase<
75 'life,
76 Target,
77 ImageUnder: Clifford + PreimageViews,
78>(
79 target: &mut Target,
80 clifford: &'life ImageUnder,
81 bits: &impl Bitwise,
82) where
83 Target: PauliBinaryOps<ImageUnder::ImageViewUpToPhase<'life>>,
84{
85 for id in bits.support() {
86 target.mul_assign_right(&clifford.z_image_view_up_to_phase(id));
87 }
88}
89
90pub fn mul_assign_right_clifford_image_up_to_phase<
91 'life,
92 Target,
93 ImageUnder: Clifford + PreimageViews,
94 ImageOf: Pauli,
95>(
96 target: &mut Target,
97 clifford: &'life ImageUnder,
98 pauli: &ImageOf,
99) where
100 Target: PauliBinaryOps<ImageUnder::ImageViewUpToPhase<'life>>,
101{
102 mul_assign_right_clifford_image_x_bits_up_to_phase(target, clifford, pauli.x_bits());
103 mul_assign_right_clifford_image_z_bits_up_to_phase(target, clifford, pauli.z_bits());
104}
105
106#[allow(clippy::similar_names)]
107pub fn is_valid_clifford<CliffordLike: Clifford + PreimageViews>(candidate: &CliffordLike) -> bool {
108 for index in 0..candidate.num_qubits() {
109 let x_preimage = candidate.preimage_x_view(index);
110 let z_preimage = candidate.preimage_z_view(index);
111 if commutes_with(&x_preimage, &z_preimage) {
112 println!("commutes_with failed for:{index}");
113 return false;
114 }
115 for other_index in index + 1..candidate.num_qubits() {
116 let other_x_preimage = candidate.preimage_x_view(other_index);
117 let other_z_preimage = candidate.preimage_z_view(other_index);
118 let xx = anti_commutes_with(&x_preimage, &other_x_preimage);
119 let xz = anti_commutes_with(&x_preimage, &other_z_preimage);
120 let zx = anti_commutes_with(&z_preimage, &other_x_preimage);
121 let zz = anti_commutes_with(&z_preimage, &other_z_preimage);
122 if xx || xz || zx || zz {
123 println!(
124 "anti_commutes_with failed for:{index}, {other_index}, {xx} {xz} {zx} {zz}"
125 );
126 return false;
127 }
128 }
129 }
130 true
131}
132
133pub fn clifford_left_mul_eq_cnot(
134 clifford: &mut impl MutablePreImages,
135 control_id: usize,
136 target_id: usize,
137) {
138 let ((mut xc, zc), (xt, mut zt)) =
139 clifford.preimage_xz_views_mut_distinct((control_id, target_id));
140 xc.mul_assign_left(&xt);
141 zt.mul_assign_left(&zc);
142}
143
144pub fn clifford_left_mul_eq_cz(
145 clifford: &mut impl MutablePreImages,
146 control_id: usize,
147 target_id: usize,
148) {
149 let ((mut xc, zc), (mut xt, zt)) =
150 clifford.preimage_xz_views_mut_distinct((control_id, target_id));
151 xc.mul_assign_left(&zt);
152 xt.mul_assign_left(&zc);
153}
154
155pub fn clifford_left_mul_eq_root_z(clifford: &mut impl MutablePreImages, qubit_id: usize) {
156 let (mut x, z) = clifford.preimage_xz_views_mut(qubit_id);
157 x.mul_assign_left(&z);
158 x.add_assign_phase_exp(1);
159}
160
161pub fn clifford_left_mul_eq_root_z_inverse(clifford: &mut impl MutablePreImages, qubit_id: usize) {
162 let (mut x, z) = clifford.preimage_xz_views_mut(qubit_id);
163 x.mul_assign_left(&z);
164 x.add_assign_phase_exp(3);
165}
166
167pub fn clifford_left_mul_eq_root_x(clifford: &mut impl MutablePreImages, qubit_id: usize) {
168 let (x, mut z) = clifford.preimage_xz_views_mut(qubit_id);
169 z.mul_assign_left(&x);
170 z.add_assign_phase_exp(1);
171}
172
173pub fn clifford_left_mul_eq_root_y(clifford: &mut impl CliffordMutable, qubit_id: usize) {
174 clifford.left_mul_z(qubit_id);
175 clifford.left_mul_hadamard(qubit_id);
176}
177
178pub fn clifford_left_mul_eq_root_y_inverse(clifford: &mut impl CliffordMutable, qubit_id: usize) {
179 clifford.left_mul_hadamard(qubit_id);
180 clifford.left_mul_z(qubit_id);
181}
182
183pub fn clifford_left_mul_eq_x(clifford: &mut impl MutablePreImages, qubit_id: usize) {
184 let (_, mut z) = clifford.preimage_xz_views_mut(qubit_id);
185 z.add_assign_phase_exp(2);
186}
187
188pub fn clifford_left_mul_eq_z(clifford: &mut impl MutablePreImages, qubit_id: usize) {
189 let (mut x, _) = clifford.preimage_xz_views_mut(qubit_id);
190 x.add_assign_phase_exp(2);
191}
192
193pub fn clifford_left_mul_eq_y(clifford: &mut impl MutablePreImages, qubit_id: usize) {
194 let (mut x, mut z) = clifford.preimage_xz_views_mut(qubit_id);
195 x.add_assign_phase_exp(2);
196 z.add_assign_phase_exp(2);
197}
198
199pub fn clifford_left_mul_eq_root_x_inverse(clifford: &mut impl MutablePreImages, qubit_id: usize) {
200 let (x, mut z) = clifford.preimage_xz_views_mut(qubit_id);
201 z.mul_assign_left(&x);
202 z.add_assign_phase_exp(3);
203}
204
205pub fn support_restricted_z_images_from_support_complement<CliffordLike>(
206 clifford: &CliffordLike,
207 support_complement: &[usize],
208) -> BitMatrix
209where
210 CliffordLike: Clifford + PreimageViews,
211 for<'a> MutableRow<'a>: BitwiseBinaryOps<<CliffordLike::PreImageView<'a> as Pauli>::Bits>,
212{
213 let num_qubits = clifford.num_qubits();
214 let mut a = BitMatrix::zeros(2 * support_complement.len(), num_qubits);
215 let complement_size = support_complement.len();
216
217 for (j, qubit_index) in support_complement.iter().enumerate() {
218 let pre_img_x = clifford.preimage_x_view(*qubit_index);
219 a.row_mut(j).assign(pre_img_x.x_bits());
220 }
221
222 for (j, qubit_index) in support_complement.iter().enumerate() {
223 let pre_img_z = clifford.preimage_z_view(*qubit_index);
224 a.row_mut(j + complement_size).assign(pre_img_z.x_bits());
225 }
226
227 // for (j, qubit_index) in enumerate(support_complement) {
228 // let pre_img_z = clifford.z_preimage_view(*qubit_index);
229 // let pre_img_x = clifford.x_preimage_view(*qubit_index);
230 // for i in 0..num_qubits {
231 // a.set((j, i), pre_img_x.x_bits().index(i));
232 // a.set((j + complement_size, i), pre_img_z.x_bits().index(i))
233 // }
234 // }
235
236 kernel_basis_matrix(&a)
237}
238
239// pub fn z_clifford_image<'life, CliffordLike, ZBits>(
240// clifford: &'life CliffordLike,
241// z_bits: ZBits,
242// ) -> <CliffordLike as Clifford<'life>>::ImageUpToPhase
243// where
244// CliffordLike: Clifford<'life>,
245// ZBits: IntoIterator<Item = bool>,
246// {
247// if clifford.num_qubits() == 0 {
248// <CliffordLike::ImageViewUpToPhase<'life> as NeutralElement>::default_size_neutral_element()
249// } else {
250// let mut res = clifford.z_image_view_up_to_phase(0).neutral_element();
251// for (index, val) in enumerate(z_bits.into_iter()) {
252// if val {
253// res.mul_assign_left(&clifford.z_image_view_up_to_phase(index));
254// }
255// }
256// res
257// }
258// }
259
260// pub fn x_clifford_image<'life, CliffordLike, ZBits>(
261// clifford: &'life CliffordLike,
262// x_bits: ZBits,
263// ) -> <CliffordLike as CliffordWithNeutrals<'life>>::ImageUpToPhase
264// where
265// CliffordLike: CliffordWithNeutrals<'life>,
266// ZBits: IntoIterator<Item = bool>,
267// {
268// if clifford.num_qubits() == 0 {
269// <CliffordLike::ImageViewUpToPhase<'life> as NeutralElement>::default_size_neutral_element()
270// } else {
271// let mut res = clifford.x_image_view_up_to_phase(0).neutral_element();
272// for (index, val) in enumerate(x_bits.into_iter()) {
273// if val {
274// res.mul_assign_left(&clifford.x_image_view_up_to_phase(index));
275// }
276// }
277// res
278// }
279// }
280
281/// Each row of result a is such that image of Z^a is supported on `supported_on_qubits`.
282/// The result has full row rank; the rank is maximal possible.
283pub fn support_restricted_z_images<CliffordLike>(
284 clifford: &CliffordLike,
285 sorted_support: &[usize],
286) -> BitMatrix
287where
288 CliffordLike: Clifford + PreimageViews,
289 for<'a> MutableRow<'a>: BitwiseBinaryOps<<CliffordLike::PreImageView<'a> as Pauli>::Bits>,
290{
291 let num_qubits = clifford.num_qubits();
292 let support_complement = complement(sorted_support, num_qubits);
293 support_restricted_z_images_from_support_complement::<CliffordLike>(
294 clifford,
295 &support_complement,
296 )
297}
298
299pub fn clifford_left_mul_eq_prepare_bell<CliffordLike>(
300 clifford: &mut CliffordLike,
301 qubit_index1: usize,
302 qubit_index2: usize,
303) where
304 CliffordLike: CliffordMutable,
305{
306 clifford.left_mul_hadamard(qubit_index1);
307 clifford.left_mul_cx(qubit_index1, qubit_index2);
308}
309
310// Prepares state |0>^{k_1} \otimes |Bell_k> \otimes |0>^{k_2}
311// Where |Bell_k> are k Bell states between qubits j and j + k for j in [k]
312#[must_use]
313pub fn clifford_to_prepare_bell_states<CliffordLike>(num_bell_pairs: usize) -> CliffordLike
314where
315 CliffordLike: CliffordMutable + Clifford,
316{
317 let mut res = CliffordLike::identity(num_bell_pairs * 2);
318 for q in 0..num_bell_pairs {
319 clifford_left_mul_eq_prepare_bell(&mut res, q, q + num_bell_pairs);
320 }
321 res
322}
323
324/// # Panics
325///
326/// Will panic if Clifford unitaries act on different number of qubits
327pub fn clifford_multiply_with<
328 PhaseType,
329 CliffordLike: Clifford<PhaseExponentValue = PhaseType>
330 + PreimageViews<PhaseExponentValue = PhaseType>
331 + MutablePreImages<PhaseExponentValue = PhaseType>,
332>(
333 left: &CliffordLike,
334 right: &CliffordLike,
335) -> CliffordLike
336where
337 for<'life> <CliffordLike as MutablePreImages>::PreImageViewMut<'life>:
338 PauliBinaryOps<<CliffordLike as Clifford>::DensePauli>,
339{
340 assert!(left.num_qubits() == right.num_qubits());
341 let mut result = CliffordLike::identity(left.num_qubits());
342 for qubit_index in 0..left.num_qubits() {
343 result
344 .preimage_x_view_mut(qubit_index)
345 .assign(&right.preimage(&left.preimage_x_view(qubit_index)));
346 result
347 .preimage_z_view_mut(qubit_index)
348 .assign(&right.preimage(&left.preimage_z_view(qubit_index)));
349 }
350 result
351}
352
353pub fn clifford_is_identity<CliffordLike: Clifford + PreimageViews>(
354 clifford: &CliffordLike,
355) -> bool {
356 for qubit_id in 0..clifford.num_qubits() {
357 if !clifford.preimage_x_view(qubit_id).is_pauli_x(qubit_id) {
358 return false;
359 }
360 if !clifford.preimage_z_view(qubit_id).is_pauli_z(qubit_id) {
361 return false;
362 }
363 }
364 true
365}
366
367/// # Panics
368///
369/// Will panic if preimages do not correspond to a Clifford unitary
370pub fn clifford_from_preimages<'life, Paulis, PauliLike: Pauli + 'life, CliffordLike>(
371 mut preimages: Paulis,
372) -> CliffordLike
373where
374 CliffordLike: Clifford + MutablePreImages + PreimageViews,
375 for<'life1> <CliffordLike as MutablePreImages>::PreImageViewMut<'life1>:
376 PauliBinaryOps<PauliLike>,
377 Paulis: ExactSizeIterator<Item = &'life PauliLike>,
378{
379 assert!(preimages.len().is_multiple_of(2));
380 let mut res = CliffordLike::zero(preimages.len() / 2);
381 for qubit_index in 0..(preimages.len() / 2) {
382 let x_preimage = preimages
383 .next()
384 .expect("there should be `preimages.len()` items");
385 let z_preimage = preimages
386 .next()
387 .expect("there should be `preimages.len()` items");
388 res.preimage_x_view_mut(qubit_index).assign(x_preimage);
389 res.preimage_z_view_mut(qubit_index).assign(z_preimage);
390 }
391 assert!(is_valid_clifford(&res));
392 res
393}
394
395pub fn clifford_from_images<'life, Paulis, PauliLike: Pauli + 'life, CliffordLike>(
396 images: Paulis,
397) -> CliffordLike
398where
399 CliffordLike: Clifford + MutablePreImages + PreimageViews,
400 for<'life1> <CliffordLike as MutablePreImages>::PreImageViewMut<'life1>:
401 PauliBinaryOps<PauliLike>,
402 Paulis: ExactSizeIterator<Item = &'life PauliLike>,
403{
404 clifford_from_preimages::<Paulis, PauliLike, CliffordLike>(images).inverse()
405}
406
407pub fn clifford_image_with_phase<CliffordLike: Clifford + PreimageViews, Bits: PauliBits>(
408 clifford: &CliffordLike,
409 xz_bits: (Bits, Bits),
410) -> CliffordLike::DensePauli
411where
412 CliffordLike::DensePauli: PauliMutable,
413 CliffordLike::DensePauli: From<(Bits, Bits)>,
414 for<'life> <CliffordLike as PreimageViews>::PreImageView<'life>:
415 PauliNeutralElement<NeutralElementType = CliffordLike::DensePauli>,
416{
417 let mut preimage = clifford.preimage_x_view(0).neutral_element();
418 mul_assign_right_clifford_preimage_x_bits(&mut preimage, clifford, &xz_bits.0);
419 mul_assign_right_clifford_preimage_z_bits(&mut preimage, clifford, &xz_bits.1);
420 preimage.complex_conjugate();
421 let mut res = CliffordLike::DensePauli::from(xz_bits);
422 res.mul_assign_phase_from(&preimage);
423 res
424}
425
426pub fn clifford_left_mul_eq_pauli_exp<
427 PauliLike: Pauli,
428 CliffordLike: PreimageViews<PhaseExponentValue = PauliLike::PhaseExponentValue>
429 + MutablePreImages<PhaseExponentValue = PauliLike::PhaseExponentValue>,
430>(
431 clifford: &mut CliffordLike,
432 pauli: &PauliLike,
433) where
434 for<'life> <CliffordLike as PreimageViews>::PreImageView<'life>:
435 PauliNeutralElement + Pauli<PhaseExponentValue = PauliLike::PhaseExponentValue>,
436 for<'life1, 'life2> <CliffordLike as MutablePreImages>::PreImageViewMut<'life1>: PauliBinaryOps<<<CliffordLike as PreimageViews>::PreImageView<'life2> as NeutralElement>::NeutralElementType>
437 + Pauli<PhaseExponentValue = PauliLike::PhaseExponentValue>,
438 for<'life> <<CliffordLike as PreimageViews>::PreImageView<'life> as NeutralElement>::NeutralElementType:
439 Pauli<PhaseExponentValue = PauliLike::PhaseExponentValue>,
440{
441 let mut pauli_preimage = clifford.preimage_x_view(0).neutral_element();
442 mul_assign_right_clifford_preimage(&mut pauli_preimage, clifford, pauli);
443 pauli_preimage.add_assign_phase_exp(1u8);
444 for index in pauli.x_bits().support() {
445 clifford
446 .preimage_z_view_mut(index)
447 .mul_assign_right(&pauli_preimage);
448 }
449 for index in pauli.z_bits().support() {
450 clifford
451 .preimage_x_view_mut(index)
452 .mul_assign_right(&pauli_preimage);
453 }
454}
455
456pub fn clifford_left_mul_eq_controlled_pauli<CliffordLike: PreimageViews + MutablePreImages, PauliLike: Pauli>(
457 clifford: &mut CliffordLike,
458 control: &PauliLike,
459 target: &PauliLike,
460) where
461 for<'life> <CliffordLike as PreimageViews>::PreImageView<'life>:
462 PauliNeutralElement + Pauli<PhaseExponentValue = PauliLike::PhaseExponentValue>,
463 for<'life> <CliffordLike as MutablePreImages>::PreImageViewMut<'life>:
464 PauliBinaryOps<<<CliffordLike as PreimageViews>::PreImageView<'life> as NeutralElement>::NeutralElementType>,
465 for<'life> <<CliffordLike as PreimageViews>::PreImageView<'life> as NeutralElement>::NeutralElementType:
466 Pauli<PhaseExponentValue = PauliLike::PhaseExponentValue>,
467{
468 debug_assert!(commutes_with(control, target));
469 let mut target_preimage = clifford.preimage_x_view(0).neutral_element();
470 let mut control_preimage = clifford.preimage_x_view(0).neutral_element();
471 mul_assign_right_clifford_preimage(&mut target_preimage, clifford, target);
472 mul_assign_right_clifford_preimage(&mut control_preimage, clifford, control);
473 for index in control.x_bits().support() {
474 clifford
475 .preimage_z_view_mut(index)
476 .mul_assign_right(&target_preimage);
477 }
478 for index in control.z_bits().support() {
479 clifford
480 .preimage_x_view_mut(index)
481 .mul_assign_right(&target_preimage);
482 }
483 for index in target.x_bits().support() {
484 clifford
485 .preimage_z_view_mut(index)
486 .mul_assign_left(&control_preimage);
487 }
488 for index in target.z_bits().support() {
489 clifford
490 .preimage_x_view_mut(index)
491 .mul_assign_left(&control_preimage);
492 }
493}
494
495#[must_use]
496pub fn clifford_from_css_preimage_indicators<CliffordLike: Clifford + MutablePreImages>(
497 x_indicators: &BitMatrix,
498 z_indicators: &BitMatrix,
499) -> CliffordLike
500where
501 for<'life> <CliffordLike as MutablePreImages>::PreImageViewMut<'life>:
502 PauliMutableBits<crate::bits::BitView<'life>>,
503{
504 let num_qubits = x_indicators.columncount();
505 let mut res = CliffordLike::zero(num_qubits);
506 for qubit_index in res.qubits() {
507 res.preimage_x_view_mut(qubit_index)
508 .x_bits_mut()
509 .assign(&x_indicators.row(qubit_index));
510 res.preimage_z_view_mut(qubit_index)
511 .z_bits_mut()
512 .assign(&z_indicators.row(qubit_index));
513 }
514 debug_assert!(res.is_valid());
515 res
516}
517
518pub fn clifford_tensored<CliffordLike>(left: &CliffordLike, right: &CliffordLike) -> CliffordLike
519where
520 CliffordLike: Clifford + MutablePreImages + PreimageViews,
521 for<'life> <CliffordLike as MutablePreImages>::PreImageViewMut<'life>:
522 PauliBinaryOps<<CliffordLike as PreimageViews>::PreImageView<'life>>,
523{
524 let lhs_num_qubits = left.num_qubits();
525 let rhs_num_qubits = right.num_qubits();
526 let num_output_qubits = lhs_num_qubits + rhs_num_qubits;
527 let mut result = CliffordLike::zero(num_output_qubits);
528 for qubit_id in 0..lhs_num_qubits {
529 result.preimage_x_view_mut(qubit_id).assign_with_offset(
530 &left.preimage_x_view(qubit_id),
531 0,
532 lhs_num_qubits,
533 );
534 result.preimage_z_view_mut(qubit_id).assign_with_offset(
535 &left.preimage_z_view(qubit_id),
536 0,
537 lhs_num_qubits,
538 );
539 }
540 for qubit_id in 0..rhs_num_qubits {
541 result
542 .preimage_x_view_mut(lhs_num_qubits + qubit_id)
543 .assign_with_offset(
544 &right.preimage_x_view(qubit_id),
545 lhs_num_qubits,
546 rhs_num_qubits,
547 );
548 result
549 .preimage_z_view_mut(lhs_num_qubits + qubit_id)
550 .assign_with_offset(
551 &right.preimage_z_view(qubit_id),
552 lhs_num_qubits,
553 rhs_num_qubits,
554 );
555 }
556 debug_assert!(result.is_valid());
557 result
558}
559
560pub fn clifford_inverse_up_to_signs<CliffordLikeTo, CliffordLikeFrom: Clifford + PreimageViews>(
561 from: &CliffordLikeFrom,
562) -> CliffordLikeTo
563where
564 CliffordLikeTo: Clifford + MutablePreImages,
565 for<'life1, 'life2> <CliffordLikeTo as MutablePreImages>::PreImageViewMut<'life1>:
566 PauliBinaryOps<<CliffordLikeFrom as PreimageViews>::ImageViewUpToPhase<'life2>>,
567{
568 let mut res = CliffordLikeTo::identity(from.num_qubits());
569 for qubit_index in 0..from.num_qubits() {
570 res.preimage_x_view_mut(qubit_index)
571 .assign(&from.x_image_view_up_to_phase(qubit_index));
572 res.preimage_z_view_mut(qubit_index)
573 .assign(&from.z_image_view_up_to_phase(qubit_index));
574 }
575 res
576}
577