1use std::collections::HashMap;
2use std::convert::TryFrom;
3
4use serde::{Deserialize, Serialize};
5
6use edge::{convert_edges, Edge, Id};
7use qcs_api_client_openapi::models::InstructionSetArchitecture;
8use qubit::{FrbSim1q, Qubit};
9
10mod edge;
11mod operator;
12mod qubit;
13
14#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
16pub(crate) struct Compiler {
17 #[serde(rename = "1Q")]
18 qubits: HashMap<String, Qubit>,
19 #[serde(rename = "2Q")]
20 edges: HashMap<String, Edge>,
21}
22
23impl TryFrom<InstructionSetArchitecture> for Compiler {
24 type Error = Error;
25
26 fn try_from(isa: InstructionSetArchitecture) -> Result<Self, Error> {
27 let architecture = isa.architecture;
28 let mut qubits = Qubit::from_nodes(&architecture.nodes);
29
30 let mut edges = convert_edges(&architecture.edges)?;
31
32 let site_ops = isa
33 .instructions
34 .iter()
35 .flat_map(|op| op.sites.iter().map(move |site| (op, site)));
36 let frb_sim_1q = FrbSim1q::try_from(isa.benchmarks)?;
37
38 for (op, site) in site_ops {
39 match (&op.node_count, &site.node_ids.len()) {
40 (Some(1), 1) => {
41 let id = &site.node_ids[0];
42 let qubit = qubits
43 .get_mut(id)
44 .ok_or_else(|| Error::QubitDoesNotExist(String::from(&op.name), *id))?;
45 qubit.add_operation(&op.name, &site.characteristics, &frb_sim_1q)?;
46 }
47 (Some(2), 2) => {
48 let id = Id::try_from(&site.node_ids)?;
49 let edge = edges
50 .get_mut(&id)
51 .ok_or_else(|| Error::EdgeDoesNotExist(String::from(&op.name), id))?;
52 edge.add_operation(&op.name, &site.characteristics)?;
53 }
54 (node_count, node_ids) => {
55 return Err(Error::IncorrectNodes(
56 (*node_count, *node_ids),
57 String::from(&op.name),
58 site.node_ids.clone(),
59 ))
60 }
61 }
62 }
63
64 let qubits = qubits
65 .into_iter()
66 .map(|(k, v)| (k.to_string(), v))
67 .collect();
68 let edges = edges.into_iter().map(|(k, v)| (k.to_string(), v)).collect();
69 Ok(Self { qubits, edges })
70 }
71}
72
73#[derive(Debug, thiserror::Error)]
75pub enum Error {
76 #[error("Operation {0} is defined for Qubit {1} but that Qubit does not exist")]
77 QubitDoesNotExist(String, i64),
78 #[error("Operation {0} is defined for Edge {1} but that Edge does not exist")]
79 EdgeDoesNotExist(String, Id),
80 #[error(
81 "The number of nodes for an operation and site_operation must be (1, 1) or (2, 2). \
82 Got {0:?} while parsing operation {1} at site {2:?}"
83 )]
84 IncorrectNodes((Option<u64>, usize), String, Vec<i64>),
85 #[error(transparent)]
86 Qubit(#[from] qubit::Error),
87 #[error(transparent)]
88 Edge(#[from] edge::Error),
89}
90
91#[cfg(test)]
92mod describe_compiler_isa {
93 use std::{convert::TryFrom, fs::read_to_string};
94
95 use float_cmp::{approx_eq, F64Margin};
96 use qcs_api_client_openapi::models::InstructionSetArchitecture;
97 use serde_json::Value;
98
99 use super::Compiler;
100
101 fn json_is_equivalent<'a>(
106 first: &'a Value,
107 second: &'a Value,
108 ) -> Result<(), (&'a Value, &'a Value)> {
109 let equal = match (first, second) {
110 (Value::Number(first_num), Value::Number(second_num)) => {
111 if !first_num.is_f64() || !second_num.is_f64() {
112 first_num == second_num
113 } else {
114 let first_f64 = first_num.as_f64().unwrap();
115 let second_f64 = second_num.as_f64().unwrap();
116 approx_eq!(
117 f64,
118 first_f64,
119 second_f64,
120 F64Margin {
121 ulps: 1,
122 epsilon: 0.000_000_1
123 }
124 )
125 }
126 }
127 (Value::Object(first_map), Value::Object(second_map)) => {
128 let mut found_missing = false;
129 for (key, first_value) in first_map {
130 let second_value = second_map.get(key);
131 if second_value.is_none() {
132 found_missing = true;
133 break;
134 }
135 let cmp = json_is_equivalent(first_value, second_value.unwrap());
136 cmp?;
137 }
138 !found_missing
139 }
140 (Value::Array(first_array), Value::Array(second_array))
141 if first_array.len() != second_array.len() =>
142 {
143 false
144 }
145 (Value::Array(first_array), Value::Array(second_array)) => {
146 let error = first_array.iter().zip(second_array).find(
147 |(first_value, second_value)| -> bool {
148 json_is_equivalent(first_value, second_value).is_err()
149 },
150 );
151 if let Some(values) = error {
152 return Err(values);
153 }
154 true
155 }
156 (first, second) => first == second,
157 };
158 if equal {
159 Ok(())
160 } else {
161 Err((first, second))
162 }
163 }
164
165 #[test]
166 fn it_correctly_converts_aspen_8() {
167 let input = read_to_string("tests/qcs-isa-Aspen-8.json")
168 .expect("Could not read Aspen 8 input data");
169 let expected_json = read_to_string("tests/compiler-isa-Aspen-8.json")
170 .expect("Could not read Aspen 8 output data");
171 let qcs_isa: InstructionSetArchitecture =
172 serde_json::from_str(&input).expect("Could not deserialize Aspen-8 input");
173 let expected: Value =
174 serde_json::from_str(&expected_json).expect("Could not deserialize Aspen-8 output");
175
176 let compiler_isa =
177 Compiler::try_from(qcs_isa).expect("Could not convert ISA to CompilerIsa");
178 let serialized =
179 serde_json::to_value(compiler_isa).expect("Unable to serialize CompilerIsa");
180
181 let result = json_is_equivalent(&serialized, &expected);
182 result.expect("JSON was not equivalent");
183 }
184}