diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 0f21146..606761b 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -4,7 +4,7 @@ use std::{ ffi::{CStr, CString}, - path::{Path, PathBuf}, + path::PathBuf, str::Utf8Error, sync::Once, }; @@ -22,7 +22,7 @@ pub(crate) mod bindings { static START: Once = Once::new(); #[derive(Debug, thiserror::Error)] -enum Error { +pub enum Error { #[error("Could not find libquil core file. Set the LIBQUIL_CORE_PATH environment variable.")] CoreFileNotFound, } @@ -45,13 +45,11 @@ fn find_core_file() -> Result { } /// Initializes libquil using it's core image. No-op after the first call. -pub(crate) fn init_libquil() { +pub(crate) fn init_libquil() -> Result<(), Error> { + let core_path = find_core_file()?; + START.call_once(|| { - let path = match find_core_file() { - Ok(path) => path, - Err(e) => panic!("{e}"), - }; - let ptr = CString::new(path).unwrap().into_raw(); + let ptr = CString::new(core_path).unwrap().into_raw(); unsafe { // The library built by maturin does link to libquil, but @@ -67,7 +65,9 @@ pub(crate) fn init_libquil() { bindings::init(ptr); let _ = CString::from_raw(ptr); } - }) + }); + + Ok(()) } pub(crate) fn handle_libquil_error(errno: libquil_error_t) -> Result<(), String> { diff --git a/lib/src/quilc.rs b/lib/src/quilc.rs index a6f795b..bd451ca 100644 --- a/lib/src/quilc.rs +++ b/lib/src/quilc.rs @@ -41,6 +41,8 @@ pub enum Error { ProgramString(String), #[error("invalid UTF-8 program: {0}")] ProgramUtf8(#[from] std::str::Utf8Error), + #[error("failed to initialize libquil: {0}")] + FailedToInitializeLibquil(#[from] crate::Error), } /// A quilc chip specification #[derive(Clone, Debug)] @@ -54,7 +56,7 @@ impl TryFrom for Chip { type Error = Error; fn try_from(json: CString) -> Result { - crate::init_libquil(); + crate::init_libquil()?; let ptr = json.into_raw(); let mut chip: chip_specification = std::ptr::null_mut(); @@ -97,7 +99,7 @@ impl TryFrom for Program { type Error = Error; fn try_from(program: CString) -> Result { - init_libquil(); + init_libquil()?; let ptr = program.into_raw(); let mut parsed_program: quil_program = std::ptr::null_mut(); @@ -128,7 +130,7 @@ impl Drop for Program { impl Program { pub fn to_string(&self) -> Result { - init_libquil(); + init_libquil()?; unsafe { let mut program_string_ptr: *mut std::os::raw::c_char = std::ptr::null_mut(); @@ -145,7 +147,7 @@ impl Program { /// Compiles the [`Program`] for the given [`Chip`] pub fn compile_program(program: &Program, chip: &Chip) -> Result { - init_libquil(); + init_libquil()?; let mut compiled_program: quil_program = std::ptr::null_mut(); unsafe { @@ -238,7 +240,8 @@ pub struct CompilationResult { /// Compiles the [`Program`] for the given [`Chip`] and restricts /// the resulting [`Program`] to satisfy "protoquil" constraints pub fn compile_protoquil(program: &Program, chip: &Chip) -> Result { - init_libquil(); + init_libquil()?; + let mut compiled_program: quil_program = std::ptr::null_mut(); let metadata_ptr: quilc_compilation_metadata = std::ptr::null_mut(); @@ -265,7 +268,8 @@ pub fn compile_protoquil(program: &Program, chip: &Chip) -> Result Result { - init_libquil(); + init_libquil()?; + let mut chip: chip_specification = std::ptr::null_mut(); unsafe { @@ -278,7 +282,7 @@ pub fn get_chip() -> Result { /// Prints the given [`Program`] to stdout pub fn print_program(program: &Program) -> Result<(), Error> { - init_libquil(); + init_libquil()?; unsafe { let err = quilc_print_program.unwrap()(program.0); @@ -299,7 +303,7 @@ pub fn conjugate_pauli_by_clifford( pauli_terms: Vec, clifford: &Program, ) -> Result { - init_libquil(); + init_libquil()?; unsafe { let mut phase = 0; @@ -336,7 +340,7 @@ pub fn generate_rb_sequence( seed: Option, interleaver: Option<&Program>, ) -> Result>, Error> { - init_libquil(); + init_libquil()?; let mut gateset = gateset.iter().map(|p| p.0).collect::>(); let mut results_ptr: *mut std::ffi::c_int = std::ptr::null_mut(); @@ -399,7 +403,7 @@ impl Display for VersionInfo { } pub fn get_version_info() -> Result { - init_libquil(); + init_libquil()?; unsafe { let mut version_info: quilc_version_info = std::ptr::null_mut(); diff --git a/lib/src/qvm.rs b/lib/src/qvm.rs index 5f87af6..250a2d7 100644 --- a/lib/src/qvm.rs +++ b/lib/src/qvm.rs @@ -28,6 +28,8 @@ pub enum Error { Wavefunction(String), #[error("failed to perform expectation: {0}")] Expectation(String), + #[error("failed to initialize libquil: {0}")] + FailedToInitializeLibquil(#[from] crate::Error), } #[derive(Debug)] @@ -43,7 +45,7 @@ impl Display for VersionInfo { } pub fn get_version_info() -> Result { - init_libquil(); + init_libquil()?; unsafe { let mut version_info: qvm_version_info = std::ptr::null_mut(); @@ -144,7 +146,7 @@ pub fn multishot( ) -> Result>>, Error> { let mut multishot = HashMap::new(); - init_libquil(); + init_libquil()?; let addresses: QvmMultishotAddresses = addresses.try_into()?; let mut result_ptr: qvm_multishot_result = std::ptr::null_mut(); @@ -205,7 +207,7 @@ pub fn multishot_measure( qubits: &[i32], trials: i32, ) -> Result>, Error> { - init_libquil(); + init_libquil()?; // NOTE(mgsk): There might be a way for this to be a Vec> // which would exactly match our return type. In practice, however, @@ -237,7 +239,7 @@ pub fn wavefunction( program: &quilc::Program, n_qubits: u32, ) -> Result, Error> { - init_libquil(); + init_libquil()?; let mut wavefunction = vec![0.0; 2 * 2u32.pow(n_qubits) as usize]; @@ -259,7 +261,7 @@ pub fn wavefunction( /// of finding the quantum state in `|i>`. For example, `v[2]` is the probability /// of finding the quantum state in `|10>`; `v[5]` the probability of `|101>`; etc. pub fn probabilities(program: &quilc::Program, n_qubits: u32) -> Result, Error> { - init_libquil(); + init_libquil()?; let mut probabilities = vec![0.0; 2u32.pow(n_qubits) as usize]; @@ -277,7 +279,7 @@ pub fn expectation( program: &quilc::Program, operators: Vec<&quilc::Program>, ) -> Result, Error> { - init_libquil(); + init_libquil()?; unsafe { let mut expectations = vec![0.0; operators.len()];