Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Use a common struct for both QPU and QVM execution results #223

Merged
merged 79 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
adeda2f
update .gitignore
MarquessV Nov 9, 2022
41598b0
first pass at ndarray implementaion for readout data
MarquessV Nov 22, 2022
3f35c9f
a pass at cleanup, add test for from_register_data_map
MarquessV Nov 22, 2022
016ef7e
another round of cleanup
MarquessV Nov 22, 2022
3356fcb
ReadoutTypes => ReadoutValue
MarquessV Nov 22, 2022
675382d
merge main
MarquessV Nov 22, 2022
b412eda
clippy: must_use and let_underscore_drop
MarquessV Nov 22, 2022
9d58991
Apply suggestions from code review
MarquessV Nov 22, 2022
a7ebfd0
more review comments
MarquessV Nov 22, 2022
14e046d
ExecuteResult => ExecutionResult
MarquessV Nov 22, 2022
8454d66
simplify ReadoutValue enum
MarquessV Nov 22, 2022
2770c08
more tests!
MarquessV Nov 22, 2022
1c7c6ec
Update crates/lib/src/execution_data.rs
MarquessV Nov 22, 2022
da53a50
avoid memory index truncation
MarquessV Nov 22, 2022
51fa1c7
fix bell state integration test
MarquessV Nov 23, 2022
e453d52
fix doc tests
MarquessV Nov 23, 2022
3ac3b18
fix deadlinks in documentation
MarquessV Nov 23, 2022
08d17d8
more doc cleanup
MarquessV Nov 23, 2022
1709974
cleanup docstrings
MarquessV Nov 28, 2022
5ef0d16
use single backticks around identifiers
MarquessV Nov 28, 2022
240c97a
single backticks
MarquessV Nov 28, 2022
9525f0a
chore: add new method for ReadotMap and serialize and deserialize
jselig-rigetti Nov 29, 2022
12718ed
add tracking issue for MemoryReference clone
MarquessV Nov 30, 2022
2d286d5
wip: add helper to use RegisterMatrix as i32 type
jselig-rigetti Nov 30, 2022
71f7340
WIP: refactor execution result types into their respective modules,
MarquessV Jan 14, 2023
a74eadd
to_readout_map w/ docstring
MarquessV Jan 14, 2023
35bf013
more docstrings, fix examples and tests
MarquessV Jan 24, 2023
59eda08
merge main, resolve conflicts, implement some of the python bindings
MarquessV Jan 25, 2023
96d6fa1
pass register matrixes across the border as ndarrays
MarquessV Jan 26, 2023
334dcf4
build out constructors and python tests
MarquessV Jan 27, 2023
f740797
docstring updates, clippy
MarquessV Jan 27, 2023
e0c2512
RegisterData docstring
MarquessV Jan 27, 2023
b0bdefd
more docstring updates
MarquessV Jan 28, 2023
0e1ae9f
clippy
MarquessV Jan 28, 2023
f7fa476
Merge branch 'main' into 215-execution-results-in-common-struct
MarquessV Jan 28, 2023
c6308c7
fix typo
MarquessV Jan 28, 2023
7174501
remove old todo comment
MarquessV Jan 28, 2023
2b249de
missing docstring
MarquessV Jan 28, 2023
0a9a73f
better test from qpu to readout map, doc lints
MarquessV Jan 28, 2023
1da1839
test assertion fix
MarquessV Jan 28, 2023
a82ad02
more docstring example fixes
MarquessV Jan 28, 2023
a219baf
define shared/related deps in workspace dependencies
MarquessV Jan 30, 2023
f3d82b6
cleanup per review comments
MarquessV Jan 31, 2023
e723226
ReadoutData -> ResultData, ReadoutMap -> RegisterMap
MarquessV Jan 31, 2023
f062ede
properly rename python bindings
MarquessV Jan 31, 2023
be90039
QpuReadout -> QpuResultData, sweep for stale references to ReadoutMap
MarquessV Feb 1, 2023
f1f7536
clean up references to QPUReadout
MarquessV Feb 1, 2023
6b2c4f0
Replace QvmMemory alias with QvmResultData struct
MarquessV Feb 1, 2023
6a27ede
handle QvmResultData in Python bindings
MarquessV Feb 1, 2023
f2b6679
use to_owned()
MarquessV Feb 1, 2023
e986592
break out reference sequence check into seperate step with itertools
MarquessV Feb 3, 2023
f44b165
fix conditions
MarquessV Feb 3, 2023
7689dcc
naming
MarquessV Feb 3, 2023
b46e0a5
another sweep for old names
MarquessV Feb 3, 2023
9b37d87
add type hints, configure fmt & linting for pyi files
MarquessV Feb 14, 2023
edc5514
use staticmethod, fix edge case in registermatrix conversion
MarquessV Feb 14, 2023
0cf934b
update Cargo.toml
MarquessV Feb 14, 2023
8dde07a
use rfold, remove extra spaces in docstrings
MarquessV Feb 14, 2023
4a7c926
fix tests
MarquessV Feb 14, 2023
83fa3e3
Update crates/lib/src/execution_data.rs
MarquessV Feb 14, 2023
2e90b58
clarify ReadoutValues docs
MarquessV Feb 14, 2023
93aa5a6
Merge branch 'main' into 215-execution-results-in-common-struct
MarquessV Feb 14, 2023
cdaaf47
Update crates/python/qcs_sdk/_execution_data.pyi
MarquessV Feb 14, 2023
63d2c52
Update crates/python/qcs_sdk/_execution_data.pyi
MarquessV Feb 14, 2023
846b9f8
Update crates/python/qcs_sdk/qpu/result_data.pyi
MarquessV Feb 14, 2023
326fc19
remove extra comment from .flake8
MarquessV Feb 14, 2023
68a570a
Update crates/lib/src/executable.rs
MarquessV Feb 15, 2023
fb7a860
Update crates/lib/src/execution_data.rs
MarquessV Feb 15, 2023
db5b0a5
Update crates/lib/examples/parametric_compilation.rs
MarquessV Feb 15, 2023
3689bfe
reduce visibility of result data members
MarquessV Feb 15, 2023
440fa43
various hint and test improvements
MarquessV Feb 15, 2023
0879bf3
fix setter method name
MarquessV Feb 15, 2023
4ebae0e
cleanup per feedback
MarquessV Feb 15, 2023
c804135
remove redundant py_try_from
MarquessV Feb 15, 2023
342a0c6
make import paths for QVM and QPU result data consistent for both types
MarquessV Feb 15, 2023
6224921
fix imports
MarquessV Feb 15, 2023
1713621
fix weird crates import path
MarquessV Feb 15, 2023
44d62f8
fix enum declaration
MarquessV Feb 16, 2023
adb2c80
allow BSD-2 licence for numpy
MarquessV Feb 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ tempfile = "3.3.0"
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] }
warp = "0.3.3"
regex = "1.7.0"
test-case = "2.2.2"
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 3 additions & 3 deletions crates/lib/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub enum RewriteArithmeticError {
Rewrite(#[from] rewrite_arithmetic::Error),
}

/// The result of a call to [`rewrite_arithmetic`] which provides the
/// The result of a call to [`rewrite_arithmetic()`] which provides the
/// information necessary to later patch-in memory values to a compiled program.
#[derive(Debug, Serialize)]
pub struct RewriteArithmeticResult {
Expand Down Expand Up @@ -112,7 +112,7 @@ pub struct TranslationResult {
///
/// # Errors
///
/// Returns a [`translation::Error`] if translation fails.
/// Returns a [`TranslationError`] if translation fails.
pub async fn translate(
native_quil: &str,
shots: u16,
Expand Down Expand Up @@ -292,7 +292,7 @@ impl From<ControllerJobExecutionResult> for ExecutionResults {
///
/// # Errors
///
/// May error if a [`gRPC`] client cannot be constructed, or a [`gRPC`]
/// May error if a [`Qcs`] client cannot be constructed, or if the ``gRPC``
/// call fails.
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
pub async fn retrieve_results(
job_id: &str,
Expand Down
66 changes: 39 additions & 27 deletions crates/lib/src/executable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use quil_rs::Program;
///
/// ```rust
/// use qcs_api_client_common::ClientConfiguration;
/// use qcs::{Executable, RegisterData};
/// use qcs::Executable;
///
///
/// const PROGRAM: &str = r##"
Expand All @@ -40,12 +40,13 @@ use quil_rs::Program;
/// #[tokio::main]
/// async fn main() {
/// let mut result = Executable::from_quil(PROGRAM).with_config(ClientConfiguration::default()).with_shots(4).execute_on_qvm().await.unwrap();
/// // We know it's i8 because we declared the memory as `BIT` in Quil.
/// // "ro" is the only source read from by default if you don't specify a .read_from()
/// let data = result.registers.remove("ro").expect("Did not receive ro data").into_i8().unwrap();
/// // In this case, we ran the program for 4 shots, so we know the length is 4.
/// assert_eq!(data.len(), 4);
/// for shot in data {
/// // We get the data as a shot wise matrix so we can iterate over rows containing all the values in
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
/// // in "ro" for each shot.
/// let data = result.readout_data.get_shot_wise_matrix("ro").expect("should have data in ro");
/// // 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);
Expand Down Expand Up @@ -143,20 +144,20 @@ impl<'executable> Executable<'executable, '_> {
/// .execute_on_qvm()
/// .await
/// .unwrap();
/// let first = result
/// .registers
/// .remove("first")
/// .expect("Did not receive first buffer")
/// .into_f64()
/// .expect("Received incorrect data type for first");
/// let second = result
/// .registers
/// .remove("second")
/// .expect("Did not receive second buffer")
/// .into_f64()
/// .expect("Received incorrect data type for second");
/// assert_eq!(first[0][0], 3.141);
/// assert_eq!(second[0][0], 1.234);
/// let first_value = result
/// .readout_data
/// .get_value("first", 0, 0)
/// .expect("should have value in first position of first register")
/// .into_real()
/// .expect("should be a floating point number");
/// let second_value = result
/// .readout_data
/// .get_value("second", 0, 0)
/// .expect("should have value in first position of second register")
/// .into_real()
/// .expect("should be a floating point number");
/// assert_eq!(first_value, 3.141);
/// assert_eq!(second_value, 1.234);
/// }
/// ```
#[must_use]
Expand Down Expand Up @@ -198,9 +199,20 @@ impl<'executable> Executable<'executable, '_> {
/// .with_parameter("theta", 0, theta)
/// .with_parameter("theta", 1, theta * 2.0)
/// .execute_on_qvm().await.unwrap();
/// let data = result.registers.remove("theta").expect("Could not read theta").into_f64().unwrap();
/// assert_eq!(data[0][0], theta);
/// assert_eq!(data[0][1], theta * 2.0);
/// let first = result
/// .readout_data
/// .get_value("theta", 0, 0)
/// .expect("first index, first shot of theta should have value")
/// .into_real()
/// .expect("value should be a float");
/// let second = result
/// .readout_data
/// .get_value("theta", 1, 0)
/// .expect("second index, first shot of theta should have value")
/// .into_real()
/// .expect("value should be a float");
/// assert_eq!(first, theta);
/// assert_eq!(second, theta * 2.0);
/// }
/// }
/// ```
Expand Down Expand Up @@ -278,7 +290,7 @@ impl Executable<'_, '_> {
///
/// # Returns
///
/// A `HashMap<String, ExecutionResult>` where the key is the name of the register that was read from (e.g. "ro").
/// An [`ExecutionResult`].
///
/// # Errors
///
Expand Down Expand Up @@ -363,10 +375,10 @@ impl<'execution> Executable<'_, 'execution> {
///
/// # Returns
///
/// A `HashMap<String, ExecutionResult>` where the key is the name of the register that was read from (e.g. "ro").
/// An [`ExecutionResult`].
///
/// # Errors
/// All errors are human readable by way of [`mod@eyre`]. Some common errors are:
/// All errors are human readable by way of [`mod@thiserror`]. Some common errors are:
///
/// 1. You are not authenticated for QCS
/// 1. Your credentials don't have an active reservation for the QPU you requested
Expand Down Expand Up @@ -487,7 +499,7 @@ pub enum Error {
/// [`Executable::retrieve_results`] can invalidate the handle.
#[error("The job handle was not valid")]
InvalidJobHandle,
/// Occurs when failing to construct a [`QcsClient`].
/// Occurs when failing to construct a [`Qcs`] client.
#[error("The QCS client configuration failed to load")]
QcsConfigLoadFailure(#[from] LoadError),
}
Expand Down
98 changes: 90 additions & 8 deletions crates/lib/src/execution_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::time::Duration;
use ndarray::prelude::*;

use qcs_api_client_grpc::models::controller::{readout_values, ReadoutValues};
use quil_rs::instruction::MemoryReference;

use crate::RegisterData;

Expand Down Expand Up @@ -40,7 +39,7 @@ pub enum ReadoutValue {
/// for all memory indexes of a given shot.
pub type RegisterMatrix = Array2<Option<ReadoutValue>>;

/// A mapping of readout fields to their [`ReadoutValues`].
/// A mapping of readout fields to their [`ReadoutValue`]s.
#[derive(Debug, Clone, PartialEq)]
#[repr(transparent)]
pub struct ReadoutMap(HashMap<String, RegisterMatrix>);
Expand Down Expand Up @@ -109,7 +108,7 @@ impl ReadoutMap {
#[must_use]
pub fn get_shot_wise_matrix(&self, register_name: &str) -> Option<RegisterMatrix> {
let register = self.0.get(register_name);
register.map(|matrix| matrix.clone().reversed_axes())
register.cloned().map(ArrayBase::reversed_axes)
}

/// `readout_values` maps program-defined readout to result-defined readout, e.g.:
Expand Down Expand Up @@ -146,8 +145,7 @@ impl ReadoutMap {
.collect(),
None => Vec::new(),
};
// TODO handle possible truncation
let shape = (reference.index as usize + 1, row.len());
let shape = (reference.index + 1, row.len());
let matrix = result
.0
.entry(reference.name)
Expand All @@ -160,8 +158,7 @@ impl ReadoutMap {
})
.or_insert_with(|| Array2::from_elem(shape, None));
for (shot_num, value) in row.iter().enumerate() {
// TODO handle possible truncation
matrix[[reference.index as usize, shot_num]] = *value;
matrix[[reference.index, shot_num]] = *value;
}
});
}
Expand Down Expand Up @@ -228,6 +225,11 @@ impl ReadoutMap {
}
}

struct MemoryReference {
name: String,
index: usize,
}

MarquessV marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Debug, thiserror::Error)]
pub(crate) enum MemoryReferenceParseError {
#[error("Could not parse memory reference: {reason}")]
Expand Down Expand Up @@ -256,7 +258,7 @@ fn parse_readout_register(

Ok(MemoryReference {
name: String::from(&register_name[..open_brace]),
index: u64::from_str(&register_name[open_brace + 1..close_brace])?,
index: usize::from_str(&register_name[open_brace + 1..close_brace])?,
})
}

Expand All @@ -265,6 +267,7 @@ mod describe_readout_map {
use maplit::hashmap;
use ndarray::prelude::*;
use std::collections::HashMap;
use test_case::test_case;

use super::{ReadoutMap, ReadoutValue, ReadoutValues, RegisterData};
use qcs_api_client_grpc::models::controller::readout_values::Values;
Expand Down Expand Up @@ -343,4 +346,83 @@ mod describe_readout_map {
]);
assert_eq!(ro, expected);
}

#[test_case(0, 0 => Some(ReadoutValue::Integer(1)) ; "returns value when both indices are in bounds")]
#[test_case(1, 0 => None ; "returns None when row index is off by 1")]
#[test_case(0, 1 => None ; "returns None when column index is off by 1")]
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
fn get_value(row: usize, col: usize) -> Option<ReadoutValue> {
let registers: HashMap<Box<str>, RegisterData> = hashmap! {
String::from("ro").into() => RegisterData::I8(vec![vec![1]]),
};

ReadoutMap::from_register_data_map(&registers).get_value("ro", row, col)
}

#[test_case(0 => Some(vec![Some(ReadoutValue::Integer(1)), Some(ReadoutValue::Integer(2))]) ; "returns correct row when index is in bounds")]
#[test_case(2 => None ; "returns None when index is off by 1")]
fn get_values_by_memory_index(index: usize) -> Option<Vec<Option<ReadoutValue>>> {
let registers: HashMap<Box<str>, RegisterData> = hashmap! {
String::from("ro").into() => RegisterData::I8(vec![vec![1, 9], vec![2, 10]]),
};

ReadoutMap::from_register_data_map(&registers).get_values_by_memory_index("ro", index)
}

#[test_case(0 => Some(vec![Some(ReadoutValue::Integer(1)), Some(ReadoutValue::Integer(2))]) ; "returns correct column when index is in bounds")]
#[test_case(2 => None ; "returns None when index is off by 1")]
fn get_values_by_shot(shot_num: usize) -> Option<Vec<Option<ReadoutValue>>> {
let registers: HashMap<Box<str>, RegisterData> = hashmap! {
String::from("ro").into() => RegisterData::I8(vec![vec![1, 2], vec![3, 4]]),
};

ReadoutMap::from_register_data_map(&registers).get_values_by_shot("ro", shot_num)
}

#[test]
fn test_get_index_wise_matrix() {
let registers: HashMap<Box<str>, RegisterData> = hashmap! {
String::from("ro").into() => RegisterData::I8(vec![vec![1, 3], vec![2, 4]]),
};

let expected = arr2(&[
[
Some(ReadoutValue::Integer(1)),
Some(ReadoutValue::Integer(2)),
],
[
Some(ReadoutValue::Integer(3)),
Some(ReadoutValue::Integer(4)),
],
]);

let readout_data = ReadoutMap::from_register_data_map(&registers);
let matrix = readout_data
.get_index_wise_matrix("ro")
.expect("ReadoutMap should have ro");
assert_eq!(matrix, expected);
}

#[test]
fn test_get_shot_wise_matrix() {
let registers: HashMap<Box<str>, RegisterData> = hashmap! {
String::from("ro").into() => RegisterData::I8(vec![vec![1, 3], vec![2, 4]]),
};

let expected = arr2(&[
[
Some(ReadoutValue::Integer(1)),
Some(ReadoutValue::Integer(3)),
],
[
Some(ReadoutValue::Integer(2)),
Some(ReadoutValue::Integer(4)),
],
]);

let readout_data = ReadoutMap::from_register_data_map(&registers);
let matrix = readout_data
.get_shot_wise_matrix("ro")
.expect("ReadoutMap should have ro");
assert_eq!(matrix, expected);
}
}
2 changes: 1 addition & 1 deletion crates/lib/src/qpu/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pub enum GrpcClientError {
GrpcError(#[from] GrpcError),
}

/// Errors that may occur while trying to use a [`OpenAPI`] client
/// Errors that may occur while trying to use an ``OpenAPI`` client
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Debug, thiserror::Error)]
pub enum OpenApiClientError<T> {
/// Error due to request failure
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/qpu/quilc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl NativeQuilRequest {
}
}

/// Description of a device to compile for, part of [`NativeQuilRequest`]
/// Description of a device to compile for.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "_type")]
pub struct TargetDevice {
Expand Down
Loading