Struct qcs::Executable

source ·
pub struct Executable<'executable, 'execution> { /* private fields */ }
Expand description

The builder interface for executing Quil programs on QVMs and QPUs.

Example

use qcs::client::Qcs;
use qcs::Executable;
use qcs::qvm;


const PROGRAM: &str = r##"
DECLARE ro BIT[2]

H 0
CNOT 0 1

MEASURE 0 ro[0]
MEASURE 1 ro[1]
"##;

#[tokio::main]
async fn main() {
    use std::num::NonZeroU16;
    use qcs::qvm;
    let qvm_client = qvm::http::HttpClient::from(&Qcs::load().await);
    let mut result = Executable::from_quil(PROGRAM).with_qcs_client(Qcs::default()).with_shots(NonZeroU16::new(4).unwrap()).execute_on_qvm(&qvm_client).await.unwrap();
    // "ro" is the only source read from by default if you don't specify a .read_from()

    // We first convert the readout data to a [`RegisterMap`] to get a mapping of registers
    // (ie. "ro") to a [`RegisterMatrix`], `M`, where M[`shot`][`index`] is the value for
    // the memory offset `index` during shot `shot`.
    // There are some programs where QPU readout data does not fit into a [`RegisterMap`], in
    // which case you should build the matrix you need from [`QpuResultData`] directly. See
    // the [`RegisterMap`] documentation for more information on when this transformation
    // might fail.
    let data = result.result_data
                        .to_register_map()
                        .expect("should convert to readout map")
                        .get_register_matrix("ro")
                        .expect("should have data in ro")
                        .as_integer()
                        .expect("should be integer matrix")
                        .to_owned();

    // In this case, we ran the program for 4 shots, so we know the number of rows is 4.
    assert_eq!(data.nrows(), 4);
    for shot in data.rows() {
        // Each shot will contain all the memory, in order, for the vector (or "register") we
        // requested the results of. In this case, "ro" (the default).
        assert_eq!(shot.len(), 2);
        // In the case of this particular program, we know ro[0] should equal ro[1]
        assert_eq!(shot[0], shot[1]);
    }
}

A Note on Lifetimes

This structure utilizes multiple lifetimes for the sake of runtime efficiency. You should be able to largely ignore these, just keep in mind that any borrowed data passed to the methods most likely needs to live as long as this struct. Check individual methods for specifics. If only using 'static strings then everything should just work.

Implementations§

source§

impl<'executable> Executable<'executable, '_>

source

pub fn from_quil<Quil: Into<Arc<str>>>(quil: Quil) -> Self

Create an Executable from a string containing a quil program. No additional work is done in this function, so the quil may actually be invalid.

The constructed Executable defaults to “ro” as a read-out register and 1 for the number of shots. Those can be overridden using Executable::read_from and Executable::with_shots respectively.

Note that changing the program for an associated Executable is not allowed, you’ll have to create a new Executable if you want to run a different program.

Arguments
  1. quil is a string slice representing the original program to be run. The returned Executable will only live as long as this reference.
source

pub fn read_from<S>(self, register: S) -> Self
where S: Into<Cow<'executable, str>>,

Specify a memory region or “register” to read results from. This must correspond to a DECLARE statement in the provided Quil program. You can call this register multiple times if you need to read multiple registers. If this method is never called, it’s assumed that a single register called “ro” is declared and should be read from.

Arguments
  1. register is a string reference of the name of a register to read from. The lifetime of this reference should be the lifetime of the Executable, which is the lifetime of the quil argument to Executable::from_quil.
Example
use qcs::client::Qcs;
use qcs::Executable;
use qcs::qvm;

const PROGRAM: &str = r#"
DECLARE first REAL[1]
DECLARE second REAL[1]

MOVE first[0] 3.141
MOVE second[0] 1.234
"#;

