diff --git a/.gitignore b/.gitignore index 69c11f92d705..bd73f4508758 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ __pycache__/ *.so # The C API header is generated automatically, therefore we do not upload it. -qiskit.h +qiskit.h # Distribution / packaging .Python @@ -145,6 +145,7 @@ qiskit/quantum_info/states/cython/*.cpp /docs/stubs /docs/locale /executed_tutorials +/docs/xml # Notebook testing images test/visual/mpl/circuit/circuit_results/*.png diff --git a/Makefile b/Makefile index fc18e72bc116..4922bd151ba7 100644 --- a/Makefile +++ b/Makefile @@ -82,10 +82,14 @@ coverage_erase: clean: coverage_erase ; +# Run clang-format +cformat: + sh tools/run_clang_format.sh + # Build C API crate and header cbuild: cargo build --release --no-default-features --features cbinding - cbindgen --crate qiskit-c-ext --output test/c/qiskit.h --lang C + cbindgen --crate qiskit-c-ext --output qiskit.h --lang C # Use ctest to run C API tests ctest: cbuild diff --git a/crates/c_ext/cbindgen.toml b/crates/c_ext/cbindgen.toml index 6673cbb3bcf6..5d7971faadac 100644 --- a/crates/c_ext/cbindgen.toml +++ b/crates/c_ext/cbindgen.toml @@ -10,4 +10,4 @@ parse_deps = true include = ["qiskit-accelerate"] [enum] -prefix_with_name = true \ No newline at end of file +prefix_with_name = true diff --git a/crates/c_ext/src/sparse_observable.rs b/crates/c_ext/src/sparse_observable.rs index d9cd77046998..dd01f59808b8 100644 --- a/crates/c_ext/src/sparse_observable.rs +++ b/crates/c_ext/src/sparse_observable.rs @@ -13,6 +13,7 @@ use num_complex::Complex64; use qiskit_accelerate::sparse_observable::{BitTerm, SparseObservable, SparseTerm}; +/// @ingroup PauliTermVec /// A Pauli term vector, containing ``(index, bit_term)`` tuples. #[derive(Debug, Clone)] pub struct PauliTermVec { @@ -22,6 +23,7 @@ pub struct PauliTermVec { bit_terms: Vec, } +/// @ingroup PauliTerm /// A struct representing a (Pauli, qubit index) tuple. #[repr(C)] pub struct PauliTerm { @@ -29,17 +31,18 @@ pub struct PauliTerm { index: u32, } +/// @ingroup PauliTerm /// Free the Pauli term. /// /// @param A pointer to the Pauli term struct. /// /// Example: -/// +/// /// SparseObservable *obs = obs_zero(100); /// PauliTermVec *paulis = paulis_new(); /// paulis_push(paulis, BitTerm_X, 99); // push X_99 onto the vector /// obs_push_consume(obs, paulis, 2.0); // add the Pauli term 2 * X_99 to the observable -/// +/// /// SparseTerm *term = obs_term(obs, 0); // get the 0th Pauli term in the observable /// PauliTerm *pauli = obsterm_pauli(term, 0); // get the 0th Pauli in the Pauli term /// printf("Bit term: %i, Index: %i", pauli->bit_term, pauli->index); @@ -54,6 +57,7 @@ pub extern "C" fn pauli_free(pauli: &mut PauliTerm) { } } +/// @ingroup PauliTermVec /// Create a new Pauli term vector. /// /// @return A pointer to an empty Pauli term vector. @@ -72,6 +76,7 @@ pub extern "C" fn paulis_new() -> *mut PauliTermVec { Box::into_raw(Box::new(paulis)) } +/// @ingroup PauliTermVec /// Create a new Pauli term vector, with a given capacity. /// /// @param capacity The capacity to allocate for the vector. @@ -93,6 +98,7 @@ pub extern "C" fn paulis_with_capacity(capacity: u64) -> *mut PauliTermVec { Box::into_raw(Box::new(paulis)) } +/// @ingroup PauliTermVec /// Free the Pauli term vector. /// /// @param paulis A pointer to the index vector to be freed. @@ -111,6 +117,7 @@ pub extern "C" fn paulis_free(paulis: &mut PauliTermVec) { } } +/// @ingroup PauliTermVec /// Push a new ``(bit_term, index)`` tuple onto the Pauli term vector. /// /// @param paulis A pointer to the Pauli term vector. @@ -129,6 +136,7 @@ pub extern "C" fn paulis_push(paulis: &mut PauliTermVec, bit_term: BitTerm, inde paulis.indices.push(index); } +/// @ingroup SparseObservable /// Construct the zero observable (without any terms). /// /// @param num_qubits The number of qubits the observable is defined on. @@ -146,6 +154,7 @@ pub extern "C" fn obs_zero(num_qubits: u32) -> *mut SparseObservable { Box::into_raw(Box::new(obs)) } +/// @ingroup SparseObservable /// Construct the identity observable. /// /// @param num_qubits The number of qubits the observable is defined on. @@ -163,6 +172,7 @@ pub extern "C" fn obs_identity(num_qubits: u32) -> *mut SparseObservable { Box::into_raw(Box::new(obs)) } +/// @ingroup SparseObservable /// Deallocate the observable. /// /// Memory deallocation is user responsibility. Every constructed observable @@ -183,13 +193,14 @@ pub extern "C" fn obs_free(obs: &mut SparseObservable) { } } +/// @ingroup SparseObservable /// @brief Add a term to the observable by copy. /// /// @param obs A pointer to the observable to which the term is added. /// @param paulis The Pauli term vector to add to the observable. /// @param coeff The coefficient of the term. /// -/// Example: +/// Example: /// /// u_int32_t num_qubits = 100; /// SparseObservable *obs = obs_zero(num_qubits); @@ -223,6 +234,7 @@ pub extern "C" fn obs_push_copy( obs.add_term(term.view()).unwrap(); } +/// @ingroup SparseObservable /// @brief Add a term to the observable and deallocate the memory for the indices and bit terms. /// /// @param obs A pointer to the observable to which the term is added. @@ -231,7 +243,7 @@ pub extern "C" fn obs_push_copy( /// /// @warning Panics if an index in the Pauli term is greater equal than the number of qubits. /// -/// Example: +/// Example: /// /// u_int32_t num_qubits = 100; /// SparseObservable *obs = obs_zero(num_qubits); @@ -267,6 +279,7 @@ pub extern "C" fn obs_push_consume( obs.add_term(term.view()).unwrap(); // TODO handle error } +/// @ingroup SparseObservable /// Get an observable term. /// /// @param obs A pointer to the observable. @@ -279,7 +292,7 @@ pub extern "C" fn obs_push_consume( /// SparseObservable *obs = obs_identity(100); /// SparseTerm *term = obs_term(obs, 0); /// // out-of-bounds indices will fail -/// // SparseTerm *will_fail = obs_term(obs, 1); +/// // SparseTerm *will_fail = obs_term(obs, 1); /// #[no_mangle] #[cfg(feature = "cbinding")] @@ -291,13 +304,14 @@ pub extern "C" fn obs_term(obs: &SparseObservable, index: u64) -> *mut SparseTer Box::into_raw(Box::new(term)) } +/// @ingroup SparseObservable /// Multiply the observable by a complex coefficient. /// /// @param obs A pointer to the observable. /// @param coeff The coefficient to multiply the observable with. /// /// Example: -/// +/// /// SparseObservable *obs = obs_identity(100); /// SparseObservable *result = obs_multiply(obs, 2); #[no_mangle] @@ -307,6 +321,7 @@ pub extern "C" fn obs_multiply(obs: &SparseObservable, coeff: &Complex64) -> *mu Box::into_raw(Box::new(result)) } +/// @ingroup SparseObservable /// Add two observables. /// /// @param left A pointer to the left observable. @@ -315,7 +330,7 @@ pub extern "C" fn obs_multiply(obs: &SparseObservable, coeff: &Complex64) -> *mu /// @return A pointer to the result ``left + right``. /// /// Example: -/// +/// /// SparseObservable *left = obs_identity(100); /// SparseObservable *right = obs_zero(100); /// SparseObservable *result = obs_add(left, right); @@ -330,6 +345,7 @@ pub extern "C" fn obs_add( Box::into_raw(Box::new(result)) } +/// @ingroup SparseObservable /// Calculate the canonical representation of the observable. /// /// @param obs A pointer to the observable. @@ -355,6 +371,7 @@ pub extern "C" fn obs_canonicalize( Box::into_raw(Box::new(result)) } +/// @ingroup SparseObservable /// Copy the observable. /// /// @param obs A pointer to the observable. @@ -373,6 +390,7 @@ pub extern "C" fn obs_copy(obs: &SparseObservable) -> *mut SparseObservable { Box::into_raw(Box::new(copied)) } +/// @ingroup SparseObservable /// Compare two observables for equality. /// /// Note that this does not compare mathematical equality, but data equality. This means @@ -394,6 +412,8 @@ pub extern "C" fn obs_copy(obs: &SparseObservable) -> *mut SparseObservable { pub extern "C" fn obs_equal(observable: &SparseObservable, other: &SparseObservable) -> bool { observable.eq(other) } + +/// @ingroup SparseObservable /// Get the number of terms in the observable. /// /// @param observable A pointer to the observable. @@ -411,6 +431,7 @@ pub extern "C" fn obs_num_terms(observable: &SparseObservable) -> u64 { observable.num_terms() as u64 } +/// @ingroup SparseObservable /// Get the number of qubits the observable is defined on. /// /// @param observable A pointer to the observable. @@ -428,6 +449,7 @@ pub extern "C" fn obs_num_qubits(observable: &SparseObservable) -> u32 { observable.num_qubits() } +/// @ingroup SparseObservable /// Print the observable. /// /// @param term A pointer to the ``SparseObservable`` to print. @@ -443,6 +465,7 @@ pub extern "C" fn obs_print(observable: &SparseObservable) { println!("{:?}", observable); } +/// @ingroup SparseTerm /// Deallocate the term. /// /// The term is **not** automatically deallocated if the observable it @@ -466,12 +489,13 @@ pub extern "C" fn obsterm_free(term: &mut SparseTerm) { } } +/// @ingroup SparseTerm /// Print a sparse term. /// /// @param term A pointer to the ``SparseTerm`` to print. /// /// Example: -/// +/// /// SparseObservable *obs = obs_identity(100); /// SparseTerm *term = obs_term(obs, 0); /// obsterm_print(term); @@ -482,6 +506,7 @@ pub extern "C" fn obsterm_print(term: &SparseTerm) { println!("{:?}", term); } +/// @ingroup SparseTerm /// Get the coefficient of a sparse term. /// /// @param term A pointer to the ``SparseTerm`` whose coefficient is returned. @@ -489,7 +514,7 @@ pub extern "C" fn obsterm_print(term: &SparseTerm) { /// @return The complex coefficient of the sparse term. /// /// Example: -/// +/// /// SparseObservable *obs = obs_identity(100); /// SparseTerm *term = obs_term(obs, 0); /// complex double coeff = obsterm_coeff(term); @@ -500,6 +525,7 @@ pub extern "C" fn obsterm_coeff(term: &SparseTerm) -> Complex64 { term.coeff() } +/// @ingroup SparseTerm /// Get the number of qubits the sparse term is defined on. /// /// @param term A pointer to the ``SparseTerm`` whose number of qubits is returned. @@ -507,7 +533,7 @@ pub extern "C" fn obsterm_coeff(term: &SparseTerm) -> Complex64 { /// @return The number of qubits the sparse term is defined on. /// /// Example: -/// +/// /// SparseObservable *obs = obs_identity(100); /// SparseTerm *term = obs_term(obs, 0); /// uint32_t num_qubits = obsterm_num_qubits(term); @@ -518,6 +544,7 @@ pub extern "C" fn obsterm_num_qubits(term: &SparseTerm) -> u32 { term.num_qubits() } +/// @ingroup SparseTerm /// Get the number of non-identity (nni) Paulis in the sparse term. /// /// @param term A pointer to the ``SparseTerm``. @@ -525,7 +552,7 @@ pub extern "C" fn obsterm_num_qubits(term: &SparseTerm) -> u32 { /// @return The number of non-identity Paulis. /// /// Example: -/// +/// /// SparseObservable *obs = obs_identity(100); /// SparseTerm *term = obs_term(obs, 0); /// uint32_t nni = obsterm_nni(term); @@ -537,6 +564,7 @@ pub extern "C" fn obsterm_nni(term: &SparseTerm) -> u32 { term.indices().len() as u32 } +/// @ingroup SparseTerm /// Get the (Pauli, qubit index) tuple inside term. /// /// @param term A pointer to the ``SparseTerm``. @@ -545,7 +573,7 @@ pub extern "C" fn obsterm_nni(term: &SparseTerm) -> u32 { /// @return The Pauli and qubit index it acts on as struct. /// /// Example: -/// +/// /// SparseObservable *obs = obs_identity(100); /// SparseTerm *term = obs_term(obs, 0); /// uint32_t nni = obsterm_nni(term); diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 000000000000..a37630dc0a84 --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,9 @@ +INPUT = docs/cdoc/ qiskit.h + +EXTRACT_ALL = YES + +GENERATE_XML = YES +XML_OUTPUT = docs/xml + +GENERATE_HTML = NO +GENERATE_LATEX = NO diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index 8581d56ace77..9bec3563a412 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -3,9 +3,9 @@ Within each section, the modules should be ordered alphabetically by module name (not RST filename). -============= -API Reference -============= +==================== +Python API Reference +==================== Circuit construction: diff --git a/docs/cdoc/index.rst b/docs/cdoc/index.rst new file mode 100644 index 000000000000..ce28556214ba --- /dev/null +++ b/docs/cdoc/index.rst @@ -0,0 +1,10 @@ +=========================== +Qiskit C API (``qiskit.h``) +=========================== + +Quantum information: + +.. toctree:: + :maxdepth: 1 + + sparse_observable diff --git a/docs/cdoc/sparse_observable.h b/docs/cdoc/sparse_observable.h new file mode 100644 index 000000000000..ff74578547a7 --- /dev/null +++ b/docs/cdoc/sparse_observable.h @@ -0,0 +1,19 @@ +/** + * @defgroup SparseObservable SparseObservable + * This is a group of functions for interacting with an opaque (Rust-space) SparseObservable + * instance. + */ + +/** + * @defgroup SparseTerm SparseTerm + * This is a group of functions for interacting with an opaque (Rust-space) SparseTerm + * instance. + */ + +/** + * @defgroup PauliTerm PauliTerm + */ + +/** + * @defgroup PauliTermVec PauliTermVec + */ diff --git a/docs/cdoc/sparse_observable.rst b/docs/cdoc/sparse_observable.rst new file mode 100644 index 000000000000..5208dc949154 --- /dev/null +++ b/docs/cdoc/sparse_observable.rst @@ -0,0 +1,11 @@ +================= +Sparse Observable +================= + +.. doxygengroup:: SparseObservable + +.. doxygengroup:: SparseTerm + +.. doxygengroup:: PauliTerm + +.. doxygengroup:: PauliTermVec diff --git a/docs/conf.py b/docs/conf.py index 3bd1fd6516de..aaa87e1e2f18 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,8 +49,13 @@ "matplotlib.sphinxext.plot_directive", "reno.sphinxext", "sphinxcontrib.katex", + "breathe", ] +breathe_projects = {"qiskit": "xml/"} + +breathe_default_project = "qiskit" + templates_path = ["_templates"] # Number figures, tables and code-blocks if they have a caption. diff --git a/docs/index.rst b/docs/index.rst index 5105a1b6d7a9..60f14b0c5d20 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,5 +11,6 @@ https://github.com/Qiskit/documentation. :hidden: Documentation Home - API Reference + C API Reference + Python API Reference Release Notes diff --git a/requirements-dev.txt b/requirements-dev.txt index 7c5a909bd395..22e0910f24c6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -47,3 +47,4 @@ threadpoolctl Sphinx>=6.0,<7.2 reno >= 4.1.0 sphinxcontrib-katex==0.9.9 +breathe>=4.35.0 diff --git a/test/c/CMakeLists.txt b/test/c/CMakeLists.txt index a8f0d2ea8fdd..6a67c1826b83 100644 --- a/test/c/CMakeLists.txt +++ b/test/c/CMakeLists.txt @@ -21,6 +21,8 @@ create_test_sourcelist (source_files test_driver.c ${discovered_tests}) # Actually define the test driver program executable to be built... add_executable (test_driver ${source_files}) +# ...include the location of the header file... +target_include_directories (test_driver PRIVATE ${CMAKE_SOURCE_DIR}) # ...and linked with the qiskit library. target_link_libraries (test_driver ${qiskit}) diff --git a/test/c/common.h b/test/c/common.h index 7b460b6c93ba..7ae014b8ea87 100644 --- a/test/c/common.h +++ b/test/c/common.h @@ -10,7 +10,7 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -#include +#include // An enumeration of test results. These should be returned by test functions to // indicate what kind of error occurred. This will be used to produce more @@ -27,20 +27,17 @@ enum TestResult { // A function to run a test function of a given name. This function will also // post-process the returned `TestResult` to product a minimal info message for // the developer running the test suite. -int run(const char* name, int (*test_function)(void)) -{ +int run(const char *name, int (*test_function)(void)) { // TODO: we could consider to change the return value of our test functions // to be a struct containing the integer return value and a custom error // message which could then be included below. int result = test_function(); int did_fail = 1; - char* msg; - if (result == Ok) - { + char *msg; + if (result == Ok) { did_fail = 0; msg = "Ok"; - } else if (result == EqualityError) - { + } else if (result == EqualityError) { msg = "FAILED with an EqualityError"; } else { msg = "FAILED with unknown error"; diff --git a/test/c/test_sparse_observable.c b/test/c/test_sparse_observable.c index c66549de604a..3ef6691114fd 100644 --- a/test/c/test_sparse_observable.c +++ b/test/c/test_sparse_observable.c @@ -274,7 +274,6 @@ int test_sparse_observable() { num_failed += RUN_TEST(test_identity); num_failed += RUN_TEST(test_add); num_failed += RUN_TEST(test_mult); - fflush(stderr); num_failed += RUN_TEST(test_canonicalize); num_failed += RUN_TEST(test_copy); num_failed += RUN_TEST(test_num_terms); @@ -282,6 +281,7 @@ int test_sparse_observable() { num_failed += RUN_TEST(test_custom_build); num_failed += RUN_TEST(test_term); + fflush(stderr); fprintf(stderr, "=== Number of failed subtests: %i\n", num_failed); return num_failed; diff --git a/tools/run_clang_format.sh b/tools/run_clang_format.sh new file mode 100644 index 000000000000..e763ea63cf90 --- /dev/null +++ b/tools/run_clang_format.sh @@ -0,0 +1,23 @@ +#! /bin/bash + +if [ -z "$1" ]; then + args="--dry-run --Werror" # do a dry run +elif [ "$1" = "apply" ]; then + args="-i" # inplace change of the files +else + # any other argument is invalid + echo "Invalid argument, either no arguments or 'apply' is supported, not: $1" + exit 1 +fi + +# this is the style file -- note that this script should +# be run from root, such that this file is correctly found +style=".clang-format" + +# get all tracked files in HEAD, and filter for files ending in .c or .h +files=$(git ls-tree --name-only -r HEAD | grep ".*\.[c,h]$") + +# apply clang format on all files +for file in $files; do + clang-format --style="file:$style" $args $file +done \ No newline at end of file diff --git a/tox.ini b/tox.ini index d4d822766675..0cb4f1ba6861 100644 --- a/tox.ini +++ b/tox.ini @@ -88,6 +88,7 @@ setenv = commands_pre = {[testenv]install_command} -r{toxinidir}/requirements-optional.txt commands = + doxygen docs/Doxyfile sphinx-build -W -j auto -T --keep-going -b html docs/ docs/_build/html {posargs} [testenv:docs-clean] @@ -96,4 +97,4 @@ deps = allowlist_externals = rm commands = - rm -rf {toxinidir}/docs/stubs/ {toxinidir}/docs/_build {toxinidir}/docs/locale + rm -rf {toxinidir}/docs/stubs/ {toxinidir}/docs/_build {toxinidir}/docs/locale {toxinidir}/docs/xml/