gqlite 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/Cargo.toml +12 -6
- data/ext/db-index/Cargo.toml +29 -0
- data/ext/db-index/src/hnsw/error.rs +66 -0
- data/ext/db-index/src/hnsw/graph_store.rs +272 -0
- data/ext/db-index/src/hnsw/id.rs +36 -0
- data/ext/db-index/src/hnsw/implementation.rs +1517 -0
- data/ext/db-index/src/hnsw/kernels.rs +1228 -0
- data/ext/db-index/src/hnsw/metric.rs +244 -0
- data/ext/db-index/src/hnsw/scalar.rs +72 -0
- data/ext/db-index/src/hnsw/simple_store.rs +140 -0
- data/ext/db-index/src/hnsw/store.rs +472 -0
- data/ext/db-index/src/hnsw/vector.rs +105 -0
- data/ext/db-index/src/hnsw/vectors.rs +568 -0
- data/ext/db-index/src/hnsw.rs +42 -0
- data/ext/db-index/src/lib.rs +3 -0
- data/ext/gqlitedb/Cargo.toml +19 -8
- data/ext/gqlitedb/benches/common/pokec.rs +62 -2
- data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
- data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
- data/ext/gqlitedb/release.toml +2 -2
- data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
- data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
- data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
- data/ext/gqlitedb/src/capi.rs +20 -24
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
- data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
- data/ext/gqlitedb/src/compiler.rs +506 -227
- data/ext/gqlitedb/src/connection.rs +151 -11
- data/ext/gqlitedb/src/consts.rs +1 -2
- data/ext/gqlitedb/src/error.rs +123 -61
- data/ext/gqlitedb/src/functions/common.rs +64 -0
- data/ext/gqlitedb/src/functions/containers.rs +2 -46
- data/ext/gqlitedb/src/functions/edge.rs +1 -1
- data/ext/gqlitedb/src/functions/math.rs +87 -15
- data/ext/gqlitedb/src/functions/node.rs +1 -1
- data/ext/gqlitedb/src/functions/path.rs +48 -11
- data/ext/gqlitedb/src/functions/scalar.rs +47 -4
- data/ext/gqlitedb/src/functions/string.rs +123 -1
- data/ext/gqlitedb/src/functions/value.rs +1 -1
- data/ext/gqlitedb/src/functions.rs +165 -28
- data/ext/gqlitedb/src/graph.rs +1 -9
- data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
- data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
- data/ext/gqlitedb/src/lib.rs +5 -4
- data/ext/gqlitedb/src/parser/gql.pest +1 -1
- data/ext/gqlitedb/src/planner.rs +329 -0
- data/ext/gqlitedb/src/prelude.rs +3 -3
- data/ext/gqlitedb/src/store/pgrx.rs +1 -1
- data/ext/gqlitedb/src/store/postgres.rs +742 -16
- data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
- data/ext/gqlitedb/src/store/redb/index.rs +274 -0
- data/ext/gqlitedb/src/store/redb.rs +1268 -113
- data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
- data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
- data/ext/gqlitedb/src/store/sqlite.rs +576 -14
- data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
- data/ext/gqlitedb/src/store.rs +140 -29
- data/ext/gqlitedb/src/tests/compiler.rs +207 -10
- data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
- data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
- data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
- data/ext/gqlitedb/src/tests/connection.rs +61 -1
- data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
- data/ext/gqlitedb/src/tests/parser.rs +54 -23
- data/ext/gqlitedb/src/tests/planner.rs +511 -0
- data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
- data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
- data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
- data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
- data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
- data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
- data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
- data/ext/gqlitedb/src/tests/store.rs +78 -14
- data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
- data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
- data/ext/gqlitedb/src/tests.rs +15 -9
- data/ext/gqlitedb/src/utils.rs +6 -1
- data/ext/gqlitedb/src/value/compare.rs +61 -3
- data/ext/gqlitedb/src/value.rs +136 -7
- data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
- data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
- data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
- data/ext/gqliterb/src/lib.rs +53 -8
- data/ext/gqlparser/Cargo.toml +25 -0
- data/ext/gqlparser/README.MD +9 -0
- data/ext/gqlparser/benches/pokec_divan.rs +34 -0
- data/ext/gqlparser/src/common.rs +69 -0
- data/ext/gqlparser/src/gqls/ast.rs +69 -0
- data/ext/gqlparser/src/gqls/constraint.rs +79 -0
- data/ext/gqlparser/src/gqls/error.rs +22 -0
- data/ext/gqlparser/src/gqls/parser.rs +813 -0
- data/ext/gqlparser/src/gqls/prelude.rs +8 -0
- data/ext/gqlparser/src/gqls/properties.rs +115 -0
- data/ext/gqlparser/src/gqls/resolve.rs +207 -0
- data/ext/gqlparser/src/gqls.rs +265 -0
- data/ext/gqlparser/src/lib.rs +7 -0
- data/ext/gqlparser/src/oc/ast.rs +680 -0
- data/ext/gqlparser/src/oc/error.rs +172 -0
- data/ext/gqlparser/src/oc/lexer.rs +429 -0
- data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
- data/ext/gqlparser/src/oc/parser.rs +2005 -0
- data/ext/gqlparser/src/oc.rs +33 -0
- data/ext/gqlparser/src/prelude.rs +3 -0
- data/ext/graphcore/Cargo.toml +1 -0
- data/ext/graphcore/src/error.rs +26 -0
- data/ext/graphcore/src/graph.rs +177 -51
- data/ext/graphcore/src/lib.rs +4 -2
- data/ext/graphcore/src/open_cypher.rs +12 -0
- data/ext/graphcore/src/table.rs +50 -2
- data/ext/graphcore/src/timestamp.rs +127 -104
- data/ext/graphcore/src/value/tensor.rs +739 -0
- data/ext/graphcore/src/value/value_map.rs +1 -1
- data/ext/graphcore/src/value.rs +343 -19
- metadata +93 -28
- data/ext/gqlitedb/src/parser/ast.rs +0 -604
- data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
- data/ext/gqlitedb/src/parser.rs +0 -4
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
//! Dense tensor values and (de)serialization
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
4
|
+
use std::convert::TryFrom;
|
|
5
|
+
|
|
6
|
+
/// Element data type of a tensor.
|
|
7
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
8
|
+
#[repr(u8)]
|
|
9
|
+
pub enum DType
|
|
10
|
+
{
|
|
11
|
+
/// 32-bit IEEE float
|
|
12
|
+
F32 = 0,
|
|
13
|
+
/// 64-bit IEEE float
|
|
14
|
+
F64 = 1,
|
|
15
|
+
/// 8-bit signed integer
|
|
16
|
+
I8 = 2,
|
|
17
|
+
/// 8-bit unsigned integer
|
|
18
|
+
U8 = 3,
|
|
19
|
+
/// 16-bit signed integer
|
|
20
|
+
I16 = 4,
|
|
21
|
+
// 5 and 6 reserved for F16/BF16
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl DType
|
|
25
|
+
{
|
|
26
|
+
/// Number of bytes per element.
|
|
27
|
+
pub fn byte_size(self) -> usize
|
|
28
|
+
{
|
|
29
|
+
match self
|
|
30
|
+
{
|
|
31
|
+
DType::F32 => 4,
|
|
32
|
+
DType::F64 => 8,
|
|
33
|
+
DType::I8 | DType::U8 => 1,
|
|
34
|
+
DType::I16 => 2,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Returns `true` for floating-point dtypes (F32, F64).
|
|
39
|
+
pub fn is_float(self) -> bool
|
|
40
|
+
{
|
|
41
|
+
matches!(self, DType::F32 | DType::F64)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Returns the canonical ASCII name used in JSON serialization.
|
|
45
|
+
pub fn as_str(self) -> &'static str
|
|
46
|
+
{
|
|
47
|
+
match self
|
|
48
|
+
{
|
|
49
|
+
DType::F32 => "F32",
|
|
50
|
+
DType::F64 => "F64",
|
|
51
|
+
DType::I8 => "I8",
|
|
52
|
+
DType::U8 => "U8",
|
|
53
|
+
DType::I16 => "I16",
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Parses the canonical ASCII name produced by [`DType::as_str`]. Returns `None` for unknown names.
|
|
58
|
+
pub fn from_name(s: &str) -> Option<DType>
|
|
59
|
+
{
|
|
60
|
+
match s
|
|
61
|
+
{
|
|
62
|
+
"F32" => Some(DType::F32),
|
|
63
|
+
"F64" => Some(DType::F64),
|
|
64
|
+
"I8" => Some(DType::I8),
|
|
65
|
+
"U8" => Some(DType::U8),
|
|
66
|
+
"I16" => Some(DType::I16),
|
|
67
|
+
_ => None,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
use crate::Error;
|
|
73
|
+
|
|
74
|
+
/// Validate shape, dims, and data length. This is the single place the checks run.
|
|
75
|
+
pub fn validate_shape(dtype: DType, shape: &[usize], data_len: usize) -> Result<(), Error>
|
|
76
|
+
{
|
|
77
|
+
if shape.len() > (u8::MAX as usize)
|
|
78
|
+
{
|
|
79
|
+
return Err(Error::TensorRankTooLarge);
|
|
80
|
+
}
|
|
81
|
+
for &dim in shape.iter()
|
|
82
|
+
{
|
|
83
|
+
if dim > (u32::MAX as usize)
|
|
84
|
+
{
|
|
85
|
+
return Err(Error::TensorDimensionTooLarge);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let mut numel: usize = 1;
|
|
89
|
+
for &d in shape.iter()
|
|
90
|
+
{
|
|
91
|
+
numel = numel
|
|
92
|
+
.checked_mul(d)
|
|
93
|
+
.ok_or(Error::TensorShapeNumelOverflow)?;
|
|
94
|
+
}
|
|
95
|
+
let bytes = numel
|
|
96
|
+
.checked_mul(dtype.byte_size())
|
|
97
|
+
.ok_or(Error::TensorShapeNumelOverflow)?;
|
|
98
|
+
if data_len != bytes
|
|
99
|
+
{
|
|
100
|
+
return Err(Error::TensorShapeDataLengthMismatch);
|
|
101
|
+
}
|
|
102
|
+
Ok(())
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Dense tensor container
|
|
106
|
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
107
|
+
pub struct Tensor
|
|
108
|
+
{
|
|
109
|
+
dtype: DType,
|
|
110
|
+
shape: Vec<usize>,
|
|
111
|
+
data: Vec<u8>,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
impl Tensor
|
|
115
|
+
{
|
|
116
|
+
// Private constructor — caller must ensure the invariant (data length matches shape & dtype)
|
|
117
|
+
fn from_raw(dtype: DType, shape: Vec<usize>, data: Vec<u8>) -> Self
|
|
118
|
+
{
|
|
119
|
+
debug_assert!(validate_shape(dtype, &shape, data.len()).is_ok());
|
|
120
|
+
Self { dtype, shape, data }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Construct from an f32 slice as a 1-D tensor
|
|
124
|
+
pub fn from_f32_slice(v: &[f32]) -> Self
|
|
125
|
+
{
|
|
126
|
+
let bytes = bytemuck::cast_slice(v).to_vec();
|
|
127
|
+
Self::from_raw(DType::F32, vec![v.len()], bytes)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Construct from an f64 slice as a 1-D tensor
|
|
131
|
+
pub fn from_f64_slice(v: &[f64]) -> Self
|
|
132
|
+
{
|
|
133
|
+
let bytes = bytemuck::cast_slice(v).to_vec();
|
|
134
|
+
Self::from_raw(DType::F64, vec![v.len()], bytes)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// Construct from an i8 slice as a 1-D tensor
|
|
138
|
+
pub fn from_i8_slice(v: &[i8]) -> Self
|
|
139
|
+
{
|
|
140
|
+
let bytes = bytemuck::cast_slice(v).to_vec();
|
|
141
|
+
Self::from_raw(DType::I8, vec![v.len()], bytes)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Construct from an u8 slice as a 1-D tensor
|
|
145
|
+
pub fn from_u8_slice(v: &[u8]) -> Self
|
|
146
|
+
{
|
|
147
|
+
let bytes = bytemuck::cast_slice(v).to_vec();
|
|
148
|
+
Self::from_raw(DType::U8, vec![v.len()], bytes)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// Construct from an i16 slice as a 1-D tensor
|
|
152
|
+
pub fn from_i16_slice(v: &[i16]) -> Self
|
|
153
|
+
{
|
|
154
|
+
let bytes = bytemuck::cast_slice(v).to_vec();
|
|
155
|
+
Self::from_raw(DType::I16, vec![v.len()], bytes)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Construct an f32 tensor with an explicit shape. Shape vs values length is validated.
|
|
159
|
+
pub fn from_f32_shaped(shape: Vec<usize>, values: &[f32]) -> Result<Self, crate::Error>
|
|
160
|
+
{
|
|
161
|
+
let data_len = values
|
|
162
|
+
.len()
|
|
163
|
+
.checked_mul(4)
|
|
164
|
+
.ok_or(crate::Error::TensorShapeNumelOverflow)?;
|
|
165
|
+
match validate_shape(DType::F32, &shape, data_len)
|
|
166
|
+
{
|
|
167
|
+
Ok(()) => Ok(Self::from_raw(
|
|
168
|
+
DType::F32,
|
|
169
|
+
shape,
|
|
170
|
+
bytemuck::cast_slice(values).to_vec(),
|
|
171
|
+
)),
|
|
172
|
+
Err(crate::Error::TensorShapeDataLengthMismatch) => Err(crate::Error::TensorShapeMismatch),
|
|
173
|
+
Err(e) => Err(e),
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// Element data type.
|
|
178
|
+
pub fn dtype(&self) -> DType
|
|
179
|
+
{
|
|
180
|
+
self.dtype
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Shape of the tensor (number of elements per dimension).
|
|
184
|
+
pub fn shape(&self) -> &[usize]
|
|
185
|
+
{
|
|
186
|
+
&self.shape
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Number of dimensions.
|
|
190
|
+
pub fn rank(&self) -> usize
|
|
191
|
+
{
|
|
192
|
+
self.shape.len()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// Number of elements (product of shape dimensions).
|
|
196
|
+
pub fn numel(&self) -> usize
|
|
197
|
+
{
|
|
198
|
+
self.shape.iter().product()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Number of elements (compat with slice/Vec API).
|
|
202
|
+
pub fn len(&self) -> usize
|
|
203
|
+
{
|
|
204
|
+
self.numel()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Returns `true` if the tensor has no elements.
|
|
208
|
+
pub fn is_empty(&self) -> bool
|
|
209
|
+
{
|
|
210
|
+
self.numel() == 0
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// Raw byte slice over the tensor data (little-endian).
|
|
214
|
+
pub fn as_bytes(&self) -> &[u8]
|
|
215
|
+
{
|
|
216
|
+
&self.data
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// Always returns `true`; tensors are always stored contiguously.
|
|
220
|
+
pub fn is_contiguous(&self) -> bool
|
|
221
|
+
{
|
|
222
|
+
true
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// Zero-copy F32 slice. Returns `None` on big-endian platforms or non-F32 dtypes.
|
|
226
|
+
#[cfg(target_endian = "little")]
|
|
227
|
+
pub fn as_f32_slice(&self) -> Option<&[f32]>
|
|
228
|
+
{
|
|
229
|
+
if self.dtype != DType::F32
|
|
230
|
+
{
|
|
231
|
+
return None;
|
|
232
|
+
}
|
|
233
|
+
bytemuck::try_cast_slice::<u8, f32>(&self.data).ok()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// Zero-copy F32 slice. Returns `None` on big-endian platforms or non-F32 dtypes.
|
|
237
|
+
#[cfg(not(target_endian = "little"))]
|
|
238
|
+
pub fn as_f32_slice(&self) -> Option<&[f32]>
|
|
239
|
+
{
|
|
240
|
+
None
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Zero-copy F64 slice. Returns `None` on big-endian platforms or non-F64 dtypes.
|
|
244
|
+
#[cfg(target_endian = "little")]
|
|
245
|
+
pub fn as_f64_slice(&self) -> Option<&[f64]>
|
|
246
|
+
{
|
|
247
|
+
if self.dtype != DType::F64
|
|
248
|
+
{
|
|
249
|
+
return None;
|
|
250
|
+
}
|
|
251
|
+
bytemuck::try_cast_slice::<u8, f64>(&self.data).ok()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/// Zero-copy F64 slice. Returns `None` on big-endian platforms or non-F64 dtypes.
|
|
255
|
+
#[cfg(not(target_endian = "little"))]
|
|
256
|
+
pub fn as_f64_slice(&self) -> Option<&[f64]>
|
|
257
|
+
{
|
|
258
|
+
None
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// Produce a Vec<f32> view of the tensor, converting other dtypes as necessary.
|
|
262
|
+
pub fn to_f32_vec(&self) -> Vec<f32>
|
|
263
|
+
{
|
|
264
|
+
match self.dtype
|
|
265
|
+
{
|
|
266
|
+
DType::F32 =>
|
|
267
|
+
{
|
|
268
|
+
#[cfg(target_endian = "little")]
|
|
269
|
+
{
|
|
270
|
+
if let Ok(s) = bytemuck::try_cast_slice::<u8, f32>(&self.data)
|
|
271
|
+
{
|
|
272
|
+
return s.to_vec();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// fallback: parse little-endian bytes
|
|
276
|
+
self
|
|
277
|
+
.data
|
|
278
|
+
.as_chunks::<4>()
|
|
279
|
+
.0
|
|
280
|
+
.iter()
|
|
281
|
+
.map(|c| {
|
|
282
|
+
let mut arr = [0u8; 4];
|
|
283
|
+
arr.copy_from_slice(c);
|
|
284
|
+
f32::from_le_bytes(arr)
|
|
285
|
+
})
|
|
286
|
+
.collect()
|
|
287
|
+
}
|
|
288
|
+
DType::F64 => self
|
|
289
|
+
.data
|
|
290
|
+
.as_chunks::<8>()
|
|
291
|
+
.0
|
|
292
|
+
.iter()
|
|
293
|
+
.map(|c| {
|
|
294
|
+
let mut arr = [0u8; 8];
|
|
295
|
+
arr.copy_from_slice(c);
|
|
296
|
+
f64::from_le_bytes(arr) as f32
|
|
297
|
+
})
|
|
298
|
+
.collect(),
|
|
299
|
+
DType::I8 => self.data.iter().map(|b| (*b as i8) as f32).collect(),
|
|
300
|
+
DType::U8 => self.data.iter().map(|b| *b as f32).collect(),
|
|
301
|
+
DType::I16 => self
|
|
302
|
+
.data
|
|
303
|
+
.as_chunks::<2>()
|
|
304
|
+
.0
|
|
305
|
+
.iter()
|
|
306
|
+
.map(|c| {
|
|
307
|
+
let mut arr = [0u8; 2];
|
|
308
|
+
arr.copy_from_slice(c);
|
|
309
|
+
i16::from_le_bytes(arr) as f32
|
|
310
|
+
})
|
|
311
|
+
.collect(),
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/// Provide an iterator over f32 elements (only supported for F32 tensors on LE targets)
|
|
316
|
+
pub fn iter(&self) -> std::slice::Iter<'_, f32>
|
|
317
|
+
{
|
|
318
|
+
#[cfg(target_endian = "little")]
|
|
319
|
+
{
|
|
320
|
+
if let Some(s) = self.as_f32_slice()
|
|
321
|
+
{
|
|
322
|
+
return s.iter();
|
|
323
|
+
}
|
|
324
|
+
panic!(
|
|
325
|
+
"Tensor::iter only supported for F32 tensors with proper alignment on little-endian targets"
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
#[cfg(not(target_endian = "little"))]
|
|
329
|
+
{
|
|
330
|
+
panic!("Tensor::iter not supported on non-little-endian targets");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// Binary encoding:
|
|
335
|
+
/// [version:1][flags:1][dtype:1][rank:1][dims: rank * u32][data...]
|
|
336
|
+
pub fn to_bytes(&self) -> Result<Vec<u8>, crate::Error>
|
|
337
|
+
{
|
|
338
|
+
validate_shape(self.dtype, &self.shape, self.data.len())?;
|
|
339
|
+
let rank = self.shape.len();
|
|
340
|
+
let mut out = Vec::with_capacity(4 + rank * 4 + self.data.len());
|
|
341
|
+
out.push(1u8); // version
|
|
342
|
+
out.push(0u8); // flags
|
|
343
|
+
out.push(self.dtype as u8);
|
|
344
|
+
out.push(u8::try_from(rank).map_err(|_| crate::Error::TensorRankTooLarge)?);
|
|
345
|
+
for &d in self.shape.iter()
|
|
346
|
+
{
|
|
347
|
+
let d32 = u32::try_from(d).map_err(|_| crate::Error::TensorDimensionTooLarge)?;
|
|
348
|
+
out.extend_from_slice(&d32.to_le_bytes());
|
|
349
|
+
}
|
|
350
|
+
out.extend_from_slice(&self.data);
|
|
351
|
+
Ok(out)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/// Deserialise a tensor from the binary wire format produced by [`Tensor::to_bytes`].
|
|
355
|
+
pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::Error>
|
|
356
|
+
{
|
|
357
|
+
if bytes.len() < 4
|
|
358
|
+
{
|
|
359
|
+
return Err(crate::Error::TensorTooShort);
|
|
360
|
+
}
|
|
361
|
+
let version = bytes[0];
|
|
362
|
+
if version != 1
|
|
363
|
+
{
|
|
364
|
+
return Err(crate::Error::TensorUnknownVersion(version));
|
|
365
|
+
}
|
|
366
|
+
let flags = bytes[1];
|
|
367
|
+
if flags != 0
|
|
368
|
+
{
|
|
369
|
+
return Err(crate::Error::TensorUnknownFlags(flags));
|
|
370
|
+
}
|
|
371
|
+
let dtype = match bytes[2]
|
|
372
|
+
{
|
|
373
|
+
0 => DType::F32,
|
|
374
|
+
1 => DType::F64,
|
|
375
|
+
2 => DType::I8,
|
|
376
|
+
3 => DType::U8,
|
|
377
|
+
4 => DType::I16,
|
|
378
|
+
v => return Err(crate::Error::TensorUnknownDType(v)),
|
|
379
|
+
};
|
|
380
|
+
let rank = bytes[3] as usize;
|
|
381
|
+
let header_len = 4usize;
|
|
382
|
+
let shape_bytes_len = rank
|
|
383
|
+
.checked_mul(4)
|
|
384
|
+
.ok_or(crate::Error::TensorNumelOverflow)?;
|
|
385
|
+
if bytes.len() < header_len + shape_bytes_len
|
|
386
|
+
{
|
|
387
|
+
return Err(crate::Error::TensorTooShort);
|
|
388
|
+
}
|
|
389
|
+
let mut shape = Vec::with_capacity(rank);
|
|
390
|
+
let mut pos = header_len;
|
|
391
|
+
for _ in 0..rank
|
|
392
|
+
{
|
|
393
|
+
let chunk = &bytes[pos..pos + 4];
|
|
394
|
+
let d = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) as usize;
|
|
395
|
+
shape.push(d);
|
|
396
|
+
pos += 4;
|
|
397
|
+
}
|
|
398
|
+
let data_len = bytes.len() - pos;
|
|
399
|
+
match validate_shape(dtype, &shape, data_len)
|
|
400
|
+
{
|
|
401
|
+
Ok(()) => Ok(Self::from_raw(dtype, shape, bytes[pos..].to_vec())),
|
|
402
|
+
Err(crate::Error::TensorShapeDataLengthMismatch) =>
|
|
403
|
+
{
|
|
404
|
+
Err(crate::Error::TensorDataLengthMismatch)
|
|
405
|
+
}
|
|
406
|
+
Err(_) => Err(crate::Error::TensorNumelOverflow),
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
impl Serialize for Tensor
|
|
412
|
+
{
|
|
413
|
+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
414
|
+
where
|
|
415
|
+
S: Serializer,
|
|
416
|
+
{
|
|
417
|
+
if serializer.is_human_readable()
|
|
418
|
+
{
|
|
419
|
+
use serde::ser::SerializeMap;
|
|
420
|
+
let mut map = serializer.serialize_map(Some(4))?;
|
|
421
|
+
map.serialize_entry("__gql_type", "tensor")?;
|
|
422
|
+
map.serialize_entry("dtype", self.dtype.as_str())?;
|
|
423
|
+
map.serialize_entry("shape", &self.shape)?;
|
|
424
|
+
|
|
425
|
+
match self.dtype
|
|
426
|
+
{
|
|
427
|
+
DType::F32 =>
|
|
428
|
+
{
|
|
429
|
+
let data: Vec<f64> = self
|
|
430
|
+
.data
|
|
431
|
+
.as_chunks::<4>()
|
|
432
|
+
.0
|
|
433
|
+
.iter()
|
|
434
|
+
.map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]]) as f64)
|
|
435
|
+
.collect();
|
|
436
|
+
map.serialize_entry("data", &data)?;
|
|
437
|
+
}
|
|
438
|
+
DType::F64 =>
|
|
439
|
+
{
|
|
440
|
+
let data: Vec<f64> = self
|
|
441
|
+
.data
|
|
442
|
+
.as_chunks::<8>()
|
|
443
|
+
.0
|
|
444
|
+
.iter()
|
|
445
|
+
.map(|b| f64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]))
|
|
446
|
+
.collect();
|
|
447
|
+
map.serialize_entry("data", &data)?;
|
|
448
|
+
}
|
|
449
|
+
DType::I8 =>
|
|
450
|
+
{
|
|
451
|
+
let data: Vec<i64> = self.data.iter().map(|&b| b as i8 as i64).collect();
|
|
452
|
+
map.serialize_entry("data", &data)?;
|
|
453
|
+
}
|
|
454
|
+
DType::U8 =>
|
|
455
|
+
{
|
|
456
|
+
let data: Vec<i64> = self.data.iter().map(|&b| b as i64).collect();
|
|
457
|
+
map.serialize_entry("data", &data)?;
|
|
458
|
+
}
|
|
459
|
+
DType::I16 =>
|
|
460
|
+
{
|
|
461
|
+
let data: Vec<i64> = self
|
|
462
|
+
.data
|
|
463
|
+
.as_chunks::<2>()
|
|
464
|
+
.0
|
|
465
|
+
.iter()
|
|
466
|
+
.map(|b| i16::from_le_bytes([b[0], b[1]]) as i64)
|
|
467
|
+
.collect();
|
|
468
|
+
map.serialize_entry("data", &data)?;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
map.end()
|
|
473
|
+
}
|
|
474
|
+
else
|
|
475
|
+
{
|
|
476
|
+
let bs = self.to_bytes().map_err(serde::ser::Error::custom)?;
|
|
477
|
+
serializer.serialize_bytes(&bs)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
struct TensorJsonVisitor;
|
|
483
|
+
|
|
484
|
+
impl<'de> serde::de::Visitor<'de> for TensorJsonVisitor
|
|
485
|
+
{
|
|
486
|
+
type Value = Tensor;
|
|
487
|
+
|
|
488
|
+
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
|
|
489
|
+
{
|
|
490
|
+
write!(f, "a tensor JSON object with \"__gql_type\": \"tensor\"")
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Tensor, A::Error>
|
|
494
|
+
{
|
|
495
|
+
let mut gql_type: Option<String> = None;
|
|
496
|
+
let mut dtype_str: Option<String> = None;
|
|
497
|
+
let mut shape: Option<Vec<usize>> = None;
|
|
498
|
+
let mut data: Option<Vec<f64>> = None;
|
|
499
|
+
|
|
500
|
+
while let Some(key) = map.next_key::<String>()?
|
|
501
|
+
{
|
|
502
|
+
match key.as_str()
|
|
503
|
+
{
|
|
504
|
+
"__gql_type" => gql_type = Some(map.next_value()?),
|
|
505
|
+
"dtype" => dtype_str = Some(map.next_value()?),
|
|
506
|
+
"shape" => shape = Some(map.next_value()?),
|
|
507
|
+
"data" => data = Some(map.next_value()?),
|
|
508
|
+
_ =>
|
|
509
|
+
{
|
|
510
|
+
map.next_value::<serde::de::IgnoredAny>()?;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
match gql_type.as_deref()
|
|
516
|
+
{
|
|
517
|
+
Some("tensor") =>
|
|
518
|
+
{}
|
|
519
|
+
Some(t) =>
|
|
520
|
+
{
|
|
521
|
+
return Err(serde::de::Error::custom(format!(
|
|
522
|
+
"__gql_type is {}, expected \"tensor\"",
|
|
523
|
+
t
|
|
524
|
+
)));
|
|
525
|
+
}
|
|
526
|
+
None => return Err(serde::de::Error::missing_field("__gql_type")),
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
let dtype_str = dtype_str.ok_or_else(|| serde::de::Error::missing_field("dtype"))?;
|
|
530
|
+
let dtype = DType::from_name(&dtype_str)
|
|
531
|
+
.ok_or_else(|| serde::de::Error::custom(format!("unknown dtype {}", dtype_str)))?;
|
|
532
|
+
let shape = shape.ok_or_else(|| serde::de::Error::missing_field("shape"))?;
|
|
533
|
+
let data = data.ok_or_else(|| serde::de::Error::missing_field("data"))?;
|
|
534
|
+
|
|
535
|
+
let numel: usize = shape.iter().product();
|
|
536
|
+
if data.len() != numel
|
|
537
|
+
{
|
|
538
|
+
return Err(serde::de::Error::custom(format!(
|
|
539
|
+
"data length {} does not match shape product {}",
|
|
540
|
+
data.len(),
|
|
541
|
+
numel
|
|
542
|
+
)));
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
let mut bytes: Vec<u8> = Vec::with_capacity(numel * dtype.byte_size());
|
|
546
|
+
match dtype
|
|
547
|
+
{
|
|
548
|
+
DType::F32 =>
|
|
549
|
+
{
|
|
550
|
+
for &v in &data
|
|
551
|
+
{
|
|
552
|
+
bytes.extend_from_slice(&(v as f32).to_le_bytes());
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
DType::F64 =>
|
|
556
|
+
{
|
|
557
|
+
for &v in &data
|
|
558
|
+
{
|
|
559
|
+
bytes.extend_from_slice(&v.to_le_bytes());
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
DType::I8 =>
|
|
563
|
+
{
|
|
564
|
+
for &v in &data
|
|
565
|
+
{
|
|
566
|
+
bytes.push((v as i8) as u8);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
DType::U8 =>
|
|
570
|
+
{
|
|
571
|
+
for &v in &data
|
|
572
|
+
{
|
|
573
|
+
bytes.push(v as u8);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
DType::I16 =>
|
|
577
|
+
{
|
|
578
|
+
for &v in &data
|
|
579
|
+
{
|
|
580
|
+
bytes.extend_from_slice(&(v as i16).to_le_bytes());
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
validate_shape(dtype, &shape, bytes.len()).map_err(serde::de::Error::custom)?;
|
|
586
|
+
Ok(Tensor::from_raw(dtype, shape, bytes))
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
impl<'de> Deserialize<'de> for Tensor
|
|
591
|
+
{
|
|
592
|
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
593
|
+
where
|
|
594
|
+
D: Deserializer<'de>,
|
|
595
|
+
{
|
|
596
|
+
if deserializer.is_human_readable()
|
|
597
|
+
{
|
|
598
|
+
deserializer.deserialize_map(TensorJsonVisitor)
|
|
599
|
+
}
|
|
600
|
+
else
|
|
601
|
+
{
|
|
602
|
+
struct BytesVisitor;
|
|
603
|
+
impl<'de> serde::de::Visitor<'de> for BytesVisitor
|
|
604
|
+
{
|
|
605
|
+
type Value = Tensor;
|
|
606
|
+
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
|
|
607
|
+
{
|
|
608
|
+
write!(f, "a byte string")
|
|
609
|
+
}
|
|
610
|
+
fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Tensor, E>
|
|
611
|
+
{
|
|
612
|
+
Tensor::from_bytes(v).map_err(E::custom)
|
|
613
|
+
}
|
|
614
|
+
fn visit_byte_buf<E: serde::de::Error>(self, v: Vec<u8>) -> Result<Tensor, E>
|
|
615
|
+
{
|
|
616
|
+
Tensor::from_bytes(&v).map_err(E::custom)
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
deserializer.deserialize_bytes(BytesVisitor)
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
impl IntoIterator for Tensor
|
|
625
|
+
{
|
|
626
|
+
type Item = f32;
|
|
627
|
+
type IntoIter = std::vec::IntoIter<f32>;
|
|
628
|
+
fn into_iter(self) -> Self::IntoIter
|
|
629
|
+
{
|
|
630
|
+
self.to_f32_vec().into_iter()
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
impl std::ops::Deref for Tensor
|
|
635
|
+
{
|
|
636
|
+
type Target = [f32];
|
|
637
|
+
fn deref(&self) -> &Self::Target
|
|
638
|
+
{
|
|
639
|
+
#[cfg(target_endian = "little")]
|
|
640
|
+
{
|
|
641
|
+
if self.dtype == DType::F32
|
|
642
|
+
{
|
|
643
|
+
bytemuck::try_cast_slice::<u8, f32>(&self.data)
|
|
644
|
+
.expect("Tensor data is not a properly aligned f32 slice")
|
|
645
|
+
}
|
|
646
|
+
else
|
|
647
|
+
{
|
|
648
|
+
panic!("Tensor deref to [f32] only supported for DType::F32");
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
#[cfg(not(target_endian = "little"))]
|
|
652
|
+
{
|
|
653
|
+
panic!("Tensor deref to [f32] not supported on non-little-endian targets");
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
impl<'a> IntoIterator for &'a Tensor
|
|
659
|
+
{
|
|
660
|
+
type Item = &'a f32;
|
|
661
|
+
type IntoIter = std::slice::Iter<'a, f32>;
|
|
662
|
+
fn into_iter(self) -> Self::IntoIter
|
|
663
|
+
{
|
|
664
|
+
self.iter()
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
impl FromIterator<f32> for Tensor
|
|
669
|
+
{
|
|
670
|
+
fn from_iter<I: IntoIterator<Item = f32>>(iter: I) -> Self
|
|
671
|
+
{
|
|
672
|
+
let v: Vec<f32> = iter.into_iter().collect();
|
|
673
|
+
Tensor::from_f32_slice(&v)
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
impl From<Vec<f32>> for Tensor
|
|
678
|
+
{
|
|
679
|
+
fn from(v: Vec<f32>) -> Self
|
|
680
|
+
{
|
|
681
|
+
Tensor::from_f32_slice(&v)
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
#[cfg(test)]
|
|
686
|
+
mod tests
|
|
687
|
+
{
|
|
688
|
+
use crate::Value;
|
|
689
|
+
|
|
690
|
+
#[test]
|
|
691
|
+
fn test_tensor_from_vec_roundtrip()
|
|
692
|
+
{
|
|
693
|
+
let v: Vec<f32> = vec![1.0f32, 2.5f32, -3.25f32];
|
|
694
|
+
let val: Value = v.clone().into();
|
|
695
|
+
let out: Vec<f32> = Vec::try_from(val).expect("Should convert back to Vec<f32>");
|
|
696
|
+
assert_eq!(out, v);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
#[test]
|
|
700
|
+
fn test_optimize_array_to_tensor()
|
|
701
|
+
{
|
|
702
|
+
let arr = Value::Array(vec![
|
|
703
|
+
Value::Float(1.0),
|
|
704
|
+
Value::Integer(2),
|
|
705
|
+
Value::Float(3.5),
|
|
706
|
+
]);
|
|
707
|
+
let opt = arr.optimize();
|
|
708
|
+
let out: Vec<f32> =
|
|
709
|
+
Vec::try_from(opt).expect("optimize should produce a Tensor convertible to Vec<f32>");
|
|
710
|
+
assert_eq!(out, vec![1.0f32, 2.0f32, 3.5f32]);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
#[test]
|
|
714
|
+
fn test_tensor_add_tensor_and_scalar()
|
|
715
|
+
{
|
|
716
|
+
let a: Value = vec![1.0f32, 2.0f32, 3.0f32].into();
|
|
717
|
+
let b: Value = vec![4.0f32, 5.0f32, 6.0f32].into();
|
|
718
|
+
let sum = (a + b).expect("tensor + tensor should succeed");
|
|
719
|
+
let out: Vec<f32> = Vec::try_from(sum).expect("result should be a tensor");
|
|
720
|
+
assert_eq!(out, vec![5.0f32, 7.0f32, 9.0f32]);
|
|
721
|
+
|
|
722
|
+
// tensor + float scalar
|
|
723
|
+
let a: Value = vec![1.0f32, 2.0f32].into();
|
|
724
|
+
let scalar = Value::Float(3.0);
|
|
725
|
+
let sum = (a + scalar).expect("tensor + float should succeed");
|
|
726
|
+
let out: Vec<f32> = Vec::try_from(sum).expect("result should be a tensor");
|
|
727
|
+
assert_eq!(out, vec![4.0f32, 5.0f32]);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
#[test]
|
|
731
|
+
fn test_tensor_pow_elementwise()
|
|
732
|
+
{
|
|
733
|
+
let a: Value = vec![2.0f32, 3.0f32].into();
|
|
734
|
+
let b: Value = vec![3.0f32, 2.0f32].into();
|
|
735
|
+
let res = a.pow(b).expect("tensor pow tensor should succeed");
|
|
736
|
+
let out: Vec<f32> = Vec::try_from(res).expect("result should be a tensor");
|
|
737
|
+
assert_eq!(out, vec![8.0f32, 9.0f32]);
|
|
738
|
+
}
|
|
739
|
+
}
|