#[tokio::main]
async fn main() {
    let qvm_client = qvm::http::HttpClient::from(&Qcs::load().await);
    let mut result = Executable::from_quil(PROGRAM)
        .with_qcs_client(Qcs::default()) // Unnecessary if you have ~/.qcs/settings.toml
        .read_from("first")
        .read_from("second")
        .execute_on_qvm(&qvm_client)
        .await
        .unwrap();
    let first_value = result
        .result_data
        .to_register_map()
        .expect("qvm memory should fit readout map")
        .get_register_matrix("first")
        .expect("readout map should have 'first'")
        .as_real()
        .expect("should be real numbered register")
        .get((0, 0))
        .expect("should have value in first position of first register")
        .clone();
    let second_value = result
        .result_data
        .to_register_map()
        .expect("qvm memory should fit readout map")
        .get_register_matrix("second")
        .expect("readout map should have 'second'")
        .as_real()
        .expect("should be real numbered register")
        .get((0, 0))
        .expect("should have value in first position of first register")
        .clone();
    assert_eq!(first_value, 3.141);
    assert_eq!(second_value, 1.234);
}
source

pub fn with_parameter<Param: Into<Box<str>>>( &mut self, param_name: Param, index: usize, value: f64 ) -> &mut Self

Sets a concrete value for parametric compilation. The validity of parameters is not checked until execution.

Arguments
  1. param_name: Reference to the name of the parameter which should correspond to a DECLARE statement in the Quil program. The lifetime of the reference should be the same as the Executable: that is the same as the quil param to Executable::from_quil.
  2. index: The index into the memory vector that you’re setting.
  3. value: The value to set for the specified memory.
Example
use qcs::client::Qcs;
use qcs::Executable;
use qcs::qvm;

const PROGRAM: &str = "DECLARE theta REAL[2]";

#[tokio::main]
async fn main() {
    let qvm_client = qvm::http::HttpClient::from(&Qcs::load().await);
    let mut exe = Executable::from_quil(PROGRAM)
        .with_qcs_client(Qcs::default()) // Unnecessary if you have ~/.qcs/settings.toml
        .read_from("theta");

    for theta in 0..2 {
        let theta = theta as f64;
        let mut result = exe
            .with_parameter("theta", 0, theta)
            .with_parameter("theta", 1, theta * 2.0)
            .execute_on_qvm(&qvm_client).await.unwrap();
        let theta_register = result
            .result_data
            .to_register_map()
            .expect("should fit readout map")
            .get_register_matrix("theta")
            .expect("should have theta")
            .as_real()
            .expect("should be real valued register")
            .to_owned();

        let first = theta_register
            .get((0, 0))
            .expect("first index, first shot of theta should have value")
            .to_owned();
        let second = theta_register
            .get((0, 1))
            .expect("first shot, second_index of theta should have value")
            .to_owned();

        assert_eq!(first, theta);
        assert_eq!(second, theta * 2.0);
    }
}
source

pub fn with_qcs_client(self, client: Qcs) -> Self

Set the default configuration to be used when constructing clients

source

pub async fn qcs_client(&mut self) -> Arc<Qcs>

Get a reference to the Qcs client used by the executable.

If one has not been set, a default client is loaded, set, and returned.

source§

impl Executable<'_, '_>

source

pub fn with_shots(self, shots: NonZeroU16) -> Self

Specify a number of times to run the program for each execution. Defaults to 1 run or “shot”.

source

pub fn with_quilc_client<C: Client + Send + Sync + 'static>( self, client: Option<C> ) -> Self

Set the client used for compilation.

To disable compilation, set this to None.

source

pub fn compiler_options(self, options: CompilerOpts) -> Self

If set, the value will override the default compiler options

source

pub async fn execute_on_qvm<V: Client + ?Sized>( &mut self, client: &V ) -> ExecutionResult

