1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//! "Values" represent typed ports in the jeff language.
//!
//! Internally, these are coalesced into a single array at the function
//! definition and each port contains an index into this array.
use crate::capnp::jeff_capnp;
use derive_more::Display;
/// Value type.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Display)]
pub enum Type {
/// Quantum bit.
///
/// Qubits are linear types.
Qubit,
/// Quantum registers.
///
/// A quantum register is an array of slots that can hold qubits.
/// Slots of a quantum register can either be empty or filled with a qubit.
///
/// Quantum registers are linear types.
///
/// If `length` is `None`, the register has dynamic length.
/// If `Some`, the register has static compile-time length.
#[display("Qureg[{}]", length.map_or("?".to_string(), |l| l.to_string()))]
QubitRegister {
/// Optional compile-time length.
length: Option<u32>,
},
/// Integers.
///
/// The type does not distinguish between signed and unsigned integers.
/// Instead it is up to the operation to interpret the integer as signed or unsigned.
/// Signed integers are represented using two's complement.
///
/// Integers of bitwidth 1 can be used as classical bits or boolean values.
#[display("Int{}", bits)]
Int {
/// Bitwidth of the integer.
bits: u8,
},
/// Integer array.
///
/// Arrays of integers of bitwidth 1 can be used as classical bit arrays.
///
/// If `length` is `None`, the array has dynamic length.
/// If `Some`, the array has static compile-time length.
#[display("IntArray{}[{}]", bits, length.map_or("?".to_string(), |l| l.to_string()))]
IntArray {
/// Bitwidth of the integers.
bits: u8,
/// Optional compile-time length.
length: Option<u32>,
},
/// Floating point numbers.
#[display("Float{}", precision.bits())]
Float {
/// Precision of the floating point number.
precision: FloatPrecision,
},
/// Array of floating point numbers.
///
/// If `length` is `None`, the array has dynamic length.
/// If `Some`, the array has static compile-time length.
#[display("FloatArray{}[{}]", precision.bits(), length.map_or("?".to_string(), |l| l.to_string()))]
FloatArray {
/// Precision of the floating point numbers.
precision: FloatPrecision,
/// Optional compile-time length.
length: Option<u32>,
},
}
impl Type {
/// Create a new integer type.
pub fn int(bits: u8) -> Self {
Self::Int { bits }
}
/// Create a new boolean type.
pub fn bool() -> Self {
Self::Int { bits: 1 }
}
/// Create a new integer array type.
///
/// If `length` is `None`, the array has dynamic length.
/// If `Some`, the array has static compile-time length.
pub fn int_array(bits: u8, length: Option<u32>) -> Self {
Self::IntArray { bits, length }
}
/// Create a new floating point type.
pub fn float(precision: FloatPrecision) -> Self {
Self::Float { precision }
}
/// Create a new floating point array type.
///
/// If `length` is `None`, the array has dynamic length.
/// If `Some`, the array has static compile-time length.
pub fn float_array(precision: FloatPrecision, length: Option<u32>) -> Self {
Self::FloatArray { precision, length }
}
/// Parse a type from a capnp reader.
pub(crate) fn read_capnp(reader: jeff_capnp::type_::Reader<'_>) -> Self {
use jeff_capnp::type_::Which;
match reader
.which()
.expect("Type id was not in the schema. Schema should have been verified.")
{
Which::Qubit(_) => Self::Qubit,
Which::Qureg(qureg) => Self::QubitRegister {
length: match qureg.which().expect(
"Qureg type id was not in the schema. Schema should have been verified.",
) {
jeff_capnp::type_::qureg::Which::Static(length) => Some(length),
jeff_capnp::type_::qureg::Which::Dynamic(_) => None,
},
},
Which::Int(bits) => Self::Int { bits },
Which::IntArray(int_array) => Self::IntArray {
bits: int_array.get_bitwidth(),
length: match int_array.get_length().which().expect(
"IntArray length id was not in the schema. Schema should have been verified.",
) {
jeff_capnp::type_::int_array::length::Which::Static(length) => Some(length),
jeff_capnp::type_::int_array::length::Which::Dynamic(_) => None,
},
},
Which::Float(prec) => Self::Float {
precision: FloatPrecision::from_capnp(prec.expect(
"FloatPrecision id was not in the schema. Schema should have been verified.",
)),
},
Which::FloatArray(float_array) => Self::FloatArray {
precision: FloatPrecision::from_capnp(float_array.get_precision().expect(
"FloatPrecision id was not in the schema. Schema should have been verified.",
)),
length: match float_array.get_length().which().expect(
"FloatArray length id was not in the schema. Schema should have been verified.",
) {
jeff_capnp::type_::float_array::length::Which::Static(length) => Some(length),
jeff_capnp::type_::float_array::length::Which::Dynamic(_) => None,
},
},
}
}
/// Build a capnp type from this type.
#[allow(unused)]
pub(crate) fn build_capnp(&self, mut builder: jeff_capnp::type_::Builder) {
match self {
Self::Qubit => builder.set_qubit(()),
Self::QubitRegister { length } => {
let mut qureg = builder.reborrow().init_qureg();
match length {
Some(length) => qureg.set_static(*length),
None => qureg.set_dynamic(()),
}
}
Self::Int { bits } => builder.set_int(*bits),
Self::IntArray { bits, length } => {
let mut int_array = builder.reborrow().init_int_array();
int_array.set_bitwidth(*bits);
let mut int_array_len = int_array.reborrow().init_length();
match length {
Some(length) => int_array_len.set_static(*length),
None => int_array_len.set_dynamic(()),
}
}
Self::Float { precision } => builder.set_float(precision.as_capnp()),
Self::FloatArray { precision, length } => {
let mut float_array = builder.reborrow().init_float_array();
float_array.set_precision(precision.as_capnp());
let mut float_array_len = float_array.reborrow().init_length();
match length {
Some(length) => float_array_len.set_static(*length),
None => float_array_len.set_dynamic(()),
}
}
}
}
}
/// Precision of floating point number.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Display)]
pub enum FloatPrecision {
/// 32-bit floating point number.
Float32,
/// 64-bit floating point number.
Float64,
}
impl FloatPrecision {
/// Parse a float precision from a capnp reader.
pub(crate) fn from_capnp(reader: jeff_capnp::FloatPrecision) -> Self {
match reader {
jeff_capnp::FloatPrecision::Float32 => Self::Float32,
jeff_capnp::FloatPrecision::Float64 => Self::Float64,
}
}
/// Returns the capnp representation of this float precision.
pub(crate) fn as_capnp(&self) -> jeff_capnp::FloatPrecision {
match self {
Self::Float32 => jeff_capnp::FloatPrecision::Float32,
Self::Float64 => jeff_capnp::FloatPrecision::Float64,
}
}
/// Returns the bitwidth of the floating point number.
pub fn bits(self) -> u8 {
match self {
Self::Float32 => 32,
Self::Float64 => 64,
}
}
}