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, '_>
impl<'executable> Executable<'executable, '_>
sourcepub fn from_quil<Quil: Into<Arc<str>>>(quil: Quil) -> Self
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
quilis a string slice representing the original program to be run. The returnedExecutablewill only live as long as this reference.
sourcepub fn read_from<S>(self, register: S) -> Self
pub fn read_from<S>(self, register: S) -> Self
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
registeris a string reference of the name of a register to read from. The lifetime of this reference should be the lifetime of theExecutable, which is the lifetime of thequilargument toExecutable::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);
}sourcepub fn with_parameter<Param: Into<Box<str>>>(
&mut self,
param_name: Param,
index: usize,
value: f64
) -> &mut Self
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
param_name: Reference to the name of the parameter which should correspond to aDECLAREstatement in the Quil program. The lifetime of the reference should be the same as theExecutable: that is the same as thequilparam toExecutable::from_quil.index: The index into the memory vector that you’re setting.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);
}
}sourcepub fn with_qcs_client(self, client: Qcs) -> Self
pub fn with_qcs_client(self, client: Qcs) -> Self
Set the default configuration to be used when constructing clients
sourcepub async fn qcs_client(&mut self) -> Arc<Qcs>
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<'_, '_>
impl Executable<'_, '_>
sourcepub fn with_shots(self, shots: NonZeroU16) -> Self
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”.
sourcepub fn with_quilc_client<C: Client + Send + Sync + 'static>(
self,
client: Option<C>
) -> Self
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.
sourcepub fn compiler_options(self, options: CompilerOpts) -> Self
pub fn compiler_options(self, options: CompilerOpts) -> Self
If set, the value will override the default compiler options
sourcepub async fn execute_on_qvm<V: Client + ?Sized>(
&mut self,
client: &V
) -> ExecutionResult
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>
impl<'execution> Executable<'_, 'execution>
sourcepub async fn execute_on_qpu_with_endpoint<S>(
&mut self,
quantum_processor_id: S,
endpoint_id: S,
translation_options: Option<TranslationOptions>
) -> ExecutionResult
pub async fn execute_on_qpu_with_endpoint<S>( &mut self, quantum_processor_id: S, endpoint_id: S, translation_options: Option<TranslationOptions> ) -> ExecutionResult
Compile the program and execute it on a QPU, waiting for results.
Arguments
quantum_processor_id: The name of the QPU to run on. This parameter affects the lifetime of theExecutable. TheExecutablewill 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:
- You are not authenticated for QCS
- Your credentials don’t have an active reservation for the QPU you requested
- quilc was not running.
- The
quilthat thisExecutablewas constructed with was invalid. - Missing parameters that should be filled with
Executable::with_parameter
sourcepub async fn execute_on_qpu<S>(
&mut self,
quantum_processor_id: S,
translation_options: Option<TranslationOptions>,
execution_options: &ExecutionOptions
) -> ExecutionResult
pub async fn execute_on_qpu<S>( &mut self, quantum_processor_id: S, translation_options: Option<TranslationOptions>, execution_options: &ExecutionOptions ) -> ExecutionResult
Compile the program and execute it on a QCS endpoint, waiting for results.
Arguments
quantum_processor_id: The ID of the QPU for which to translate the program. This parameter affects the lifetime of theExecutable. TheExecutablewill only live as long as the last parameter passed into this function.translation_options: An optionalTranslationOptionsthat is used to configure how the program in translated. 3execution_options: TheExecutionOptionsto use. If the connection strategy used iscrate::qpu::api::ConnectionStrategy::EndpointIdthen direct access to that endpoint overrides thequantum_processor_idparameter.
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:
- You are not authenticated for QCS
- Your credentials don’t have an active reservation for the QPU you requested
- quilc was not running.
- The
quilthat thisExecutablewas constructed with was invalid. - Missing parameters that should be filled with
Executable::with_parameter
sourcepub async fn submit_to_qpu<S>(
&mut self,
quantum_processor_id: S,
translation_options: Option<TranslationOptions>,
execution_options: &ExecutionOptions
) -> Result<JobHandle<'execution>, Error>
pub async fn submit_to_qpu<S>( &mut self, quantum_processor_id: S, translation_options: Option<TranslationOptions>, execution_options: &ExecutionOptions ) -> Result<JobHandle<'execution>, Error>
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
quantum_processor_id: The ID of the QPU for which to translate the program. This parameter affects the lifetime of theExecutable. TheExecutablewill only live as long as the last parameter passed into this function.translation_options: An optionalTranslationOptionsthat is used to configure how the program in translated. 3execution_options: TheExecutionOptionsto use. If the connection strategy used iscrate::qpu::api::ConnectionStrategy::EndpointIdthen direct access to that endpoint overrides thequantum_processor_idparameter.
Errors
sourcepub 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>
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>
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
sourcepub async fn retrieve_results(
&mut self,
job_handle: JobHandle<'execution>
) -> ExecutionResult
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
Trait Implementations§
source§impl<'executable, 'execution> Clone for Executable<'executable, 'execution>
impl<'executable, 'execution> Clone for Executable<'executable, 'execution>
source§fn clone(&self) -> Executable<'executable, 'execution>
fn clone(&self) -> Executable<'executable, 'execution>
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto 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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request