Add SabreLayout to the C API#14711
Conversation
|
One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 17142393122Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
|
I've rebased this PR on main, but it's still depending on: #14715 which is in the branch. I will rebase it again after that PR merges which is needed to enable the tests as they're written here. I could adjust the tests to not rely on global gates, but I figured since it uncovered a bug that I should leave it using a global 1q gate. |
|
This has been rebased and is unblocked now |
This commit adds a standalone transpiler pass function to the C API for running the SabreLayout transpiler pass. This function returns a new result type QkSabreLayoutResult that contains the output circuit along with the layouts from running the pass. Fixes Qiskit#14449
jakelishman
left a comment
There was a problem hiding this comment.
I'll look again after the interface change to QkTranspileLayout. There's a few places in this PR (and maybe others?) where we're doing match result { Ok(x) => x, Err(e) => panic!("{e}") }, which I think is just Result::unwrap, but with Display formatting instead of Debug?
| /// @ingroup QkSabreLayoutResult | ||
| /// Get the circuit from sabre layout result | ||
| /// | ||
| /// @param result a pointer to the result of the pass. | ||
| /// | ||
| /// @returns A pointer to the output circuit | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Behavior is undefined if ``result`` is not a valid, non-null pointer to a | ||
| /// ``QkSabreLayoutResult``. The pointer to the returned circuit is owned by | ||
| /// the result object, it should not be passed to ``qk_circuit_free()`` as it | ||
| /// will be freed by ``qk_sabre_layout_result_free()``. | ||
| #[no_mangle] | ||
| #[cfg(feature = "cbinding")] | ||
| pub unsafe extern "C" fn qk_sabre_layout_result_circuit( | ||
| result: *mut SabreLayoutResult, | ||
| ) -> *mut CircuitData { | ||
| let result = unsafe { mut_ptr_as_ref(result) }; | ||
| (&mut result.circuit) as *mut _ | ||
| } |
There was a problem hiding this comment.
It's not going to be relevant because of the change in direction of the PR, but were it to stay like this, I would have liked to think more about the ownership / lifetime model being implied by this - it's not really clear to me who'd be responsible for freeing the circuit, and how the freeing of the total layout object would have worked under the assumption that the C caller wanted to "move" ownership of the circuit out of this return object.
| /// @ingroup QkSabreLayoutResult | ||
| /// Get the number of qubits in the initial layout | ||
| /// | ||
| /// @param result a pointer to the result | ||
| /// | ||
| /// @returns The number of qubits in the initial layout | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Behavior is undefined if ``result`` is not a valid, non-null pointer to a | ||
| /// ``QkSabreLayoutResult``. | ||
| #[no_mangle] | ||
| #[cfg(feature = "cbinding")] | ||
| pub unsafe extern "C" fn qk_sabre_layout_result_initial_layout_num_qubits( | ||
| result: *const SabreLayoutResult, | ||
| ) -> u32 { | ||
| let result = unsafe { const_ptr_as_ref(result) }; | ||
| result.initial_layout.num_qubits() as u32 | ||
| } |
There was a problem hiding this comment.
Again, this will be invalidated by the pivot in return type, but I think this would have always returned the same as qk_sabre_layout_result_final_layout_num_qubits, so we probably could have just had one?
| /// This function is multithreaded and will launch a thread pool with threads equal to the number | ||
| /// of CPUs by default. You can tune the number of threads with the ``RAYON_NUM_THREADS`` | ||
| /// environment variable. For example, setting ``RAYON_NUM_THREADS=4`` would limit the thread pool | ||
| /// to 4 threads. |
There was a problem hiding this comment.
In the future, we're almost certainly going to want to expose some sort of per-function and per-process "threading configuration" objects into C space, given the target audience.
There was a problem hiding this comment.
Yeah, I am putting these comments in the docstrings because I've already had discussions about calling some of the C API functions in an OpenMP context and wanted to make sure we were at least documenting how it worked now. But I agree we'll probably want to expose a structure maybe as part of TranspileOptions to control the threading behavior.
Yeah that's fair, I was thinking there was a header in the message with something about |
| pub unsafe extern "C" fn qk_transpiler_pass_standalone_sabre_layout( | ||
| circuit: *mut CircuitData, | ||
| target: *const Target, | ||
| max_iterations: usize, | ||
| num_swap_trials: usize, | ||
| num_random_trials: usize, | ||
| seed: i64, | ||
| ) -> *mut TranspileLayout { |
There was a problem hiding this comment.
Returning a TranspileLayout like this discounts the possibility that the user might already have an "output permutation" they care about tracking (c.f. Split2qUnitaries or ElidePermutations). I'm fine if we decide for now that "standalone means it stands entirely alone" and so there's no other context (before TranspileLayout moves into CircuitData/DAGCircuit properly), I'm mostly just flagging this decision for the record.
There was a problem hiding this comment.
We didn't expose the API for permutation composition to C. We could add it I guess, but I think for right now the intent is for the standalone functions to be standalone. That's the position we took for the other C standalone pass functions too. We'll need to think about the larger workflows for building custom pass managers in >=2.3.
| bool compare_result = compare_circuits(qc, expected_circuit); | ||
| if (!compare_result) { | ||
| result = EqualityError; | ||
| goto layout_cleanup; | ||
| } |
There was a problem hiding this comment.
Mostly unimportant, but is this the standard C return-value convention for a comparator? Things like strcmp and process return codes return 0 on match/success, but we're returning 0 (false) for the failure case.
There was a problem hiding this comment.
I can invert the logic, I didn't really think about it too much when I wrote the helper function.
| #[repr(C)] | ||
| pub struct QkSabreLayoutOptions { | ||
| /// The number of forward-backward iterations in the sabre routing algorithm | ||
| max_iterations: usize, | ||
| /// The number of trials to run of the sabre routing algorithm for each iteration. When > 1 the | ||
| /// trial that routing trial that results in the output with the fewest swap gates will be | ||
| /// selected. | ||
| num_swap_trials: usize, | ||
| /// The number of random layout trials to run. The trial that results in the output with the | ||
| /// fewest swap gates will be selected. | ||
| num_random_trials: usize, | ||
| /// A seed value for the pRNG used internally. | ||
| seed: u64, | ||
| } |
There was a problem hiding this comment.
In VF2 land I went for making the internals of the struct private to let us extend it in an API-safe way, but I think it's good to try both ways and see which feels better, while we're still uncommitted to a stable interface - let's leave this as-is.
* Add SabreLayout to the C API This commit adds a standalone transpiler pass function to the C API for running the SabreLayout transpiler pass. This function returns a new result type QkSabreLayoutResult that contains the output circuit along with the layouts from running the pass. Fixes Qiskit#14449 * Improve testing * Fix tests * Fix docs * Finish API docs for standalone function * Pivot interface to use TranspileLayout This commit reworks the C interface for sabre layout to use TranspileLayout for the return type remove the Sabre result type. It was largely duplicative and it's better to just use the same interface. * Remove stale references to QkSabreLayoutResult in docs * Fix test memory leaks * Make input to pass function an options struct * Set number of input qubits correctly in output layout * Move helper functions to common.c * Use a stack variable instructions in compare_circuits() * Fix output_permutation in the return * Apply suggestions from code review Co-authored-by: Jake Lishman <jake@binhbar.com> * Update docstring for circuit arg * Don't use PyErr debug or display * Add missing newlines to test failure strings * Update error messages about output permutation test failures * Correct permutation variable names * Correct copyright header --------- Co-authored-by: Jake Lishman <jake@binhbar.com> Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
* Add SabreLayout to the C API This commit adds a standalone transpiler pass function to the C API for running the SabreLayout transpiler pass. This function returns a new result type QkSabreLayoutResult that contains the output circuit along with the layouts from running the pass. Fixes Qiskit#14449 * Improve testing * Fix tests * Fix docs * Finish API docs for standalone function * Pivot interface to use TranspileLayout This commit reworks the C interface for sabre layout to use TranspileLayout for the return type remove the Sabre result type. It was largely duplicative and it's better to just use the same interface. * Remove stale references to QkSabreLayoutResult in docs * Fix test memory leaks * Make input to pass function an options struct * Set number of input qubits correctly in output layout * Move helper functions to common.c * Use a stack variable instructions in compare_circuits() * Fix output_permutation in the return * Apply suggestions from code review Co-authored-by: Jake Lishman <jake@binhbar.com> * Update docstring for circuit arg * Don't use PyErr debug or display * Add missing newlines to test failure strings * Update error messages about output permutation test failures * Correct permutation variable names * Correct copyright header --------- Co-authored-by: Jake Lishman <jake@binhbar.com> Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Summary
This commit adds a standalone transpiler pass function to the C API for
running the SabreLayout transpiler pass. This function returns a
new result type QkSabreLayoutResult that contains the output circuit
along with the layouts from running the pass.
Details and comments
Fixes #14449
TODO:
This PR is based on top of #14668 and #14715 and will need to be rebased once those two PRs merge. In the meantime you can review just the contents of this PR by looking at the HEAD commit on the PR: d1aff29 and d088960This has been rebased now.