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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +12 -6
  3. data/ext/db-index/Cargo.toml +29 -0
  4. data/ext/db-index/src/hnsw/error.rs +66 -0
  5. data/ext/db-index/src/hnsw/graph_store.rs +272 -0
  6. data/ext/db-index/src/hnsw/id.rs +36 -0
  7. data/ext/db-index/src/hnsw/implementation.rs +1517 -0
  8. data/ext/db-index/src/hnsw/kernels.rs +1228 -0
  9. data/ext/db-index/src/hnsw/metric.rs +244 -0
  10. data/ext/db-index/src/hnsw/scalar.rs +72 -0
  11. data/ext/db-index/src/hnsw/simple_store.rs +140 -0
  12. data/ext/db-index/src/hnsw/store.rs +472 -0
  13. data/ext/db-index/src/hnsw/vector.rs +105 -0
  14. data/ext/db-index/src/hnsw/vectors.rs +568 -0
  15. data/ext/db-index/src/hnsw.rs +42 -0
  16. data/ext/db-index/src/lib.rs +3 -0
  17. data/ext/gqlitedb/Cargo.toml +19 -8
  18. data/ext/gqlitedb/benches/common/pokec.rs +62 -2
  19. data/ext/gqlitedb/benches/pokec_divan.rs +66 -2
  20. data/ext/gqlitedb/benches/pokec_iai.rs +60 -3
  21. data/ext/gqlitedb/release.toml +2 -2
  22. data/ext/gqlitedb/src/aggregators/arithmetic.rs +3 -1
  23. data/ext/gqlitedb/src/aggregators/containers.rs +1 -1
  24. data/ext/gqlitedb/src/aggregators/stats.rs +21 -12
  25. data/ext/gqlitedb/src/capi.rs +20 -24
  26. data/ext/gqlitedb/src/compiler/expression_analyser.rs +28 -20
  27. data/ext/gqlitedb/src/compiler/variables_manager.rs +104 -38
  28. data/ext/gqlitedb/src/compiler.rs +506 -227
  29. data/ext/gqlitedb/src/connection.rs +151 -11
  30. data/ext/gqlitedb/src/consts.rs +1 -2
  31. data/ext/gqlitedb/src/error.rs +123 -61
  32. data/ext/gqlitedb/src/functions/common.rs +64 -0
  33. data/ext/gqlitedb/src/functions/containers.rs +2 -46
  34. data/ext/gqlitedb/src/functions/edge.rs +1 -1
  35. data/ext/gqlitedb/src/functions/math.rs +87 -15
  36. data/ext/gqlitedb/src/functions/node.rs +1 -1
  37. data/ext/gqlitedb/src/functions/path.rs +48 -11
  38. data/ext/gqlitedb/src/functions/scalar.rs +47 -4
  39. data/ext/gqlitedb/src/functions/string.rs +123 -1
  40. data/ext/gqlitedb/src/functions/value.rs +1 -1
  41. data/ext/gqlitedb/src/functions.rs +165 -28
  42. data/ext/gqlitedb/src/graph.rs +1 -9
  43. data/ext/gqlitedb/src/interpreter/evaluators.rs +968 -130
  44. data/ext/gqlitedb/src/interpreter/instructions.rs +39 -2
  45. data/ext/gqlitedb/src/lib.rs +5 -4
  46. data/ext/gqlitedb/src/parser/gql.pest +1 -1
  47. data/ext/gqlitedb/src/planner.rs +329 -0
  48. data/ext/gqlitedb/src/prelude.rs +3 -3
  49. data/ext/gqlitedb/src/store/pgrx.rs +1 -1
  50. data/ext/gqlitedb/src/store/postgres.rs +742 -16
  51. data/ext/gqlitedb/src/store/redb/hnsw_store.rs +702 -0
  52. data/ext/gqlitedb/src/store/redb/index.rs +274 -0
  53. data/ext/gqlitedb/src/store/redb.rs +1268 -113
  54. data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +28 -0
  55. data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +103 -0
  56. data/ext/gqlitedb/src/store/sqlbase.rs +146 -16
  57. data/ext/gqlitedb/src/store/sqlite.rs +576 -14
  58. data/ext/gqlitedb/src/store/vector_extract.rs +56 -0
  59. data/ext/gqlitedb/src/store.rs +140 -29
  60. data/ext/gqlitedb/src/tests/compiler.rs +207 -10
  61. data/ext/gqlitedb/src/tests/connection/postgres.rs +38 -3
  62. data/ext/gqlitedb/src/tests/connection/redb.rs +23 -0
  63. data/ext/gqlitedb/src/tests/connection/sqlite.rs +31 -0
  64. data/ext/gqlitedb/src/tests/connection.rs +61 -1
  65. data/ext/gqlitedb/src/tests/evaluators.rs +162 -7
  66. data/ext/gqlitedb/src/tests/parser.rs +54 -23
  67. data/ext/gqlitedb/src/tests/planner.rs +511 -0
  68. data/ext/gqlitedb/src/tests/store/postgres.rs +7 -0
  69. data/ext/gqlitedb/src/tests/store/redb.rs +8 -0
  70. data/ext/gqlitedb/src/tests/store/sqlite.rs +8 -0
  71. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +182 -0
  72. data/ext/gqlitedb/src/tests/store/vector_index/redb.rs +386 -0
  73. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +166 -0
  74. data/ext/gqlitedb/src/tests/store/vector_index.rs +2313 -0
  75. data/ext/gqlitedb/src/tests/store.rs +78 -14
  76. data/ext/gqlitedb/src/tests/templates/ast.rs +92 -16
  77. data/ext/gqlitedb/src/tests/templates/programs.rs +14 -7
  78. data/ext/gqlitedb/src/tests.rs +15 -9
  79. data/ext/gqlitedb/src/utils.rs +6 -1
  80. data/ext/gqlitedb/src/value/compare.rs +61 -3
  81. data/ext/gqlitedb/src/value.rs +136 -7
  82. data/ext/gqlitedb/templates/sql/postgres/metadata_delete.sql +1 -0
  83. data/ext/gqlitedb/templates/sql/postgres/node_select.sql +1 -1
  84. data/ext/gqlitedb/templates/sql/sqlite/metadata_delete.sql +1 -0
  85. data/ext/gqliterb/src/lib.rs +53 -8
  86. data/ext/gqlparser/Cargo.toml +25 -0
  87. data/ext/gqlparser/README.MD +9 -0
  88. data/ext/gqlparser/benches/pokec_divan.rs +34 -0
  89. data/ext/gqlparser/src/common.rs +69 -0
  90. data/ext/gqlparser/src/gqls/ast.rs +69 -0
  91. data/ext/gqlparser/src/gqls/constraint.rs +79 -0
  92. data/ext/gqlparser/src/gqls/error.rs +22 -0
  93. data/ext/gqlparser/src/gqls/parser.rs +813 -0
  94. data/ext/gqlparser/src/gqls/prelude.rs +8 -0
  95. data/ext/gqlparser/src/gqls/properties.rs +115 -0
  96. data/ext/gqlparser/src/gqls/resolve.rs +207 -0
  97. data/ext/gqlparser/src/gqls.rs +265 -0
  98. data/ext/gqlparser/src/lib.rs +7 -0
  99. data/ext/gqlparser/src/oc/ast.rs +680 -0
  100. data/ext/gqlparser/src/oc/error.rs +172 -0
  101. data/ext/gqlparser/src/oc/lexer.rs +429 -0
  102. data/ext/gqlparser/src/oc/parser/tests.rs +2284 -0
  103. data/ext/gqlparser/src/oc/parser.rs +2005 -0
  104. data/ext/gqlparser/src/oc.rs +33 -0
  105. data/ext/gqlparser/src/prelude.rs +3 -0
  106. data/ext/graphcore/Cargo.toml +1 -0
  107. data/ext/graphcore/src/error.rs +26 -0
  108. data/ext/graphcore/src/graph.rs +177 -51
  109. data/ext/graphcore/src/lib.rs +4 -2
  110. data/ext/graphcore/src/open_cypher.rs +12 -0
  111. data/ext/graphcore/src/table.rs +50 -2
  112. data/ext/graphcore/src/timestamp.rs +127 -104
  113. data/ext/graphcore/src/value/tensor.rs +739 -0
  114. data/ext/graphcore/src/value/value_map.rs +1 -1
  115. data/ext/graphcore/src/value.rs +343 -19
  116. metadata +93 -28
  117. data/ext/gqlitedb/src/parser/ast.rs +0 -604
  118. data/ext/gqlitedb/src/parser/parser_impl.rs +0 -1213
  119. 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
+ }