From 8da594c75ac30afa4cf284a7090ecefe5a584627 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Sat, 10 Aug 2024 06:00:09 +0530 Subject: [PATCH 01/30] initial commit --- crates/accelerate/src/synthesis/linear/mod.rs | 1 + crates/accelerate/src/synthesis/linear/pmh.rs | 63 ++++++++++++++++++- .../linear_phase/cnot_phase_synth.py | 1 + 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index 49dfeefd3869..bfc1e6a95630 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -188,5 +188,6 @@ pub fn linear(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(random_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(check_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(pmh::synth_cnot_count_full_pmh))?; + m.add_wrapped(wrap_pyfunction!(pmh::synth_cnot_phase_aam))?; Ok(()) } diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index d7283fd580e4..f202901b7232 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -12,7 +12,7 @@ use hashbrown::HashMap; use ndarray::{s, Array1, Array2, ArrayViewMut2, Axis}; -use numpy::PyReadonlyArray2; +use numpy::{PyReadonlyArray2, PyReadonlyArray1}; use smallvec::smallvec; use std::cmp; @@ -23,6 +23,7 @@ use qiskit_circuit::Qubit; use pyo3::prelude::*; use super::utils::_add_row_or_col; +use std::f64::consts::PI; /// This helper function allows transposed access to a matrix. fn _index(transpose: bool, i: usize, j: usize) -> (usize, usize) { @@ -193,3 +194,63 @@ pub fn synth_cnot_count_full_pmh( CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } + + +#[pyfunction] +#[pyo3(signature = (cnots, angles, section_size=2))] +pub fn synth_cnot_phase_aam( + py:Python, + cnots: PyReadonlyArray2, + angles: PyReadonlyArray1, + section_size: Option, +) -> PyResult { + + let cnots_arr = cnots.as_array().to_owned(); + let angles_arr = angles.as_array().to_owned(); + let num_qubits = cnots_arr.shape()[0]; + let mut cnots_arr_cpy = cnots_arr.clone(); + let mut instructions = vec![]; + + let mut rust_angles = Vec::new(); + for obj in angles_arr + { + rust_angles.push(obj.extract::(py)?); + } + + let state = Array2::::eye(num_qubits); + + for qubit_idx in 0..num_qubits + { + let mut index = 0 as usize; + + while index != cnots_arr_cpy.shape()[1] + { + + let icnot = cnots_arr_cpy.column(index).to_vec(); + + if icnot == state.row(qubit_idx as usize).to_vec() + { + match rust_angles.get(index) + { + Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), + None => (), + }; + + rust_angles.remove(index); + cnots_arr_cpy.remove_index(numpy::ndarray::Axis(1), index); + index -=1; + } + index +=1; + } + } + + + + CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) +} + diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index 25320029ef54..20d072c6ca9a 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -23,6 +23,7 @@ from qiskit.exceptions import QiskitError from qiskit.synthesis.linear import synth_cnot_count_full_pmh +from qiskit._accelerate.synthesis.linear import synth_cnot_phase_aam def synth_cnot_phase_aam( cnots: list[list[int]], angles: list[str], section_size: int = 2 From 193058bc450f9dc8a8710997fc5a80c26a5b6e12 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Sat, 10 Aug 2024 17:27:59 +0530 Subject: [PATCH 02/30] added some code --- crates/accelerate/src/synthesis/linear/pmh.rs | 87 +++++++++++++++++-- .../linear_phase/cnot_phase_synth.py | 3 +- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index f202901b7232..a77bede03582 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -205,10 +205,10 @@ pub fn synth_cnot_phase_aam( section_size: Option, ) -> PyResult { - let cnots_arr = cnots.as_array().to_owned(); + let S = cnots.as_array().to_owned(); let angles_arr = angles.as_array().to_owned(); - let num_qubits = cnots_arr.shape()[0]; - let mut cnots_arr_cpy = cnots_arr.clone(); + let num_qubits = S.shape()[0]; + let mut S_cpy = S.clone(); let mut instructions = vec![]; let mut rust_angles = Vec::new(); @@ -217,16 +217,15 @@ pub fn synth_cnot_phase_aam( rust_angles.push(obj.extract::(py)?); } - let state = Array2::::eye(num_qubits); + let mut state = Array2::::eye(num_qubits); for qubit_idx in 0..num_qubits { let mut index = 0 as usize; - while index != cnots_arr_cpy.shape()[1] + loop { - - let icnot = cnots_arr_cpy.column(index).to_vec(); + let icnot = S_cpy.column(index).to_vec(); if icnot == state.row(qubit_idx as usize).to_vec() { @@ -242,12 +241,82 @@ pub fn synth_cnot_phase_aam( }; rust_angles.remove(index); - cnots_arr_cpy.remove_index(numpy::ndarray::Axis(1), index); + S_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == S_cpy.shape()[1] {break;} index -=1; } index +=1; } - } + } + + + let epsilion = num_qubits; + let mut Q = vec![(S, 0..num_qubits, epsilion)]; + + while Q.len() != 0 + { + + let (_S, _I, _i) = Q.pop().unwrap(); + + if _S.len() == 0 {continue;} + + if 0 <= _i && _i < num_qubits + { + let condition = true; + while condition + { + let mut condition = false; + + for _j in 0..num_qubits + { + if (_j != _i) && (u32::from(_S.row(_j as usize).sum()) == _S.row(_j).len() as u32) + { + condition = true; + instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_i as u32), Qubit(_i as u32)])); + + for _k in 0..state.ncols() + { + state[(_i, _k)] ^= state[(_j, _k)]; + } + + let mut index = 0 as usize; + + loop + { + let icnot = S_cpy.column(index).to_vec(); + + if icnot == state.row(_i as usize).to_vec() + { + + match rust_angles.get(index) + { + Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_i as u32)])), + None => (), + }; + + rust_angles.remove(index); + S_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == S_cpy.shape()[1] { break; } + index -=1; + } + index +=1; + } + + loop + { + + } + } + } + } + } + if _I.len() == 0 {continue;} + } diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index 20d072c6ca9a..760e75914e39 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -23,7 +23,8 @@ from qiskit.exceptions import QiskitError from qiskit.synthesis.linear import synth_cnot_count_full_pmh -from qiskit._accelerate.synthesis.linear import synth_cnot_phase_aam +from qiskit._accelerate.synthesis.linear import synth_cnot_phase_aam as synth_cnot_phase_aam_xlated + def synth_cnot_phase_aam( cnots: list[list[int]], angles: list[str], section_size: int = 2 From 2ae9379946f3c4239b8c318062005ab37ed34776 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Thu, 22 Aug 2024 14:50:24 +0530 Subject: [PATCH 03/30] refactor code --- crates/accelerate/src/synthesis/linear/mod.rs | 1 - crates/accelerate/src/synthesis/linear/pmh.rs | 127 ----------------- .../linear_phase/cnot_phase_synth.rs | 128 ++++++++++++++++++ .../src/synthesis/linear_phase/mod.rs | 20 +++ crates/accelerate/src/synthesis/mod.rs | 2 + 5 files changed, 150 insertions(+), 128 deletions(-) create mode 100644 crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs create mode 100644 crates/accelerate/src/synthesis/linear_phase/mod.rs diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index bfc1e6a95630..49dfeefd3869 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -188,6 +188,5 @@ pub fn linear(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(random_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(check_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(pmh::synth_cnot_count_full_pmh))?; - m.add_wrapped(wrap_pyfunction!(pmh::synth_cnot_phase_aam))?; Ok(()) } diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index a77bede03582..917454d4a70a 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -196,130 +196,3 @@ pub fn synth_cnot_count_full_pmh( } -#[pyfunction] -#[pyo3(signature = (cnots, angles, section_size=2))] -pub fn synth_cnot_phase_aam( - py:Python, - cnots: PyReadonlyArray2, - angles: PyReadonlyArray1, - section_size: Option, -) -> PyResult { - - let S = cnots.as_array().to_owned(); - let angles_arr = angles.as_array().to_owned(); - let num_qubits = S.shape()[0]; - let mut S_cpy = S.clone(); - let mut instructions = vec![]; - - let mut rust_angles = Vec::new(); - for obj in angles_arr - { - rust_angles.push(obj.extract::(py)?); - } - - let mut state = Array2::::eye(num_qubits); - - for qubit_idx in 0..num_qubits - { - let mut index = 0 as usize; - - loop - { - let icnot = S_cpy.column(index).to_vec(); - - if icnot == state.row(qubit_idx as usize).to_vec() - { - match rust_angles.get(index) - { - Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), - None => (), - }; - - rust_angles.remove(index); - S_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == S_cpy.shape()[1] {break;} - index -=1; - } - index +=1; - } - } - - - let epsilion = num_qubits; - let mut Q = vec![(S, 0..num_qubits, epsilion)]; - - while Q.len() != 0 - { - - let (_S, _I, _i) = Q.pop().unwrap(); - - if _S.len() == 0 {continue;} - - if 0 <= _i && _i < num_qubits - { - let condition = true; - while condition - { - let mut condition = false; - - for _j in 0..num_qubits - { - if (_j != _i) && (u32::from(_S.row(_j as usize).sum()) == _S.row(_j).len() as u32) - { - condition = true; - instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_i as u32), Qubit(_i as u32)])); - - for _k in 0..state.ncols() - { - state[(_i, _k)] ^= state[(_j, _k)]; - } - - let mut index = 0 as usize; - - loop - { - let icnot = S_cpy.column(index).to_vec(); - - if icnot == state.row(_i as usize).to_vec() - { - - match rust_angles.get(index) - { - Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_i as u32)])), - None => (), - }; - - rust_angles.remove(index); - S_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == S_cpy.shape()[1] { break; } - index -=1; - } - index +=1; - } - - loop - { - - } - } - } - } - } - if _I.len() == 0 {continue;} - } - - - - CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) -} - diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs new file mode 100644 index 000000000000..a168b51496b4 --- /dev/null +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -0,0 +1,128 @@ + + +#[pyfunction] +#[pyo3(signature = (cnots, angles, section_size=2))] +pub fn synth_cnot_phase_aam( + py:Python, + cnots: PyReadonlyArray2, + angles: PyReadonlyArray1, + section_size: Option, +) -> PyResult { + + let S = cnots.as_array().to_owned(); + let angles_arr = angles.as_array().to_owned(); + let num_qubits = S.shape()[0]; + let mut S_cpy = S.clone(); + let mut instructions = vec![]; + + let mut rust_angles = Vec::new(); + for obj in angles_arr + { + rust_angles.push(obj.extract::(py)?); + } + + let mut state = Array2::::eye(num_qubits); + + for qubit_idx in 0..num_qubits + { + let mut index = 0 as usize; + + loop + { + let icnot = S_cpy.column(index).to_vec(); + + if icnot == state.row(qubit_idx as usize).to_vec() + { + match rust_angles.get(index) + { + Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), + None => (), + }; + + rust_angles.remove(index); + S_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == S_cpy.shape()[1] {break;} + index -=1; + } + index +=1; + } + } + + + let epsilion = num_qubits; + let mut Q = vec![(S, 0..num_qubits, epsilion)]; + + while Q.len() != 0 + { + + let (_S, _I, _i) = Q.pop().unwrap(); + + if _S.len() == 0 {continue;} + + if 0 <= _i && _i < num_qubits + { + let condition = true; + while condition + { + let mut condition = false; + + for _j in 0..num_qubits + { + if (_j != _i) && (u32::from(_S.row(_j as usize).sum()) == _S.row(_j).len() as u32) + { + condition = true; + instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_i as u32), Qubit(_i as u32)])); + + for _k in 0..state.ncols() + { + state[(_i, _k)] ^= state[(_j, _k)]; + } + + let mut index = 0 as usize; + + loop + { + let icnot = S_cpy.column(index).to_vec(); + + if icnot == state.row(_i as usize).to_vec() + { + + match rust_angles.get(index) + { + Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_i as u32)])), + Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_i as u32)])), + None => (), + }; + + rust_angles.remove(index); + S_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == S_cpy.shape()[1] { break; } + index -=1; + } + index +=1; + } + + loop + { + + } + } + } + } + } + if _I.len() == 0 {continue;} + } + + + + CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) +} diff --git a/crates/accelerate/src/synthesis/linear_phase/mod.rs b/crates/accelerate/src/synthesis/linear_phase/mod.rs new file mode 100644 index 000000000000..b5c74efb7127 --- /dev/null +++ b/crates/accelerate/src/synthesis/linear_phase/mod.rs @@ -0,0 +1,20 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2024 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +mod cnot_phase_synth; + +#[pymodule] +pub fn linear_phase(m: &Bound) -> PyResult<()> +{ + m.add_wrapped(wrap_pyfunction!(cnot_phase_synth::synth_cnot_phase_aam))?; + Ok(()) +} diff --git a/crates/accelerate/src/synthesis/mod.rs b/crates/accelerate/src/synthesis/mod.rs index 1b9908ef80cf..cd7ebc159677 100644 --- a/crates/accelerate/src/synthesis/mod.rs +++ b/crates/accelerate/src/synthesis/mod.rs @@ -12,6 +12,7 @@ mod clifford; pub mod linear; +pub mod linear_phase; mod permutation; use pyo3::prelude::*; @@ -20,6 +21,7 @@ use pyo3::wrap_pymodule; #[pymodule] pub fn synthesis(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(linear::linear))?; + m.add_wrapped(wrap_pymodule!(linear_phase::linear_phase))?; m.add_wrapped(wrap_pymodule!(permutation::permutation))?; m.add_wrapped(wrap_pymodule!(clifford::clifford))?; Ok(()) From 60379b23ae4236f449ecb5e1e51fd8fb83638eef Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 23 Aug 2024 05:35:11 +0530 Subject: [PATCH 04/30] done some cargo-clippy linting --- crates/accelerate/src/synthesis/linear/pmh.rs | 3 +- .../linear_phase/cnot_phase_synth.rs | 81 ++++++++++++------- .../linear_phase/cnot_phase_synth.py | 2 +- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index 917454d4a70a..696011bbdb81 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -12,7 +12,7 @@ use hashbrown::HashMap; use ndarray::{s, Array1, Array2, ArrayViewMut2, Axis}; -use numpy::{PyReadonlyArray2, PyReadonlyArray1}; +use numpy::PyReadonlyArray2; use smallvec::smallvec; use std::cmp; @@ -23,7 +23,6 @@ use qiskit_circuit::Qubit; use pyo3::prelude::*; use super::utils::_add_row_or_col; -use std::f64::consts::PI; /// This helper function allows transposed access to a matrix. fn _index(transpose: bool, i: usize, j: usize) -> (usize, usize) { diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index a168b51496b4..00c5b16e8e97 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -1,4 +1,23 @@ - +// This code is part of Qiskit. +// +// (C) Copyright IBM 2024 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +use ndarray::{Array2}; +use numpy::{PyReadonlyArray2, PyReadonlyArray1}; +use smallvec::smallvec; +use qiskit_circuit::circuit_data::CircuitData; +use qiskit_circuit::operations::{Param, StandardGate}; +use qiskit_circuit::Qubit; +use pyo3::prelude::*; +use std::f64::consts::PI; #[pyfunction] #[pyo3(signature = (cnots, angles, section_size=2))] @@ -9,10 +28,10 @@ pub fn synth_cnot_phase_aam( section_size: Option, ) -> PyResult { - let S = cnots.as_array().to_owned(); + let s = cnots.as_array().to_owned(); let angles_arr = angles.as_array().to_owned(); - let num_qubits = S.shape()[0]; - let mut S_cpy = S.clone(); + let num_qubits = s.shape()[0]; + let mut s_cpy = s.clone(); let mut instructions = vec![]; let mut rust_angles = Vec::new(); @@ -25,13 +44,13 @@ pub fn synth_cnot_phase_aam( for qubit_idx in 0..num_qubits { - let mut index = 0 as usize; + let mut index = 0_usize; loop { - let icnot = S_cpy.column(index).to_vec(); + let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(qubit_idx as usize).to_vec() + if icnot == state.row(qubit_idx).to_vec() { match rust_angles.get(index) { @@ -45,8 +64,8 @@ pub fn synth_cnot_phase_aam( }; rust_angles.remove(index); - S_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == S_cpy.shape()[1] {break;} + s_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == s_cpy.shape()[1] {break;} index -=1; } index +=1; @@ -55,57 +74,57 @@ pub fn synth_cnot_phase_aam( let epsilion = num_qubits; - let mut Q = vec![(S, 0..num_qubits, epsilion)]; + let mut q = vec![(s, 0..num_qubits, epsilion)]; - while Q.len() != 0 + while !q.is_empty() { - let (_S, _I, _i) = Q.pop().unwrap(); + let (_s, _i, _ep) = q.pop().unwrap(); - if _S.len() == 0 {continue;} + if _s.is_empty() {continue;} - if 0 <= _i && _i < num_qubits + if 0 <= _ep && _ep < num_qubits { - let condition = true; + let mut condition = true; while condition { - let mut condition = false; + condition = false; for _j in 0..num_qubits { - if (_j != _i) && (u32::from(_S.row(_j as usize).sum()) == _S.row(_j).len() as u32) + if (_j != _ep) && (u32::from(_s.row(_j).sum()) == _s.row(_j).len() as u32) { condition = true; - instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_i as u32), Qubit(_i as u32)])); + instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_ep as u32), Qubit(_ep as u32)])); for _k in 0..state.ncols() { - state[(_i, _k)] ^= state[(_j, _k)]; + state[(_ep, _k)] ^= state[(_j, _k)]; } - let mut index = 0 as usize; + let mut index = 0_usize; loop { - let icnot = S_cpy.column(index).to_vec(); + let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(_i as usize).to_vec() + if icnot == state.row(_ep).to_vec() { match rust_angles.get(index) { - Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_i as u32)])), - Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_i as u32)])), + Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_ep as u32)])), + Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), + Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_ep as u32)])), + Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), + Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_ep as u32)])), + Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_ep as u32)])), None => (), }; rust_angles.remove(index); - S_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == S_cpy.shape()[1] { break; } + s_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == s_cpy.shape()[1] { break; } index -=1; } index +=1; @@ -119,7 +138,7 @@ pub fn synth_cnot_phase_aam( } } } - if _I.len() == 0 {continue;} + if _i.is_empty() {continue;} } diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index 760e75914e39..cb93e06ce14b 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -23,7 +23,7 @@ from qiskit.exceptions import QiskitError from qiskit.synthesis.linear import synth_cnot_count_full_pmh -from qiskit._accelerate.synthesis.linear import synth_cnot_phase_aam as synth_cnot_phase_aam_xlated +from qiskit._accelerate.synthesis.linear_phase import synth_cnot_phase_aam as synth_cnot_phase_aam_xlated def synth_cnot_phase_aam( From 671b6342f678cc0bae89209ebd8bb0aba589aa5b Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Mon, 26 Aug 2024 14:10:01 +0530 Subject: [PATCH 05/30] added code --- crates/accelerate/src/synthesis/linear/pmh.rs | 2 - .../linear_phase/cnot_phase_synth.rs | 113 ++++++++++++++++-- .../linear_phase/cnot_phase_synth.py | 5 + 3 files changed, 111 insertions(+), 9 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index 696011bbdb81..d7283fd580e4 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -193,5 +193,3 @@ pub fn synth_cnot_count_full_pmh( CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } - - diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 00c5b16e8e97..288223e42854 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -74,10 +74,14 @@ pub fn synth_cnot_phase_aam( let epsilion = num_qubits; - let mut q = vec![(s, 0..num_qubits, epsilion)]; + let mut q = vec![(s, (0..num_qubits).collect::>(), epsilion)]; - while !q.is_empty() - { + loop + { + if q.is_empty() + { + break; + } let (_s, _i, _ep) = q.pop().unwrap(); @@ -95,7 +99,7 @@ pub fn synth_cnot_phase_aam( if (_j != _ep) && (u32::from(_s.row(_j).sum()) == _s.row(_j).len() as u32) { condition = true; - instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_ep as u32), Qubit(_ep as u32)])); + instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_j as u32), Qubit(_ep as u32)])); for _k in 0..state.ncols() { @@ -104,9 +108,9 @@ pub fn synth_cnot_phase_aam( let mut index = 0_usize; - loop + for _s_idx in 0..state.nrows() { - let icnot = s_cpy.column(index).to_vec(); + let icnot = s_cpy.column(_s_idx).to_vec(); if icnot == state.row(_ep).to_vec() { @@ -130,15 +134,110 @@ pub fn synth_cnot_phase_aam( index +=1; } - loop + let temp_var = (_s.clone(), _i.clone(), _ep); + if !q.contains(&temp_var) + { + q.push(temp_var); + } + + for data in &q { + let (ref mut _temp_s, _, _) = data; + + if _temp_s.is_empty() {continue;} + for idx in 0.._temp_s.row(_j).len() + { + _temp_s[(_j, idx)] ^= _temp_s[(_ep, idx)]; + } } } } } } + if _i.is_empty() {continue;} + + let mut maxes: Vec = vec![]; + for row in _s.rows() + { + maxes.push(std::cmp::max(row.iter().filter(|&&x| x == 0).count(), row.iter().filter(|&&x| x == 1).count())); + } + + let mut maxes2: Vec = vec![]; + for _i_idx in _i.clone() + { + maxes2.push(maxes[_i_idx]); + } + + let mut _temp_max = maxes2[0]; + let mut _temp_argmax = 0_usize; + + for (idx, &ele) in maxes2.iter().enumerate() + { + if ele > _temp_max + { + _temp_max = ele; + _temp_argmax = idx; + } + } + + let _j = _i[_temp_argmax]; + + let mut cnots0_t = vec![]; + let mut cnots1_t = vec![]; + + let mut cnots0_shape_data = (0_usize, 0_usize); + let mut cnots1_shape_data = (0_usize, 0_usize); + for cols in _s.columns() + { + if cols[_j] == 0 + { + cnots0_shape_data.0 += 1; + cnots0_shape_data.1 = cols.to_vec().len() as usize; + for ele in cols.to_vec() + { + cnots0_t.push(ele); + } + } + else if cols[_j] == 1 + { + cnots1_shape_data.0 += 1; + cnots1_shape_data.1 = cols.to_vec().len() as usize; + for ele in cols.to_vec() + { + cnots1_t.push(ele); + } + } + } + + + let cnots0 = Array2::from_shape_vec((cnots0_shape_data.0, cnots0_shape_data.1), cnots0_t).unwrap(); + let cnots1 = Array2::from_shape_vec((cnots1_shape_data.0, cnots1_shape_data.1), cnots1_t).unwrap(); + + let cnots0 = cnots0.t().to_owned(); + let cnots1 = cnots1.t().to_owned(); + + if _ep == num_qubits + { + let _temp_data = (cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _j); + if !q.contains(&_temp_data) + { + q.push(_temp_data); + } + } + else + { + let _temp_data = (cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep); + if !q.contains(&_temp_data) + { + q.push(_temp_data); + } + } + + q.push((cnots0, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep)); + + } diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index cb93e06ce14b..f7f5294203b9 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -25,6 +25,11 @@ from qiskit._accelerate.synthesis.linear_phase import synth_cnot_phase_aam as synth_cnot_phase_aam_xlated +def synth_cnot_pahse_aam_xlated(cnots: list[list[int]], angles: list[str], section_size:int 2) -> QuantumCircuit + _circuit_data = synth_cnot_phase_aam_xlated(cnots, angles, section_size) + qc_from_rust = QuantumCircuit._from_circuit_data(_circuit_data) + return qc_from_rust + def synth_cnot_phase_aam( cnots: list[list[int]], angles: list[str], section_size: int = 2 From ae9142555072af4002fdf5555f85dafc1deafa37 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Tue, 27 Aug 2024 05:55:31 +0530 Subject: [PATCH 06/30] added some more code --- crates/accelerate/src/synthesis/linear/mod.rs | 2 +- crates/accelerate/src/synthesis/linear/pmh.rs | 19 +++++++++++++++---- .../linear_phase/cnot_phase_synth.rs | 16 ++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index 08a0b1e104b3..89ba00725c29 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -14,7 +14,7 @@ use crate::QiskitError; use numpy::{IntoPyArray, PyArray2, PyReadonlyArray2, PyReadwriteArray2}; use pyo3::prelude::*; -mod pmh; +pub mod pmh; pub mod utils; #[pyfunction] diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index d7283fd580e4..c78f5b5b9f0c 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -13,7 +13,7 @@ use hashbrown::HashMap; use ndarray::{s, Array1, Array2, ArrayViewMut2, Axis}; use numpy::PyReadonlyArray2; -use smallvec::smallvec; +use smallvec::{smallvec, SmallVec}; use std::cmp; use qiskit_circuit::circuit_data::CircuitData; @@ -159,7 +159,18 @@ pub fn synth_cnot_count_full_pmh( section_size: Option, ) -> PyResult { let arrayview = matrix.as_array(); - let mut mat: Array2 = arrayview.to_owned(); + let mat: Array2 = arrayview.to_owned(); + let num_qubits = mat.nrows(); + + let instructions = _synth_cnot_count_full_pmh(mat, section_size); + CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) +} + + +type Instructions = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); +pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> Vec +{ + let mut mat = mat; let num_qubits = mat.nrows(); // is a quadratic matrix // If given, use the user-specified input size. If None, we default to @@ -168,7 +179,7 @@ pub fn synth_cnot_count_full_pmh( // In addition, we at least set a block size of 2, which, in practice, minimizes the bound // until ~100 qubits. let alpha = 0.56; - let blocksize = match section_size { + let blocksize = match sec_size { Some(section_size) => section_size as usize, None => std::cmp::max(2, (alpha * (num_qubits as f64).log2()).floor() as usize), }; @@ -190,6 +201,6 @@ pub fn synth_cnot_count_full_pmh( smallvec![Qubit(ctrl as u32), Qubit(target as u32)], ) }); + instructions.collect() - CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 288223e42854..fd7e689410ad 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -16,6 +16,7 @@ use smallvec::smallvec; use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::Qubit; +use crate::synthesis::linear::pmh::_synth_cnot_count_full_pmh; use pyo3::prelude::*; use std::f64::consts::PI; @@ -25,7 +26,7 @@ pub fn synth_cnot_phase_aam( py:Python, cnots: PyReadonlyArray2, angles: PyReadonlyArray1, - section_size: Option, + section_size: Option, ) -> PyResult { let s = cnots.as_array().to_owned(); @@ -87,7 +88,7 @@ pub fn synth_cnot_phase_aam( if _s.is_empty() {continue;} - if 0 <= _ep && _ep < num_qubits + if 0 <= _ep as i32 && _ep < num_qubits { let mut condition = true; while condition @@ -140,9 +141,9 @@ pub fn synth_cnot_phase_aam( q.push(temp_var); } - for data in &q + for data in &mut q { - let (ref mut _temp_s, _, _) = data; + let (ref mut _temp_s, _temp_i, _temp_ep) = data; if _temp_s.is_empty() {continue;} @@ -194,7 +195,7 @@ pub fn synth_cnot_phase_aam( if cols[_j] == 0 { cnots0_shape_data.0 += 1; - cnots0_shape_data.1 = cols.to_vec().len() as usize; + cnots0_shape_data.1 = cols.to_vec().len(); for ele in cols.to_vec() { cnots0_t.push(ele); @@ -203,7 +204,7 @@ pub fn synth_cnot_phase_aam( else if cols[_j] == 1 { cnots1_shape_data.0 += 1; - cnots1_shape_data.1 = cols.to_vec().len() as usize; + cnots1_shape_data.1 = cols.to_vec().len(); for ele in cols.to_vec() { cnots1_t.push(ele); @@ -240,6 +241,9 @@ pub fn synth_cnot_phase_aam( } + let state_bool = state.mapv(|x| x != 0); + let data_back = _synth_cnot_count_full_pmh(state_bool, section_size); + CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) From 64ce56b3f316d16d34ee68cb54ec2276eb47ea68 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Tue, 27 Aug 2024 17:22:39 +0530 Subject: [PATCH 07/30] refactor code --- .../src/synthesis/linear_phase/cnot_phase_synth.rs | 10 ++++++---- qiskit/synthesis/linear_phase/cnot_phase_synth.py | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index fd7e689410ad..6bfd935698b4 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -47,7 +47,7 @@ pub fn synth_cnot_phase_aam( { let mut index = 0_usize; - loop + while index < s_cpy.ncols() { let icnot = s_cpy.column(index).to_vec(); @@ -67,11 +67,12 @@ pub fn synth_cnot_phase_aam( rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); if index == s_cpy.shape()[1] {break;} + if index == 0 {continue;} index -=1; } index +=1; } - } + } let epsilion = num_qubits; @@ -109,9 +110,9 @@ pub fn synth_cnot_phase_aam( let mut index = 0_usize; - for _s_idx in 0..state.nrows() + while index < s_cpy.ncols() { - let icnot = s_cpy.column(_s_idx).to_vec(); + let icnot = s_cpy.column(index).to_vec(); if icnot == state.row(_ep).to_vec() { @@ -130,6 +131,7 @@ pub fn synth_cnot_phase_aam( rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); if index == s_cpy.shape()[1] { break; } + if index == 0 {continue;} index -=1; } index +=1; diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index f7f5294203b9..7f7615580990 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -25,8 +25,10 @@ from qiskit._accelerate.synthesis.linear_phase import synth_cnot_phase_aam as synth_cnot_phase_aam_xlated -def synth_cnot_pahse_aam_xlated(cnots: list[list[int]], angles: list[str], section_size:int 2) -> QuantumCircuit - _circuit_data = synth_cnot_phase_aam_xlated(cnots, angles, section_size) +def synth_cnot_pahse_aam_xlated(cnots: list[list[int]], angles: list[str], section_size:int = 2) -> QuantumCircuit: + cnots_array = np.asarray(cnots).astype(np.uint8) + angles_array = np.asarray(angles, dtype = object) + _circuit_data = synth_cnot_phase_aam_xlated(cnots_array, angles_array, section_size) qc_from_rust = QuantumCircuit._from_circuit_data(_circuit_data) return qc_from_rust From fe0158976d8827ff696dbbc0d74597650f7cfcac Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Tue, 27 Aug 2024 17:25:41 +0530 Subject: [PATCH 08/30] refactor code --- .../src/synthesis/linear_phase/cnot_phase_synth.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 6bfd935698b4..f71c736cd588 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -244,9 +244,10 @@ pub fn synth_cnot_phase_aam( } let state_bool = state.mapv(|x| x != 0); - let data_back = _synth_cnot_count_full_pmh(state_bool, section_size); - - - + let instrs = _synth_cnot_count_full_pmh(state_bool, section_size); + for inst in instrs + { + instructions.push(inst); + } CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } From 1ecab6d900bc0a047fba6168432e8701f5888f2c Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Wed, 28 Aug 2024 05:36:02 +0530 Subject: [PATCH 09/30] refactor code --- .../linear_phase/cnot_phase_synth.rs | 57 ++++++++++--------- .../linear_phase/cnot_phase_synth.py | 11 +++- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index f71c736cd588..fed4fad266d5 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -46,14 +46,14 @@ pub fn synth_cnot_phase_aam( for qubit_idx in 0..num_qubits { let mut index = 0_usize; + let mut swtch: bool = true; while index < s_cpy.ncols() { let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(qubit_idx).to_vec() { - match rust_angles.get(index) + match rust_angles.get(index) { Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), @@ -63,33 +63,30 @@ pub fn synth_cnot_phase_aam( Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), None => (), }; - - rust_angles.remove(index); - s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.shape()[1] {break;} - if index == 0 {continue;} - index -=1; + rust_angles.remove(index); + s_cpy.remove_index(numpy::ndarray::Axis(1), index); + if index == s_cpy.shape()[1] {break;} + if index == 0 {swtch = false;} + else {index -=1;} } - index +=1; + if swtch {index +=1;} + else {swtch = true;} } } - let epsilion = num_qubits; + let epsilion: usize = num_qubits; let mut q = vec![(s, (0..num_qubits).collect::>(), epsilion)]; - loop - { - if q.is_empty() - { - break; - } + + while !q.is_empty() + { let (_s, _i, _ep) = q.pop().unwrap(); if _s.is_empty() {continue;} - if 0 <= _ep as i32 && _ep < num_qubits + if 0 <= _ep as isize && _ep < num_qubits { let mut condition = true; while condition @@ -98,7 +95,7 @@ pub fn synth_cnot_phase_aam( for _j in 0..num_qubits { - if (_j != _ep) && (u32::from(_s.row(_j).sum()) == _s.row(_j).len() as u32) + if (_j != _ep) && (usize::from(_s.row(_j).sum()) == _s.row(_j).len()) { condition = true; instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_j as u32), Qubit(_ep as u32)])); @@ -108,15 +105,14 @@ pub fn synth_cnot_phase_aam( state[(_ep, _k)] ^= state[(_j, _k)]; } - let mut index = 0_usize; + let mut index = 0_usize; + let mut swtch: bool = true; while index < s_cpy.ncols() { let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(_ep).to_vec() { - match rust_angles.get(index) { Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_ep as u32)])), @@ -127,14 +123,14 @@ pub fn synth_cnot_phase_aam( Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_ep as u32)])), None => (), }; - rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.shape()[1] { break; } - if index == 0 {continue;} - index -=1; + if index == s_cpy.shape()[1] {break;} + if index == 0 {swtch = false;} + else {index -=1;} } - index +=1; + if swtch {index +=1;} + else {swtch = true; } } let temp_var = (_s.clone(), _i.clone(), _ep); @@ -238,13 +234,18 @@ pub fn synth_cnot_phase_aam( } } - q.push((cnots0, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep)); + let _temp_data = (cnots0, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep); + if !q.contains(&_temp_data) + { + q.push(_temp_data); + } } let state_bool = state.mapv(|x| x != 0); - let instrs = _synth_cnot_count_full_pmh(state_bool, section_size); + let mut instrs = _synth_cnot_count_full_pmh(state_bool, section_size); + instrs.reverse(); for inst in instrs { instructions.push(inst); diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index 7f7615580990..c469283854e5 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -23,11 +23,16 @@ from qiskit.exceptions import QiskitError from qiskit.synthesis.linear import synth_cnot_count_full_pmh -from qiskit._accelerate.synthesis.linear_phase import synth_cnot_phase_aam as synth_cnot_phase_aam_xlated +from qiskit._accelerate.synthesis.linear_phase import ( + synth_cnot_phase_aam as synth_cnot_phase_aam_xlated, +) -def synth_cnot_pahse_aam_xlated(cnots: list[list[int]], angles: list[str], section_size:int = 2) -> QuantumCircuit: + +def synth_cnot_pahse_aam_xlated( + cnots: list[list[int]], angles: list[str], section_size: int = 2 +) -> QuantumCircuit: cnots_array = np.asarray(cnots).astype(np.uint8) - angles_array = np.asarray(angles, dtype = object) + angles_array = np.asarray(angles, dtype=object) _circuit_data = synth_cnot_phase_aam_xlated(cnots_array, angles_array, section_size) qc_from_rust = QuantumCircuit._from_circuit_data(_circuit_data) return qc_from_rust From a217522a32e6492a6ab092dff49016f13d7dbb05 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Wed, 28 Aug 2024 14:37:53 +0530 Subject: [PATCH 10/30] refactor code --- .../src/synthesis/linear_phase/cnot_phase_synth.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index fed4fad266d5..47098ccde457 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -65,7 +65,7 @@ pub fn synth_cnot_phase_aam( }; rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.shape()[1] {break;} + if index == s_cpy.ncols() {break;} if index == 0 {swtch = false;} else {index -=1;} } @@ -86,7 +86,7 @@ pub fn synth_cnot_phase_aam( if _s.is_empty() {continue;} - if 0 <= _ep as isize && _ep < num_qubits + if 0 <= (_ep as isize) && _ep < num_qubits { let mut condition = true; while condition @@ -95,7 +95,7 @@ pub fn synth_cnot_phase_aam( for _j in 0..num_qubits { - if (_j != _ep) && (usize::from(_s.row(_j).sum()) == _s.row(_j).len()) + if (_j != _ep) && (_s.row(_j).sum() as usize == _s.row(_j).len()) { condition = true; instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_j as u32), Qubit(_ep as u32)])); @@ -125,7 +125,7 @@ pub fn synth_cnot_phase_aam( }; rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.shape()[1] {break;} + if index == s_cpy.ncols() {break;} if index == 0 {swtch = false;} else {index -=1;} } From 4a36e1e743233ab3ddd3fc7c54a65496c69e1313 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Thu, 29 Aug 2024 05:32:34 +0530 Subject: [PATCH 11/30] refactor code --- .../src/synthesis/linear_phase/cnot_phase_synth.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 47098ccde457..6fefee2969cf 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -82,11 +82,11 @@ pub fn synth_cnot_phase_aam( while !q.is_empty() { - let (_s, _i, _ep) = q.pop().unwrap(); + let (mut _s, mut _i, mut _ep) = q.pop().unwrap(); if _s.is_empty() {continue;} - if 0 <= (_ep as isize) && _ep < num_qubits + if _ep < num_qubits { let mut condition = true; while condition @@ -133,7 +133,7 @@ pub fn synth_cnot_phase_aam( else {swtch = true; } } - let temp_var = (_s.clone(), _i.clone(), _ep); + let temp_var = (_s, _i, _ep); if !q.contains(&temp_var) { q.push(temp_var); @@ -150,6 +150,8 @@ pub fn synth_cnot_phase_aam( _temp_s[(_j, idx)] ^= _temp_s[(_ep, idx)]; } } + let _temp_data = q[q.len()-1].clone(); + (_s, _i, _ep) = _temp_data; } } } From 0b681f9981b56c50ba8b398d94d132f9f98ba167 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Thu, 29 Aug 2024 14:44:50 +0530 Subject: [PATCH 12/30] refactor code --- .../linear_phase/cnot_phase_synth.rs | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 6fefee2969cf..0f968d86b838 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -133,12 +133,19 @@ pub fn synth_cnot_phase_aam( else {swtch = true; } } - let temp_var = (_s, _i, _ep); - if !q.contains(&temp_var) + q.push((_s, _i, _ep)); + let mut unique_q = vec![]; + for data in q.into_iter() { - q.push(temp_var); + if !unique_q.contains(&data) + { + unique_q.push(data); + } + } + q = unique_q; + for data in &mut q { let (ref mut _temp_s, _temp_i, _temp_ep) = data; @@ -150,8 +157,8 @@ pub fn synth_cnot_phase_aam( _temp_s[(_j, idx)] ^= _temp_s[(_ep, idx)]; } } - let _temp_data = q[q.len()-1].clone(); - (_s, _i, _ep) = _temp_data; + + (_s, _i, _ep) = q.pop().unwrap(); } } } @@ -216,33 +223,19 @@ pub fn synth_cnot_phase_aam( let cnots0 = Array2::from_shape_vec((cnots0_shape_data.0, cnots0_shape_data.1), cnots0_t).unwrap(); let cnots1 = Array2::from_shape_vec((cnots1_shape_data.0, cnots1_shape_data.1), cnots1_t).unwrap(); - let cnots0 = cnots0.t().to_owned(); - let cnots1 = cnots1.t().to_owned(); + let cnots0 = cnots0.reversed_axes().to_owned(); + let cnots1 = cnots1.reversed_axes().to_owned(); if _ep == num_qubits { - let _temp_data = (cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _j); - if !q.contains(&_temp_data) - { - q.push(_temp_data); - } + q.push((cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _j)); } else { - let _temp_data = (cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep); - if !q.contains(&_temp_data) - { - q.push(_temp_data); - } - } - - let _temp_data = (cnots0, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep); - if !q.contains(&_temp_data) - { - q.push(_temp_data); + q.push((cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep)); } - + q.push((cnots0, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep)); } let state_bool = state.mapv(|x| x != 0); From d2142f6fa77699332ab5fe55a52c62eb8e32cc09 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Thu, 29 Aug 2024 17:54:45 +0530 Subject: [PATCH 13/30] refactor code --- .../linear_phase/cnot_phase_synth.rs | 71 ++++++++----------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 0f968d86b838..0bbab8d080f3 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -31,7 +31,7 @@ pub fn synth_cnot_phase_aam( let s = cnots.as_array().to_owned(); let angles_arr = angles.as_array().to_owned(); - let num_qubits = s.shape()[0]; + let num_qubits = s.nrows(); let mut s_cpy = s.clone(); let mut instructions = vec![]; @@ -53,17 +53,15 @@ pub fn synth_cnot_phase_aam( let icnot = s_cpy.column(index).to_vec(); if icnot == state.row(qubit_idx).to_vec() { - match rust_angles.get(index) + match rust_angles.remove(index) { - Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), - None => (), + gate if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + gate if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + gate if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + gate if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + gate if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), + angles_in_pi => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), }; - rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); if index == s_cpy.ncols() {break;} if index == 0 {swtch = false;} @@ -113,17 +111,15 @@ pub fn synth_cnot_phase_aam( let icnot = s_cpy.column(index).to_vec(); if icnot == state.row(_ep).to_vec() { - match rust_angles.get(index) + match rust_angles.remove(index) { - Some(gate) if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_ep as u32)])), - Some(gate) if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), - Some(gate) if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_ep as u32)])), - Some(gate) if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), - Some(gate) if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_ep as u32)])), - Some(angles_in_pi) => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_ep as u32)])), - None => (), + gate if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_ep as u32)])), + gate if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), + gate if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_ep as u32)])), + gate if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), + gate if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_ep as u32)])), + angles_in_pi => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_ep as u32)])), }; - rust_angles.remove(index); s_cpy.remove_index(numpy::ndarray::Axis(1), index); if index == s_cpy.ncols() {break;} if index == 0 {swtch = false;} @@ -148,7 +144,7 @@ pub fn synth_cnot_phase_aam( for data in &mut q { - let (ref mut _temp_s, _temp_i, _temp_ep) = data; + let (ref mut _temp_s, _, _) = data; if _temp_s.is_empty() {continue;} @@ -166,7 +162,7 @@ pub fn synth_cnot_phase_aam( if _i.is_empty() {continue;} - let mut maxes: Vec = vec![]; + let mut maxes: Vec = vec![]; for row in _s.rows() { maxes.push(std::cmp::max(row.iter().filter(|&&x| x == 0).count(), row.iter().filter(|&&x| x == 1).count())); @@ -195,33 +191,26 @@ pub fn synth_cnot_phase_aam( let mut cnots0_t = vec![]; let mut cnots1_t = vec![]; - let mut cnots0_shape_data = (0_usize, 0_usize); - let mut cnots1_shape_data = (0_usize, 0_usize); + let mut cnots0_t_shape = (0_usize, _s.column(0).len()); + let mut cnots1_t_shape = (0_usize, 0_usize); + cnots1_t_shape.1 = cnots0_t_shape.1; for cols in _s.columns() { if cols[_j] == 0 { - cnots0_shape_data.0 += 1; - cnots0_shape_data.1 = cols.to_vec().len(); - for ele in cols.to_vec() - { - cnots0_t.push(ele); - } + cnots0_t_shape.0+=1; + cnots0_t.append(&mut cols.to_vec()); } else if cols[_j] == 1 { - cnots1_shape_data.0 += 1; - cnots1_shape_data.1 = cols.to_vec().len(); - for ele in cols.to_vec() - { - cnots1_t.push(ele); - } + cnots1_t_shape.0+=1; + cnots1_t.append(&mut cols.to_vec()); } } - let cnots0 = Array2::from_shape_vec((cnots0_shape_data.0, cnots0_shape_data.1), cnots0_t).unwrap(); - let cnots1 = Array2::from_shape_vec((cnots1_shape_data.0, cnots1_shape_data.1), cnots1_t).unwrap(); + let cnots0 = Array2::from_shape_vec((cnots0_t_shape.0, cnots0_t_shape.1), cnots0_t).unwrap(); + let cnots1 = Array2::from_shape_vec((cnots1_t_shape.0, cnots1_t_shape.1), cnots1_t).unwrap(); let cnots0 = cnots0.reversed_axes().to_owned(); let cnots1 = cnots1.reversed_axes().to_owned(); @@ -239,11 +228,7 @@ pub fn synth_cnot_phase_aam( } let state_bool = state.mapv(|x| x != 0); - let mut instrs = _synth_cnot_count_full_pmh(state_bool, section_size); - instrs.reverse(); - for inst in instrs - { - instructions.push(inst); - } + let mut instrs = _synth_cnot_count_full_pmh(state_bool, section_size).into_iter().rev().collect(); + instructions.append(&mut instrs); CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } From 8982c2c6203f4255158c0d2940d5a702917d7bd9 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Thu, 29 Aug 2024 19:33:54 +0530 Subject: [PATCH 14/30] refactor code --- .../synthesis/linear_phase/cnot_phase_synth.rs | 16 +++++----------- .../synthesis/linear_phase/cnot_phase_synth.py | 7 +++---- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 0bbab8d080f3..7fb36e591080 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -10,14 +10,14 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use ndarray::{Array2}; -use numpy::{PyReadonlyArray2, PyReadonlyArray1}; +use ndarray::Array2; +use numpy::PyReadonlyArray2; use smallvec::smallvec; use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::Qubit; use crate::synthesis::linear::pmh::_synth_cnot_count_full_pmh; -use pyo3::prelude::*; +use pyo3::{prelude::*, types::PyList}; use std::f64::consts::PI; #[pyfunction] @@ -25,22 +25,16 @@ use std::f64::consts::PI; pub fn synth_cnot_phase_aam( py:Python, cnots: PyReadonlyArray2, - angles: PyReadonlyArray1, + angles: &Bound, section_size: Option, ) -> PyResult { let s = cnots.as_array().to_owned(); - let angles_arr = angles.as_array().to_owned(); let num_qubits = s.nrows(); let mut s_cpy = s.clone(); let mut instructions = vec![]; - let mut rust_angles = Vec::new(); - for obj in angles_arr - { - rust_angles.push(obj.extract::(py)?); - } - + let mut rust_angles: Vec = angles.iter().filter_map(|data| data.extract::().ok()).collect(); let mut state = Array2::::eye(num_qubits); for qubit_idx in 0..num_qubits diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index c469283854e5..a8ab5573be40 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -32,10 +32,9 @@ def synth_cnot_pahse_aam_xlated( cnots: list[list[int]], angles: list[str], section_size: int = 2 ) -> QuantumCircuit: cnots_array = np.asarray(cnots).astype(np.uint8) - angles_array = np.asarray(angles, dtype=object) - _circuit_data = synth_cnot_phase_aam_xlated(cnots_array, angles_array, section_size) - qc_from_rust = QuantumCircuit._from_circuit_data(_circuit_data) - return qc_from_rust + angles = [angle if isinstance(angle, str) else f'{angle}' for angle in angles] + _circuit_data = synth_cnot_phase_aam_xlated(cnots_array, angles, section_size) + return QuantumCircuit._from_circuit_data(_circuit_data) def synth_cnot_phase_aam( From 032b8c2a0b96c6ea1baa8793e75b1a41c415ea67 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Thu, 29 Aug 2024 20:01:11 +0530 Subject: [PATCH 15/30] removed associated python code --- .../linear_phase/cnot_phase_synth.py | 134 +----------------- 1 file changed, 4 insertions(+), 130 deletions(-) diff --git a/qiskit/synthesis/linear_phase/cnot_phase_synth.py b/qiskit/synthesis/linear_phase/cnot_phase_synth.py index a8ab5573be40..1cabc7a737b8 100644 --- a/qiskit/synthesis/linear_phase/cnot_phase_synth.py +++ b/qiskit/synthesis/linear_phase/cnot_phase_synth.py @@ -17,26 +17,14 @@ """ from __future__ import annotations -import copy import numpy as np from qiskit.circuit import QuantumCircuit from qiskit.exceptions import QiskitError -from qiskit.synthesis.linear import synth_cnot_count_full_pmh - from qiskit._accelerate.synthesis.linear_phase import ( synth_cnot_phase_aam as synth_cnot_phase_aam_xlated, ) -def synth_cnot_pahse_aam_xlated( - cnots: list[list[int]], angles: list[str], section_size: int = 2 -) -> QuantumCircuit: - cnots_array = np.asarray(cnots).astype(np.uint8) - angles = [angle if isinstance(angle, str) else f'{angle}' for angle in angles] - _circuit_data = synth_cnot_phase_aam_xlated(cnots_array, angles, section_size) - return QuantumCircuit._from_circuit_data(_circuit_data) - - def synth_cnot_phase_aam( cnots: list[list[int]], angles: list[str], section_size: int = 2 ) -> QuantumCircuit: @@ -95,125 +83,11 @@ def synth_cnot_phase_aam( Quantum Science and Technology 4.1 (2018): 015002. `arXiv:1712.01859 `_ """ - num_qubits = len(cnots) - - # Create a quantum circuit on num_qubits - qcir = QuantumCircuit(num_qubits) if len(cnots[0]) != len(angles): raise QiskitError('Size of "cnots" and "angles" do not match.') - range_list = list(range(num_qubits)) - epsilon = num_qubits - sta = [] - cnots_copy = np.transpose(np.array(copy.deepcopy(cnots))) - # This matrix keeps track of the state in the algorithm - state = np.eye(num_qubits).astype("int") - - # Check if some phase-shift gates can be applied, before adding any C-NOT gates - for qubit in range(num_qubits): - index = 0 - for icnots in cnots_copy: - if np.array_equal(icnots, state[qubit]): - if angles[index] == "t": - qcir.t(qubit) - elif angles[index] == "tdg": - qcir.tdg(qubit) - elif angles[index] == "s": - qcir.s(qubit) - elif angles[index] == "sdg": - qcir.sdg(qubit) - elif angles[index] == "z": - qcir.z(qubit) - else: - qcir.p(angles[index] % np.pi, qubit) - del angles[index] - cnots_copy = np.delete(cnots_copy, index, axis=0) - if index == len(cnots_copy): - break - index -= 1 - index += 1 - - # Implementation of the pseudo-code (Algorithm 1) in the aforementioned paper - sta.append([cnots, range_list, epsilon]) - while sta: - [cnots, ilist, qubit] = sta.pop() - if cnots == []: - continue - if 0 <= qubit < num_qubits: - condition = True - while condition: - condition = False - for j in range(num_qubits): - if (j != qubit) and (sum(cnots[j]) == len(cnots[j])): - condition = True - qcir.cx(j, qubit) - state[qubit] ^= state[j] - index = 0 - for icnots in cnots_copy: - if np.array_equal(icnots, state[qubit]): - if angles[index] == "t": - qcir.t(qubit) - elif angles[index] == "tdg": - qcir.tdg(qubit) - elif angles[index] == "s": - qcir.s(qubit) - elif angles[index] == "sdg": - qcir.sdg(qubit) - elif angles[index] == "z": - qcir.z(qubit) - else: - qcir.p(angles[index] % np.pi, qubit) - del angles[index] - cnots_copy = np.delete(cnots_copy, index, axis=0) - if index == len(cnots_copy): - break - index -= 1 - index += 1 - for x in _remove_duplicates(sta + [[cnots, ilist, qubit]]): - [cnotsp, _, _] = x - if cnotsp == []: - continue - for ttt in range(len(cnotsp[j])): - cnotsp[j][ttt] ^= cnotsp[qubit][ttt] - if ilist == []: - continue - # See line 18 in pseudo-code of Algorithm 1 in the aforementioned paper - # this choice of j maximizes the size of the largest subset (S_0 or S_1) - # and the larger a subset, the closer it gets to the ideal in the - # Gray code of one CNOT per string. - j = ilist[np.argmax([[max(row.count(0), row.count(1)) for row in cnots][k] for k in ilist])] - cnots0 = [] - cnots1 = [] - for y in list(map(list, zip(*cnots))): - if y[j] == 0: - cnots0.append(y) - elif y[j] == 1: - cnots1.append(y) - cnots0 = list(map(list, zip(*cnots0))) - cnots1 = list(map(list, zip(*cnots1))) - if qubit == epsilon: - sta.append([cnots1, list(set(ilist).difference([j])), j]) - else: - sta.append([cnots1, list(set(ilist).difference([j])), qubit]) - sta.append([cnots0, list(set(ilist).difference([j])), qubit]) - qcir &= synth_cnot_count_full_pmh(state, section_size).inverse() - return qcir - - -def _remove_duplicates(lists): - """ - Remove duplicates in list - - Args: - lists (list): a list which may contain duplicate elements. - - Returns: - list: a list which contains only unique elements. - """ - - unique_list = [] - for element in lists: - if element not in unique_list: - unique_list.append(element) - return unique_list + cnots_array = np.asarray(cnots).astype(np.uint8) + angles = [angle if isinstance(angle, str) else f"{angle}" for angle in angles] + _circuit_data = synth_cnot_phase_aam_xlated(cnots_array, angles, section_size) + return QuantumCircuit._from_circuit_data(_circuit_data) From 583ca174837d75a444facf261a750cfcb8e6aa30 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 30 Aug 2024 04:02:32 +0530 Subject: [PATCH 16/30] rust lint --- crates/accelerate/src/synthesis/linear/pmh.rs | 5 +- .../linear_phase/cnot_phase_synth.rs | 268 +++++++++++------- 2 files changed, 167 insertions(+), 106 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index c78f5b5b9f0c..fc5bf4d98917 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -166,10 +166,8 @@ pub fn synth_cnot_count_full_pmh( CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } - type Instructions = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); -pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> Vec -{ +pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> Vec { let mut mat = mat; let num_qubits = mat.nrows(); // is a quadratic matrix @@ -202,5 +200,4 @@ pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> V ) }); instructions.collect() - } diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 7fb36e591080..3542b8521c23 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -10,140 +10,194 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use crate::synthesis::linear::pmh::_synth_cnot_count_full_pmh; use ndarray::Array2; use numpy::PyReadonlyArray2; -use smallvec::smallvec; +use pyo3::{prelude::*, types::PyList}; use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::Qubit; -use crate::synthesis::linear::pmh::_synth_cnot_count_full_pmh; -use pyo3::{prelude::*, types::PyList}; +use smallvec::smallvec; use std::f64::consts::PI; #[pyfunction] #[pyo3(signature = (cnots, angles, section_size=2))] pub fn synth_cnot_phase_aam( - py:Python, + py: Python, cnots: PyReadonlyArray2, angles: &Bound, section_size: Option, ) -> PyResult { - let s = cnots.as_array().to_owned(); let num_qubits = s.nrows(); let mut s_cpy = s.clone(); let mut instructions = vec![]; - let mut rust_angles: Vec = angles.iter().filter_map(|data| data.extract::().ok()).collect(); + let mut rust_angles: Vec = angles + .iter() + .filter_map(|data| data.extract::().ok()) + .collect(); let mut state = Array2::::eye(num_qubits); - for qubit_idx in 0..num_qubits - { + for qubit_idx in 0..num_qubits { let mut index = 0_usize; let mut swtch: bool = true; - while index < s_cpy.ncols() - { + while index < s_cpy.ncols() { let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(qubit_idx).to_vec() - { - match rust_angles.remove(index) - { - gate if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - gate if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - gate if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - gate if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - gate if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(qubit_idx as u32)])), - angles_in_pi => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(qubit_idx as u32)])), + if icnot == state.row(qubit_idx).to_vec() { + match rust_angles.remove(index) { + gate if gate == "t" => instructions.push(( + StandardGate::TGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + )), + gate if gate == "tdg" => instructions.push(( + StandardGate::TdgGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + )), + gate if gate == "s" => instructions.push(( + StandardGate::SGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + )), + gate if gate == "sdg" => instructions.push(( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + )), + gate if gate == "z" => instructions.push(( + StandardGate::ZGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + )), + angles_in_pi => instructions.push(( + StandardGate::PhaseGate, + smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], + smallvec![Qubit(qubit_idx as u32)], + )), }; s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.ncols() {break;} - if index == 0 {swtch = false;} - else {index -=1;} + if index == s_cpy.ncols() { + break; + } + if index == 0 { + swtch = false; + } else { + index -= 1; + } + } + if swtch { + index += 1; + } else { + swtch = true; } - if swtch {index +=1;} - else {swtch = true;} } - } - + } let epsilion: usize = num_qubits; let mut q = vec![(s, (0..num_qubits).collect::>(), epsilion)]; - - while !q.is_empty() - { - + while !q.is_empty() { let (mut _s, mut _i, mut _ep) = q.pop().unwrap(); - if _s.is_empty() {continue;} + if _s.is_empty() { + continue; + } - if _ep < num_qubits - { + if _ep < num_qubits { let mut condition = true; - while condition - { + while condition { condition = false; - for _j in 0..num_qubits - { - if (_j != _ep) && (_s.row(_j).sum() as usize == _s.row(_j).len()) - { + for _j in 0..num_qubits { + if (_j != _ep) && (_s.row(_j).sum() as usize == _s.row(_j).len()) { condition = true; - instructions.push((StandardGate::CXGate, smallvec![], smallvec![Qubit(_j as u32), Qubit(_ep as u32)])); + instructions.push(( + StandardGate::CXGate, + smallvec![], + smallvec![Qubit(_j as u32), Qubit(_ep as u32)], + )); - for _k in 0..state.ncols() - { + for _k in 0..state.ncols() { state[(_ep, _k)] ^= state[(_j, _k)]; } - let mut index = 0_usize; let mut swtch: bool = true; - while index < s_cpy.ncols() - { + while index < s_cpy.ncols() { let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(_ep).to_vec() - { - match rust_angles.remove(index) - { - gate if gate == "t" => instructions.push((StandardGate::TGate, smallvec![], smallvec![Qubit(_ep as u32)])), - gate if gate == "tdg" => instructions.push((StandardGate::TdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), - gate if gate == "s" => instructions.push((StandardGate::SGate, smallvec![], smallvec![Qubit(_ep as u32)])), - gate if gate == "sdg" => instructions.push((StandardGate::SdgGate, smallvec![], smallvec![Qubit(_ep as u32)])), - gate if gate == "z" => instructions.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(_ep as u32)])), - angles_in_pi => instructions.push((StandardGate::PhaseGate, smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], smallvec![Qubit(_ep as u32)])), + if icnot == state.row(_ep).to_vec() { + match rust_angles.remove(index) { + gate if gate == "t" => instructions.push(( + StandardGate::TGate, + smallvec![], + smallvec![Qubit(_ep as u32)], + )), + gate if gate == "tdg" => instructions.push(( + StandardGate::TdgGate, + smallvec![], + smallvec![Qubit(_ep as u32)], + )), + gate if gate == "s" => instructions.push(( + StandardGate::SGate, + smallvec![], + smallvec![Qubit(_ep as u32)], + )), + gate if gate == "sdg" => instructions.push(( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(_ep as u32)], + )), + gate if gate == "z" => instructions.push(( + StandardGate::ZGate, + smallvec![], + smallvec![Qubit(_ep as u32)], + )), + angles_in_pi => instructions.push(( + StandardGate::PhaseGate, + smallvec![Param::Float( + (angles_in_pi.parse::()?) % PI + )], + smallvec![Qubit(_ep as u32)], + )), }; s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.ncols() {break;} - if index == 0 {swtch = false;} - else {index -=1;} + if index == s_cpy.ncols() { + break; + } + if index == 0 { + swtch = false; + } else { + index -= 1; + } + } + if swtch { + index += 1; + } else { + swtch = true; } - if swtch {index +=1;} - else {swtch = true; } } q.push((_s, _i, _ep)); let mut unique_q = vec![]; - for data in q.into_iter() - { - if !unique_q.contains(&data) - { + for data in q.into_iter() { + if !unique_q.contains(&data) { unique_q.push(data); } - } q = unique_q; - for data in &mut q - { + for data in &mut q { let (ref mut _temp_s, _, _) = data; - if _temp_s.is_empty() {continue;} + if _temp_s.is_empty() { + continue; + } - for idx in 0.._temp_s.row(_j).len() - { + for idx in 0.._temp_s.row(_j).len() { _temp_s[(_j, idx)] ^= _temp_s[(_ep, idx)]; } } @@ -154,27 +208,28 @@ pub fn synth_cnot_phase_aam( } } - if _i.is_empty() {continue;} + if _i.is_empty() { + continue; + } let mut maxes: Vec = vec![]; - for row in _s.rows() - { - maxes.push(std::cmp::max(row.iter().filter(|&&x| x == 0).count(), row.iter().filter(|&&x| x == 1).count())); + for row in _s.rows() { + maxes.push(std::cmp::max( + row.iter().filter(|&&x| x == 0).count(), + row.iter().filter(|&&x| x == 1).count(), + )); } let mut maxes2: Vec = vec![]; - for _i_idx in _i.clone() - { + for _i_idx in _i.clone() { maxes2.push(maxes[_i_idx]); } let mut _temp_max = maxes2[0]; let mut _temp_argmax = 0_usize; - for (idx, &ele) in maxes2.iter().enumerate() - { - if ele > _temp_max - { + for (idx, &ele) in maxes2.iter().enumerate() { + if ele > _temp_max { _temp_max = ele; _temp_argmax = idx; } @@ -188,41 +243,50 @@ pub fn synth_cnot_phase_aam( let mut cnots0_t_shape = (0_usize, _s.column(0).len()); let mut cnots1_t_shape = (0_usize, 0_usize); cnots1_t_shape.1 = cnots0_t_shape.1; - for cols in _s.columns() - { - if cols[_j] == 0 - { - cnots0_t_shape.0+=1; + for cols in _s.columns() { + if cols[_j] == 0 { + cnots0_t_shape.0 += 1; cnots0_t.append(&mut cols.to_vec()); - } - else if cols[_j] == 1 - { - cnots1_t_shape.0+=1; + } else if cols[_j] == 1 { + cnots1_t_shape.0 += 1; cnots1_t.append(&mut cols.to_vec()); } } - - let cnots0 = Array2::from_shape_vec((cnots0_t_shape.0, cnots0_t_shape.1), cnots0_t).unwrap(); - let cnots1 = Array2::from_shape_vec((cnots1_t_shape.0, cnots1_t_shape.1), cnots1_t).unwrap(); + let cnots0 = + Array2::from_shape_vec((cnots0_t_shape.0, cnots0_t_shape.1), cnots0_t).unwrap(); + let cnots1 = + Array2::from_shape_vec((cnots1_t_shape.0, cnots1_t_shape.1), cnots1_t).unwrap(); let cnots0 = cnots0.reversed_axes().to_owned(); let cnots1 = cnots1.reversed_axes().to_owned(); - if _ep == num_qubits - { - q.push((cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _j)); - } - else - { - q.push((cnots1, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep)); + if _ep == num_qubits { + q.push(( + cnots1, + _i.clone().into_iter().filter(|&x| x != _j).collect(), + _j, + )); + } else { + q.push(( + cnots1, + _i.clone().into_iter().filter(|&x| x != _j).collect(), + _ep, + )); } - q.push((cnots0, _i.clone().into_iter().filter(|&x| x != _j).collect(), _ep)); + q.push(( + cnots0, + _i.clone().into_iter().filter(|&x| x != _j).collect(), + _ep, + )); } let state_bool = state.mapv(|x| x != 0); - let mut instrs = _synth_cnot_count_full_pmh(state_bool, section_size).into_iter().rev().collect(); + let mut instrs = _synth_cnot_count_full_pmh(state_bool, section_size) + .into_iter() + .rev() + .collect(); instructions.append(&mut instrs); CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } From d1394f4660116305ff65fe16f0d7e754a128ca3b Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 30 Aug 2024 05:56:58 +0530 Subject: [PATCH 17/30] added release note; added docstring to rust algo --- .../src/synthesis/linear_phase/cnot_phase_synth.rs | 4 ++++ .../oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 3542b8521c23..57cc2b100d44 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -20,6 +20,10 @@ use qiskit_circuit::Qubit; use smallvec::smallvec; use std::f64::consts::PI; +/// This function implements a Gray-code inspired algorithm of synthesizing a circuit +/// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. +/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 +/// of paper "https://arxiv.org/abs/1712.01859". #[pyfunction] #[pyo3(signature = (cnots, angles, section_size=2))] pub fn synth_cnot_phase_aam( diff --git a/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml b/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml new file mode 100644 index 000000000000..a0994a845c16 --- /dev/null +++ b/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml @@ -0,0 +1,8 @@ +--- +upgrade_synthesis: + - | + Ported :func:`~.synth_cnot_phase_aam` to rust. The algorithm computes minimal-CNOT + circuit for a given phase-polynomial. The newly ported rust code has a speedup of + about x262 as compared to previous python implementation when used to synthesize + parity network for 120 terms of phase-polynomial with 150 Qubits. + From 6956e0f39ece3198b62bc403edcbed2f84768784 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 30 Aug 2024 06:20:43 +0530 Subject: [PATCH 18/30] rust lint --- .../accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 57cc2b100d44..09b1ad406133 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -22,7 +22,7 @@ use std::f64::consts::PI; /// This function implements a Gray-code inspired algorithm of synthesizing a circuit /// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. -/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 +/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 /// of paper "https://arxiv.org/abs/1712.01859". #[pyfunction] #[pyo3(signature = (cnots, angles, section_size=2))] From 0091c98e56e9a9238408416da4e7189b4ca3bc18 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 30 Aug 2024 11:22:39 +0530 Subject: [PATCH 19/30] refactor code --- .../linear_phase/cnot_phase_synth.rs | 36 +++++++++---------- ...synth_cnot_phase_aam-9c0566d2c2842dbd.yaml | 2 +- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 09b1ad406133..f24ef3411206 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -216,28 +216,24 @@ pub fn synth_cnot_phase_aam( continue; } - let mut maxes: Vec = vec![]; - for row in _s.rows() { - maxes.push(std::cmp::max( - row.iter().filter(|&&x| x == 0).count(), - row.iter().filter(|&&x| x == 1).count(), - )); - } - - let mut maxes2: Vec = vec![]; - for _i_idx in _i.clone() { - maxes2.push(maxes[_i_idx]); - } + let maxes: Vec = _s + .axis_iter(numpy::ndarray::Axis(0)) + .map(|row| { + std::cmp::max( + row.iter().filter(|&&x| x == 0).count(), + row.iter().filter(|&&x| x == 1).count(), + ) + }) + .collect(); - let mut _temp_max = maxes2[0]; - let mut _temp_argmax = 0_usize; + let maxes2: Vec = _i.iter().map(|&_i_idx| maxes[_i_idx]).collect(); - for (idx, &ele) in maxes2.iter().enumerate() { - if ele > _temp_max { - _temp_max = ele; - _temp_argmax = idx; - } - } + let _temp_argmax = maxes2 + .iter() + .enumerate() + .max_by(|(_, x), (_, y)| x.cmp(y)) + .map(|(idx, _)| idx) + .unwrap(); let _j = _i[_temp_argmax]; diff --git a/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml b/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml index a0994a845c16..e1b3d0f3a5dc 100644 --- a/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml +++ b/releasenotes/notes/oxidize-synth_cnot_phase_aam-9c0566d2c2842dbd.yaml @@ -3,6 +3,6 @@ upgrade_synthesis: - | Ported :func:`~.synth_cnot_phase_aam` to rust. The algorithm computes minimal-CNOT circuit for a given phase-polynomial. The newly ported rust code has a speedup of - about x262 as compared to previous python implementation when used to synthesize + about x309 as compared to previous python implementation when used to synthesize parity network for 120 terms of phase-polynomial with 150 Qubits. From 156da411a15447a4dc2ca5c76edc532c6b4a047a Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Sun, 1 Sep 2024 19:14:54 +0530 Subject: [PATCH 20/30] applied suggestion partially --- crates/accelerate/src/synthesis/linear/pmh.rs | 16 +++++++++------- .../synthesis/linear_phase/cnot_phase_synth.rs | 11 ++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index fc5bf4d98917..79f268a245ae 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -162,12 +162,15 @@ pub fn synth_cnot_count_full_pmh( let mat: Array2 = arrayview.to_owned(); let num_qubits = mat.nrows(); - let instructions = _synth_cnot_count_full_pmh(mat, section_size); + let instructions = synth_pmh(mat, section_size); CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } type Instructions = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); -pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> Vec { +pub fn synth_pmh( + mat: Array2, + sec_size: Option, +) -> impl DoubleEndedIterator { let mut mat = mat; let num_qubits = mat.nrows(); // is a quadratic matrix @@ -188,9 +191,9 @@ pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> V let upper_cnots = lower_cnot_synth(mat.view_mut(), blocksize, true); // iterator over the gates - let instructions = upper_cnots - .iter() - .map(|(i, j)| (*j, *i)) + upper_cnots + .into_iter() + .map(|(i, j)| (j, i)) .chain(lower_cnots.into_iter().rev()) .map(|(ctrl, target)| { ( @@ -198,6 +201,5 @@ pub fn _synth_cnot_count_full_pmh(mat: Array2, sec_size: Option) -> V smallvec![], smallvec![Qubit(ctrl as u32), Qubit(target as u32)], ) - }); - instructions.collect() + }) } diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index f24ef3411206..d8f91d348798 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -10,7 +10,7 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use crate::synthesis::linear::pmh::_synth_cnot_count_full_pmh; +use crate::synthesis::linear::pmh::synth_pmh; use ndarray::Array2; use numpy::PyReadonlyArray2; use pyo3::{prelude::*, types::PyList}; @@ -100,8 +100,8 @@ pub fn synth_cnot_phase_aam( } } - let epsilion: usize = num_qubits; - let mut q = vec![(s, (0..num_qubits).collect::>(), epsilion)]; + let epsilon: usize = num_qubits; + let mut q = vec![(s, (0..num_qubits).collect::>(), epsilon)]; while !q.is_empty() { let (mut _s, mut _i, mut _ep) = q.pop().unwrap(); @@ -283,10 +283,7 @@ pub fn synth_cnot_phase_aam( } let state_bool = state.mapv(|x| x != 0); - let mut instrs = _synth_cnot_count_full_pmh(state_bool, section_size) - .into_iter() - .rev() - .collect(); + let mut instrs = synth_pmh(state_bool, section_size).rev().collect(); instructions.append(&mut instrs); CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } From b236d6268237132e483c2d9388c55ce61fd625d7 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Mon, 2 Sep 2024 06:11:23 +0530 Subject: [PATCH 21/30] reverting suggestions --- crates/accelerate/src/synthesis/linear/pmh.rs | 16 +++++++--------- .../synthesis/linear_phase/cnot_phase_synth.rs | 5 ++++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index 79f268a245ae..a676c72eddb7 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -167,10 +167,7 @@ pub fn synth_cnot_count_full_pmh( } type Instructions = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); -pub fn synth_pmh( - mat: Array2, - sec_size: Option, -) -> impl DoubleEndedIterator { +pub fn synth_pmh(mat: Array2, section_size: Option) -> Vec { let mut mat = mat; let num_qubits = mat.nrows(); // is a quadratic matrix @@ -180,7 +177,7 @@ pub fn synth_pmh( // In addition, we at least set a block size of 2, which, in practice, minimizes the bound // until ~100 qubits. let alpha = 0.56; - let blocksize = match sec_size { + let blocksize = match section_size { Some(section_size) => section_size as usize, None => std::cmp::max(2, (alpha * (num_qubits as f64).log2()).floor() as usize), }; @@ -191,9 +188,9 @@ pub fn synth_pmh( let upper_cnots = lower_cnot_synth(mat.view_mut(), blocksize, true); // iterator over the gates - upper_cnots - .into_iter() - .map(|(i, j)| (j, i)) + let instructions = upper_cnots + .iter() + .map(|(i, j)| (*j, *i)) .chain(lower_cnots.into_iter().rev()) .map(|(ctrl, target)| { ( @@ -201,5 +198,6 @@ pub fn synth_pmh( smallvec![], smallvec![Qubit(ctrl as u32), Qubit(target as u32)], ) - }) + }); + instructions.collect() } diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index d8f91d348798..5a2133f51eab 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -283,7 +283,10 @@ pub fn synth_cnot_phase_aam( } let state_bool = state.mapv(|x| x != 0); - let mut instrs = synth_pmh(state_bool, section_size).rev().collect(); + let mut instrs = synth_pmh(state_bool, section_size) + .into_iter() + .rev() + .collect(); instructions.append(&mut instrs); CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } From d28da2a9a90ede0c8264e1bd80cdaa9a376bde03 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Wed, 4 Sep 2024 07:51:53 +0530 Subject: [PATCH 22/30] added a test --- .../synthesis/test_cnot_phase_synthesis.py | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/test/python/synthesis/test_cnot_phase_synthesis.py b/test/python/synthesis/test_cnot_phase_synthesis.py index 85d3633f2aa2..fe6598b031fb 100644 --- a/test/python/synthesis/test_cnot_phase_synthesis.py +++ b/test/python/synthesis/test_cnot_phase_synthesis.py @@ -14,6 +14,7 @@ import unittest import ddt +from numpy import pi from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.circuit.library import UnitaryGate @@ -27,7 +28,14 @@ class TestGraySynth(QiskitTestCase): """Test the Gray-Synth algorithm.""" - def test_gray_synth(self): + @ddt.data( + (["s", "t", "z", "s", "t", "t"],), + # Angles applied on PhaseGate are 'angles%numpy.pi', + # So, to get PhaseGate(numpy.pi) we subtract a tiny value from pi. + ([pi / 2, pi / 4, pi - 1e-09, pi / 2, pi / 4, pi / 4],), + ) + @ddt.unpack + def test_gray_synth(self, angles): """Test synthesis of a small parity network via gray_synth. The algorithm should take the following matrix as an input: @@ -40,25 +48,45 @@ def test_gray_synth(self): [0, 1, 0, 0, 1, 0]] Along with some rotation angles: - ['s', 't', 'z', 's', 't', 't']) - - which together specify the Fourier expansion in the sum-over-paths representation - of a quantum circuit. - - And should return the following circuit (or an equivalent one): - ┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐ - q_0: |0>──────────┤ X ├┤ X ├┤ T ├┤ X ├┤ X ├┤ X ├┤ X ├┤ T ├┤ X ├┤ T ├┤ X ├┤ X ├┤ Z ├┤ X ├ - └─┬─┘└─┬─┘└───┘└─┬─┘└─┬─┘└─┬─┘└─┬─┘└───┘└─┬─┘└───┘└─┬─┘└─┬─┘└───┘└─┬─┘ - q_1: |0>────────────┼────┼─────────■────┼────┼────┼─────────┼─────────┼────┼─────────■── - │ │ │ │ │ │ │ │ - q_2: |0>───────■────■────┼──────────────■────┼────┼─────────┼────■────┼────┼──────────── - ┌───┐┌─┴─┐┌───┐ │ │ │ │ ┌─┴─┐ │ │ - q_3: |0>┤ S ├┤ X ├┤ S ├──■───────────────────┼────┼─────────■──┤ X ├──┼────┼──────────── - └───┘└───┘└───┘ │ │ └───┘ │ │ - q_4: |0>─────────────────────────────────────■────┼───────────────────■────┼──────────── - │ │ - q_5: |0>──────────────────────────────────────────■────────────────────────■──────────── - + ['s', 't', 'z', 's', 't', 't'] + + Equivalently, this also tests for roation angles in float, passed like this: + [pi/2, pi/4, pi-0.000000001, pi/2, pi/4, pi/4] + + `S` and rotation angles together specify the Fourier expansion in the sum-over-paths + representation of a quantum circuit. + + It should return the following circuit (or an equivalent one) for + ['s', 't', 'z', 's', 't', 't']: + + 0: ──■─────────■──────────────■─────────────────────────────■─────── + ┌─┴─┐┌───┐ │ │ ┌─┴─┐ + 1: ┤ X ├┤ Z ├──┼─────────■────┼────────────────────────■──┤ X ├───── + └───┘└───┘ │ │ │ │ └───┘ + 2: ────────────┼─────────┼────┼────■───────────────────┼─────────■── + ┌───┐ ┌─┴─┐┌───┐ │ ┌─┴─┐┌─┴─┐┌───┐ │ ┌─┴─┐ + 3: ┤ S ├─────┤ X ├┤ T ├──┼──┤ X ├┤ X ├┤ S ├──■─────────┼────■──┤ X ├ + └───┘ └───┘└───┘ │ └───┘└───┘└───┘ │ │ │ └───┘ + 4: ──────────────────────┼────■──────────────┼─────────┼────┼────■── + ┌─┴─┐┌─┴─┐┌───┐ ┌─┴─┐┌───┐┌─┴─┐┌─┴─┐┌─┴─┐ + 5: ────────────────────┤ X ├┤ X ├┤ T ├─────┤ X ├┤ T ├┤ X ├┤ X ├┤ X ├ + └───┘└───┘└───┘ └───┘└───┘└───┘└───┘└───┘ + + and, should return the following circuit (or an equivalent one) for + [pi/2, pi/4, pi-1e-09, pi/2, pi/4, pi/4]: + + 0: ────■───────────────■───────────────────■────────────────────────────────────────────■─────── + ┌─┴─┐ ┌──────┐ │ │ ┌─┴─┐ + 1: ──┤ X ├───┤ P(π) ├──┼──────────────■────┼───────────────────────────────────────■──┤ X ├───── + └───┘ └──────┘ │ │ │ │ └───┘ + 2: ────────────────────┼──────────────┼────┼──────■────────────────────────────────┼─────────■── + ┌────────┐ ┌─┴─┐┌────────┐ │ ┌─┴─┐ ┌─┴─┐ ┌────────┐ │ ┌─┴─┐ + 3: ┤ P(π/2) ├────────┤ X ├┤ P(π/4) ├──┼──┤ X ├──┤ X ├───┤ P(π/2) ├──■──────────────┼────■──┤ X ├ + └────────┘ └───┘└────────┘ │ └───┘ └───┘ └────────┘ │ │ │ └───┘ + 4: ───────────────────────────────────┼────■────────────────────────┼──────────────┼────┼────■── + ┌─┴─┐┌─┴─┐┌────────┐ ┌─┴─┐┌────────┐┌─┴─┐┌─┴─┐┌─┴─┐ + 5: ─────────────────────────────────┤ X ├┤ X ├┤ P(π/4) ├──────────┤ X ├┤ P(π/4) ├┤ X ├┤ X ├┤ X ├ + └───┘└───┘└────────┘ └───┘└────────┘└───┘└───┘└───┘ """ cnots = [ [0, 1, 1, 0, 1, 1], @@ -68,7 +96,6 @@ def test_gray_synth(self): [0, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 0], ] - angles = ["s", "t", "z", "s", "t", "t"] c_gray = synth_cnot_phase_aam(cnots, angles) unitary_gray = UnitaryGate(Operator(c_gray)) From 5973f85c1fd12a255f48ff0b48f370d1d1dab5d8 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 1 Nov 2024 06:28:45 +0530 Subject: [PATCH 23/30] replace vector with iterator --- .../linear_phase/cnot_phase_synth.rs | 145 +++++++++++------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 5a2133f51eab..dd58a2df4c1d 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -17,9 +17,95 @@ use pyo3::{prelude::*, types::PyList}; use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::Qubit; -use smallvec::smallvec; +use smallvec::{smallvec, SmallVec}; use std::f64::consts::PI; +type Instruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); + +struct InstructionIterator { + s_cpy: Array2, + state_cpy: Array2, + rust_angles_cpy: Vec, + num_qubits: usize, + qubit_idx: usize, + index: usize, +} + +impl InstructionIterator { + fn new(s_cpy: Array2, state_cpy: Array2, rust_angles_cpy: Vec) -> Self { + let num_qubits = s_cpy.nrows(); + Self { + s_cpy, + state_cpy, + rust_angles_cpy, + num_qubits, + qubit_idx: 0, + index: 0, + } + } +} + +impl Iterator for InstructionIterator { + type Item = Instruction; + + fn next(&mut self) -> Option { + if self.qubit_idx > self.num_qubits { + return None; + } + + if self.index < self.s_cpy.ncols() { + let mut gate_instr: Option = None; + self.index += 1; + let icnot = self.s_cpy.column(self.index).to_vec(); + let target_state = self.state_cpy.row(self.qubit_idx).to_vec(); + + if icnot == target_state { + self.s_cpy.remove_index(numpy::ndarray::Axis(1), self.index); + let angle = self.rust_angles_cpy.remove(self.index); + self.index -= 1; + + gate_instr = Some(match angle.as_str() { + "t" => ( + StandardGate::TGate, + smallvec![], + smallvec![Qubit(self.qubit_idx as u32)], + ), + "tgd" => ( + StandardGate::TdgGate, + smallvec![], + smallvec![Qubit(self.qubit_idx as u32)], + ), + "s" => ( + StandardGate::SGate, + smallvec![], + smallvec![Qubit(self.qubit_idx as u32)], + ), + "sdg" => ( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(self.qubit_idx as u32)], + ), + "z" => ( + StandardGate::ZGate, + smallvec![], + smallvec![Qubit(self.qubit_idx as u32)], + ), + angles_in_pi => ( + StandardGate::PhaseGate, + smallvec![Param::Float((angles_in_pi.parse::().ok()?) % PI)], + smallvec![Qubit(self.qubit_idx as u32)], + ), + }); + } + return gate_instr; + } else { + self.qubit_idx += 1; + self.index = 0; + return self.next(); + } + } +} + /// This function implements a Gray-code inspired algorithm of synthesizing a circuit /// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. /// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 @@ -43,62 +129,9 @@ pub fn synth_cnot_phase_aam( .collect(); let mut state = Array2::::eye(num_qubits); - for qubit_idx in 0..num_qubits { - let mut index = 0_usize; - let mut swtch: bool = true; + let instr_iter = InstructionIterator::new(s.clone(), state.clone(), rust_angles.clone()); - while index < s_cpy.ncols() { - let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(qubit_idx).to_vec() { - match rust_angles.remove(index) { - gate if gate == "t" => instructions.push(( - StandardGate::TGate, - smallvec![], - smallvec![Qubit(qubit_idx as u32)], - )), - gate if gate == "tdg" => instructions.push(( - StandardGate::TdgGate, - smallvec![], - smallvec![Qubit(qubit_idx as u32)], - )), - gate if gate == "s" => instructions.push(( - StandardGate::SGate, - smallvec![], - smallvec![Qubit(qubit_idx as u32)], - )), - gate if gate == "sdg" => instructions.push(( - StandardGate::SdgGate, - smallvec![], - smallvec![Qubit(qubit_idx as u32)], - )), - gate if gate == "z" => instructions.push(( - StandardGate::ZGate, - smallvec![], - smallvec![Qubit(qubit_idx as u32)], - )), - angles_in_pi => instructions.push(( - StandardGate::PhaseGate, - smallvec![Param::Float((angles_in_pi.parse::()?) % PI)], - smallvec![Qubit(qubit_idx as u32)], - )), - }; - s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.ncols() { - break; - } - if index == 0 { - swtch = false; - } else { - index -= 1; - } - } - if swtch { - index += 1; - } else { - swtch = true; - } - } - } + instructions.append(&mut instr_iter.collect::>()); let epsilon: usize = num_qubits; let mut q = vec![(s, (0..num_qubits).collect::>(), epsilon)]; From 353e5ff21b3b295dde95a15119e7b103d36135bc Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Fri, 1 Nov 2024 15:03:35 +0530 Subject: [PATCH 24/30] refactor code; lint --- .../linear_phase/cnot_phase_synth.rs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index dd58a2df4c1d..73fe38be0000 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -43,26 +43,30 @@ impl InstructionIterator { index: 0, } } + + fn current_state(&self) -> (Array2, Vec) { + (self.s_cpy.clone(), self.rust_angles_cpy.clone()) + } } impl Iterator for InstructionIterator { type Item = Instruction; fn next(&mut self) -> Option { - if self.qubit_idx > self.num_qubits { + if self.qubit_idx >= self.num_qubits { return None; } if self.index < self.s_cpy.ncols() { let mut gate_instr: Option = None; - self.index += 1; let icnot = self.s_cpy.column(self.index).to_vec(); + self.index += 1; let target_state = self.state_cpy.row(self.qubit_idx).to_vec(); if icnot == target_state { + self.index -= 1; self.s_cpy.remove_index(numpy::ndarray::Axis(1), self.index); let angle = self.rust_angles_cpy.remove(self.index); - self.index -= 1; gate_instr = Some(match angle.as_str() { "t" => ( @@ -97,11 +101,15 @@ impl Iterator for InstructionIterator { ), }); } - return gate_instr; + if gate_instr.is_none() { + self.next() + } else { + gate_instr + } } else { self.qubit_idx += 1; self.index = 0; - return self.next(); + self.next() } } } @@ -120,18 +128,21 @@ pub fn synth_cnot_phase_aam( ) -> PyResult { let s = cnots.as_array().to_owned(); let num_qubits = s.nrows(); - let mut s_cpy = s.clone(); let mut instructions = vec![]; - let mut rust_angles: Vec = angles + let rust_angles: Vec = angles .iter() .filter_map(|data| data.extract::().ok()) .collect(); let mut state = Array2::::eye(num_qubits); - let instr_iter = InstructionIterator::new(s.clone(), state.clone(), rust_angles.clone()); + let mut instr_iter = InstructionIterator::new(s.clone(), state.clone(), rust_angles); + + let new_iter = std::iter::from_fn(|| instr_iter.next()); + let mut ins: Vec = new_iter.collect::>(); + let (mut s_cpy, mut rust_angles) = instr_iter.current_state(); - instructions.append(&mut instr_iter.collect::>()); + instructions.append(&mut ins); let epsilon: usize = num_qubits; let mut q = vec![(s, (0..num_qubits).collect::>(), epsilon)]; From 6262d167284c44cc5e7349ade1f9434a67da033c Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Mon, 4 Nov 2024 17:36:40 +0530 Subject: [PATCH 25/30] refactor code --- crates/accelerate/src/synthesis/linear/pmh.rs | 16 +- .../linear_phase/cnot_phase_synth.rs | 501 ++++++++++-------- 2 files changed, 301 insertions(+), 216 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index a676c72eddb7..7973868be50b 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -166,8 +166,11 @@ pub fn synth_cnot_count_full_pmh( CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) } -type Instructions = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); -pub fn synth_pmh(mat: Array2, section_size: Option) -> Vec { +type Instruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); +pub fn synth_pmh( + mat: Array2, + section_size: Option, +) -> impl DoubleEndedIterator { let mut mat = mat; let num_qubits = mat.nrows(); // is a quadratic matrix @@ -188,9 +191,9 @@ pub fn synth_pmh(mat: Array2, section_size: Option) -> Vec, section_size: Option) -> Vec, SmallVec<[Qubit; 2]>); -struct InstructionIterator { +#[derive(Clone)] +struct PhaseIterator { s_cpy: Array2, - state_cpy: Array2, - rust_angles_cpy: Vec, + state: Array2, + rust_angles: Vec, num_qubits: usize, qubit_idx: usize, index: usize, } -impl InstructionIterator { - fn new(s_cpy: Array2, state_cpy: Array2, rust_angles_cpy: Vec) -> Self { - let num_qubits = s_cpy.nrows(); +impl PhaseIterator { + fn new( + num_qubits: usize, + s_cpy: Array2, + state: Array2, + rust_angles: Vec, + ) -> Self { Self { s_cpy, - state_cpy, - rust_angles_cpy, + state, + rust_angles, num_qubits, qubit_idx: 0, index: 0, } } - - fn current_state(&self) -> (Array2, Vec) { - (self.s_cpy.clone(), self.rust_angles_cpy.clone()) - } } -impl Iterator for InstructionIterator { +impl Iterator for PhaseIterator { type Item = Instruction; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { if self.qubit_idx >= self.num_qubits { return None; } @@ -61,12 +61,12 @@ impl Iterator for InstructionIterator { let mut gate_instr: Option = None; let icnot = self.s_cpy.column(self.index).to_vec(); self.index += 1; - let target_state = self.state_cpy.row(self.qubit_idx).to_vec(); + let target_state = self.state.row(self.qubit_idx).to_vec(); if icnot == target_state { self.index -= 1; self.s_cpy.remove_index(numpy::ndarray::Axis(1), self.index); - let angle = self.rust_angles_cpy.remove(self.index); + let angle = self.rust_angles.remove(self.index); gate_instr = Some(match angle.as_str() { "t" => ( @@ -101,10 +101,10 @@ impl Iterator for InstructionIterator { ), }); } - if gate_instr.is_none() { - self.next() - } else { + if gate_instr.is_some() { gate_instr + } else { + self.next() } } else { self.qubit_idx += 1; @@ -114,223 +114,306 @@ impl Iterator for InstructionIterator { } } -/// This function implements a Gray-code inspired algorithm of synthesizing a circuit -/// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. -/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 -/// of paper "https://arxiv.org/abs/1712.01859". -#[pyfunction] -#[pyo3(signature = (cnots, angles, section_size=2))] -pub fn synth_cnot_phase_aam( - py: Python, - cnots: PyReadonlyArray2, - angles: &Bound, - section_size: Option, -) -> PyResult { - let s = cnots.as_array().to_owned(); - let num_qubits = s.nrows(); - let mut instructions = vec![]; +#[derive(Clone)] +struct CXPhaseIterator { + s_cpy: Array2, + state: Array2, + rust_angles: Vec, + q: Vec<(Array2, Vec, usize)>, + num_qubits: usize, + qubit_idx: usize, + keep_iterating: bool, + loop_active: bool, + _s: Array2, + _i: Vec, + _ep: usize, + phase_iter_handle: Option, +} - let rust_angles: Vec = angles - .iter() - .filter_map(|data| data.extract::().ok()) - .collect(); - let mut state = Array2::::eye(num_qubits); +impl CXPhaseIterator { + fn new( + num_qubits: usize, + s_cpy: Array2, + state: Array2, + rust_angles: Vec, + q: Vec<(Array2, Vec, usize)>, + ) -> Self { + let (init_s, init_i, init_ep) = q.last().unwrap().clone(); + Self { + s_cpy, + state, + rust_angles, + q, + num_qubits, + qubit_idx: 0, + keep_iterating: false, + loop_active: false, + _s: init_s, + _i: init_i, + _ep: init_ep, + phase_iter_handle: None, + } + } +} + +impl Iterator for CXPhaseIterator { + type Item = Instruction; - let mut instr_iter = InstructionIterator::new(s.clone(), state.clone(), rust_angles); + fn next(&mut self) -> Option { + if let Some(handle) = self.phase_iter_handle.as_mut() { + let data = handle.next(); + if data.is_none() { + self.s_cpy = handle.s_cpy.clone(); + self.rust_angles = handle.rust_angles.clone(); + self.phase_iter_handle = None; + } else { + return data; + } + } - let new_iter = std::iter::from_fn(|| instr_iter.next()); - let mut ins: Vec = new_iter.collect::>(); - let (mut s_cpy, mut rust_angles) = instr_iter.current_state(); + if !self.q.is_empty() || self.loop_active || self.keep_iterating { + if !self.loop_active && !self.keep_iterating { + (self._s, self._i, self._ep) = self.q.pop().unwrap(); + } - instructions.append(&mut ins); + if !self.loop_active && !self.keep_iterating && self._s.is_empty() { + return self.next(); + } - let epsilon: usize = num_qubits; - let mut q = vec![(s, (0..num_qubits).collect::>(), epsilon)]; + if self._ep < self.num_qubits || self.loop_active || self.keep_iterating { + if !self.loop_active && !self.keep_iterating { + self.keep_iterating = true; + } - while !q.is_empty() { - let (mut _s, mut _i, mut _ep) = q.pop().unwrap(); + if self.keep_iterating || self.loop_active { + if !self.loop_active { + self.keep_iterating = false; + } + if self.qubit_idx < self.num_qubits { + if (self.qubit_idx != self._ep) + && (self._s.row(self.qubit_idx).sum() as usize + == self._s.row(self.qubit_idx).len()) + { + for _k in 0..self.state.ncols() { + self.state[(self._ep, _k)] ^= self.state[(self.qubit_idx, _k)]; + } - if _s.is_empty() { - continue; - } + self.phase_iter_handle = Some(PhaseIterator::new( + self.num_qubits, + self.s_cpy.clone(), + self.state.clone(), + self.rust_angles.clone(), + )); - if _ep < num_qubits { - let mut condition = true; - while condition { - condition = false; - - for _j in 0..num_qubits { - if (_j != _ep) && (_s.row(_j).sum() as usize == _s.row(_j).len()) { - condition = true; - instructions.push(( - StandardGate::CXGate, - smallvec![], - smallvec![Qubit(_j as u32), Qubit(_ep as u32)], - )); - - for _k in 0..state.ncols() { - state[(_ep, _k)] ^= state[(_j, _k)]; - } + self.q.push((self._s.clone(), self._i.clone(), self._ep)); - let mut index = 0_usize; - let mut swtch: bool = true; - while index < s_cpy.ncols() { - let icnot = s_cpy.column(index).to_vec(); - if icnot == state.row(_ep).to_vec() { - match rust_angles.remove(index) { - gate if gate == "t" => instructions.push(( - StandardGate::TGate, - smallvec![], - smallvec![Qubit(_ep as u32)], - )), - gate if gate == "tdg" => instructions.push(( - StandardGate::TdgGate, - smallvec![], - smallvec![Qubit(_ep as u32)], - )), - gate if gate == "s" => instructions.push(( - StandardGate::SGate, - smallvec![], - smallvec![Qubit(_ep as u32)], - )), - gate if gate == "sdg" => instructions.push(( - StandardGate::SdgGate, - smallvec![], - smallvec![Qubit(_ep as u32)], - )), - gate if gate == "z" => instructions.push(( - StandardGate::ZGate, - smallvec![], - smallvec![Qubit(_ep as u32)], - )), - angles_in_pi => instructions.push(( - StandardGate::PhaseGate, - smallvec![Param::Float( - (angles_in_pi.parse::()?) % PI - )], - smallvec![Qubit(_ep as u32)], - )), - }; - s_cpy.remove_index(numpy::ndarray::Axis(1), index); - if index == s_cpy.ncols() { - break; + for data in &mut self.q { + let (ref mut _temp_s, _, _) = data; + if _temp_s.is_empty() { + continue; } - if index == 0 { - swtch = false; - } else { - index -= 1; + for idx in 0.._temp_s.row(self.qubit_idx).len() { + _temp_s[(self.qubit_idx, idx)] ^= _temp_s[(self._ep, idx)]; } } - if swtch { - index += 1; - } else { - swtch = true; - } + (self._s, self._i, self._ep) = self.q.pop().unwrap(); + + self.qubit_idx += 1; + self.keep_iterating = true; + self.loop_active = true; + return Some(( + StandardGate::CXGate, + smallvec![], + smallvec![ + Qubit((self.qubit_idx - 1) as u32), + Qubit(self._ep as u32) + ], + )); + } else { + self.qubit_idx += 1; + self.loop_active = true; + return self.next(); } - - q.push((_s, _i, _ep)); - let mut unique_q = vec![]; - for data in q.into_iter() { - if !unique_q.contains(&data) { - unique_q.push(data); - } + } else { + self.qubit_idx = 0; + self.loop_active = false; + if self.keep_iterating { + return self.next(); } + } + } + } - q = unique_q; - - for data in &mut q { - let (ref mut _temp_s, _, _) = data; - - if _temp_s.is_empty() { - continue; - } + if self._i.is_empty() { + return self.next(); + } - for idx in 0.._temp_s.row(_j).len() { - _temp_s[(_j, idx)] ^= _temp_s[(_ep, idx)]; - } - } + let maxes: Vec = self + ._s + .axis_iter(numpy::ndarray::Axis(0)) + .map(|row| { + std::cmp::max( + row.iter().filter(|&&x| x == 0).count(), + row.iter().filter(|&&x| x == 1).count(), + ) + }) + .collect(); + + let maxes2: Vec = self._i.iter().map(|&_i_idx| maxes[_i_idx]).collect(); + + let _temp_argmax = maxes2 + .iter() + .enumerate() + .max_by(|(_, x), (_, y)| x.cmp(y)) + .map(|(idx, _)| idx) + .unwrap(); + + let _j = self._i[_temp_argmax]; + + let mut cnots0_t = vec![]; + let mut cnots1_t = vec![]; + + let mut cnots0_t_shape = (0_usize, self._s.column(0).len()); + let mut cnots1_t_shape = (0_usize, 0_usize); + cnots1_t_shape.1 = cnots0_t_shape.1; + for cols in self._s.columns() { + if cols[_j] == 0 { + cnots0_t_shape.0 += 1; + cnots0_t.append(&mut cols.to_vec()); + } else if cols[_j] == 1 { + cnots1_t_shape.0 += 1; + cnots1_t.append(&mut cols.to_vec()); + } + } - (_s, _i, _ep) = q.pop().unwrap(); - } + let cnots0 = + Array2::from_shape_vec((cnots0_t_shape.0, cnots0_t_shape.1), cnots0_t).unwrap(); + let cnots1 = + Array2::from_shape_vec((cnots1_t_shape.0, cnots1_t_shape.1), cnots1_t).unwrap(); + + let cnots0 = cnots0.reversed_axes().to_owned(); + let cnots1 = cnots1.reversed_axes().to_owned(); + + if self._ep == self.num_qubits { + let data = ( + cnots1, + self._i.clone().into_iter().filter(|&x| x != _j).collect(), + _j, + ); + if !self.q.contains(&data) { + self.q.push(data); + } + } else { + let data = ( + cnots1, + self._i.clone().into_iter().filter(|&x| x != _j).collect(), + self._ep, + ); + if !self.q.contains(&data) { + self.q.push(data); } } + let data = ( + cnots0, + self._i.clone().into_iter().filter(|&x| x != _j).collect(), + self._ep, + ); + if !self.q.contains(&data) { + self.q.push(data); + } + self.next() + } else { + None } + } +} - if _i.is_empty() { - continue; +#[derive(Clone)] +struct BindingIterator { + num_qubits: usize, + q: Vec<(Array2, Vec, usize)>, + phase_iter_handle: PhaseIterator, + cx_phase_iter_handle: Option, + phase_iterator_done: bool, +} + +impl BindingIterator { + fn new(s: Array2, angles: Vec, state: Array2) -> Self { + let num_qubits = s.nrows(); + let q = vec![( + s.clone(), + (0..num_qubits).collect::>(), + num_qubits, + )]; + Self { + num_qubits, + q, + phase_iter_handle: PhaseIterator::new(num_qubits, s, state, angles), + cx_phase_iter_handle: None, + phase_iterator_done: false, } + } +} - let maxes: Vec = _s - .axis_iter(numpy::ndarray::Axis(0)) - .map(|row| { - std::cmp::max( - row.iter().filter(|&&x| x == 0).count(), - row.iter().filter(|&&x| x == 1).count(), - ) - }) - .collect(); - - let maxes2: Vec = _i.iter().map(|&_i_idx| maxes[_i_idx]).collect(); - - let _temp_argmax = maxes2 - .iter() - .enumerate() - .max_by(|(_, x), (_, y)| x.cmp(y)) - .map(|(idx, _)| idx) - .unwrap(); - - let _j = _i[_temp_argmax]; - - let mut cnots0_t = vec![]; - let mut cnots1_t = vec![]; - - let mut cnots0_t_shape = (0_usize, _s.column(0).len()); - let mut cnots1_t_shape = (0_usize, 0_usize); - cnots1_t_shape.1 = cnots0_t_shape.1; - for cols in _s.columns() { - if cols[_j] == 0 { - cnots0_t_shape.0 += 1; - cnots0_t.append(&mut cols.to_vec()); - } else if cols[_j] == 1 { - cnots1_t_shape.0 += 1; - cnots1_t.append(&mut cols.to_vec()); +impl Iterator for BindingIterator { + type Item = Instruction; + fn next(&mut self) -> Option { + if !self.phase_iterator_done { + let data = self.phase_iter_handle.next(); + if data.is_none() { + self.cx_phase_iter_handle = Some(CXPhaseIterator::new( + self.num_qubits, + self.phase_iter_handle.s_cpy.clone(), + self.phase_iter_handle.state.clone(), + self.phase_iter_handle.rust_angles.clone(), + self.q.clone(), + )); + self.phase_iterator_done = true; + self.next() + } else { + data } + } else if let Some(handle) = self.cx_phase_iter_handle.as_mut() { + handle.next() + } else { + None } + } +} + +/// This function implements a Gray-code inspired algorithm of synthesizing a circuit +/// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. +/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 +/// of paper "https://arxiv.org/abs/1712.01859". +#[pyfunction] +#[pyo3(signature = (cnots, angles, section_size=2))] +pub fn synth_cnot_phase_aam( + py: Python, + cnots: PyReadonlyArray2, + angles: &Bound, + section_size: Option, +) -> PyResult { + let s = cnots.as_array().to_owned(); + let num_qubits = s.nrows(); - let cnots0 = - Array2::from_shape_vec((cnots0_t_shape.0, cnots0_t_shape.1), cnots0_t).unwrap(); - let cnots1 = - Array2::from_shape_vec((cnots1_t_shape.0, cnots1_t_shape.1), cnots1_t).unwrap(); + let rust_angles = angles + .iter() + .filter_map(|data| data.extract::().ok()) + .collect::>(); - let cnots0 = cnots0.reversed_axes().to_owned(); - let cnots1 = cnots1.reversed_axes().to_owned(); + let state = Array2::::eye(num_qubits); - if _ep == num_qubits { - q.push(( - cnots1, - _i.clone().into_iter().filter(|&x| x != _j).collect(), - _j, - )); - } else { - q.push(( - cnots1, - _i.clone().into_iter().filter(|&x| x != _j).collect(), - _ep, - )); - } + let mut binding_iter_handle = BindingIterator::new(s, rust_angles, state); - q.push(( - cnots0, - _i.clone().into_iter().filter(|&x| x != _j).collect(), - _ep, - )); - } + // Optimize this one! + let cx_phase_iter = std::iter::from_fn(|| binding_iter_handle.next()); + let cx_phase_iter_vec = cx_phase_iter.collect::>(); + + let residual_state = binding_iter_handle.cx_phase_iter_handle.unwrap().state; + let state_bool = residual_state.mapv(|x| x != 0); + + let synth_pmh_iter = synth_pmh(state_bool, section_size).rev(); + let cnot_synth_iter = cx_phase_iter_vec.into_iter().chain(synth_pmh_iter); - let state_bool = state.mapv(|x| x != 0); - let mut instrs = synth_pmh(state_bool, section_size) - .into_iter() - .rev() - .collect(); - instructions.append(&mut instrs); - CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) + CircuitData::from_standard_gates(py, num_qubits as u32, cnot_synth_iter, Param::Float(0.0)) } From 41a89061e55c00bf6d98fcdf137cdb6e627a7ef2 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Tue, 5 Nov 2024 13:37:47 +0530 Subject: [PATCH 26/30] refactor code --- .../linear_phase/cnot_phase_synth.rs | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index c7d475b445bc..77c3defefe3d 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -29,6 +29,7 @@ struct PhaseIterator { num_qubits: usize, qubit_idx: usize, index: usize, + iter_once: bool, } impl PhaseIterator { @@ -45,6 +46,7 @@ impl PhaseIterator { num_qubits, qubit_idx: 0, index: 0, + iter_once: false, } } } @@ -107,6 +109,9 @@ impl Iterator for PhaseIterator { self.next() } } else { + if self.iter_once { + return None; + } self.qubit_idx += 1; self.index = 0; self.next() @@ -198,15 +203,29 @@ impl Iterator for CXPhaseIterator { self.state[(self._ep, _k)] ^= self.state[(self.qubit_idx, _k)]; } - self.phase_iter_handle = Some(PhaseIterator::new( + let mut phase_iter_h = PhaseIterator::new( self.num_qubits, self.s_cpy.clone(), self.state.clone(), self.rust_angles.clone(), - )); + ); + + phase_iter_h.iter_once = true; + phase_iter_h.qubit_idx = self._ep; + self.phase_iter_handle = Some(phase_iter_h); self.q.push((self._s.clone(), self._i.clone(), self._ep)); + let mut unique_q = vec![]; + for data in self.q.iter() { + if !unique_q.contains(data) { + let d = (*data).clone(); + unique_q.push(d); + } + } + + self.q = unique_q; + for data in &mut self.q { let (ref mut _temp_s, _, _) = data; if _temp_s.is_empty() { @@ -216,6 +235,7 @@ impl Iterator for CXPhaseIterator { _temp_s[(self.qubit_idx, idx)] ^= _temp_s[(self._ep, idx)]; } } + (self._s, self._i, self._ep) = self.q.pop().unwrap(); self.qubit_idx += 1; @@ -295,32 +315,24 @@ impl Iterator for CXPhaseIterator { let cnots1 = cnots1.reversed_axes().to_owned(); if self._ep == self.num_qubits { - let data = ( + self.q.push(( cnots1, self._i.clone().into_iter().filter(|&x| x != _j).collect(), _j, - ); - if !self.q.contains(&data) { - self.q.push(data); - } + )); } else { - let data = ( + self.q.push(( cnots1, self._i.clone().into_iter().filter(|&x| x != _j).collect(), self._ep, - ); - if !self.q.contains(&data) { - self.q.push(data); - } + )); } - let data = ( + self.q.push(( cnots0, self._i.clone().into_iter().filter(|&x| x != _j).collect(), self._ep, - ); - if !self.q.contains(&data) { - self.q.push(data); - } + )); + self.next() } else { None From 0cd01ce4e26f589c599cf5716630b4b0ac96876a Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Tue, 5 Nov 2024 17:44:57 +0530 Subject: [PATCH 27/30] refactor code --- .../src/synthesis/linear_phase/cnot_phase_synth.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 77c3defefe3d..3ef0cc94e4f2 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -21,7 +21,6 @@ use smallvec::{smallvec, SmallVec}; use std::f64::consts::PI; type Instruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); -#[derive(Clone)] struct PhaseIterator { s_cpy: Array2, state: Array2, @@ -61,9 +60,9 @@ impl Iterator for PhaseIterator { if self.index < self.s_cpy.ncols() { let mut gate_instr: Option = None; - let icnot = self.s_cpy.column(self.index).to_vec(); + let icnot = self.s_cpy.column(self.index); self.index += 1; - let target_state = self.state.row(self.qubit_idx).to_vec(); + let target_state = self.state.row(self.qubit_idx); if icnot == target_state { self.index -= 1; @@ -76,7 +75,7 @@ impl Iterator for PhaseIterator { smallvec![], smallvec![Qubit(self.qubit_idx as u32)], ), - "tgd" => ( + "tdg" => ( StandardGate::TdgGate, smallvec![], smallvec![Qubit(self.qubit_idx as u32)], @@ -119,7 +118,6 @@ impl Iterator for PhaseIterator { } } -#[derive(Clone)] struct CXPhaseIterator { s_cpy: Array2, state: Array2, @@ -340,7 +338,6 @@ impl Iterator for CXPhaseIterator { } } -#[derive(Clone)] struct BindingIterator { num_qubits: usize, q: Vec<(Array2, Vec, usize)>, From 75bc902a1a2f62d4523e41b1c9123113ce0655b7 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Sat, 16 Nov 2024 20:54:28 +0530 Subject: [PATCH 28/30] removed impl Iterator and applied from_fn --- .../linear_phase/cnot_phase_synth.rs | 518 +++++++----------- 1 file changed, 197 insertions(+), 321 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 3ef0cc94e4f2..046e0103ce99 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -18,256 +18,209 @@ use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::Qubit; use smallvec::{smallvec, SmallVec}; +use std::cell::RefCell; use std::f64::consts::PI; +use std::rc::Rc; type Instruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); -struct PhaseIterator { - s_cpy: Array2, - state: Array2, - rust_angles: Vec, - num_qubits: usize, - qubit_idx: usize, - index: usize, - iter_once: bool, +fn get_instr(angle: String, qubit_idx: usize) -> Option { + Some(match angle.as_str() { + "t" => ( + StandardGate::TGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + ), + "tdg" => ( + StandardGate::TdgGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + ), + "s" => ( + StandardGate::SGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + ), + "sdg" => ( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + ), + "z" => ( + StandardGate::ZGate, + smallvec![], + smallvec![Qubit(qubit_idx as u32)], + ), + angles_in_pi => ( + StandardGate::PhaseGate, + smallvec![Param::Float((angles_in_pi.parse::().ok()?) % PI)], + smallvec![Qubit(qubit_idx as u32)], + ), + }) } -impl PhaseIterator { - fn new( - num_qubits: usize, - s_cpy: Array2, - state: Array2, - rust_angles: Vec, - ) -> Self { - Self { - s_cpy, - state, - rust_angles, - num_qubits, - qubit_idx: 0, - index: 0, - iter_once: false, - } - } -} - -impl Iterator for PhaseIterator { - type Item = Instruction; +/// This function implements a Gray-code inspired algorithm of synthesizing a circuit +/// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. +/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 +/// of paper "https://arxiv.org/abs/1712.01859". +#[pyfunction] +#[pyo3(signature = (cnots, angles, section_size=2))] +pub fn synth_cnot_phase_aam( + py: Python, + cnots: PyReadonlyArray2, + angles: &Bound, + section_size: Option, +) -> PyResult { + let s = cnots.as_array().to_owned(); + let num_qubits = s.nrows(); - fn next(&mut self) -> Option { - if self.qubit_idx >= self.num_qubits { - return None; - } + let mut rust_angles = angles + .iter() + .filter_map(|data| { + data.extract::() + .or_else(|_| data.extract::().map(|f| f.to_string())) + .ok() + }) + .collect::>(); - if self.index < self.s_cpy.ncols() { - let mut gate_instr: Option = None; - let icnot = self.s_cpy.column(self.index); - self.index += 1; - let target_state = self.state.row(self.qubit_idx); + let mut state = Array2::::eye(num_qubits); + + let x: Rc>>> = Rc::new(RefCell::new(None)); + let x_clone = Rc::clone(&x); + + let mut s_cpy = s.clone(); + let mut q = vec![( + s.clone(), + (0..num_qubits).collect::>(), + num_qubits, + )]; + let mut _s = s; + let mut _i = (0..num_qubits).collect::>(); + let mut _ep = num_qubits; + + // variables keeping the state of iteration + let mut keep_iterating: bool = true; + let mut cx_gate_done: bool = false; + let mut phase_loop_on: bool = false; + let mut phase_done: bool = false; + let mut cx_phase_done: bool = false; + let mut qubit_idx: usize = 0; + let mut index: usize = 0; + + let cx_phase_iter = std::iter::from_fn(move || { + let mut gate_instr: Option = None; + + while !phase_done { + let icnot = s_cpy.column(index); + index += 1; + let target_state = state.row(qubit_idx); if icnot == target_state { - self.index -= 1; - self.s_cpy.remove_index(numpy::ndarray::Axis(1), self.index); - let angle = self.rust_angles.remove(self.index); - - gate_instr = Some(match angle.as_str() { - "t" => ( - StandardGate::TGate, - smallvec![], - smallvec![Qubit(self.qubit_idx as u32)], - ), - "tdg" => ( - StandardGate::TdgGate, - smallvec![], - smallvec![Qubit(self.qubit_idx as u32)], - ), - "s" => ( - StandardGate::SGate, - smallvec![], - smallvec![Qubit(self.qubit_idx as u32)], - ), - "sdg" => ( - StandardGate::SdgGate, - smallvec![], - smallvec![Qubit(self.qubit_idx as u32)], - ), - "z" => ( - StandardGate::ZGate, - smallvec![], - smallvec![Qubit(self.qubit_idx as u32)], - ), - angles_in_pi => ( - StandardGate::PhaseGate, - smallvec![Param::Float((angles_in_pi.parse::().ok()?) % PI)], - smallvec![Qubit(self.qubit_idx as u32)], - ), - }); + index -= 1; + s_cpy.remove_index(numpy::ndarray::Axis(1), index); + let angle = rust_angles.remove(index); + gate_instr = get_instr(angle, qubit_idx); + break; } - if gate_instr.is_some() { - gate_instr - } else { - self.next() + if index == s_cpy.ncols() { + qubit_idx += 1; + index = 0; } - } else { - if self.iter_once { - return None; + if qubit_idx == num_qubits { + phase_done = true; + index = 0; + qubit_idx = 0; } - self.qubit_idx += 1; - self.index = 0; - self.next() - } - } -} - -struct CXPhaseIterator { - s_cpy: Array2, - state: Array2, - rust_angles: Vec, - q: Vec<(Array2, Vec, usize)>, - num_qubits: usize, - qubit_idx: usize, - keep_iterating: bool, - loop_active: bool, - _s: Array2, - _i: Vec, - _ep: usize, - phase_iter_handle: Option, -} + } // end phase_apply loop -impl CXPhaseIterator { - fn new( - num_qubits: usize, - s_cpy: Array2, - state: Array2, - rust_angles: Vec, - q: Vec<(Array2, Vec, usize)>, - ) -> Self { - let (init_s, init_i, init_ep) = q.last().unwrap().clone(); - Self { - s_cpy, - state, - rust_angles, - q, - num_qubits, - qubit_idx: 0, - keep_iterating: false, - loop_active: false, - _s: init_s, - _i: init_i, - _ep: init_ep, - phase_iter_handle: None, - } - } -} - -impl Iterator for CXPhaseIterator { - type Item = Instruction; - - fn next(&mut self) -> Option { - if let Some(handle) = self.phase_iter_handle.as_mut() { - let data = handle.next(); - if data.is_none() { - self.s_cpy = handle.s_cpy.clone(); - self.rust_angles = handle.rust_angles.clone(); - self.phase_iter_handle = None; - } else { - return data; + 'outer_loop: while phase_done && !cx_phase_done { + if !phase_loop_on && q.is_empty() { + cx_phase_done = true; + break 'outer_loop; } - } - - if !self.q.is_empty() || self.loop_active || self.keep_iterating { - if !self.loop_active && !self.keep_iterating { - (self._s, self._i, self._ep) = self.q.pop().unwrap(); + if !phase_loop_on { + (_s, _i, _ep) = q.pop().unwrap(); } - - if !self.loop_active && !self.keep_iterating && self._s.is_empty() { - return self.next(); + if !phase_loop_on && _s.is_empty() { + continue 'outer_loop; } - if self._ep < self.num_qubits || self.loop_active || self.keep_iterating { - if !self.loop_active && !self.keep_iterating { - self.keep_iterating = true; + while keep_iterating && (_ep < num_qubits) { + if !phase_loop_on { + keep_iterating = false; } - - if self.keep_iterating || self.loop_active { - if !self.loop_active { - self.keep_iterating = false; - } - if self.qubit_idx < self.num_qubits { - if (self.qubit_idx != self._ep) - && (self._s.row(self.qubit_idx).sum() as usize - == self._s.row(self.qubit_idx).len()) - { - for _k in 0..self.state.ncols() { - self.state[(self._ep, _k)] ^= self.state[(self.qubit_idx, _k)]; + while qubit_idx < num_qubits { + if (qubit_idx != _ep) + && (_s.row(qubit_idx).sum() as usize == _s.row(qubit_idx).len()) + { + if !cx_gate_done && !phase_loop_on { + keep_iterating = true; + cx_gate_done = true; + gate_instr = Some(( + StandardGate::CXGate, + smallvec![], + smallvec![Qubit((qubit_idx) as u32), Qubit(_ep as u32)], + )); + index = 0; + phase_loop_on = true; + for _k in 0..state.ncols() { + state[(_ep, _k)] ^= state[(qubit_idx, _k)]; } + break 'outer_loop; + } - let mut phase_iter_h = PhaseIterator::new( - self.num_qubits, - self.s_cpy.clone(), - self.state.clone(), - self.rust_angles.clone(), - ); - - phase_iter_h.iter_once = true; - phase_iter_h.qubit_idx = self._ep; - self.phase_iter_handle = Some(phase_iter_h); - - self.q.push((self._s.clone(), self._i.clone(), self._ep)); - - let mut unique_q = vec![]; - for data in self.q.iter() { - if !unique_q.contains(data) { - let d = (*data).clone(); - unique_q.push(d); - } + cx_gate_done = false; + while phase_loop_on { + if index == s_cpy.ncols() { + phase_loop_on = false; + break; } + let icnot = s_cpy.column(index); + index += 1; + let target_state = state.row(_ep); + if icnot == target_state { + index -= 1; + s_cpy.remove_index(numpy::ndarray::Axis(1), index); + let angle = rust_angles.remove(index); + gate_instr = get_instr(angle, _ep); + break 'outer_loop; + } + } - self.q = unique_q; + q.push((_s.clone(), _i.clone(), _ep)); + let mut unique_q = vec![]; - for data in &mut self.q { - let (ref mut _temp_s, _, _) = data; - if _temp_s.is_empty() { - continue; - } - for idx in 0.._temp_s.row(self.qubit_idx).len() { - _temp_s[(self.qubit_idx, idx)] ^= _temp_s[(self._ep, idx)]; - } + for data in q.iter() { + if !unique_q.contains(data) { + let d = (*data).clone(); + unique_q.push(d); } + } + q = unique_q; - (self._s, self._i, self._ep) = self.q.pop().unwrap(); + for data in &mut q { + let (ref mut _temp_s, _, _) = data; + if _temp_s.is_empty() { + continue; + } - self.qubit_idx += 1; - self.keep_iterating = true; - self.loop_active = true; - return Some(( - StandardGate::CXGate, - smallvec![], - smallvec![ - Qubit((self.qubit_idx - 1) as u32), - Qubit(self._ep as u32) - ], - )); - } else { - self.qubit_idx += 1; - self.loop_active = true; - return self.next(); - } - } else { - self.qubit_idx = 0; - self.loop_active = false; - if self.keep_iterating { - return self.next(); + for idx in 0.._temp_s.row(qubit_idx).len() { + _temp_s[(qubit_idx, idx)] ^= _temp_s[(_ep, idx)]; + } } - } - } - } - if self._i.is_empty() { - return self.next(); + (_s, _i, _ep) = q.pop().unwrap(); + } // end of if _ep < num_qubits ... + qubit_idx += 1; + } // end of while check qubit_idx < num_qubits ... + qubit_idx = 0; + } // end of while keep iterating ... + keep_iterating = true; + + if _i.is_empty() { + continue 'outer_loop; } - let maxes: Vec = self - ._s + let maxes: Vec = _s .axis_iter(numpy::ndarray::Axis(0)) .map(|row| { std::cmp::max( @@ -277,7 +230,7 @@ impl Iterator for CXPhaseIterator { }) .collect(); - let maxes2: Vec = self._i.iter().map(|&_i_idx| maxes[_i_idx]).collect(); + let maxes2: Vec = _i.iter().map(|&_i_idx| maxes[_i_idx]).collect(); let _temp_argmax = maxes2 .iter() @@ -286,15 +239,15 @@ impl Iterator for CXPhaseIterator { .map(|(idx, _)| idx) .unwrap(); - let _j = self._i[_temp_argmax]; + let _j = _i[_temp_argmax]; let mut cnots0_t = vec![]; let mut cnots1_t = vec![]; - let mut cnots0_t_shape = (0_usize, self._s.column(0).len()); + let mut cnots0_t_shape = (0_usize, _s.column(0).len()); let mut cnots1_t_shape = (0_usize, 0_usize); cnots1_t_shape.1 = cnots0_t_shape.1; - for cols in self._s.columns() { + for cols in _s.columns() { if cols[_j] == 0 { cnots0_t_shape.0 += 1; cnots0_t.append(&mut cols.to_vec()); @@ -312,117 +265,40 @@ impl Iterator for CXPhaseIterator { let cnots0 = cnots0.reversed_axes().to_owned(); let cnots1 = cnots1.reversed_axes().to_owned(); - if self._ep == self.num_qubits { - self.q.push(( + if _ep == num_qubits { + q.push(( cnots1, - self._i.clone().into_iter().filter(|&x| x != _j).collect(), + _i.clone().into_iter().filter(|&x| x != _j).collect(), _j, )); } else { - self.q.push(( + q.push(( cnots1, - self._i.clone().into_iter().filter(|&x| x != _j).collect(), - self._ep, + _i.clone().into_iter().filter(|&x| x != _j).collect(), + _ep, )); } - self.q.push(( + q.push(( cnots0, - self._i.clone().into_iter().filter(|&x| x != _j).collect(), - self._ep, + _i.clone().into_iter().filter(|&x| x != _j).collect(), + _ep, )); + } // end 'outer_loop - self.next() - } else { + if phase_done && cx_phase_done { + *x_clone.borrow_mut() = Some(state.clone()); None - } - } -} - -struct BindingIterator { - num_qubits: usize, - q: Vec<(Array2, Vec, usize)>, - phase_iter_handle: PhaseIterator, - cx_phase_iter_handle: Option, - phase_iterator_done: bool, -} - -impl BindingIterator { - fn new(s: Array2, angles: Vec, state: Array2) -> Self { - let num_qubits = s.nrows(); - let q = vec![( - s.clone(), - (0..num_qubits).collect::>(), - num_qubits, - )]; - Self { - num_qubits, - q, - phase_iter_handle: PhaseIterator::new(num_qubits, s, state, angles), - cx_phase_iter_handle: None, - phase_iterator_done: false, - } - } -} - -impl Iterator for BindingIterator { - type Item = Instruction; - fn next(&mut self) -> Option { - if !self.phase_iterator_done { - let data = self.phase_iter_handle.next(); - if data.is_none() { - self.cx_phase_iter_handle = Some(CXPhaseIterator::new( - self.num_qubits, - self.phase_iter_handle.s_cpy.clone(), - self.phase_iter_handle.state.clone(), - self.phase_iter_handle.rust_angles.clone(), - self.q.clone(), - )); - self.phase_iterator_done = true; - self.next() - } else { - data - } - } else if let Some(handle) = self.cx_phase_iter_handle.as_mut() { - handle.next() } else { - None + gate_instr } - } -} + }); -/// This function implements a Gray-code inspired algorithm of synthesizing a circuit -/// over CNOT and phase-gates with minimal-CNOT for a given phase-polynomial. -/// The algorithm is described as "Gray-Synth" algorithm in Algorithm-1, page 12 -/// of paper "https://arxiv.org/abs/1712.01859". -#[pyfunction] -#[pyo3(signature = (cnots, angles, section_size=2))] -pub fn synth_cnot_phase_aam( - py: Python, - cnots: PyReadonlyArray2, - angles: &Bound, - section_size: Option, -) -> PyResult { - let s = cnots.as_array().to_owned(); - let num_qubits = s.nrows(); - - let rust_angles = angles - .iter() - .filter_map(|data| data.extract::().ok()) - .collect::>(); - - let state = Array2::::eye(num_qubits); - - let mut binding_iter_handle = BindingIterator::new(s, rust_angles, state); - - // Optimize this one! - let cx_phase_iter = std::iter::from_fn(|| binding_iter_handle.next()); - let cx_phase_iter_vec = cx_phase_iter.collect::>(); - - let residual_state = binding_iter_handle.cx_phase_iter_handle.unwrap().state; - let state_bool = residual_state.mapv(|x| x != 0); + let cx_phase_vec = cx_phase_iter.collect::>(); + let borrowed_x = x.borrow(); + let residue_state = borrowed_x.as_ref().unwrap(); + let state_bool = residue_state.mapv(|x| x != 0); let synth_pmh_iter = synth_pmh(state_bool, section_size).rev(); - let cnot_synth_iter = cx_phase_iter_vec.into_iter().chain(synth_pmh_iter); - - CircuitData::from_standard_gates(py, num_qubits as u32, cnot_synth_iter, Param::Float(0.0)) + let synth_aam_iter = cx_phase_vec.into_iter().chain(synth_pmh_iter); + CircuitData::from_standard_gates(py, num_qubits as u32, synth_aam_iter, Param::Float(0.0)) } From 477e62e1ca5e2f21d12bc0d8fc120cab183a8386 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Sun, 17 Nov 2024 05:23:25 +0530 Subject: [PATCH 29/30] removed moving from into_iter to vec to into_iter --- .../linear_phase/cnot_phase_synth.rs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 046e0103ce99..11289daa7b1b 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -18,9 +18,7 @@ use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::operations::{Param, StandardGate}; use qiskit_circuit::Qubit; use smallvec::{smallvec, SmallVec}; -use std::cell::RefCell; use std::f64::consts::PI; -use std::rc::Rc; type Instruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); fn get_instr(angle: String, qubit_idx: usize) -> Option { @@ -84,9 +82,6 @@ pub fn synth_cnot_phase_aam( let mut state = Array2::::eye(num_qubits); - let x: Rc>>> = Rc::new(RefCell::new(None)); - let x_clone = Rc::clone(&x); - let mut s_cpy = s.clone(); let mut q = vec![( s.clone(), @@ -105,6 +100,8 @@ pub fn synth_cnot_phase_aam( let mut cx_phase_done: bool = false; let mut qubit_idx: usize = 0; let mut index: usize = 0; + let mut pmh_init_done: bool = false; + let mut synth_pmh_iter: Option>> = None; let cx_phase_iter = std::iter::from_fn(move || { let mut gate_instr: Option = None; @@ -285,20 +282,23 @@ pub fn synth_cnot_phase_aam( )); } // end 'outer_loop - if phase_done && cx_phase_done { - *x_clone.borrow_mut() = Some(state.clone()); - None - } else { - gate_instr + if phase_done && cx_phase_done && !pmh_init_done { + synth_pmh_iter = Some(Box::new( + synth_pmh(state.mapv(|x| x != 0), section_size).rev(), + )); + pmh_init_done = true; + } + + if pmh_init_done { + if let Some(ref mut data) = synth_pmh_iter { + gate_instr = data.next(); + } else { + gate_instr = None; + } } - }); - let cx_phase_vec = cx_phase_iter.collect::>(); + gate_instr + }); - let borrowed_x = x.borrow(); - let residue_state = borrowed_x.as_ref().unwrap(); - let state_bool = residue_state.mapv(|x| x != 0); - let synth_pmh_iter = synth_pmh(state_bool, section_size).rev(); - let synth_aam_iter = cx_phase_vec.into_iter().chain(synth_pmh_iter); - CircuitData::from_standard_gates(py, num_qubits as u32, synth_aam_iter, Param::Float(0.0)) + CircuitData::from_standard_gates(py, num_qubits as u32, cx_phase_iter, Param::Float(0.0)) } From f5a92c91d69edaff68dcbbc71a78265849759616 Mon Sep 17 00:00:00 2001 From: MozammilQ Date: Sun, 17 Nov 2024 06:16:17 +0530 Subject: [PATCH 30/30] applied Julien suggestions --- crates/accelerate/src/synthesis/linear/pmh.rs | 6 +++-- .../linear_phase/cnot_phase_synth.rs | 24 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/pmh.rs b/crates/accelerate/src/synthesis/linear/pmh.rs index 7973868be50b..25c126321022 100644 --- a/crates/accelerate/src/synthesis/linear/pmh.rs +++ b/crates/accelerate/src/synthesis/linear/pmh.rs @@ -161,6 +161,8 @@ pub fn synth_cnot_count_full_pmh( let arrayview = matrix.as_array(); let mat: Array2 = arrayview.to_owned(); let num_qubits = mat.nrows(); + let section_size: Option = + section_size.and_then(|num| if num >= 0 { Some(num as usize) } else { None }); let instructions = synth_pmh(mat, section_size); CircuitData::from_standard_gates(py, num_qubits as u32, instructions, Param::Float(0.0)) @@ -169,7 +171,7 @@ pub fn synth_cnot_count_full_pmh( type Instruction = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>); pub fn synth_pmh( mat: Array2, - section_size: Option, + section_size: Option, ) -> impl DoubleEndedIterator { let mut mat = mat; let num_qubits = mat.nrows(); // is a quadratic matrix @@ -181,7 +183,7 @@ pub fn synth_pmh( // until ~100 qubits. let alpha = 0.56; let blocksize = match section_size { - Some(section_size) => section_size as usize, + Some(section_size) => section_size, None => std::cmp::max(2, (alpha * (num_qubits as f64).log2()).floor() as usize), }; diff --git a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs index 11289daa7b1b..e7d0444fcd84 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cnot_phase_synth.rs @@ -68,7 +68,12 @@ pub fn synth_cnot_phase_aam( angles: &Bound, section_size: Option, ) -> PyResult { + // converting to Option + let section_size: Option = + section_size.and_then(|num| if num >= 0 { Some(num as usize) } else { None }); + let s = cnots.as_array().to_owned(); + let s = s.mapv(|x| x != 0); let num_qubits = s.nrows(); let mut rust_angles = angles @@ -80,7 +85,8 @@ pub fn synth_cnot_phase_aam( }) .collect::>(); - let mut state = Array2::::eye(num_qubits); + let state = Array2::::eye(num_qubits); + let mut state = state.mapv(|x| x != 0); let mut s_cpy = s.clone(); let mut q = vec![( @@ -146,9 +152,7 @@ pub fn synth_cnot_phase_aam( keep_iterating = false; } while qubit_idx < num_qubits { - if (qubit_idx != _ep) - && (_s.row(qubit_idx).sum() as usize == _s.row(qubit_idx).len()) - { + if (qubit_idx != _ep) && !_s.row(qubit_idx).iter().any(|&b| !b) { if !cx_gate_done && !phase_loop_on { keep_iterating = true; cx_gate_done = true; @@ -221,8 +225,8 @@ pub fn synth_cnot_phase_aam( .axis_iter(numpy::ndarray::Axis(0)) .map(|row| { std::cmp::max( - row.iter().filter(|&&x| x == 0).count(), - row.iter().filter(|&&x| x == 1).count(), + row.iter().filter(|&&x| !x).count(), + row.iter().filter(|&&x| x).count(), ) }) .collect(); @@ -245,10 +249,10 @@ pub fn synth_cnot_phase_aam( let mut cnots1_t_shape = (0_usize, 0_usize); cnots1_t_shape.1 = cnots0_t_shape.1; for cols in _s.columns() { - if cols[_j] == 0 { + if !cols[_j] { cnots0_t_shape.0 += 1; cnots0_t.append(&mut cols.to_vec()); - } else if cols[_j] == 1 { + } else { cnots1_t_shape.0 += 1; cnots1_t.append(&mut cols.to_vec()); } @@ -283,9 +287,7 @@ pub fn synth_cnot_phase_aam( } // end 'outer_loop if phase_done && cx_phase_done && !pmh_init_done { - synth_pmh_iter = Some(Box::new( - synth_pmh(state.mapv(|x| x != 0), section_size).rev(), - )); + synth_pmh_iter = Some(Box::new(synth_pmh(state.clone(), section_size).rev())); pmh_init_done = true; }