From 120b73d21deb91826e2ad884e2d0f0ab236d9fd5 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Fri, 2 Aug 2024 15:00:31 +0100 Subject: [PATCH] Avoid exporting incorrect `PyInit_*` symbols (#12889) Using the `#[pymodule]` derive macro in PyO3 0.21 always causes a `PyInit_*` symbol with a matching name to be exported in the output `cdylib`. This is required for the top-level module, in order for Python to import it---it needs to know which symbol in a shared library file it should call---but submodules must be manually initialised, so do not need it. Including it is typically harmless (and something we've been doing for a long time), but it is technically against the coding rules for CPython extensions[^1]. Recent versions of `abi3audit` (0.0.11+) have tightened their symbol checkers to better match the CPython guidelines, which causes our wheels to be rejected by their audits. This is, in theory, not a break of abi3 because CPython could never introduce an API-elvel `PyInit_*` function themselves without causing problems, so there ought to be no problems for our users, even with future Python versions. That said, we still want to pass the audit, because the coding guidelines are useful. This commit is not the cleanest way of doing things. PyO3 0.22 includes a `#[pymodule(submodule)]` option on the attribute macro, which lets us use all the standard code generation while suppressing the unnecessary `PyInit_*` symbol. When we are ready to move to PyO3 0.22, we probably want to revert this commit to switch to that form. [^1]: https://docs.python.org/3/c-api/intro.html --- .../accelerate/src/convert_2q_block_matrix.rs | 1 - crates/accelerate/src/dense_layout.rs | 1 - crates/accelerate/src/error_map.rs | 1 - .../src/euler_one_qubit_decomposer.rs | 1 - crates/accelerate/src/isometry.rs | 1 - crates/accelerate/src/nlayout.rs | 1 - crates/accelerate/src/optimize_1q_gates.rs | 1 - crates/accelerate/src/pauli_exp_val.rs | 1 - crates/accelerate/src/results/mod.rs | 1 - crates/accelerate/src/sabre/mod.rs | 1 - crates/accelerate/src/sampled_exp_val.rs | 1 - crates/accelerate/src/sparse_pauli_op.rs | 1 - crates/accelerate/src/star_prerouting.rs | 1 - crates/accelerate/src/stochastic_swap.rs | 1 - .../accelerate/src/synthesis/clifford/mod.rs | 1 - crates/accelerate/src/synthesis/linear/mod.rs | 1 - crates/accelerate/src/synthesis/mod.rs | 17 ++++-- .../src/synthesis/permutation/mod.rs | 1 - .../accelerate/src/target_transpiler/mod.rs | 3 +- crates/accelerate/src/two_qubit_decompose.rs | 1 - crates/accelerate/src/uc_gate.rs | 1 - crates/accelerate/src/utils.rs | 1 - crates/accelerate/src/vf2_layout.rs | 1 - crates/circuit/src/lib.rs | 3 +- crates/pyext/src/lib.rs | 58 +++++++++++-------- crates/qasm2/src/lib.rs | 1 - crates/qasm3/src/lib.rs | 1 - 27 files changed, 48 insertions(+), 56 deletions(-) diff --git a/crates/accelerate/src/convert_2q_block_matrix.rs b/crates/accelerate/src/convert_2q_block_matrix.rs index 12146dd9d08..e9f6e343b6b 100644 --- a/crates/accelerate/src/convert_2q_block_matrix.rs +++ b/crates/accelerate/src/convert_2q_block_matrix.rs @@ -145,7 +145,6 @@ pub fn collect_2q_blocks_filter(node: &Bound) -> Option { } } -#[pymodule] pub fn convert_2q_block_matrix(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(blocks_to_matrix))?; m.add_wrapped(wrap_pyfunction!(collect_2q_blocks_filter))?; diff --git a/crates/accelerate/src/dense_layout.rs b/crates/accelerate/src/dense_layout.rs index 9529742d7e6..a7466748417 100644 --- a/crates/accelerate/src/dense_layout.rs +++ b/crates/accelerate/src/dense_layout.rs @@ -244,7 +244,6 @@ pub fn best_subset_inner( [rows, cols, best_map] } -#[pymodule] pub fn dense_layout(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(best_subset))?; Ok(()) diff --git a/crates/accelerate/src/error_map.rs b/crates/accelerate/src/error_map.rs index 8dbd2e4290a..b61733ae151 100644 --- a/crates/accelerate/src/error_map.rs +++ b/crates/accelerate/src/error_map.rs @@ -111,7 +111,6 @@ impl ErrorMap { } } -#[pymodule] pub fn error_map(m: &Bound) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/crates/accelerate/src/euler_one_qubit_decomposer.rs b/crates/accelerate/src/euler_one_qubit_decomposer.rs index 7bbb6871db0..7463777af62 100644 --- a/crates/accelerate/src/euler_one_qubit_decomposer.rs +++ b/crates/accelerate/src/euler_one_qubit_decomposer.rs @@ -1059,7 +1059,6 @@ pub fn collect_1q_runs_filter(node: &Bound) -> bool { } } -#[pymodule] pub fn euler_one_qubit_decomposer(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(params_zyz))?; m.add_wrapped(wrap_pyfunction!(params_xyx))?; diff --git a/crates/accelerate/src/isometry.rs b/crates/accelerate/src/isometry.rs index ceaba2946b3..a54116b2b2f 100644 --- a/crates/accelerate/src/isometry.rs +++ b/crates/accelerate/src/isometry.rs @@ -345,7 +345,6 @@ fn b(k: usize, s: usize) -> usize { k - (a(k, s) * 2_usize.pow(s as u32)) } -#[pymodule] pub fn isometry(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(diag_is_identity_up_to_global_phase))?; m.add_wrapped(wrap_pyfunction!(find_squs_for_disentangling))?; diff --git a/crates/accelerate/src/nlayout.rs b/crates/accelerate/src/nlayout.rs index b3709d2804b..e0235e5c954 100644 --- a/crates/accelerate/src/nlayout.rs +++ b/crates/accelerate/src/nlayout.rs @@ -216,7 +216,6 @@ impl NLayout { } } -#[pymodule] pub fn nlayout(m: &Bound) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/crates/accelerate/src/optimize_1q_gates.rs b/crates/accelerate/src/optimize_1q_gates.rs index a683604c19e..64924a33913 100644 --- a/crates/accelerate/src/optimize_1q_gates.rs +++ b/crates/accelerate/src/optimize_1q_gates.rs @@ -90,7 +90,6 @@ pub fn compose_u3_rust( out_angles } -#[pymodule] pub fn optimize_1q_gates(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(compose_u3_rust))?; Ok(()) diff --git a/crates/accelerate/src/pauli_exp_val.rs b/crates/accelerate/src/pauli_exp_val.rs index 8ee4b019b3e..bf9569b485e 100644 --- a/crates/accelerate/src/pauli_exp_val.rs +++ b/crates/accelerate/src/pauli_exp_val.rs @@ -193,7 +193,6 @@ pub fn density_expval_pauli_with_x( } } -#[pymodule] pub fn pauli_expval(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(expval_pauli_no_x))?; m.add_wrapped(wrap_pyfunction!(expval_pauli_with_x))?; diff --git a/crates/accelerate/src/results/mod.rs b/crates/accelerate/src/results/mod.rs index 36282a749e6..6d5a7936397 100644 --- a/crates/accelerate/src/results/mod.rs +++ b/crates/accelerate/src/results/mod.rs @@ -16,7 +16,6 @@ pub mod marginalization; use pyo3::prelude::*; use pyo3::wrap_pyfunction; -#[pymodule] pub fn results(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(marginalization::marginal_counts))?; m.add_wrapped(wrap_pyfunction!(marginalization::marginal_distribution))?; diff --git a/crates/accelerate/src/sabre/mod.rs b/crates/accelerate/src/sabre/mod.rs index 287fdd743df..77057b69c27 100644 --- a/crates/accelerate/src/sabre/mod.rs +++ b/crates/accelerate/src/sabre/mod.rs @@ -106,7 +106,6 @@ impl BlockResult { } } -#[pymodule] pub fn sabre(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(route::sabre_routing))?; m.add_wrapped(wrap_pyfunction!(layout::sabre_layout_and_routing))?; diff --git a/crates/accelerate/src/sampled_exp_val.rs b/crates/accelerate/src/sampled_exp_val.rs index 0b8836a9416..3424c12dcdf 100644 --- a/crates/accelerate/src/sampled_exp_val.rs +++ b/crates/accelerate/src/sampled_exp_val.rs @@ -87,7 +87,6 @@ pub fn sampled_expval_complex( Ok(out.re) } -#[pymodule] pub fn sampled_exp_val(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(sampled_expval_float))?; m.add_wrapped(wrap_pyfunction!(sampled_expval_complex))?; diff --git a/crates/accelerate/src/sparse_pauli_op.rs b/crates/accelerate/src/sparse_pauli_op.rs index 8a51d8ee781..73c4ab7a73d 100644 --- a/crates/accelerate/src/sparse_pauli_op.rs +++ b/crates/accelerate/src/sparse_pauli_op.rs @@ -819,7 +819,6 @@ impl_to_matrix_sparse!( u64 ); -#[pymodule] pub fn sparse_pauli_op(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(unordered_unique))?; m.add_wrapped(wrap_pyfunction!(decompose_dense))?; diff --git a/crates/accelerate/src/star_prerouting.rs b/crates/accelerate/src/star_prerouting.rs index fd2156ad201..dc777a84476 100644 --- a/crates/accelerate/src/star_prerouting.rs +++ b/crates/accelerate/src/star_prerouting.rs @@ -207,7 +207,6 @@ fn apply_swap( } } -#[pymodule] pub fn star_prerouting(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(star_preroute))?; Ok(()) diff --git a/crates/accelerate/src/stochastic_swap.rs b/crates/accelerate/src/stochastic_swap.rs index d4e3890b9cc..5260c85b3d4 100644 --- a/crates/accelerate/src/stochastic_swap.rs +++ b/crates/accelerate/src/stochastic_swap.rs @@ -335,7 +335,6 @@ pub fn swap_trials( Ok((best_edges, best_layout, best_depth)) } -#[pymodule] pub fn stochastic_swap(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(swap_trials))?; m.add_class::()?; diff --git a/crates/accelerate/src/synthesis/clifford/mod.rs b/crates/accelerate/src/synthesis/clifford/mod.rs index ac178b56670..4828a566673 100644 --- a/crates/accelerate/src/synthesis/clifford/mod.rs +++ b/crates/accelerate/src/synthesis/clifford/mod.rs @@ -57,7 +57,6 @@ fn synth_clifford_bm(py: Python, clifford: PyReadonlyArray2) -> PyResult) -> PyResult<()> { m.add_function(wrap_pyfunction!(synth_clifford_greedy, m)?)?; m.add_function(wrap_pyfunction!(synth_clifford_bm, m)?)?; diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index 49dfeefd386..08a0b1e104b 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -175,7 +175,6 @@ fn check_invertible_binary_matrix(py: Python, mat: PyReadonlyArray2) -> Py Ok(out.to_object(py)) } -#[pymodule] pub fn linear(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(gauss_elimination_with_perm))?; m.add_wrapped(wrap_pyfunction!(gauss_elimination))?; diff --git a/crates/accelerate/src/synthesis/mod.rs b/crates/accelerate/src/synthesis/mod.rs index 1b9908ef80c..fae05c6739c 100644 --- a/crates/accelerate/src/synthesis/mod.rs +++ b/crates/accelerate/src/synthesis/mod.rs @@ -15,12 +15,19 @@ pub mod linear; mod permutation; use pyo3::prelude::*; -use pyo3::wrap_pymodule; -#[pymodule] pub fn synthesis(m: &Bound) -> PyResult<()> { - m.add_wrapped(wrap_pymodule!(linear::linear))?; - m.add_wrapped(wrap_pymodule!(permutation::permutation))?; - m.add_wrapped(wrap_pymodule!(clifford::clifford))?; + let linear_mod = PyModule::new_bound(m.py(), "linear")?; + linear::linear(&linear_mod)?; + m.add_submodule(&linear_mod)?; + + let permutation_mod = PyModule::new_bound(m.py(), "permutation")?; + permutation::permutation(&permutation_mod)?; + m.add_submodule(&permutation_mod)?; + + let clifford_mod = PyModule::new_bound(m.py(), "clifford")?; + clifford::clifford(&clifford_mod)?; + m.add_submodule(&clifford_mod)?; + Ok(()) } diff --git a/crates/accelerate/src/synthesis/permutation/mod.rs b/crates/accelerate/src/synthesis/permutation/mod.rs index fccb0cfb972..55dc3efe4a8 100644 --- a/crates/accelerate/src/synthesis/permutation/mod.rs +++ b/crates/accelerate/src/synthesis/permutation/mod.rs @@ -114,7 +114,6 @@ pub fn _synth_permutation_depth_lnn_kms( ) } -#[pymodule] pub fn permutation(m: &Bound) -> PyResult<()> { m.add_function(wrap_pyfunction!(_validate_permutation, m)?)?; m.add_function(wrap_pyfunction!(_inverse_pattern, m)?)?; diff --git a/crates/accelerate/src/target_transpiler/mod.rs b/crates/accelerate/src/target_transpiler/mod.rs index b5c56dc6d09..bb0ec166c07 100644 --- a/crates/accelerate/src/target_transpiler/mod.rs +++ b/crates/accelerate/src/target_transpiler/mod.rs @@ -1254,8 +1254,7 @@ where obj_bound.is_instance(other_obj.bind(py)) } -#[pymodule] -pub fn target(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { +pub fn target(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; Ok(()) diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs index dfee4c5b0bf..b423b1d6526 100644 --- a/crates/accelerate/src/two_qubit_decompose.rs +++ b/crates/accelerate/src/two_qubit_decompose.rs @@ -2254,7 +2254,6 @@ pub fn local_equivalence(weyl: PyReadonlyArray1) -> PyResult<[f64; 3]> { Ok([g0_equiv + 0., g1_equiv + 0., g2_equiv + 0.]) } -#[pymodule] pub fn two_qubit_decompose(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(_num_basis_gates))?; m.add_wrapped(wrap_pyfunction!(two_qubit_decompose_up_to_diagonal))?; diff --git a/crates/accelerate/src/uc_gate.rs b/crates/accelerate/src/uc_gate.rs index 21fd7fa0465..ec79f4d4d2b 100644 --- a/crates/accelerate/src/uc_gate.rs +++ b/crates/accelerate/src/uc_gate.rs @@ -156,7 +156,6 @@ pub fn dec_ucg_help( ) } -#[pymodule] pub fn uc_gate(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(dec_ucg_help))?; Ok(()) diff --git a/crates/accelerate/src/utils.rs b/crates/accelerate/src/utils.rs index 6df00f7f8b7..598256192f8 100644 --- a/crates/accelerate/src/utils.rs +++ b/crates/accelerate/src/utils.rs @@ -41,7 +41,6 @@ pub fn eigenvalues(py: Python, unitary: PyReadonlyArray2>) -> PyObj .into() } -#[pymodule] pub fn utils(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(eigenvalues))?; Ok(()) diff --git a/crates/accelerate/src/vf2_layout.rs b/crates/accelerate/src/vf2_layout.rs index e84b2bfbfb8..476197397af 100644 --- a/crates/accelerate/src/vf2_layout.rs +++ b/crates/accelerate/src/vf2_layout.rs @@ -106,7 +106,6 @@ pub fn score_layout( Ok(1. - fidelity) } -#[pymodule] pub fn vf2_layout(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(score_layout))?; m.add_class::()?; diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 8a13aab33ce..739bf998a61 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -56,8 +56,7 @@ impl From for BitType { } } -#[pymodule] -pub fn circuit(m: Bound) -> PyResult<()> { +pub fn circuit(m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/crates/pyext/src/lib.rs b/crates/pyext/src/lib.rs index f9711641a93..04b0c060934 100644 --- a/crates/pyext/src/lib.rs +++ b/crates/pyext/src/lib.rs @@ -11,7 +11,6 @@ // that they have been altered from the originals. use pyo3::prelude::*; -use pyo3::wrap_pymodule; use qiskit_accelerate::{ convert_2q_block_matrix::convert_2q_block_matrix, dense_layout::dense_layout, @@ -24,30 +23,41 @@ use qiskit_accelerate::{ vf2_layout::vf2_layout, }; +#[inline(always)] +#[doc(hidden)] +fn add_submodule(m: &Bound, constructor: F, name: &str) -> PyResult<()> +where + F: FnOnce(&Bound) -> PyResult<()>, +{ + let new_mod = PyModule::new_bound(m.py(), name)?; + constructor(&new_mod)?; + m.add_submodule(&new_mod) +} + #[pymodule] fn _accelerate(m: &Bound) -> PyResult<()> { - m.add_wrapped(wrap_pymodule!(qiskit_circuit::circuit))?; - m.add_wrapped(wrap_pymodule!(qiskit_qasm2::qasm2))?; - m.add_wrapped(wrap_pymodule!(qiskit_qasm3::qasm3))?; - m.add_wrapped(wrap_pymodule!(convert_2q_block_matrix))?; - m.add_wrapped(wrap_pymodule!(dense_layout))?; - m.add_wrapped(wrap_pymodule!(error_map))?; - m.add_wrapped(wrap_pymodule!(euler_one_qubit_decomposer))?; - m.add_wrapped(wrap_pymodule!(isometry))?; - m.add_wrapped(wrap_pymodule!(nlayout))?; - m.add_wrapped(wrap_pymodule!(optimize_1q_gates))?; - m.add_wrapped(wrap_pymodule!(pauli_expval))?; - m.add_wrapped(wrap_pymodule!(synthesis))?; - m.add_wrapped(wrap_pymodule!(results))?; - m.add_wrapped(wrap_pymodule!(sabre))?; - m.add_wrapped(wrap_pymodule!(sampled_exp_val))?; - m.add_wrapped(wrap_pymodule!(sparse_pauli_op))?; - m.add_wrapped(wrap_pymodule!(star_prerouting))?; - m.add_wrapped(wrap_pymodule!(stochastic_swap))?; - m.add_wrapped(wrap_pymodule!(target))?; - m.add_wrapped(wrap_pymodule!(two_qubit_decompose))?; - m.add_wrapped(wrap_pymodule!(uc_gate))?; - m.add_wrapped(wrap_pymodule!(utils))?; - m.add_wrapped(wrap_pymodule!(vf2_layout))?; + add_submodule(m, qiskit_circuit::circuit, "circuit")?; + add_submodule(m, qiskit_qasm2::qasm2, "qasm2")?; + add_submodule(m, qiskit_qasm3::qasm3, "qasm3")?; + add_submodule(m, convert_2q_block_matrix, "convert_2q_block_matrix")?; + add_submodule(m, dense_layout, "dense_layout")?; + add_submodule(m, error_map, "error_map")?; + add_submodule(m, euler_one_qubit_decomposer, "euler_one_qubit_decomposer")?; + add_submodule(m, isometry, "isometry")?; + add_submodule(m, nlayout, "nlayout")?; + add_submodule(m, optimize_1q_gates, "optimize_1q_gates")?; + add_submodule(m, pauli_expval, "pauli_expval")?; + add_submodule(m, synthesis, "synthesis")?; + add_submodule(m, results, "results")?; + add_submodule(m, sabre, "sabre")?; + add_submodule(m, sampled_exp_val, "sampled_exp_val")?; + add_submodule(m, sparse_pauli_op, "sparse_pauli_op")?; + add_submodule(m, star_prerouting, "star_prerouting")?; + add_submodule(m, stochastic_swap, "stochastic_swap")?; + add_submodule(m, target, "target")?; + add_submodule(m, two_qubit_decompose, "two_qubit_decompose")?; + add_submodule(m, uc_gate, "uc_gate")?; + add_submodule(m, utils, "utils")?; + add_submodule(m, vf2_layout, "vf2_layout")?; Ok(()) } diff --git a/crates/qasm2/src/lib.rs b/crates/qasm2/src/lib.rs index 7129b17f7fd..beac72674e9 100644 --- a/crates/qasm2/src/lib.rs +++ b/crates/qasm2/src/lib.rs @@ -123,7 +123,6 @@ fn bytecode_from_file( /// An interface to the Rust components of the parser stack, and the types it uses to represent the /// output. The principal entry points for Python are :func:`bytecode_from_string` and /// :func:`bytecode_from_file`, which produce iterables of :class:`Bytecode` objects. -#[pymodule] pub fn qasm2(module: &Bound) -> PyResult<()> { module.add_class::()?; module.add_class::()?; diff --git a/crates/qasm3/src/lib.rs b/crates/qasm3/src/lib.rs index 9a651a5c12e..bca862406f3 100644 --- a/crates/qasm3/src/lib.rs +++ b/crates/qasm3/src/lib.rs @@ -153,7 +153,6 @@ pub fn load( /// Internal module supplying the OpenQASM 3 import capabilities. The entries in it should largely /// be re-exposed directly to public Python space. -#[pymodule] pub fn qasm3(module: &Bound) -> PyResult<()> { module.add_function(wrap_pyfunction!(loads, module)?)?; module.add_function(wrap_pyfunction!(load, module)?)?;