quest-sys 0.16.1

Bindings to the QuEST quantum computer simulator C library
Documentation
// use cmake::Config;
use std::fs;
use std::path::PathBuf;
use std::{env, path::Path};

fn main() {
    let out_dir_path = PathBuf::from(env::var("OUT_DIR").expect("Cannot find OUT_DIR"));
    #[cfg(feature = "rebuild")]
    let out_dir_path_rebuild = out_dir_path.clone();
    let quest_library_path = build_with_cc(out_dir_path);

    println!(
        "cargo:rustc-link-search=native={}",
        quest_library_path.display()
    );
    println!("cargo:rustc-link-lib=static=QuEST");
    println!("cargo:rerun-if-changed=wrapper.h");

    // list functions for which bindings should be created
    #[cfg(feature = "rebuild")]
    let builder = bindgen::Builder::default()
        .header("wrapper.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .allowlist_function("copyStateFromGPU")
        .allowlist_function("copyStateToPU")
        .allowlist_function("create.*")
        .allowlist_function("cloneQureg")
        .allowlist_function("destroy.*")
        .allowlist_function("report.*")
        .allowlist_function("get.*")
        .allowlist_function("set.*")
        .allowlist_function("setComplexMatrixN")
        .allowlist_function("initStateFromAmps")
        .allowlist_function("report.*")
        .allowlist_function("rotate.*")
        .allowlist_function("report.*")
        .allowlist_function("controlled.*")
        .allowlist_function("report.*")
        .allowlist_function("multiControlled.*")
        .allowlist_function("report.*")
        .allowlist_function("pauli.*")
        .allowlist_function("report.*")
        .allowlist_function("init.*")
        .allowlist_function("report.*")
        .allowlist_function("mix.*")
        .allowlist_function("sGate")
        .allowlist_function("tGate")
        .allowlist_function("phaseShift")
        .allowlist_function("compactUnitary")
        .allowlist_function("apply.*")
        .allowlist_function("hadamard*")
        .allowlist_function("swapGate")
        .allowlist_function("unitary")
        .allowlist_function("twoQubitUnitary")
        .allowlist_function("multiQubitUnitary")
        .allowlist_function("measure")
        .allowlist_function("statevec_twoQubitUnitary")
        .allowlist_function("calc.*")
        .allowlist_function("seedQuEST");

    #[cfg(feature = "rebuild")]
    let bindings = builder.generate().expect("Unable to generate bindings");

    #[cfg(feature = "rebuild")]
    bindings
        .write_to_file(out_dir_path_rebuild.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

fn build_with_cc(out_dir: PathBuf) -> PathBuf {
    let base_path = Path::new("QuEST").join("QuEST");
    let src_path = base_path.join("src");
    let include_path = base_path.join("include");
    let mut files = vec![
        src_path.join("QuEST.c"),
        src_path.join("QuEST_common.c"),
        src_path.join("QuEST_qasm.c"),
        src_path.join("QuEST_validation.c"),
        src_path.join("mt19937ar.c"),
    ];
    let out_path = out_dir.join("build");
    fs::create_dir_all(out_path.clone()).expect("Cannot create directory for x86_64 library");

    #[cfg(not(feature = "cuda"))]
    #[cfg(not(feature = "cuquantum"))]
    {
        files.push(src_path.join("CPU").join("QuEST_cpu.c"));
        files.push(src_path.join("CPU").join("QuEST_cpu_local.c"));

        #[cfg(target_arch = "x86_64")]
        cc::Build::new()
            .include(src_path)
            .include(include_path)
            .files(files)
            .define("MULTITHREADED", "0")
            .opt_level(2)
            .debug(false)
            .warnings(false)
            .static_flag(true)
            .out_dir(out_path.clone())
            .flag("-std=c99")
            .flag("-mavx")
            .compile("QuEST");
        #[cfg(not(target_arch = "x86_64"))]
        cc::Build::new()
            .include(src_path)
            .include(include_path)
            .files(files)
            .define("MULTITHREADED", "0")
            .opt_level(2)
            .debug(false)
            .warnings(false)
            .static_flag(true)
            .out_dir(out_path.clone())
            .flag("-std=c99")
            .compile("QuEST");
    }

    #[cfg(feature = "cuda")]
    {
        if cfg!(feature = "cuquantum") {
            panic!("cuQuantum feature is different from CUDA feature. Choose one.");
        }
        files.push(src_path.join("GPU").join("QuEST_gpu.cu"));
        files.push(src_path.join("GPU").join("QuEST_gpu_common.cu"));
        cc::Build::new()
            .include(src_path.clone())
            .include(include_path.clone())
            .files(files.clone())
            .define("MULTITHREADED", "0")
            .define("USE_HID", "0")
            .opt_level(2)
            .debug(false)
            .warnings(false)
            .out_dir(out_path.clone())
            .cuda(true)
            .compile("QuEST");
    }
    #[cfg(feature = "cuquantum")]
    {
        if cfg!(feature = "cuda") {
            panic!("cuQuantum feature is different from CUDA feature. Choose one.");
        }
        let custate_include_lib = PathBuf::from(
            env::var("CUSTATE_INCLUDE_DIR")
                .expect("Cannot find CUSTATE_INCLUDE_DIR. Needs to be given explicitely."),
        );

        println!("cargo:rustc-link-lib=custatevec");

        files.push(src_path.join("GPU").join("QuEST_cuQuantum.cu"));
        files.push(src_path.join("GPU").join("QuEST_gpu_common.cu"));
        cc::Build::new()
            .include(src_path)
            .include(include_path)
            .include(custate_include_lib)
            .files(files)
            .define("MULTITHREADED", "0")
            .define("USE_CUQUANTUM", "1")
            .define("USE_HID", "0")
            .opt_level(2)
            .debug(false)
            .warnings(false)
            .out_dir(out_path.clone())
            .cuda(true)
            .compile("QuEST");
    }
    out_path
}

// fn standard_cmake_build() -> PathBuf {
//     // use CMake to build quest and return path where the static library is placed
//     let partial_quest_path = PathBuf::from("QuEST").join("QuEST");
//     #[cfg(feature = "openmp")]
//     let quest_library_path = Config::new(partial_quest_path)
//         .no_build_target(true)
//         .very_verbose(true)
//         .always_configure(true)
//         // activated openmp mulit-threading
//         .define("MULTITHREADED", "1")
//         // .define("CMAKE_C_COMPILER", "clang")
//         .build()
//         .join("build");

//     #[cfg(not(feature = "openmp"))]
//     let quest_library_path =
//         Config::new(partial_quest_path)
//         .no_build_target(true)
//         .very_verbose(true)
//         .always_configure(true)
//         // .define("CMAKE_OSX_ARCHITECTURES","x86_64;arm64")
//         // deactivates multi-threading
//         .define("MULTITHREADED", "0")
//         .build()
//         .join("build");
//     #[cfg(not( target_os = "windows"))]
//     return quest_library_path;
//     #[cfg( target_os = "windows")]
//     match env::var("PROFILE").expect("Cannot find PROFILE env variable").as_str(){
//         "debug" => {return quest_library_path.join("Debug");},
//         "release" => {return quest_library_path.join("Release");}
//         _ => {panic!("Profile is not debug or release. Correct windows library location not known.")}
//     }

// }