roqoqo_qryd/
lib.rs

1// Copyright © 2021-2022 HQS Quantum Simulations GmbH. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the
9// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
10// express or implied. See the License for the specific language governing permissions and
11// limitations under the License.
12//
13
14#![deny(missing_docs)]
15#![warn(rustdoc::private_intra_doc_links)]
16#![warn(rustdoc::missing_crate_level_docs)]
17#![warn(rustdoc::missing_doc_code_examples)]
18#![warn(rustdoc::private_doc_tests)]
19#![deny(missing_debug_implementations)]
20
21//! # roqoqo-qryd
22//!
23//! The `roqoqo-qryd` rust crate implements [qoqo](https://github.com/HQSquantumsimulations/qoqo) support for quantum computers and quantum computer emulators of the [QRydDemo](https://thequantumlaend.de/qryddemo/) project.
24//!
25//! The QRydDemo project builds on Quantum computers using Rydberg atoms.
26//! qoqo is quantum computing toolkit by [HQS Quantum Simulations](https://quantumsimulations.de).
27//!
28//! The roqoqo-qryd package contains the following functionality:
29//!
30//! ### Interface to the current QRydDemo WebAPI
31//!
32//! At the moment QRydDemo WebAPI allows access to Quantum Hardware Emulators of different device topology. roqoqo-qryd supports interfacing with the corresponding [REST-API](https://api.qryddemo.itp3.uni-stuttgart.de/docs) with low level calls as well as a high-level backend to qoqo quantum programs. For this it provides the backend `APIBackend` to evaluate roqoqo quantum programs and the `api_devices` module to represent devices available on the emulators.
33//!
34//! ### QRydDemo specific hardware operations (prototype)
35//!
36//! Rydberg atom based quantum devices support, in principle, operations not commonly found in other quantum hardware. Changes in device topology are one of these operations. roqoqo-qryd adds support for changes in device topology to roqoqo via the operations in its `pragma_operations` module.
37//! Note that this is a preview prototype and does not represent a finalized set of operations on the QRydDemo hardware.
38//!
39//! ### Local simulator supporting specific hardware operations
40//!
41//! roqoqo-qryd includes a local [QuEST](https://github.com/QuEST-Kit/QuEST) based simulator for quantum devices supporting the Rydberg specific quantum operations. The simulator is intended to let users test the capabilities of quantum hardware with the additional operations.
42//! roqoqo-qryd provides the simulator via the `SimulatorBackend` backend the implements the roqoqo `Backend` trait.The backend uses the device prototypes in roqoqo-qryd's `qryd_devices` module.
43//! Note that the devices for the simulator do not represent a finalised design for QRydDemo.
44
45/// Devices representing QRyd quantum computer(s)
46pub mod qryd_devices;
47pub use qryd_devices::*;
48
49/// Tweezer devices representing QRyd quantum computer(s)
50pub mod tweezer_devices;
51pub use tweezer_devices::*;
52
53/// Devices representing QRyd quantum computer(s)
54pub mod api_devices;
55pub use api_devices::*;
56
57/// QRyd specific PragmaOperations that support changing the QRyd device during a circuit evaluation
58pub mod pragma_operations;
59pub use pragma_operations::*;
60
61/// Emulator device, TweezerDevice instance with all-to-all connectivity
62pub mod emulator_devices;
63pub use emulator_devices::*;
64
65/// Simulator backend for the QRyd quantum computer
66#[cfg(feature = "simulator")]
67mod simulator_backend;
68#[cfg(feature = "simulator")]
69pub use simulator_backend::*;
70
71/// WebAPI backend for the QRyd quantum computer(s)
72#[cfg(feature = "web-api")]
73pub mod api_backend;
74#[cfg(feature = "web-api")]
75pub use api_backend::*;
76
77#[cfg(feature = "web-api")]
78use roqoqo::RoqoqoBackendError;
79#[cfg(feature = "web-api")]
80use std::env;
81
82/// Compute the angle according to the appropriate relation and phi/theta values.
83///
84/// # Arguments
85///
86/// `relation_name` - The name of the relation to refer to.
87/// `theta` - The theta angle to check.
88///
89/// # Returns
90///
91/// `Some<f64>` - The phi-theta relation.
92/// 'None' - The relation does not exist.
93///
94pub fn phi_theta_relation(relation_name: &str, mut theta: f64) -> Option<f64> {
95    while theta < 0.0 {
96        theta += 2.0 * std::f64::consts::PI;
97    }
98    while theta > 2.0 * std::f64::consts::PI {
99        theta -= 2.0 * std::f64::consts::PI
100    }
101    match relation_name {
102        "DefaultRelation" => Some(
103            5.11382
104                - 0.32933
105                    * f64::ln(1.63085 * theta * theta * f64::exp(2.0 * theta) + theta + 0.02889),
106        ),
107        _ => None,
108    }
109}
110
111/// Enum for a Device that can be a TweezerDevice or an EmulatorDevice.
112#[derive(Debug)]
113pub enum CombinedDevice {
114    /// Variant for Tweezer devices
115    Tweezer(TweezerDevice),
116    /// Variant for Emulator devices
117    Emulator(EmulatorDevice),
118}
119
120/// Creates a new TweezerDevice instance containing populated tweezer data or EmulatorDevice instance.
121///
122/// This requires a valid QRYD_API_TOKEN. Visit `https://thequantumlaend.de/get-access/` to get one.
123///
124/// # Arguments
125///
126/// * `device_name` - The name of the device to instantiate. Defaults to "qryd_emulator".
127/// * `access_token` - An access_token is required to access QRYD hardware and emulators.
128///                    The access_token can either be given as an argument here
129///                         or set via the environmental variable `$QRYD_API_TOKEN`.
130/// * `seed` - Optionally overwrite seed value from downloaded device instance.
131/// * `dev` - The boolean to set the dev header to.
132/// * `api_version` - The version of the QRYD API to use. Defaults to "v1_1".
133///
134/// # Returns
135///
136/// * `CombinedDevice` - The new CombinedDevice instance, with variant TweezerDevice or
137///     EmulatorDevice depending on the pulled information.
138///
139/// # Errors
140///
141/// * `RoqoqoBackendError`
142#[cfg(feature = "web-api")]
143pub fn device_from_api(
144    device_name: Option<String>,
145    access_token: Option<String>,
146    seed: Option<usize>,
147    dev: Option<bool>,
148    api_version: Option<String>,
149) -> Result<CombinedDevice, RoqoqoBackendError> {
150    // Preparing variables
151    let device_name_internal = device_name.unwrap_or_else(|| String::from("qryd_emulator"));
152    let api_version = api_version.unwrap_or_else(|| String::from("v1_1"));
153    let dev = dev.unwrap_or(false);
154    let hqs_env_var = env::var("QRYD_API_HQS").is_ok();
155    let access_token_internal: String = match access_token {
156        Some(s) => s,
157        None => {
158            env::var("QRYD_API_TOKEN").map_err(|_| RoqoqoBackendError::MissingAuthentication {
159                msg: "QRYD access token is missing.".to_string(),
160            })?
161        }
162    };
163
164    // Client setup
165    let client = reqwest::blocking::Client::builder()
166        .https_only(true)
167        .build()
168        .map_err(|x| RoqoqoBackendError::NetworkError {
169            msg: format!("Could not create https client {:?}.", x),
170        })?;
171
172    // Response gathering
173    let resp = match (dev, hqs_env_var) {
174        (true, true) => client
175            .get(format!(
176                "https://api.qryddemo.itp3.uni-stuttgart.de/{}/devices/{}",
177                api_version, device_name_internal
178            ))
179            .header("X-API-KEY", access_token_internal)
180            .header("X-DEV", "?1")
181            .header("X-HQS", "?1")
182            .send()
183            .map_err(|e| RoqoqoBackendError::NetworkError {
184                msg: format!("{:?}", e),
185            })?,
186        (true, false) => client
187            .get(format!(
188                "https://api.qryddemo.itp3.uni-stuttgart.de/{}/devices/{}",
189                api_version, device_name_internal
190            ))
191            .header("X-API-KEY", access_token_internal)
192            .header("X-DEV", "?1")
193            .send()
194            .map_err(|e| RoqoqoBackendError::NetworkError {
195                msg: format!("{:?}", e),
196            })?,
197        (false, true) => client
198            .get(format!(
199                "https://api.qryddemo.itp3.uni-stuttgart.de/{}/devices/{}",
200                api_version, device_name_internal
201            ))
202            .header("X-API-KEY", access_token_internal)
203            .header("X-HQS", "?1")
204            .send()
205            .map_err(|e| RoqoqoBackendError::NetworkError {
206                msg: format!("{:?}", e),
207            })?,
208        (false, false) => client
209            .get(format!(
210                "https://api.qryddemo.itp3.uni-stuttgart.de/{}/devices/{}",
211                api_version, device_name_internal
212            ))
213            .header("X-API-KEY", access_token_internal)
214            .send()
215            .map_err(|e| RoqoqoBackendError::NetworkError {
216                msg: format!("{:?}", e),
217            })?,
218    };
219
220    // Response handling
221    let status_code = resp.status();
222    if status_code == reqwest::StatusCode::OK {
223        if let Ok(mut device) = resp.json::<TweezerDevice>() {
224            if device.available_gates.is_some() {
225                if let Some(new_seed) = seed {
226                    device.seed = Some(new_seed);
227                }
228                device.device_name = device_name_internal;
229                Ok(CombinedDevice::Emulator(EmulatorDevice {
230                    internal: device,
231                }))
232            } else {
233                if let Some(default) = device.default_layout.clone() {
234                    device.switch_layout(&default, None).unwrap();
235                }
236                if let Some(new_seed) = seed {
237                    device.seed = Some(new_seed);
238                }
239                device.device_name = device_name_internal;
240                Ok(CombinedDevice::Tweezer(device))
241            }
242        } else {
243            Err(RoqoqoBackendError::GenericError {
244                msg: "Failed deserialization from device_from_api().".to_string(),
245            })
246        }
247    } else {
248        Err(RoqoqoBackendError::NetworkError {
249            msg: format!(
250                "Request to server failed with HTTP status code {:?}.",
251                status_code
252            ),
253        })
254    }
255}