From 368995847da0b102cf7187bc6f7beef0d2d487fd Mon Sep 17 00:00:00 2001 From: Michael Bryant Date: Tue, 8 Aug 2023 17:48:04 -0700 Subject: [PATCH 01/22] fix: don't wrap ReadoutValues twice --- crates/python/src/execution_data.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index 7e953d78b..acaa0356d 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -10,7 +10,7 @@ use rigetti_pyo3::{ }; use crate::qvm::PyQvmResultData; -use crate::{grpc::models::controller::PyReadoutValuesValues, qpu::PyQpuResultData}; +use crate::{grpc::models::controller::{PyReadoutValues, PyReadoutValuesValues}, qpu::PyQpuResultData}; py_wrap_union_enum! { PyResultData(ResultData) as "ResultData" { @@ -58,13 +58,6 @@ impl PyExecutionData { } } -// From gRPC -py_wrap_data_struct! { - PyReadoutValues(ReadoutValues) as "ReadoutValues" { - values: Option => Option - } -} - py_wrap_type! { PyRegisterMap(RegisterMap) as "RegisterMap"; } From 47e9c12dd77f63aa90e49668e58c8c49ff5e9a5d Mon Sep 17 00:00:00 2001 From: Michael Bryant Date: Tue, 8 Aug 2023 17:51:50 -0700 Subject: [PATCH 02/22] feat(python): add constructor to QVMResultData and as_ndarray to ReadoutValues and RegisterData --- crates/python/qcs_sdk/__init__.pyi | 10 ++++++++++ crates/python/qcs_sdk/qpu/__init__.pyi | 4 ++++ crates/python/src/grpc/models/controller.rs | 17 +++++++++++++++- crates/python/src/qvm/mod.rs | 22 ++++++++++++++------- crates/python/src/register_data.rs | 21 +++++++++++++++----- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/crates/python/qcs_sdk/__init__.pyi b/crates/python/qcs_sdk/__init__.pyi index 15b50f6b6..02fa2ac09 100644 --- a/crates/python/qcs_sdk/__init__.pyi +++ b/crates/python/qcs_sdk/__init__.pyi @@ -261,6 +261,12 @@ class ResultData: - ``from_*``: wrap underlying values as this enum type. """ + def __new__(cls, inner: Union[QPUResultData, QVMResultData]) -> "ResultData": + """ + Create a new ResultData from either QVM or QPU result data. + """ + ... + def to_register_map(self) -> RegisterMap: """ Convert ``ResultData`` from its inner representation as ``QVMResultData`` or @@ -299,6 +305,7 @@ class ResultData: @final class ExecutionData: + def __new__(cls, result_data: ResultData, duration: Optional[datetime.timedelta]): ... @property def result_data(self) -> ResultData: ... @result_data.setter @@ -332,6 +339,9 @@ class RegisterData: ) -> Union[List[List[int]], List[List[float]], List[List[complex]]]: """Returns the inner value.""" ... + def as_ndarray(self) -> Union[NDArray[np.int64], NDArray[np.float64], NDArray[np.complex128]]: + """Returns the values as an ``ndarray``.""" + ... def is_i8(self) -> bool: ... def is_i16(self) -> bool: ... def is_f64(self) -> bool: ... diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 47ed7058b..43a8b6cfb 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -29,6 +29,10 @@ class ReadoutValues: """ + def __new__(cls, values: Union[List[int], List[float], List[complex]]): + """Construct a new ReadoutValues from a list of values.""" + ... + def inner(self) -> Union[List[int], List[float], List[complex]]: """Return the inner list of readout values.""" ... diff --git a/crates/python/src/grpc/models/controller.rs b/crates/python/src/grpc/models/controller.rs index 6a28403a8..5465bac6c 100644 --- a/crates/python/src/grpc/models/controller.rs +++ b/crates/python/src/grpc/models/controller.rs @@ -1,3 +1,4 @@ +use numpy::PyArray; use pyo3::{ prelude::*, types::{PyComplex, PyInt, PyList}, @@ -5,7 +6,7 @@ use pyo3::{ use qcs_api_client_grpc::models::controller::{ readout_values::Values, Complex64, Complex64ReadoutValues, IntegerReadoutValues, ReadoutValues, }; -use rigetti_pyo3::{num_complex::Complex32 as NumComplex32, py_wrap_struct}; +use rigetti_pyo3::{num_complex::Complex32 as NumComplex32, py_wrap_struct, PyWrapper}; use rigetti_pyo3::{py_wrap_data_struct, py_wrap_union_enum, PyTryFrom, ToPython}; py_wrap_data_struct! { @@ -14,6 +15,20 @@ py_wrap_data_struct! { } } +#[pymethods] +impl PyReadoutValues { + pub fn as_ndarray(&self, py: Python<'_>) -> PyResult> { + match &self.as_inner().values { + None => Ok(None), + Some(Values::IntegerValues(ints)) => Ok(Some(PyArray::from_slice(py, &ints.values).to_object(py))), + Some(Values::ComplexValues(complex)) => Ok(Some(PyArray::from_iter(py, { + complex.values.iter().map(|value| NumComplex32::new(value.real, value.imaginary)) + }).to_object(py))), + + } + } +} + py_wrap_union_enum! { PyReadoutValuesValues(Values) as "ReadoutValuesValues" { integer_values: IntegerValues => PyIntegerReadoutValues, diff --git a/crates/python/src/qvm/mod.rs b/crates/python/src/qvm/mod.rs index 064122f43..657ea6f7e 100644 --- a/crates/python/src/qvm/mod.rs +++ b/crates/python/src/qvm/mod.rs @@ -16,13 +16,6 @@ mod api; use api::PyAddressRequest; -wrap_error!(RustQvmError(qcs::qvm::Error)); -py_wrap_error!(api, RustQvmError, QVMError, PyRuntimeError); - -py_wrap_type! { - PyQvmResultData(QvmResultData) as "QVMResultData" -} - create_init_submodule! { classes: [PyQvmResultData, PyQvmOptions], errors: [QVMError], @@ -32,8 +25,23 @@ create_init_submodule! { ], } +wrap_error!(RustQvmError(qcs::qvm::Error)); +py_wrap_error!(api, RustQvmError, QVMError, PyRuntimeError); + +py_wrap_type! { + PyQvmResultData(QvmResultData) as "QVMResultData" +} + #[pymethods] impl PyQvmResultData { + #[new] + fn new(memory: HashMap) -> Self { + let memory = memory.into_iter() + .map(|(key, value)| (key, value.into_inner())) + .collect(); + Self::from(QvmResultData::from_memory_map(memory)) + } + #[staticmethod] fn from_memory_map(py: Python<'_>, memory: HashMap) -> PyResult { Ok(Self(QvmResultData::from_memory_map(HashMap::< diff --git a/crates/python/src/register_data.rs b/crates/python/src/register_data.rs index 9f6fbc1ac..c2f5e2645 100644 --- a/crates/python/src/register_data.rs +++ b/crates/python/src/register_data.rs @@ -1,9 +1,8 @@ -use pyo3::{ - types::{PyComplex, PyFloat, PyInt}, - Py, -}; +use numpy::{PyArray, ToPyArray}; +use numpy::ndarray::IxDyn; +use pyo3::{types::{PyComplex, PyFloat, PyInt}, Py, pymethods, Python, PyAny, PyResult, PyErr, ToPyObject, PyObject}; use qcs::RegisterData; -use rigetti_pyo3::py_wrap_union_enum; +use rigetti_pyo3::{py_wrap_union_enum, PyWrapper}; py_wrap_union_enum! { PyRegisterData(RegisterData) as "RegisterData" { @@ -13,3 +12,15 @@ py_wrap_union_enum! { complex32: Complex32 => Vec>> } } + +#[pymethods] +impl PyRegisterData { + pub fn as_ndarray(&self, py: Python<'_>) -> PyResult { + match self.as_inner() { + RegisterData::I8(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), + RegisterData::F64(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), + RegisterData::I16(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), + RegisterData::Complex32(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), + }.map_err(PyErr::from) + } +} \ No newline at end of file From 5956daf35a3028c60798828c41475b7b12b621d6 Mon Sep 17 00:00:00 2001 From: marquessv Date: Thu, 10 Aug 2023 10:48:12 -0700 Subject: [PATCH 03/22] allow RegisterMap to be used as dictionary --- crates/lib/src/execution_data.rs | 2 +- crates/lib/src/qpu/api.rs | 5 +- crates/python/qcs_sdk/__init__.pyi | 3 +- crates/python/src/execution_data.rs | 123 +++++++++++++++++- crates/python/src/grpc/models/controller.rs | 17 ++- crates/python/src/qvm/mod.rs | 3 +- crates/python/src/register_data.rs | 30 +++-- .../execution_data/test_execution_data.py | 22 +++- 8 files changed, 178 insertions(+), 27 deletions(-) diff --git a/crates/lib/src/execution_data.rs b/crates/lib/src/execution_data.rs index 7bbda14b9..aae083d17 100644 --- a/crates/lib/src/execution_data.rs +++ b/crates/lib/src/execution_data.rs @@ -83,7 +83,7 @@ pub enum RegisterMatrix { /// register. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[repr(transparent)] -pub struct RegisterMap(HashMap); +pub struct RegisterMap(pub HashMap); /// Errors that may occur when trying to build a [`RegisterMatrix`] from execution data #[allow(missing_docs)] diff --git a/crates/lib/src/qpu/api.rs b/crates/lib/src/qpu/api.rs index e9cb42aab..6cc4cb886 100644 --- a/crates/lib/src/qpu/api.rs +++ b/crates/lib/src/qpu/api.rs @@ -169,7 +169,10 @@ pub async fn retrieve_results( .map_err(QpuApiError::from) .and_then( |result| match controller_job_execution_result::Status::from_i32(result.status) { - Some(controller_job_execution_result::Status::Success) => Ok(result), + Some(controller_job_execution_result::Status::Success) => { + dbg!(&result); + Ok(result) + } status => Err(QpuApiError::JobExecutionFailed { status: status .map_or("UNDEFINED", |status| status.as_str_name()) diff --git a/crates/python/qcs_sdk/__init__.pyi b/crates/python/qcs_sdk/__init__.pyi index 02fa2ac09..aabd7ec9b 100644 --- a/crates/python/qcs_sdk/__init__.pyi +++ b/crates/python/qcs_sdk/__init__.pyi @@ -266,7 +266,6 @@ class ResultData: Create a new ResultData from either QVM or QPU result data. """ ... - def to_register_map(self) -> RegisterMap: """ Convert ``ResultData`` from its inner representation as ``QVMResultData`` or @@ -305,7 +304,7 @@ class ResultData: @final class ExecutionData: - def __new__(cls, result_data: ResultData, duration: Optional[datetime.timedelta]): ... + def __new__(cls, result_data: ResultData, duration: Optional[datetime.timedelta] = None): ... @property def result_data(self) -> ResultData: ... @result_data.setter diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index acaa0356d..38e6a6b56 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -1,16 +1,20 @@ use std::time::Duration; use numpy::{Complex64, PyArray2}; -use pyo3::{exceptions::PyValueError, pymethods, types::PyDelta, Py, PyResult, Python}; +use pyo3::exceptions::PyKeyError; +use pyo3::{ + exceptions::PyValueError, pyclass, pymethods, types::PyDelta, Py, PyRef, PyRefMut, PyResult, + Python, +}; +use pyo3::{IntoPy, PyAny}; use qcs::{ExecutionData, RegisterMap, RegisterMatrix, ResultData}; -use qcs_api_client_grpc::models::controller::{readout_values::Values, ReadoutValues}; use rigetti_pyo3::{ py_wrap_data_struct, py_wrap_error, py_wrap_type, py_wrap_union_enum, wrap_error, PyTryFrom, PyWrapper, ToPython, ToPythonError, }; +use crate::qpu::PyQpuResultData; use crate::qvm::PyQvmResultData; -use crate::{grpc::models::controller::{PyReadoutValues, PyReadoutValuesValues}, qpu::PyQpuResultData}; py_wrap_union_enum! { PyResultData(ResultData) as "ResultData" { @@ -59,6 +63,7 @@ impl PyExecutionData { } py_wrap_type! { + #[pyo3(mapping)] PyRegisterMap(RegisterMap) as "RegisterMap"; } @@ -68,6 +73,14 @@ py_wrap_type! { #[pymethods] impl PyRegisterMatrix { + fn to_ndarray(&self, py: Python<'_>) -> Py { + self.as_integer(py) + .map(|array| array.into_py(py)) + .or(self.as_real(py).map(|array| array.into_py(py))) + .or(self.as_complex(py).map(|array| array.into_py(py))) + .expect("A RegisterMatrix can't be any other type.") + } + #[staticmethod] fn from_integer(matrix: &PyArray2) -> PyRegisterMatrix { Self(RegisterMatrix::Integer(matrix.to_owned_array())) @@ -146,9 +159,109 @@ impl PyRegisterMatrix { #[pymethods] impl PyRegisterMap { - pub fn get_register_matrix(&self, register_name: String) -> Option { + pub fn get_register_matrix(&self, register_name: &str) -> Option { self.as_inner() - .get_register_matrix(®ister_name) + .get_register_matrix(register_name) .map(PyRegisterMatrix::from) } + + pub fn __len__(&self) -> usize { + self.as_inner().0.len() + } + + pub fn __contains__(&self, key: String) -> bool { + self.as_inner().0.contains_key(&key) + } + + pub fn __getitem__(&self, item: &str) -> PyResult { + self.get_register_matrix(item) + .ok_or(PyKeyError::new_err(format!( + "Key {item} not found in RegisterMap" + ))) + } + + pub fn __iter__(&self, py: Python<'_>) -> PyResult> { + Py::new( + py, + PyRegisterMapKeysIter { + inner: self.as_inner().0.clone().into_iter(), + }, + ) + } + + pub fn keys(&self, py: Python<'_>) -> PyResult> { + self.__iter__(py) + } + + pub fn values(&self, py: Python<'_>) -> PyResult> { + Py::new( + py, + PyRegisterMapValuesIter { + inner: self.as_inner().0.clone().into_iter(), + }, + ) + } + + pub fn items(&self, py: Python<'_>) -> PyResult> { + Py::new( + py, + PyRegisterMapItemsIter { + inner: self.as_inner().0.clone().into_iter(), + }, + ) + } + + pub fn get(&self, key: &str, default: Option) -> Option { + self.__getitem__(key).ok().or(default) + } +} + +#[pyclass] +pub struct PyRegisterMapItemsIter { + inner: std::collections::hash_map::IntoIter, +} + +#[pymethods] +impl PyRegisterMapItemsIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<(String, PyRegisterMatrix)> { + slf.inner + .next() + .map(|(register, matrix)| (register, PyRegisterMatrix(matrix))) + } +} + +#[pyclass] +pub struct PyRegisterMapKeysIter { + inner: std::collections::hash_map::IntoIter, +} + +#[pymethods] +impl PyRegisterMapKeysIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + slf.inner.next().map(|(register, _)| register) + } +} + +#[pyclass] +pub struct PyRegisterMapValuesIter { + inner: std::collections::hash_map::IntoIter, +} + +#[pymethods] +impl PyRegisterMapValuesIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + slf.inner.next().map(|(_, matrix)| PyRegisterMatrix(matrix)) + } } diff --git a/crates/python/src/grpc/models/controller.rs b/crates/python/src/grpc/models/controller.rs index 5465bac6c..c2e5d1abc 100644 --- a/crates/python/src/grpc/models/controller.rs +++ b/crates/python/src/grpc/models/controller.rs @@ -20,11 +20,18 @@ impl PyReadoutValues { pub fn as_ndarray(&self, py: Python<'_>) -> PyResult> { match &self.as_inner().values { None => Ok(None), - Some(Values::IntegerValues(ints)) => Ok(Some(PyArray::from_slice(py, &ints.values).to_object(py))), - Some(Values::ComplexValues(complex)) => Ok(Some(PyArray::from_iter(py, { - complex.values.iter().map(|value| NumComplex32::new(value.real, value.imaginary)) - }).to_object(py))), - + Some(Values::IntegerValues(ints)) => { + Ok(Some(PyArray::from_slice(py, &ints.values).to_object(py))) + } + Some(Values::ComplexValues(complex)) => Ok(Some( + PyArray::from_iter(py, { + complex + .values + .iter() + .map(|value| NumComplex32::new(value.real, value.imaginary)) + }) + .to_object(py), + )), } } } diff --git a/crates/python/src/qvm/mod.rs b/crates/python/src/qvm/mod.rs index 657ea6f7e..ab0195ecc 100644 --- a/crates/python/src/qvm/mod.rs +++ b/crates/python/src/qvm/mod.rs @@ -36,7 +36,8 @@ py_wrap_type! { impl PyQvmResultData { #[new] fn new(memory: HashMap) -> Self { - let memory = memory.into_iter() + let memory = memory + .into_iter() .map(|(key, value)| (key, value.into_inner())) .collect(); Self::from(QvmResultData::from_memory_map(memory)) diff --git a/crates/python/src/register_data.rs b/crates/python/src/register_data.rs index c2f5e2645..b045a3f73 100644 --- a/crates/python/src/register_data.rs +++ b/crates/python/src/register_data.rs @@ -1,6 +1,9 @@ -use numpy::{PyArray, ToPyArray}; -use numpy::ndarray::IxDyn; -use pyo3::{types::{PyComplex, PyFloat, PyInt}, Py, pymethods, Python, PyAny, PyResult, PyErr, ToPyObject, PyObject}; +use numpy::PyArray; +use pyo3::{ + pymethods, + types::{PyComplex, PyFloat, PyInt}, + Py, PyErr, PyObject, PyResult, Python, ToPyObject, +}; use qcs::RegisterData; use rigetti_pyo3::{py_wrap_union_enum, PyWrapper}; @@ -17,10 +20,19 @@ py_wrap_union_enum! { impl PyRegisterData { pub fn as_ndarray(&self, py: Python<'_>) -> PyResult { match self.as_inner() { - RegisterData::I8(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), - RegisterData::F64(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), - RegisterData::I16(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), - RegisterData::Complex32(matrix) => PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)), - }.map_err(PyErr::from) + RegisterData::I8(matrix) => { + PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)) + } + RegisterData::F64(matrix) => { + PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)) + } + RegisterData::I16(matrix) => { + PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)) + } + RegisterData::Complex32(matrix) => { + PyArray::from_vec2(py, matrix.as_slice()).map(|arr| arr.to_object(py)) + } + } + .map_err(PyErr::from) } -} \ No newline at end of file +} diff --git a/crates/python/tests/execution_data/test_execution_data.py b/crates/python/tests/execution_data/test_execution_data.py index 08f73251a..f0901bf95 100644 --- a/crates/python/tests/execution_data/test_execution_data.py +++ b/crates/python/tests/execution_data/test_execution_data.py @@ -64,9 +64,7 @@ def test_integer(self): register_matrix = RegisterMatrix.from_integer(m) assert register_matrix.is_integer() register_matrix = register_matrix.as_integer() - assert ( - register_matrix is not None - ), "register_matrix should be an integer matrix" + assert register_matrix is not None, "register_matrix should be an integer matrix" assert_array_equal(register_matrix, m) def test_real(self): @@ -89,3 +87,21 @@ def test_complex(self): register_matrix = register_matrix.as_complex() assert register_matrix is not None, "register_matrix should be a complex matrix" assert_array_equal(register_matrix, m) + + +class TestRegisterMap: + def test_iter(self): + memory_map = { + "ro": RegisterData.from_i16([[0, 1, 2], [1, 2, 3]]), + "foo": RegisterData.from_i16([[0, 1, 2], [1, 2, 3]]), + } + qvm_result_data = QVMResultData.from_memory_map(memory_map) + result_data = ResultData.from_qvm(qvm_result_data) + register_map = result_data.to_register_map() + expected_keys = {"ro", "foo"} + actual_keys = set() + for key, matrix in register_map.items(): + actual_keys.add(key) + assert np.all(matrix.to_ndarray() == np.matrix([[0, 1, 2], [1, 2, 3]])) + + assert expected_keys == actual_keys == set(register_map.keys()) From 19542bdcf479a145afd90ff7e3ea827669ff2d9b Mon Sep 17 00:00:00 2001 From: marquessv Date: Thu, 10 Aug 2023 11:07:28 -0700 Subject: [PATCH 04/22] remove dbg print --- crates/lib/src/qpu/api.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/lib/src/qpu/api.rs b/crates/lib/src/qpu/api.rs index 6cc4cb886..e9cb42aab 100644 --- a/crates/lib/src/qpu/api.rs +++ b/crates/lib/src/qpu/api.rs @@ -169,10 +169,7 @@ pub async fn retrieve_results( .map_err(QpuApiError::from) .and_then( |result| match controller_job_execution_result::Status::from_i32(result.status) { - Some(controller_job_execution_result::Status::Success) => { - dbg!(&result); - Ok(result) - } + Some(controller_job_execution_result::Status::Success) => Ok(result), status => Err(QpuApiError::JobExecutionFailed { status: status .map_or("UNDEFINED", |status| status.as_str_name()) From e665246b3dffb0163603e529f0eaf4effcb3a4cd Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 10:18:20 -0700 Subject: [PATCH 05/22] feat(python)!: The ExecutionData now takes a datetime duration --- crates/python/src/execution_data.rs | 30 +++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index 38e6a6b56..a10ea74be 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -9,8 +9,8 @@ use pyo3::{ use pyo3::{IntoPy, PyAny}; use qcs::{ExecutionData, RegisterMap, RegisterMatrix, ResultData}; use rigetti_pyo3::{ - py_wrap_data_struct, py_wrap_error, py_wrap_type, py_wrap_union_enum, wrap_error, PyTryFrom, - PyWrapper, ToPython, ToPythonError, + impl_repr, py_wrap_data_struct, py_wrap_error, py_wrap_type, py_wrap_union_enum, wrap_error, + PyTryFrom, PyWrapper, ToPython, ToPythonError, }; use crate::qpu::PyQpuResultData; @@ -22,6 +22,7 @@ py_wrap_union_enum! { qvm: Qvm => PyQvmResultData } } +impl_repr!(PyQpuResultData); wrap_error!(RustRegisterMatrixConversionError( qcs::RegisterMatrixConversionError @@ -50,14 +51,28 @@ py_wrap_data_struct! { duration: Option => Option> } } +impl_repr!(PyExecutionData); #[pymethods] impl PyExecutionData { #[new] - fn __new__(py: Python<'_>, result_data: PyResultData, duration: Option) -> PyResult { + fn __new__( + py: Python<'_>, + result_data: PyResultData, + duration: Option>, + ) -> PyResult { Ok(Self(ExecutionData { result_data: ResultData::py_try_from(py, &result_data)?, - duration: duration.map(Duration::from_micros), + duration: match duration { + None => None, + Some(delta) => Some( + delta + .as_ref(py) + .call_method0("total_seconds") + .map(|result| result.extract::())? + .map(Duration::from_secs_f64)?, + ), + }, })) } } @@ -66,10 +81,12 @@ py_wrap_type! { #[pyo3(mapping)] PyRegisterMap(RegisterMap) as "RegisterMap"; } +impl_repr!(PyRegisterMap); py_wrap_type! { PyRegisterMatrix(RegisterMatrix) as "RegisterMatrix" } +impl_repr!(PyRegisterMatrix); #[pymethods] impl PyRegisterMatrix { @@ -234,6 +251,11 @@ impl PyRegisterMapItemsIter { } } +// The keys and values iterators are built on the iterator of the full +// `HashMap`, because the iterators returned by `keys()` and `values()` +// return an iterator with a _reference_ to the underlying `HashMap`. +// The reference would require these structs to specify a lifetime, +// which is incompatible with `#[pyclass]`. #[pyclass] pub struct PyRegisterMapKeysIter { inner: std::collections::hash_map::IntoIter, From 697ed8ad0dc7a85c916e139bc1d329d2be34eb80 Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 10:19:11 -0700 Subject: [PATCH 06/22] feat: ExecutionResult now has a `from_register` constructor --- crates/python/src/qpu/api.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/python/src/qpu/api.rs b/crates/python/src/qpu/api.rs index 60f948030..ded1082cc 100644 --- a/crates/python/src/qpu/api.rs +++ b/crates/python/src/qpu/api.rs @@ -158,6 +158,25 @@ impl From for ExecutionResult { } } +#[pymethods] +impl ExecutionResult { + #[staticmethod] + fn from_register(register: PyRegister) -> Self { + match register.as_inner() { + Register::I32(values) => ExecutionResult { + shape: [values.len(), 1], + dtype: "integer".into(), + data: register, + }, + Register::Complex32(values) => ExecutionResult { + shape: [values.len(), 1], + dtype: "complex".into(), + data: register, + }, + } + } +} + #[pyclass] #[derive(Clone, Debug)] pub struct ExecutionResults { @@ -169,6 +188,20 @@ pub struct ExecutionResults { pub execution_duration_microseconds: Option, } +#[pymethods] +impl ExecutionResults { + #[new] + fn new( + buffers: HashMap, + execution_duration_microseconds: Option, + ) -> Self { + Self { + buffers, + execution_duration_microseconds, + } + } +} + impl From for ExecutionResults { fn from(value: ControllerJobExecutionResult) -> Self { let buffers = value From 57a514ce7ed13f83974b73964621400803507336 Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 10:19:43 -0700 Subject: [PATCH 07/22] feat(python): QpuResultData now has an `asdict()` method for retrieving the results as a flattened hashmap --- crates/python/src/qpu/result_data.rs | 27 +++++++++++++++++++++++++-- crates/python/tests/qpu/test_qpu.py | 7 ++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/python/src/qpu/result_data.rs b/crates/python/src/qpu/result_data.rs index c95e434d5..b66bd0a47 100644 --- a/crates/python/src/qpu/result_data.rs +++ b/crates/python/src/qpu/result_data.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; use pyo3::{ pymethods, - types::{PyComplex, PyFloat, PyInt}, - Py, PyResult, Python, + types::{PyComplex, PyDict, PyFloat, PyInt}, + Py, PyAny, PyResult, Python, ToPyObject, }; use qcs::qpu::{QpuResultData, ReadoutValues}; use rigetti_pyo3::{py_wrap_type, py_wrap_union_enum, PyTryFrom, PyWrapper, ToPython}; @@ -43,4 +43,27 @@ impl PyQpuResultData { fn readout_values(&self, py: Python<'_>) -> PyResult> { self.as_inner().readout_values().to_python(py) } + + fn asdict(&self, py: Python<'_>) -> PyResult> { + let dict = PyDict::new(py); + dict.set_item("mappings", self.mappings(py)?)?; + dict.set_item( + "readout_values", + self.as_inner() + .readout_values() + .iter() + .map(|(register, values)| { + ( + register.to_string(), + match values { + ReadoutValues::Integer(values) => values.to_object(py), + ReadoutValues::Real(values) => values.to_object(py), + ReadoutValues::Complex(values) => values.to_object(py), + }, + ) + }) + .collect::>>(), + )?; + Ok(dict.into()) + } } diff --git a/crates/python/tests/qpu/test_qpu.py b/crates/python/tests/qpu/test_qpu.py index 1478b6c1c..888cb854c 100644 --- a/crates/python/tests/qpu/test_qpu.py +++ b/crates/python/tests/qpu/test_qpu.py @@ -8,6 +8,7 @@ list_quantum_processors_async, ) + def test_readout_values(): inner = [0, 1] readout_values = ReadoutValues.from_integer(inner) @@ -23,12 +24,13 @@ def test_readout_values(): def test_qpu_result_data(): - mappings = { "a": "_q0" } - readout_values = { "a": ReadoutValues.from_integer([0, 1]) } + mappings = {"a": "_q0"} + readout_values = {"a": ReadoutValues.from_integer([0, 1])} result_data = QPUResultData(mappings, readout_values) assert result_data.mappings == mappings assert result_data.readout_values["a"].as_integer() == readout_values["a"].as_integer() + assert result_data.asdict() == {"mappings": {"a": "_q0"}, "readout_values": {"a": [0, 1]}} @pytest.mark.qcs_session @@ -47,4 +49,3 @@ def test_list_quantum_processors_timeout(): async def test_list_quantum_processors_async(): quantum_processor_ids = await list_quantum_processors_async() assert len(quantum_processor_ids) > 0 - From 212b001348bb14876803f3521d3ad02f2b384e38 Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 11:50:16 -0700 Subject: [PATCH 08/22] update typehints --- crates/python/qcs_sdk/__init__.pyi | 15 ++++++++++++++- crates/python/qcs_sdk/qpu/__init__.pyi | 16 +++++++++++----- crates/python/qcs_sdk/qpu/api.pyi | 3 +++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/crates/python/qcs_sdk/__init__.pyi b/crates/python/qcs_sdk/__init__.pyi index aabd7ec9b..57b258936 100644 --- a/crates/python/qcs_sdk/__init__.pyi +++ b/crates/python/qcs_sdk/__init__.pyi @@ -1,6 +1,6 @@ import datetime from enum import Enum -from typing import Dict, List, Sequence, Optional, Union, final +from typing import Dict, List, Sequence, Optional, Union, final, Iterable, Tuple import numpy as np from numpy.typing import NDArray @@ -205,6 +205,11 @@ class RegisterMatrix: def from_real(inner: NDArray[np.float64]) -> "RegisterMatrix": ... @staticmethod def from_complex(inner: NDArray[np.complex128]) -> "RegisterMatrix": ... + def to_ndarray(self) -> Union[NDArray[np.complex128], NDArray[np.int64], NDArray[np.float64]]: + """ + Get the RegisterMatrix as numpy ``ndarray``. + """ + ... @final class RegisterMap: @@ -213,6 +218,14 @@ class RegisterMap: def get_register_matrix(self, register_name: str) -> Optional[RegisterMatrix]: """Get the ``RegisterMatrix`` for the given register. Returns `None` if the register doesn't exist.""" ... + def get(self, default: Optional[RegisterMatrix] = None) -> Optional[RegisterMatrix]: ... + def items(self) -> Iterable[Tuple[str, RegisterMatrix]]: ... + def keys(self) -> Iterable[str]: ... + def values(self) -> Iterable[RegisterMatrix]: ... + def __iter__(self) -> Iterable[str]: ... + def __getitem__(self, item: str) -> RegisterMatrix: ... + def __contains__(self, key: str) -> bool: ... + def __len__(self) -> int: ... @final class ResultData: diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 43a8b6cfb..96945a5f5 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -1,4 +1,4 @@ -from typing import Dict, List, Mapping, Sequence, Optional, Union, final +from typing import Dict, List, Mapping, Sequence, Optional, TypedDict, Union, final from qcs_sdk.client import QCSClient @@ -32,7 +32,6 @@ class ReadoutValues: def __new__(cls, values: Union[List[int], List[float], List[complex]]): """Construct a new ReadoutValues from a list of values.""" ... - def inner(self) -> Union[List[int], List[float], List[complex]]: """Return the inner list of readout values.""" ... @@ -58,9 +57,7 @@ class QPUResultData: Encapsulates data returned from the QPU after executing a job. """ - def __new__( - cls, mappings: Mapping[str, str], readout_values: Mapping[str, ReadoutValues] - ): ... + def __new__(cls, mappings: Mapping[str, str], readout_values: Mapping[str, ReadoutValues]): ... @property def mappings(self) -> Dict[str, str]: """ @@ -73,6 +70,15 @@ class QPUResultData: Get the mappings of a readout values identifier (ie. "q0") to a set of ``ReadoutValues`` """ ... + def asdict( + self, + ) -> QPUResultDataDict: + """ """ + ... + +class QPUResultDataDict(TypedDict): + mappings: Dict[str, str] + readout_values: Dict[str, Union[List[int], List[complex], List[float]]] class ListQuantumProcessorsError(RuntimeError): """A request to list available Quantum Processors failed.""" diff --git a/crates/python/qcs_sdk/qpu/api.pyi b/crates/python/qcs_sdk/qpu/api.pyi index 39e7041b8..7bb899d23 100644 --- a/crates/python/qcs_sdk/qpu/api.pyi +++ b/crates/python/qcs_sdk/qpu/api.pyi @@ -48,6 +48,9 @@ class Register: class ExecutionResult: """Execution readout data from a particular memory location.""" + @staticmethod + def from_register(register: Register) -> "ExecutionResult": + """Build an `ExecutionResult` from a `Register`.""" @property def shape(self) -> List[int]: """The shape of the result data.""" From 6e888da7e7c59db45c3575e651b687fd9c477c8b Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 12:18:44 -0700 Subject: [PATCH 09/22] update stubtest allowlist --- crates/python/.stubtest-allowlist | 3 ++- crates/python/qcs_sdk/qpu/__init__.pyi | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/python/.stubtest-allowlist b/crates/python/.stubtest-allowlist index d69c5b1df..b05747448 100644 --- a/crates/python/.stubtest-allowlist +++ b/crates/python/.stubtest-allowlist @@ -1,3 +1,4 @@ -qcs_sdk.qcs_sdk qcs_sdk._gather_diagnostics qcs_sdk.diagnostics +qcs_sdk.qcs_sdk +qcs_sdk.qpu.QPUResultDataDict diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 96945a5f5..56f11f0a6 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -76,9 +76,10 @@ class QPUResultData: """ """ ... -class QPUResultDataDict(TypedDict): - mappings: Dict[str, str] - readout_values: Dict[str, Union[List[int], List[complex], List[float]]] +QPUResultDataDict = TypedDict( + "QPUResultDataDict", + {"mappings": Dict[str, str], "readout_values": Dict[str, Union[List[int], List[complex], List[float]]]}, +) class ListQuantumProcessorsError(RuntimeError): """A request to list available Quantum Processors failed.""" From 7e6ceed1e5e98f83a7b5a4cf71a748207e7a0cfb Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 16:30:34 -0700 Subject: [PATCH 10/22] replace asdict with to_raw_readout_data methods --- crates/python/.stubtest-allowlist | 1 - crates/python/qcs_sdk/qpu/__init__.pyi | 32 ++++++++++++++----- crates/python/qcs_sdk/qvm/__init__.pyi | 19 +++++++++++- crates/python/src/qpu/mod.rs | 3 +- crates/python/src/qpu/result_data.rs | 43 +++++++++++++++++--------- crates/python/src/qvm/mod.rs | 37 +++++++++++++++++++++- crates/python/tests/qvm/test_qvm.py | 8 +++++ 7 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 crates/python/tests/qvm/test_qvm.py diff --git a/crates/python/.stubtest-allowlist b/crates/python/.stubtest-allowlist index b05747448..5bae07b5f 100644 --- a/crates/python/.stubtest-allowlist +++ b/crates/python/.stubtest-allowlist @@ -1,4 +1,3 @@ qcs_sdk._gather_diagnostics qcs_sdk.diagnostics qcs_sdk.qcs_sdk -qcs_sdk.qpu.QPUResultDataDict diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 56f11f0a6..452d66f51 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -1,4 +1,4 @@ -from typing import Dict, List, Mapping, Sequence, Optional, TypedDict, Union, final +from typing import Dict, List, Mapping, Sequence, Optional, Union, final, Any from qcs_sdk.client import QCSClient @@ -55,6 +55,11 @@ class ReadoutValues: class QPUResultData: """ Encapsulates data returned from the QPU after executing a job. + + ``QPUResultData`` contains "mappings", which map declared memory regions + in a program (ie. "ro[0]") to that regions readout key in "readout_values". + "readout_values" maps those readout keys to the values emitted for that region + across all shots. """ def __new__(cls, mappings: Mapping[str, str], readout_values: Mapping[str, ReadoutValues]): ... @@ -70,16 +75,27 @@ class QPUResultData: Get the mappings of a readout values identifier (ie. "q0") to a set of ``ReadoutValues`` """ ... - def asdict( + def to_raw_readout_data( self, - ) -> QPUResultDataDict: - """ """ + ) -> RawQPUReadoutData: + """ + Get a copy of this result data flattened into a ``RawQPUReadoutData`` + """ ... -QPUResultDataDict = TypedDict( - "QPUResultDataDict", - {"mappings": Dict[str, str], "readout_values": Dict[str, Union[List[int], List[complex], List[float]]]}, -) +class RawQPUReadoutData: + @property + def mapping(self) -> Dict[str, str]: + """ + Get the mappings of a memory region (ie. "ro[0]") to it's key name in readout_values + """ + ... + @property + def readout_values(self) -> Dict[str, Union[List[int], List[float], List[complex]]]: + """ + Get the mappings of a readout values identifier (ie. "q0") to a list of those readout values + """ + ... class ListQuantumProcessorsError(RuntimeError): """A request to list available Quantum Processors failed.""" diff --git a/crates/python/qcs_sdk/qvm/__init__.pyi b/crates/python/qcs_sdk/qvm/__init__.pyi index 826983518..b25a82d4d 100644 --- a/crates/python/qcs_sdk/qvm/__init__.pyi +++ b/crates/python/qcs_sdk/qvm/__init__.pyi @@ -1,4 +1,4 @@ -from typing import final, Mapping, Optional, Sequence, Tuple, Union +from typing import final, Mapping, Optional, Sequence, Tuple, Union, Dict, List from qcs_sdk import RegisterData, QCSClient @@ -24,6 +24,23 @@ class QVMResultData: Get the mapping of register names (ie. "ro") to a ``RegisterData`` matrix containing the register values. """ ... + @property + def to_raw_readout_data( + self, + ) -> RawQVMReadoutData: + """ + Get a copy of this result data flattened into a ``RawQVMReadoutData`` + """ + ... + +@final +class RawQVMReadoutData: + @property + def memory(self) -> Dict[str, Union[List[List[int]], List[List[float]]]]: + """ + The mapping of register names (ie. "ro") to a 2-d list containing the + values for that register. + """ @final class QVMOptions: diff --git a/crates/python/src/qpu/mod.rs b/crates/python/src/qpu/mod.rs index 283481dce..a77054551 100644 --- a/crates/python/src/qpu/mod.rs +++ b/crates/python/src/qpu/mod.rs @@ -3,7 +3,7 @@ use std::time::Duration; use pyo3::{exceptions::PyRuntimeError, pyfunction, PyResult}; use rigetti_pyo3::{create_init_submodule, py_wrap_error, wrap_error, ToPythonError}; -pub use result_data::{PyQpuResultData, PyReadoutValues}; +pub use result_data::{PyQpuResultData, PyReadoutValues, RawQpuReadoutData}; pub mod api; pub mod isa; @@ -17,6 +17,7 @@ use crate::py_sync::py_function_sync_async; create_init_submodule! { classes: [ PyQpuResultData, + RawQpuReadoutData, PyReadoutValues ], errors: [ diff --git a/crates/python/src/qpu/result_data.rs b/crates/python/src/qpu/result_data.rs index b66bd0a47..fb32d66c7 100644 --- a/crates/python/src/qpu/result_data.rs +++ b/crates/python/src/qpu/result_data.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; use pyo3::{ - pymethods, - types::{PyComplex, PyDict, PyFloat, PyInt}, - Py, PyAny, PyResult, Python, ToPyObject, + pyclass, pymethods, + types::{PyComplex, PyFloat, PyInt, PyList}, + IntoPy, Py, PyResult, Python, }; use qcs::qpu::{QpuResultData, ReadoutValues}; use rigetti_pyo3::{py_wrap_type, py_wrap_union_enum, PyTryFrom, PyWrapper, ToPython}; @@ -44,26 +44,39 @@ impl PyQpuResultData { self.as_inner().readout_values().to_python(py) } - fn asdict(&self, py: Python<'_>) -> PyResult> { - let dict = PyDict::new(py); - dict.set_item("mappings", self.mappings(py)?)?; - dict.set_item( - "readout_values", - self.as_inner() + fn to_raw_readout_data(&self, py: Python<'_>) -> RawQpuReadoutData { + RawQpuReadoutData { + mappings: self.as_inner().mappings().clone(), + readout_values: self + .as_inner() .readout_values() .iter() .map(|(register, values)| { ( register.to_string(), match values { - ReadoutValues::Integer(values) => values.to_object(py), - ReadoutValues::Real(values) => values.to_object(py), - ReadoutValues::Complex(values) => values.to_object(py), + ReadoutValues::Integer(values) => PyList::new(py, values).into_py(py), + ReadoutValues::Real(values) => PyList::new(py, values).into_py(py), + ReadoutValues::Complex(values) => PyList::new(py, values).into_py(py), }, ) }) - .collect::>>(), - )?; - Ok(dict.into()) + .collect::>>(), + } + } +} + +#[derive(Debug)] +#[pyclass(name = "RawQPUReadoutData")] +pub struct RawQpuReadoutData { + #[pyo3(get)] + mappings: HashMap, + #[pyo3(get)] + readout_values: HashMap>, +} + +impl RawQpuReadoutData { + fn __repr__(&self) -> String { + format!("{self:?}") } } diff --git a/crates/python/src/qvm/mod.rs b/crates/python/src/qvm/mod.rs index ab0195ecc..df2e24661 100644 --- a/crates/python/src/qvm/mod.rs +++ b/crates/python/src/qvm/mod.rs @@ -1,3 +1,4 @@ +use pyo3::types::PyList; use qcs::{ qvm::{QvmOptions, QvmResultData}, RegisterData, @@ -17,7 +18,7 @@ mod api; use api::PyAddressRequest; create_init_submodule! { - classes: [PyQvmResultData, PyQvmOptions], + classes: [PyQvmResultData, PyQvmOptions, RawQvmReadoutData], errors: [QVMError], funcs: [py_run, py_run_async], submodules: [ @@ -57,6 +58,40 @@ impl PyQvmResultData { fn memory(&self, py: Python<'_>) -> PyResult> { self.as_inner().memory().to_python(py) } + + fn to_raw_readout_data(&self, py: Python<'_>) -> RawQvmReadoutData { + RawQvmReadoutData { + memory: self + .as_inner() + .memory() + .iter() + .map(|(register, matrix)| { + ( + register.to_string(), + match matrix { + RegisterData::I8(matrix) => PyList::new(py, matrix).into_py(py), + RegisterData::F64(matrix) => PyList::new(py, matrix).into_py(py), + RegisterData::I16(matrix) => PyList::new(py, matrix).into_py(py), + RegisterData::Complex32(matrix) => PyList::new(py, matrix).into_py(py), + }, + ) + }) + .collect::>>(), + } + } +} + +#[pyclass(name = "RawQVMReadoutData")] +#[derive(Debug)] +struct RawQvmReadoutData { + #[pyo3(get)] + memory: HashMap>, +} + +impl RawQvmReadoutData { + fn __repr__(&self) -> String { + format!("{self:?}") + } } py_wrap_type! { diff --git a/crates/python/tests/qvm/test_qvm.py b/crates/python/tests/qvm/test_qvm.py new file mode 100644 index 000000000..af539174f --- /dev/null +++ b/crates/python/tests/qvm/test_qvm.py @@ -0,0 +1,8 @@ +from qcs_sdk import RegisterData +from qcs_sdk.qvm import QVMResultData + + +def test_qvm_result_data(): + register_data = RegisterData.from_i8([[1, 2, 3], [4, 5, 6]]) + result_data = QVMResultData({"ro": register_data}) + assert result_data.asdict() == {"memory": {"ro": [[1, 2, 3], [4, 5, 6]]}} From f5edafddb1d1e353eb2054b5427398e23cec45ed Mon Sep 17 00:00:00 2001 From: marquessv Date: Mon, 14 Aug 2023 16:44:25 -0700 Subject: [PATCH 11/22] update tests that used asdict --- crates/python/tests/qpu/test_qpu.py | 4 +++- crates/python/tests/qvm/test_qvm.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/python/tests/qpu/test_qpu.py b/crates/python/tests/qpu/test_qpu.py index 888cb854c..7f64f5b04 100644 --- a/crates/python/tests/qpu/test_qpu.py +++ b/crates/python/tests/qpu/test_qpu.py @@ -30,7 +30,9 @@ def test_qpu_result_data(): assert result_data.mappings == mappings assert result_data.readout_values["a"].as_integer() == readout_values["a"].as_integer() - assert result_data.asdict() == {"mappings": {"a": "_q0"}, "readout_values": {"a": [0, 1]}} + raw_data = result_data.to_raw_readout_data() + assert raw_data.mappings == {"a": "_q0"} + assert raw_data.readout_values == {"a": [0, 1]} @pytest.mark.qcs_session diff --git a/crates/python/tests/qvm/test_qvm.py b/crates/python/tests/qvm/test_qvm.py index af539174f..c3ca7a959 100644 --- a/crates/python/tests/qvm/test_qvm.py +++ b/crates/python/tests/qvm/test_qvm.py @@ -4,5 +4,5 @@ def test_qvm_result_data(): register_data = RegisterData.from_i8([[1, 2, 3], [4, 5, 6]]) - result_data = QVMResultData({"ro": register_data}) - assert result_data.asdict() == {"memory": {"ro": [[1, 2, 3], [4, 5, 6]]}} + raw_data = QVMResultData({"ro": register_data}).to_raw_readout_data() + assert raw_data.memory == {"ro": [[1, 2, 3], [4, 5, 6]]} From ee52b6021fa61f04bcf15fd36816ddfe73ef39f7 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 10:05:18 -0700 Subject: [PATCH 12/22] fix type hints --- crates/python/qcs_sdk/qpu/__init__.pyi | 3 ++- crates/python/qcs_sdk/qvm/__init__.pyi | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 452d66f51..9c7bd6644 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -83,9 +83,10 @@ class QPUResultData: """ ... +@final class RawQPUReadoutData: @property - def mapping(self) -> Dict[str, str]: + def mappings(self) -> Dict[str, str]: """ Get the mappings of a memory region (ie. "ro[0]") to it's key name in readout_values """ diff --git a/crates/python/qcs_sdk/qvm/__init__.pyi b/crates/python/qcs_sdk/qvm/__init__.pyi index b25a82d4d..0e77908a4 100644 --- a/crates/python/qcs_sdk/qvm/__init__.pyi +++ b/crates/python/qcs_sdk/qvm/__init__.pyi @@ -24,7 +24,6 @@ class QVMResultData: Get the mapping of register names (ie. "ro") to a ``RegisterData`` matrix containing the register values. """ ... - @property def to_raw_readout_data( self, ) -> RawQVMReadoutData: From dd58e12b93e8cf4f346a78dc24e90645705e7636 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 10:15:01 -0700 Subject: [PATCH 13/22] simplify mapping and input arguments --- crates/python/src/execution_data.rs | 21 +++++++++------------ crates/python/src/qpu/result_data.rs | 4 ++++ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index a10ea74be..380ab2ca8 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -63,16 +63,13 @@ impl PyExecutionData { ) -> PyResult { Ok(Self(ExecutionData { result_data: ResultData::py_try_from(py, &result_data)?, - duration: match duration { - None => None, - Some(delta) => Some( - delta - .as_ref(py) - .call_method0("total_seconds") - .map(|result| result.extract::())? - .map(Duration::from_secs_f64)?, - ), - }, + duration: duration.map(|delta| { + delta + .as_ref(py) + .call_method0("total_seconds") + .map(|result| result.extract::())? + .map(Duration::from_secs_f64) + })?, })) } } @@ -186,8 +183,8 @@ impl PyRegisterMap { self.as_inner().0.len() } - pub fn __contains__(&self, key: String) -> bool { - self.as_inner().0.contains_key(&key) + pub fn __contains__(&self, key: &str) -> bool { + self.as_inner().0.contains_key(key) } pub fn __getitem__(&self, item: &str) -> PyResult { diff --git a/crates/python/src/qpu/result_data.rs b/crates/python/src/qpu/result_data.rs index fb32d66c7..1a3edfa53 100644 --- a/crates/python/src/qpu/result_data.rs +++ b/crates/python/src/qpu/result_data.rs @@ -66,6 +66,10 @@ impl PyQpuResultData { } } +/// A wrapper type for data returned by the QPU in a more flat structure than +/// [`PyQpuResultData`] offers. This makes it more convenient to work with +/// the data if you don't care what type of number the readout values for +/// each register contains. #[derive(Debug)] #[pyclass(name = "RawQPUReadoutData")] pub struct RawQpuReadoutData { From 90c8916105a15a3a7eef988685db7cc0a939ce2f Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 11:00:26 -0700 Subject: [PATCH 14/22] cleanup --- crates/python/src/execution_data.rs | 10 +++++----- crates/python/src/grpc/models/controller.rs | 21 --------------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index 380ab2ca8..ddaef07c7 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -88,11 +88,11 @@ impl_repr!(PyRegisterMatrix); #[pymethods] impl PyRegisterMatrix { fn to_ndarray(&self, py: Python<'_>) -> Py { - self.as_integer(py) - .map(|array| array.into_py(py)) - .or(self.as_real(py).map(|array| array.into_py(py))) - .or(self.as_complex(py).map(|array| array.into_py(py))) - .expect("A RegisterMatrix can't be any other type.") + match self.as_inner() { + RegisterMatrix::Integer(_) => self.to_integer(py).into_py(py), + RegisterMatrix::Real(_) => self.to_real(py).into_py(py), + RegisterMatrix::Complex(_) => self.to_complex(py).into_py(py), + } } #[staticmethod] diff --git a/crates/python/src/grpc/models/controller.rs b/crates/python/src/grpc/models/controller.rs index c2e5d1abc..452f3a507 100644 --- a/crates/python/src/grpc/models/controller.rs +++ b/crates/python/src/grpc/models/controller.rs @@ -15,27 +15,6 @@ py_wrap_data_struct! { } } -#[pymethods] -impl PyReadoutValues { - pub fn as_ndarray(&self, py: Python<'_>) -> PyResult> { - match &self.as_inner().values { - None => Ok(None), - Some(Values::IntegerValues(ints)) => { - Ok(Some(PyArray::from_slice(py, &ints.values).to_object(py))) - } - Some(Values::ComplexValues(complex)) => Ok(Some( - PyArray::from_iter(py, { - complex - .values - .iter() - .map(|value| NumComplex32::new(value.real, value.imaginary)) - }) - .to_object(py), - )), - } - } -} - py_wrap_union_enum! { PyReadoutValuesValues(Values) as "ReadoutValuesValues" { integer_values: IntegerValues => PyIntegerReadoutValues, From e3a9eb76f28118823dd2e1e836ee681fe98ce678 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 11:05:44 -0700 Subject: [PATCH 15/22] update rustls --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96cd6ff1b..f34a90f1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2420,9 +2420,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.0" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d" +checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" dependencies = [ "log", "ring", @@ -2465,9 +2465,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" dependencies = [ "ring", "untrusted", @@ -3002,7 +3002,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" dependencies = [ - "rustls 0.21.0", + "rustls 0.21.6", "tokio", ] From fd54ecbabedd20528c0ee7c557aa8c875f486b88 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 11:21:46 -0700 Subject: [PATCH 16/22] fix compilation errors --- crates/python/src/execution_data.rs | 29 +++++++++++---------- crates/python/src/grpc/models/controller.rs | 3 +-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index ddaef07c7..14771ddef 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -3,10 +3,9 @@ use std::time::Duration; use numpy::{Complex64, PyArray2}; use pyo3::exceptions::PyKeyError; use pyo3::{ - exceptions::PyValueError, pyclass, pymethods, types::PyDelta, Py, PyRef, PyRefMut, PyResult, - Python, + exceptions::PyValueError, pyclass, pymethods, types::PyDelta, Py, PyObject, PyRef, PyRefMut, + PyResult, Python, ToPyObject, }; -use pyo3::{IntoPy, PyAny}; use qcs::{ExecutionData, RegisterMap, RegisterMatrix, ResultData}; use rigetti_pyo3::{ impl_repr, py_wrap_data_struct, py_wrap_error, py_wrap_type, py_wrap_union_enum, wrap_error, @@ -63,13 +62,15 @@ impl PyExecutionData { ) -> PyResult { Ok(Self(ExecutionData { result_data: ResultData::py_try_from(py, &result_data)?, - duration: duration.map(|delta| { - delta - .as_ref(py) - .call_method0("total_seconds") - .map(|result| result.extract::())? - .map(Duration::from_secs_f64) - })?, + duration: duration + .map(|delta| { + delta + .as_ref(py) + .call_method0("total_seconds") + .map(|result| result.extract::())? + .map(Duration::from_secs_f64) + }) + .transpose()?, })) } } @@ -87,11 +88,11 @@ impl_repr!(PyRegisterMatrix); #[pymethods] impl PyRegisterMatrix { - fn to_ndarray(&self, py: Python<'_>) -> Py { + fn to_ndarray(&self, py: Python<'_>) -> PyResult { match self.as_inner() { - RegisterMatrix::Integer(_) => self.to_integer(py).into_py(py), - RegisterMatrix::Real(_) => self.to_real(py).into_py(py), - RegisterMatrix::Complex(_) => self.to_complex(py).into_py(py), + RegisterMatrix::Integer(_) => self.to_integer(py).map(|matrix| matrix.to_object(py)), + RegisterMatrix::Real(_) => self.to_real(py).map(|matrix| matrix.to_object(py)), + RegisterMatrix::Complex(_) => self.to_complex(py).map(|matrix| matrix.to_object(py)), } } diff --git a/crates/python/src/grpc/models/controller.rs b/crates/python/src/grpc/models/controller.rs index 452f3a507..6a28403a8 100644 --- a/crates/python/src/grpc/models/controller.rs +++ b/crates/python/src/grpc/models/controller.rs @@ -1,4 +1,3 @@ -use numpy::PyArray; use pyo3::{ prelude::*, types::{PyComplex, PyInt, PyList}, @@ -6,7 +5,7 @@ use pyo3::{ use qcs_api_client_grpc::models::controller::{ readout_values::Values, Complex64, Complex64ReadoutValues, IntegerReadoutValues, ReadoutValues, }; -use rigetti_pyo3::{num_complex::Complex32 as NumComplex32, py_wrap_struct, PyWrapper}; +use rigetti_pyo3::{num_complex::Complex32 as NumComplex32, py_wrap_struct}; use rigetti_pyo3::{py_wrap_data_struct, py_wrap_union_enum, PyTryFrom, ToPython}; py_wrap_data_struct! { From e37d41655a0858ffd64f87c59fcf00c204c4093a Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 11:31:59 -0700 Subject: [PATCH 17/22] unpin reqwest to allow webpki to be updated --- Cargo.lock | 80 +++++++++++++------------------------------ crates/lib/Cargo.toml | 2 +- 2 files changed, 24 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f34a90f1e..d16ae1228 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1000,7 +1000,7 @@ dependencies = [ "tokio", "tokio-rustls 0.22.0", "tower-service", - "webpki 0.21.4", + "webpki", ] [[package]] @@ -1017,20 +1017,21 @@ dependencies = [ "rustls-native-certs 0.5.0", "tokio", "tokio-rustls 0.22.0", - "webpki 0.21.4", + "webpki", ] [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.20.8", + "rustls 0.21.6", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", ] [[package]] @@ -2223,9 +2224,9 @@ checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ "base64 0.21.0", "bytes", @@ -2236,7 +2237,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.2", + "hyper-rustls 0.24.1", "ipnet", "js-sys", "log", @@ -2245,14 +2246,14 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.20.8", + "rustls 0.21.6", "rustls-native-certs 0.6.2", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", "tokio-socks", "tower-service", "url", @@ -2403,19 +2404,7 @@ dependencies = [ "log", "ring", "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct 0.7.0", - "webpki 0.22.0", + "webpki", ] [[package]] @@ -2982,25 +2971,14 @@ checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls 0.19.1", "tokio", - "webpki 0.21.4", + "webpki", ] [[package]] name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.8", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls 0.21.6", "tokio", @@ -3101,7 +3079,7 @@ dependencies = [ "rustls-native-certs 0.6.2", "rustls-pemfile", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -3514,24 +3492,11 @@ dependencies = [ "untrusted", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki 0.22.0", -] +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "which" @@ -3733,11 +3698,12 @@ dependencies = [ [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index f21895cc0..d365e14af 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -29,7 +29,7 @@ qcs-api-client-common.workspace = true qcs-api-client-openapi.workspace = true qcs-api-client-grpc.workspace = true quil-rs.workspace = true -reqwest = { version = "0.11.12", default-features = false, features = [ +reqwest = { version = "0.11.20", default-features = false, features = [ "rustls-tls", "json", ] } From 5fc7b2df3dee6f4782af91a67adc261e6ca22537 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 25 Aug 2023 16:00:08 -0700 Subject: [PATCH 18/22] add to_raw_readout_data method to ResultData --- Cargo.toml | 2 ++ crates/python/qcs_sdk/__init__.pyi | 9 +++++++-- crates/python/qcs_sdk/qpu/__init__.pyi | 10 +++++++++- crates/python/src/execution_data.rs | 15 +++++++++++++-- crates/python/src/qpu/result_data.rs | 2 +- crates/python/src/qvm/mod.rs | 4 ++-- 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e07b4884f..c386dc8c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = ["crates/*"] +resolver = "2" [workspace.dependencies] qcs-api = "0.2.1" @@ -27,3 +28,4 @@ pyo3 = { version = "0.17" } pyo3-asyncio = { version = "0.17", features = ["tokio-runtime"] } pyo3-build-config = { version = "0.17" } rigetti-pyo3 = { version = "0.1.0", features = ["complex"] } + diff --git a/crates/python/qcs_sdk/__init__.pyi b/crates/python/qcs_sdk/__init__.pyi index 57b258936..e0517d049 100644 --- a/crates/python/qcs_sdk/__init__.pyi +++ b/crates/python/qcs_sdk/__init__.pyi @@ -5,8 +5,8 @@ from typing import Dict, List, Sequence, Optional, Union, final, Iterable, Tuple import numpy as np from numpy.typing import NDArray -from qcs_sdk.qpu import QPUResultData -from qcs_sdk.qvm import QVMResultData +from qcs_sdk.qpu import QPUResultData, RawQpuReadoutData +from qcs_sdk.qvm import QVMResultData, RawQvmReadoutData from qcs_sdk.compiler.quilc import CompilerOpts from qcs_sdk.client import QCSClient as QCSClient @@ -299,6 +299,11 @@ class ResultData: selects the last value per-shot based on the program that was run. """ ... + def to_raw_readout_data(self) -> Union[RawQpuReadoutData, RawQvmReadoutData]: + """ + Get the raw data returned from the QVM or QPU. See ``RawQpuReadoutData`` and + ``RawQvmReadoutData`` for more information. + """ def inner( self, ) -> Union[QVMResultData, QPUResultData]: diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 9c7bd6644..ae9de3f21 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -79,12 +79,20 @@ class QPUResultData: self, ) -> RawQPUReadoutData: """ - Get a copy of this result data flattened into a ``RawQPUReadoutData`` + Get a copy of this result data flattened into a ``RawQPUReadoutData``. This reduces + the contained data down to primitive types, offering a simpler structure at the + cost of the type safety provided by ``ReadoutValues``. """ ... @final class RawQPUReadoutData: + """ + Encapsulates data returned from the QPU after executing a job. Compared to + ``QPUReadoutData``, the readout values in this class are returned as lists + of numbers instead of values wrapped by the ``ReadoutValues`` class. + """ + @property def mappings(self) -> Dict[str, str]: """ diff --git a/crates/python/src/execution_data.rs b/crates/python/src/execution_data.rs index 14771ddef..6fa5b3821 100644 --- a/crates/python/src/execution_data.rs +++ b/crates/python/src/execution_data.rs @@ -3,8 +3,8 @@ use std::time::Duration; use numpy::{Complex64, PyArray2}; use pyo3::exceptions::PyKeyError; use pyo3::{ - exceptions::PyValueError, pyclass, pymethods, types::PyDelta, Py, PyObject, PyRef, PyRefMut, - PyResult, Python, ToPyObject, + exceptions::PyValueError, pyclass, pymethods, types::PyDelta, IntoPy, Py, PyObject, PyRef, + PyRefMut, PyResult, Python, ToPyObject, }; use qcs::{ExecutionData, RegisterMap, RegisterMatrix, ResultData}; use rigetti_pyo3::{ @@ -42,6 +42,17 @@ impl PyResultData { .map_err(ToPythonError::to_py_err)? .to_python(py) } + + pub fn to_raw_readout_data(&self, py: Python) -> PyResult { + match self.as_inner() { + ResultData::Qpu(_) => self + .to_qpu(py) + .map(|data| data.to_raw_readout_data(py).into_py(py)), + ResultData::Qvm(_) => self + .to_qvm(py) + .map(|data| data.to_raw_readout_data(py).into_py(py)), + } + } } py_wrap_data_struct! { diff --git a/crates/python/src/qpu/result_data.rs b/crates/python/src/qpu/result_data.rs index 1a3edfa53..6b6388655 100644 --- a/crates/python/src/qpu/result_data.rs +++ b/crates/python/src/qpu/result_data.rs @@ -44,7 +44,7 @@ impl PyQpuResultData { self.as_inner().readout_values().to_python(py) } - fn to_raw_readout_data(&self, py: Python<'_>) -> RawQpuReadoutData { + pub(crate) fn to_raw_readout_data(&self, py: Python<'_>) -> RawQpuReadoutData { RawQpuReadoutData { mappings: self.as_inner().mappings().clone(), readout_values: self diff --git a/crates/python/src/qvm/mod.rs b/crates/python/src/qvm/mod.rs index df2e24661..546b8673e 100644 --- a/crates/python/src/qvm/mod.rs +++ b/crates/python/src/qvm/mod.rs @@ -59,7 +59,7 @@ impl PyQvmResultData { self.as_inner().memory().to_python(py) } - fn to_raw_readout_data(&self, py: Python<'_>) -> RawQvmReadoutData { + pub(crate) fn to_raw_readout_data(&self, py: Python<'_>) -> RawQvmReadoutData { RawQvmReadoutData { memory: self .as_inner() @@ -83,7 +83,7 @@ impl PyQvmResultData { #[pyclass(name = "RawQVMReadoutData")] #[derive(Debug)] -struct RawQvmReadoutData { +pub(crate) struct RawQvmReadoutData { #[pyo3(get)] memory: HashMap>, } From 4f8d12fb9373ab7ec90095236670e3b257251c39 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Mon, 28 Aug 2023 12:34:30 -0700 Subject: [PATCH 19/22] type hints, tokio fix --- crates/lib/Cargo.toml | 2 +- crates/python/qcs_sdk/__init__.pyi | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index d365e14af..633db1218 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -37,7 +37,7 @@ rmp-serde = "1.1.1" serde = { version = "1.0.145", features = ["derive"] } serde_json.workspace = true thiserror.workspace = true -tokio = { workspace = true, features = ["fs"] } +tokio = { workspace = true, features = ["fs", "rt-multi-thread"] } toml = "0.7.3" tracing = { version = "0.1", optional = true, features = ["log"] } uuid = { version = "1.2.1", features = ["v4"] } diff --git a/crates/python/qcs_sdk/__init__.pyi b/crates/python/qcs_sdk/__init__.pyi index e0517d049..e5482847a 100644 --- a/crates/python/qcs_sdk/__init__.pyi +++ b/crates/python/qcs_sdk/__init__.pyi @@ -5,8 +5,8 @@ from typing import Dict, List, Sequence, Optional, Union, final, Iterable, Tuple import numpy as np from numpy.typing import NDArray -from qcs_sdk.qpu import QPUResultData, RawQpuReadoutData -from qcs_sdk.qvm import QVMResultData, RawQvmReadoutData +from qcs_sdk.qpu import QPUResultData, RawQPUReadoutData +from qcs_sdk.qvm import QVMResultData, RawQVMReadoutData from qcs_sdk.compiler.quilc import CompilerOpts from qcs_sdk.client import QCSClient as QCSClient @@ -299,10 +299,10 @@ class ResultData: selects the last value per-shot based on the program that was run. """ ... - def to_raw_readout_data(self) -> Union[RawQpuReadoutData, RawQvmReadoutData]: + def to_raw_readout_data(self) -> Union[RawQPUReadoutData, RawQVMReadoutData]: """ - Get the raw data returned from the QVM or QPU. See ``RawQpuReadoutData`` and - ``RawQvmReadoutData`` for more information. + Get the raw data returned from the QVM or QPU. See ``RawQPUReadoutData`` and + ``RawQVMReadoutData`` for more information. """ def inner( self, @@ -351,6 +351,7 @@ class RegisterData: """ + def __new__(cls, inner: Union[List[List[int]], List[List[float]], List[List[complex]]]) -> "RegisterData": ... def inner( self, ) -> Union[List[List[int]], List[List[float]], List[List[complex]]]: From a1391c39736089e1f625b8438370ec21b5741350 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Mon, 28 Aug 2023 12:46:21 -0700 Subject: [PATCH 20/22] ignore `webpki` advisory --- deny.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deny.toml b/deny.toml index 8c34ee53e..ea1c58ef3 100644 --- a/deny.toml +++ b/deny.toml @@ -24,7 +24,11 @@ yanked = "deny" notice = "deny" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. -ignore = [] +ignore = [ + RUSTSEC-2023-0052, # Introduced by transitive dependency `webpki`. + # `hyper-proxy`, then `qcs-api-client-rust` need to update in order to remove + # `webpki`. +] # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: From 3feb3c1ef9c537ee6b30d4e6f80122cdbd5d3ad7 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Mon, 28 Aug 2023 13:02:09 -0700 Subject: [PATCH 21/22] that needs quotes --- deny.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deny.toml b/deny.toml index ea1c58ef3..b925716b4 100644 --- a/deny.toml +++ b/deny.toml @@ -25,9 +25,9 @@ notice = "deny" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ - RUSTSEC-2023-0052, # Introduced by transitive dependency `webpki`. - # `hyper-proxy`, then `qcs-api-client-rust` need to update in order to remove - # `webpki`. + "RUSTSEC-2023-0052", # Introduced by transitive dependency `webpki`. + # `hyper-proxy`, then `qcs-api-client-rust` need to update in order to remove + # `webpki`. ] # This section is considered when running `cargo deny check licenses` From ca2a24244509f5e0b4d19cb88e55c79c0e6f098d Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Tue, 29 Aug 2023 09:28:19 -0700 Subject: [PATCH 22/22] allow missing panic docs for function --- crates/lib/src/executable.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/lib/src/executable.rs b/crates/lib/src/executable.rs index 6f02d034f..43880f300 100644 --- a/crates/lib/src/executable.rs +++ b/crates/lib/src/executable.rs @@ -111,6 +111,7 @@ impl<'executable> Executable<'executable, '_> { /// 1. `quil` is a string slice representing the original program to be run. The returned /// [`Executable`] will only live as long as this reference. #[must_use] + #[allow(clippy::missing_panics_doc)] pub fn from_quil>>(quil: Quil) -> Self { Self { quil: quil.into(),