dqcsim 0.0.4

DQCsim: Delft Quantum Classical Simulator
Documentation
use super::*;
use crate::host::accelerator::Accelerator;

/// Constructs a DQCsim simulation.
///>
///> The provided handle is consumed if it is a simulation configuration,
///> regardless of whether simulation construction succeeds.
#[no_mangle]
pub extern "C" fn dqcs_sim_new(scfg: dqcs_handle_t) -> dqcs_handle_t {
    api_return(0, || {
        take!(scfg as SimulatorConfiguration);
        API_STATE.with(|state| state.borrow().thread_locals_assert_free())?;
        let sim = Simulator::new(scfg)?;
        let sim = insert(sim);
        API_STATE.with(|state| state.borrow_mut().thread_locals_claim(sim).unwrap());
        Ok(sim)
    })
}

/// Starts a program on the simulated accelerator.
///>
///> This is an asynchronous call: nothing happens until `yield()`,
///> `recv()`, or `wait()` is called.
///>
///> The `ArbData` handle is optional; if 0 is passed, an empty data object is
///> used. If a handle is passed, it is consumed if and only if the API call
///> succeeds.
#[no_mangle]
pub extern "C" fn dqcs_sim_start(sim: dqcs_handle_t, data: dqcs_handle_t) -> dqcs_return_t {
    api_return_none(|| {
        resolve!(sim as &mut Simulator);
        resolve!(data as pending ArbData);
        let data_ob = {
            let x: &ArbData = data.as_ref().unwrap();
            x.clone()
        };
        sim.simulation.start(data_ob)?;
        delete!(resolved data);
        Ok(())
    })
}

/// Waits for the simulated accelerator to finish its current program.
///>
///> When this succeeds, the return value of the accelerator's `run()`
///> function is returned in the form of a new handle. When it fails, 0 is
///> returned.
///>
///> Deadlocks are detected and prevented by returning an error.
#[no_mangle]
pub extern "C" fn dqcs_sim_wait(sim: dqcs_handle_t) -> dqcs_handle_t {
    api_return(0, || {
        resolve!(sim as &mut Simulator);
        Ok(insert(sim.simulation.wait()?))
    })
}

/// Sends a message to the simulated accelerator.
///>
///> This is an asynchronous call: nothing happens until `yield()`,
///> `recv()`, or `wait()` is called.
///>
///> The `ArbData` handle is optional; if 0 is passed, an empty data object is
///> used. If a handle is passed, it is consumed if and only if the API call
///> succeeds.
#[no_mangle]
pub extern "C" fn dqcs_sim_send(sim: dqcs_handle_t, data: dqcs_handle_t) -> dqcs_return_t {
    api_return_none(|| {
        resolve!(sim as &mut Simulator);
        resolve!(data as pending ArbData);
        let data_ob = {
            let x: &ArbData = data.as_ref().unwrap();
            x.clone()
        };
        sim.simulation.send(data_ob)?;
        delete!(resolved data);
        Ok(())
    })
}

/// Waits for the simulated accelerator to send a message to us.
///>
///> When this succeeds, the received data is returned in the form of a new
///> handle. When it fails, 0 is returned.
///>
///> Deadlocks are detected and prevented by returning an error.
#[no_mangle]
pub extern "C" fn dqcs_sim_recv(sim: dqcs_handle_t) -> dqcs_handle_t {
    api_return(0, || {
        resolve!(sim as &mut Simulator);
        Ok(insert(sim.simulation.recv()?))
    })
}

/// Yields to the simulator.
///>
///> The simulation runs until it blocks again. This is useful if you want an
///> immediate response to an otherwise asynchronous call through the logging
///> system or some communication channel outside of DQCsim's control.
///>
///> This function silently returns immediately if no asynchronous data was
///> pending or if the simulator is waiting for something that has not been
///> sent yet.
#[no_mangle]
pub extern "C" fn dqcs_sim_yield(sim: dqcs_handle_t) -> dqcs_return_t {
    api_return_none(|| {
        resolve!(sim as &mut Simulator);
        sim.simulation.yield_to_accelerator()?;
        Ok(())
    })
}

/// Sends an `ArbCmd` message to one of the plugins, referenced by name.
///>
///> `ArbCmd`s are executed immediately after yielding to the simulator, so
///> all pending asynchronous calls are flushed and executed *before* the
///> `ArbCmd`.
///>
///> When this succeeds, the received data is returned in the form of a new
///> handle. When it fails, 0 is returned.
///>
///> The `ArbCmd` handle is consumed if and only if the API call succeeds.
#[no_mangle]
pub extern "C" fn dqcs_sim_arb(
    sim: dqcs_handle_t,
    name: *const c_char,
    cmd: dqcs_handle_t,
) -> dqcs_handle_t {
    api_return(0, || {
        resolve!(sim as &mut Simulator);
        resolve!(cmd as pending ArbCmd);
        clone!(cmd_ob: ArbCmd = resolved cmd);
        let data = sim.simulation.arb(receive_str(name)?, cmd_ob)?;
        delete!(resolved cmd);
        Ok(insert(data))
    })
}