Execute on a QVM which must be available at the configured URL (default http://localhost:5000).

Warning

This function uses tokio::task::spawn_blocking internally. See the docs for that function to avoid blocking shutdown of the runtime.

Returns

An ExecutionResult.

Errors

See Error.

source§

impl<'execution> Executable<'_, 'execution>

source

pub async fn execute_on_qpu_with_endpoint<S>( &mut self, quantum_processor_id: S, endpoint_id: S, translation_options: Option<TranslationOptions> ) -> ExecutionResult
where S: Into<Cow<'execution, str>>,

Compile the program and execute it on a QPU, waiting for results.

Arguments
  1. quantum_processor_id: The name of the QPU to run on. This parameter affects the lifetime of the Executable. The Executable will only live as long as the last parameter passed into this function.
Warning

This function uses tokio::task::spawn_blocking internally. See the docs for that function to avoid blocking shutdown of the runtime.

Returns

An ExecutionResult.

Errors

All errors are human readable by way of thiserror. Some common errors are:

  1. You are not authenticated for QCS
  2. Your credentials don’t have an active reservation for the QPU you requested
  3. quilc was not running.
  4. The quil that this Executable was constructed with was invalid.
  5. Missing parameters that should be filled with Executable::with_parameter
source

pub async fn execute_on_qpu<S>( &mut self, quantum_processor_id: S, translation_options: Option<TranslationOptions>, execution_options: &ExecutionOptions ) -> ExecutionResult
where S: Into<Cow<'execution, str>>,

Compile the program and execute it on a QCS endpoint, waiting for results.

Arguments
  1. quantum_processor_id: The ID of the QPU for which to translate the program. This parameter affects the lifetime of the Executable. The Executable will only live as long as the last parameter passed into this function.
  2. translation_options: An optional TranslationOptions that is used to configure how the program in translated. 3 execution_options: The ExecutionOptions to use. If the connection strategy used is crate::qpu::api::ConnectionStrategy::EndpointId then direct access to that endpoint overrides the quantum_processor_id parameter.
Warning

This function uses tokio::task::spawn_blocking internally. See the docs for that function to avoid blocking shutdown of the runtime.

Returns

An ExecutionResult.

Errors

All errors are human readable by way of thiserror. Some common errors are:

  1. You are not authenticated for QCS
  2. Your credentials don’t have an active reservation for the QPU you requested
  3. quilc was not running.
  4. The quil that this Executable was constructed with was invalid.
  5. Missing parameters that should be filled with Executable::with_parameter
source

pub async fn submit_to_qpu<S>( &mut self, quantum_processor_id: S, translation_options: Option<TranslationOptions>, execution_options: &ExecutionOptions ) -> Result<JobHandle<'execution>, Error>
where S: Into<Cow<'execution, str>>,

Compile and submit the program to a QPU, but do not wait for execution to complete.

Call Executable::retrieve_results to wait for execution to complete and retrieve the results.

Arguments
  1. quantum_processor_id: The ID of the QPU for which to translate the program. This parameter affects the lifetime of the Executable. The Executable will only live as long as the last parameter passed into this function.
  2. translation_options: An optional TranslationOptions that is used to configure how the program in translated. 3 execution_options: The ExecutionOptions to use. If the connection strategy used is crate::qpu::api::ConnectionStrategy::EndpointId then direct access to that endpoint overrides the quantum_processor_id parameter.
Errors

See Executable::execute_on_qpu.

source

pub async fn submit_to_qpu_with_endpoint<S>( &mut self, quantum_processor_id: S, endpoint_id: S, translation_options: Option<TranslationOptions> ) -> Result<JobHandle<'execution>, Error>
where S: Into<Cow<'execution, str>>,

Compile and submit the program to a QCS endpoint, but do not wait for execution to complete.

Call Executable::retrieve_results to wait for execution to complete and retrieve the results.

Errors

See Executable::execute_on_qpu.

source

pub async fn retrieve_results( &mut self, job_handle: JobHandle<'execution> ) -> ExecutionResult

Wait for the results of a job submitted via Executable::submit_to_qpu to complete.

Errors

See Executable::execute_on_qpu.

Trait Implementations§

source§

impl<'executable, 'execution> Clone for Executable<'executable, 'execution>

source§

fn clone(&self) -> Executable<'executable, 'execution>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl<'executable, 'execution> !RefUnwindSafe for Executable<'executable, 'execution>

§

impl<'executable, 'execution> Send for Executable<'executable, 'execution>

§

impl<'executable, 'execution> Sync for Executable<'executable, 'execution>

§

impl<'executable, 'execution> Unpin for Executable<'executable, 'execution>

§

impl<'executable, 'execution> !UnwindSafe for Executable<'executable, 'execution>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoRequest<T> for T

source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more