/// Sends an `ArbCmd` message to one of the plugins, referenced by index.
///>
///> The frontend always has index 0. 1 through N are used for the operators
///> in front to back order (where N is the number of operators). The
///> backend is at index N+1.
///>
///> Python-style negative indices are supported. That is, -1 can be used to
///> refer to the backend, -2 to the last operator, and so on.
///>
///> `ArbCmd`s are executed immediately after yielding to the simulator, so
///> all pending asynchronous calls are flushed and executed *before* the
///> `ArbCmd`.
///>
///> When this succeeds, the received data is returned in the form of a new
///> handle. When it fails, 0 is returned.
///>
///> The `ArbCmd` handle is consumed if and only if the API call succeeds.
#[no_mangle]
pub extern "C" fn dqcs_sim_arb_idx(
    sim: dqcs_handle_t,
    index: ssize_t,
    cmd: dqcs_handle_t,
) -> dqcs_handle_t {
    api_return(0, || {
        resolve!(sim as &mut Simulator);
        resolve!(cmd as pending ArbCmd);
        clone!(cmd_ob: ArbCmd = resolved cmd);
        let data = sim.simulation.arb_idx(index, cmd_ob)?;
        delete!(resolved cmd);
        Ok(insert(data))
    })
}

/// Queries the implementation name of a plugin, referenced by instance
/// name.
///>
///> On success, this **returns a newly allocated string containing the
///> name. Free it with `free()` when you're done with it to avoid memory
///> leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
#[no_mangle]
pub extern "C" fn dqcs_sim_get_name(sim: dqcs_handle_t, name: *const c_char) -> *mut c_char {
    api_return_string(|| {
        resolve!(sim as &Simulator);
        Ok(sim
            .simulation
            .get_metadata(receive_str(name)?)?
            .get_name()
            .to_string())
    })
}

/// Queries the implementation name of a plugin, referenced by index.
///>
///> On success, this **returns a newly allocated string containing the
///> name. Free it with `free()` when you're done with it to avoid memory
///> leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
#[no_mangle]
pub extern "C" fn dqcs_sim_get_name_idx(sim: dqcs_handle_t, index: ssize_t) -> *mut c_char {
    api_return_string(|| {
        resolve!(sim as &Simulator);
        Ok(sim
            .simulation
            .get_metadata_idx(index)?
            .get_name()
            .to_string())
    })
}

/// Queries the author of a plugin, referenced by instance name.
///>
///> On success, this **returns a newly allocated string containing the
///> author. Free it with `free()` when you're done with it to avoid memory
///> leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
#[no_mangle]
pub extern "C" fn dqcs_sim_get_author(sim: dqcs_handle_t, name: *const c_char) -> *mut c_char {
    api_return_string(|| {
        resolve!(sim as &Simulator);
        Ok(sim
            .simulation
            .get_metadata(receive_str(name)?)?
            .get_author()
            .to_string())
    })
}

/// Queries the author of a plugin, referenced by index.
///>
///> On success, this **returns a newly allocated string containing the
///> author. Free it with `free()` when you're done with it to avoid memory
///> leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
#[no_mangle]
pub extern "C" fn dqcs_sim_get_author_idx(sim: dqcs_handle_t, index: ssize_t) -> *mut c_char {
    api_return_string(|| {
        resolve!(sim as &Simulator);
        Ok(sim
            .simulation
            .get_metadata_idx(index)?
            .get_author()
            .to_string())
    })
}

/// Queries the version of a plugin, referenced by instance name.
///>
///> On success, this **returns a newly allocated string containing the
///> version. Free it with `free()` when you're done with it to avoid memory
///> leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
#[no_mangle]
pub extern "C" fn dqcs_sim_get_version(sim: dqcs_handle_t, name: *const c_char) -> *mut c_char {
    api_return_string(|| {
        resolve!(sim as &Simulator);
        Ok(sim
            .simulation
            .get_metadata(receive_str(name)?)?
            .get_version()
            .to_string())
    })
}

/// Queries the version of a plugin, referenced by index.
///>
///> On success, this **returns a newly allocated string containing the
///> version. Free it with `free()` when you're done with it to avoid memory
///> leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
#[no_mangle]
pub extern "C" fn dqcs_sim_get_version_idx(sim: dqcs_handle_t, index: ssize_t) -> *mut c_char {
    api_return_string(|| {
        resolve!(sim as &Simulator);
        Ok(sim
            .simulation
            .get_metadata_idx(index)?
            .get_version()
            .to_string())
    })
}

/// Writes a reproduction file for the simulation so far.
#[no_mangle]
pub extern "C" fn dqcs_sim_write_reproduction_file(
    sim: dqcs_handle_t,
    filename: *const c_char,
) -> dqcs_return_t {
    api_return_none(|| {
        resolve!(sim as &Simulator);
        sim.simulation
            .write_reproduction_file(receive_str(filename)?)
    })
}