onnxruntime-ep-mlx 0.29.4__tar.gz → 0.29.6.dev1__tar.gz
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.
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/PKG-INFO +1 -1
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/pyproject.toml +1 -1
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/Cargo.toml +1 -1
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/compiled.rs +192 -19
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/engine.rs +31 -4
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ep.rs +125 -26
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/attention.rs +137 -21
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/controlflow.rs +92 -5
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/conv.rs +59 -18
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/elementwise.rs +63 -35
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/image.rs +1 -1
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/math.rs +49 -49
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/matmul.rs +2 -2
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/misc.rs +44 -14
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/mod.rs +1 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/norm.rs +44 -13
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/onnx_ml_linear.rs +12 -2
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/onnx_ml_preprocess.rs +13 -7
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/quant.rs +225 -15
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/random.rs +29 -9
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/recurrent.rs +15 -12
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/reduction.rs +106 -19
- onnxruntime_ep_mlx-0.29.6.dev1/rust/src/ops/selective_scan.rs +514 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/shape.rs +78 -32
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/signal.rs +40 -8
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/ssm.rs +47 -35
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/stragglers.rs +2 -2
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/ops/vision.rs +59 -9
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/registry.rs +260 -9
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/trace.rs +27 -13
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/.gitignore +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/README.md +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/hatch_build.py +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/build.rs +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/factory.rs +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/lib.rs +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/logging.rs +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/mlx.rs +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/src/sys.rs +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/wrapper_mlx.h +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/rust/wrapper_ort.h +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/src/onnxruntime_ep_mlx/__init__.py +0 -0
- {onnxruntime_ep_mlx-0.29.4 → onnxruntime_ep_mlx-0.29.6.dev1}/src/onnxruntime_ep_mlx/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: onnxruntime-ep-mlx
|
|
3
|
-
Version: 0.29.
|
|
3
|
+
Version: 0.29.6.dev1
|
|
4
4
|
Summary: MLX-native ONNX Runtime execution provider (plugin EP) for Apple Silicon
|
|
5
5
|
Project-URL: Homepage, https://github.com/justinchuby/onnxruntime-mlx
|
|
6
6
|
Project-URL: Repository, https://github.com/justinchuby/onnxruntime-mlx
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "onnxruntime-ep-mlx"
|
|
7
|
-
version = "0.29.
|
|
7
|
+
version = "0.29.6.dev1"
|
|
8
8
|
description = "MLX-native ONNX Runtime execution provider (plugin EP) for Apple Silicon"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -37,6 +37,7 @@ use crate::engine::{
|
|
|
37
37
|
dim_i32, is_separate_qkv_attention, mlx_dtype_from_onnx, read_ctx_input_raw, rope_row_key,
|
|
38
38
|
};
|
|
39
39
|
use crate::mlx::{self, Array, Closure, VectorArray};
|
|
40
|
+
use crate::registry::CompileShapeSafety;
|
|
40
41
|
use crate::sys::mlx as mlxsys;
|
|
41
42
|
use crate::sys::ort;
|
|
42
43
|
|
|
@@ -50,6 +51,29 @@ pub fn compile_enabled(has_control_flow: bool) -> bool {
|
|
|
50
51
|
!has_control_flow && !killed
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
fn node_compile_shape_safety(node: &NodeDesc) -> CompileShapeSafety {
|
|
55
|
+
node.compile_shape_safety
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn compile_shape_mode_supported(nodes: &[NodeDesc], shapeless: bool) -> bool {
|
|
59
|
+
nodes.iter().all(|node| {
|
|
60
|
+
(if shapeless {
|
|
61
|
+
node_compile_shape_safety(node).allows_shapeless()
|
|
62
|
+
} else {
|
|
63
|
+
node_compile_shape_safety(node).allows_shape_keyed()
|
|
64
|
+
}) && node
|
|
65
|
+
.subgraphs
|
|
66
|
+
.iter()
|
|
67
|
+
.all(|subgraph| compile_shape_mode_supported(&subgraph.nodes, shapeless))
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Decode uses `mlx_compile(..., shapeless=true)`, so plans containing a primitive without MLX
|
|
72
|
+
/// output-shape inference must use their shape-keyed/eager route instead.
|
|
73
|
+
pub fn decode_enabled(has_control_flow: bool, nodes: &[NodeDesc]) -> bool {
|
|
74
|
+
compile_enabled(has_control_flow) && compile_shape_mode_supported(nodes, true)
|
|
75
|
+
}
|
|
76
|
+
|
|
53
77
|
/// Op types whose subgraphs keep their existing (eager / compiled-decode) routes because they are
|
|
54
78
|
/// incompatible with a single static-shape fused trace:
|
|
55
79
|
/// * attention ops carry KV-cache aliasing + delta copy-out semantics not modelled by the general
|
|
@@ -82,14 +106,17 @@ fn is_general_compile_unsafe(n: &NodeDesc) -> bool {
|
|
|
82
106
|
/// containing an op that is unsafe to trace (see [`is_general_compile_unsafe`]). An
|
|
83
107
|
/// extra kill-switch `ONNXRUNTIME_EP_MLX_NO_GENERAL_COMPILE` forces eager for A/B numerical validation without
|
|
84
108
|
/// touching the decode path.
|
|
85
|
-
/// A node that would read a DATA-DEPENDENT runtime shape/scalar
|
|
86
|
-
///
|
|
87
|
-
///
|
|
88
|
-
///
|
|
89
|
-
|
|
109
|
+
/// A node that would read a DATA-DEPENDENT runtime shape/scalar while translating the graph.
|
|
110
|
+
///
|
|
111
|
+
/// Shape-keyed MLX caches observe tensor shapes/dtypes, not tensor values. A direct runtime
|
|
112
|
+
/// Reshape/Expand/Slice/Range parameter or CumSum axis would otherwise be read from the live
|
|
113
|
+
/// `OrtKernelContext` during the first trace and silently baked into every later call with the same
|
|
114
|
+
/// input shapes. Initializers and shape-const intermediates (derived from `Shape`/`Size`) are safe:
|
|
115
|
+
/// their value is fixed, or changes exactly when the shape key changes.
|
|
116
|
+
fn reads_data_dependent_parameter(n: &NodeDesc) -> bool {
|
|
90
117
|
if n.subgraphs
|
|
91
118
|
.iter()
|
|
92
|
-
.any(|sg| sg.nodes.iter().any(
|
|
119
|
+
.any(|sg| sg.nodes.iter().any(reads_data_dependent_parameter))
|
|
93
120
|
{
|
|
94
121
|
return true;
|
|
95
122
|
}
|
|
@@ -97,12 +124,13 @@ fn reads_data_dependent_shape(n: &NodeDesc) -> bool {
|
|
|
97
124
|
"Reshape" | "Expand" => &[1],
|
|
98
125
|
"Slice" => &[1, 2, 3, 4],
|
|
99
126
|
"Range" => &[0, 1, 2],
|
|
127
|
+
"CumSum" => &[1],
|
|
100
128
|
_ => return false,
|
|
101
129
|
};
|
|
102
130
|
idx.iter().any(|&i| {
|
|
103
|
-
n.inputs
|
|
104
|
-
.
|
|
105
|
-
|
|
131
|
+
n.inputs.get(i).is_some_and(|tr| {
|
|
132
|
+
!matches!(tr.source, Src::Initializer | Src::Absent) && !tr.constant && !tr.shape_const
|
|
133
|
+
})
|
|
106
134
|
})
|
|
107
135
|
}
|
|
108
136
|
|
|
@@ -118,7 +146,8 @@ pub fn general_enabled(has_control_flow: bool, nodes: &[NodeDesc]) -> bool {
|
|
|
118
146
|
}
|
|
119
147
|
!nodes
|
|
120
148
|
.iter()
|
|
121
|
-
.any(|n| is_general_compile_unsafe(n) ||
|
|
149
|
+
.any(|n| is_general_compile_unsafe(n) || reads_data_dependent_parameter(n))
|
|
150
|
+
&& compile_shape_mode_supported(nodes, false)
|
|
122
151
|
}
|
|
123
152
|
|
|
124
153
|
fn host_scalar_i64(
|
|
@@ -262,10 +291,9 @@ pub fn prefill_enabled(has_control_flow: bool, nodes: &[NodeDesc]) -> bool {
|
|
|
262
291
|
{
|
|
263
292
|
return false;
|
|
264
293
|
}
|
|
265
|
-
nodes
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
})
|
|
294
|
+
compile_shape_mode_supported(nodes, false)
|
|
295
|
+
&& !nodes.iter().any(reads_data_dependent_parameter)
|
|
296
|
+
&& nodes.iter().any(|n| n.op_type == "GroupQueryAttention")
|
|
269
297
|
}
|
|
270
298
|
|
|
271
299
|
/// Query sequence length S = trailing dim of the `input_ids` dynamic ctx input (decode => 1, prefill
|
|
@@ -837,13 +865,13 @@ fn build_closure(
|
|
|
837
865
|
kctx: *mut ort::OrtKernelContext,
|
|
838
866
|
stream: mlxsys::mlx_stream,
|
|
839
867
|
) -> bool {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
868
|
+
let cfg = slot.get(unsafe { &*plan_ptr }).config;
|
|
869
|
+
if !compile_shape_mode_supported(
|
|
870
|
+
&unsafe { &*plan_ptr }.nodes,
|
|
871
|
+
cfg.shape_mode == ShapeMode::Shapeless,
|
|
872
|
+
) {
|
|
844
873
|
return true;
|
|
845
874
|
}
|
|
846
|
-
let cfg = slot.get(unsafe { &*plan_ptr }).config;
|
|
847
875
|
|
|
848
876
|
let mut dyn_inputs: Vec<DynInput> = Vec::new();
|
|
849
877
|
let mut ext_outputs: Vec<OutRef> = Vec::new();
|
|
@@ -1201,3 +1229,148 @@ fn trace_body(
|
|
|
1201
1229
|
}
|
|
1202
1230
|
Ok(())
|
|
1203
1231
|
}
|
|
1232
|
+
|
|
1233
|
+
#[cfg(test)]
|
|
1234
|
+
mod shape_inference_tests {
|
|
1235
|
+
use super::*;
|
|
1236
|
+
use crate::engine::TensorRef;
|
|
1237
|
+
|
|
1238
|
+
fn input(name: &str, source: Src) -> TensorRef {
|
|
1239
|
+
TensorRef {
|
|
1240
|
+
name: name.to_string(),
|
|
1241
|
+
source,
|
|
1242
|
+
ctx_index: 0,
|
|
1243
|
+
constant: false,
|
|
1244
|
+
shape_const: false,
|
|
1245
|
+
init: None,
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
fn cumsum(axis_source: Src) -> NodeDesc {
|
|
1250
|
+
let mut node = NodeDesc::new(
|
|
1251
|
+
"CumSum".to_string(),
|
|
1252
|
+
String::new(),
|
|
1253
|
+
14,
|
|
1254
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1255
|
+
reason: crate::registry::MLX_SCAN_SHAPE_REASON,
|
|
1256
|
+
},
|
|
1257
|
+
);
|
|
1258
|
+
node.inputs = vec![input("data", Src::CtxInput), input("axis", axis_source)];
|
|
1259
|
+
node
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
#[test]
|
|
1263
|
+
fn missing_output_shape_emitters_require_shape_specialized_compile() {
|
|
1264
|
+
for node in [
|
|
1265
|
+
NodeDesc::new(
|
|
1266
|
+
"CumSum".to_string(),
|
|
1267
|
+
String::new(),
|
|
1268
|
+
14,
|
|
1269
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1270
|
+
reason: crate::registry::MLX_SCAN_SHAPE_REASON,
|
|
1271
|
+
},
|
|
1272
|
+
),
|
|
1273
|
+
NodeDesc::new(
|
|
1274
|
+
"Slice".to_string(),
|
|
1275
|
+
String::new(),
|
|
1276
|
+
14,
|
|
1277
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1278
|
+
reason: crate::registry::MLX_SLICE_SHAPE_REASON,
|
|
1279
|
+
},
|
|
1280
|
+
),
|
|
1281
|
+
NodeDesc::new(
|
|
1282
|
+
"LinearAttention".to_string(),
|
|
1283
|
+
"com.microsoft".to_string(),
|
|
1284
|
+
1,
|
|
1285
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1286
|
+
reason: "emits MLX Slice, Pad, and Scan",
|
|
1287
|
+
},
|
|
1288
|
+
),
|
|
1289
|
+
NodeDesc::new(
|
|
1290
|
+
"QMoE".to_string(),
|
|
1291
|
+
"com.microsoft".to_string(),
|
|
1292
|
+
1,
|
|
1293
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1294
|
+
reason: crate::registry::MLX_SLICE_SHAPE_REASON,
|
|
1295
|
+
},
|
|
1296
|
+
),
|
|
1297
|
+
] {
|
|
1298
|
+
assert!(!node_compile_shape_safety(&node).allows_shapeless());
|
|
1299
|
+
}
|
|
1300
|
+
assert!(
|
|
1301
|
+
node_compile_shape_safety(&NodeDesc::new(
|
|
1302
|
+
"Add".to_string(),
|
|
1303
|
+
String::new(),
|
|
1304
|
+
14,
|
|
1305
|
+
CompileShapeSafety::Shapeless,
|
|
1306
|
+
))
|
|
1307
|
+
.allows_shapeless()
|
|
1308
|
+
);
|
|
1309
|
+
assert!(compile_shape_mode_supported(
|
|
1310
|
+
&[NodeDesc::new(
|
|
1311
|
+
"CumSum".to_string(),
|
|
1312
|
+
String::new(),
|
|
1313
|
+
14,
|
|
1314
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1315
|
+
reason: crate::registry::MLX_SCAN_SHAPE_REASON,
|
|
1316
|
+
},
|
|
1317
|
+
)],
|
|
1318
|
+
false
|
|
1319
|
+
));
|
|
1320
|
+
assert!(!compile_shape_mode_supported(
|
|
1321
|
+
&[NodeDesc::new(
|
|
1322
|
+
"CumSum".to_string(),
|
|
1323
|
+
String::new(),
|
|
1324
|
+
14,
|
|
1325
|
+
CompileShapeSafety::ShapeKeyedOnly {
|
|
1326
|
+
reason: crate::registry::MLX_SCAN_SHAPE_REASON,
|
|
1327
|
+
},
|
|
1328
|
+
)],
|
|
1329
|
+
true
|
|
1330
|
+
));
|
|
1331
|
+
let eager_only_gqa = NodeDesc::new(
|
|
1332
|
+
"GroupQueryAttention".to_string(),
|
|
1333
|
+
"com.microsoft".to_string(),
|
|
1334
|
+
1,
|
|
1335
|
+
CompileShapeSafety::EagerOnly {
|
|
1336
|
+
reason: "attention-bias form",
|
|
1337
|
+
},
|
|
1338
|
+
);
|
|
1339
|
+
assert!(!compile_shape_mode_supported(
|
|
1340
|
+
std::slice::from_ref(&eager_only_gqa),
|
|
1341
|
+
false
|
|
1342
|
+
));
|
|
1343
|
+
assert!(!compile_shape_mode_supported(&[eager_only_gqa], true));
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
#[test]
|
|
1347
|
+
fn runtime_cumsum_axis_is_not_a_shape_key() {
|
|
1348
|
+
assert!(
|
|
1349
|
+
reads_data_dependent_parameter(&cumsum(Src::CtxInput)),
|
|
1350
|
+
"a runtime axis value must not be baked into a shape-keyed closure"
|
|
1351
|
+
);
|
|
1352
|
+
assert!(
|
|
1353
|
+
!reads_data_dependent_parameter(&cumsum(Src::Initializer)),
|
|
1354
|
+
"an initializer axis is compile-time data"
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
#[test]
|
|
1359
|
+
fn direct_runtime_shape_parameter_is_data_dependent() {
|
|
1360
|
+
let mut reshape = NodeDesc::new(
|
|
1361
|
+
"Reshape".to_string(),
|
|
1362
|
+
String::new(),
|
|
1363
|
+
14,
|
|
1364
|
+
CompileShapeSafety::Shapeless,
|
|
1365
|
+
);
|
|
1366
|
+
reshape.inputs = vec![input("data", Src::CtxInput), input("shape", Src::CtxInput)];
|
|
1367
|
+
assert!(reads_data_dependent_parameter(&reshape));
|
|
1368
|
+
|
|
1369
|
+
reshape.inputs[1].source = Src::Intermediate;
|
|
1370
|
+
reshape.inputs[1].shape_const = true;
|
|
1371
|
+
assert!(
|
|
1372
|
+
!reads_data_dependent_parameter(&reshape),
|
|
1373
|
+
"Shape/Size-derived parameters change with the shape-keyed cache key"
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
@@ -27,8 +27,7 @@ pub struct InitData {
|
|
|
27
27
|
pub data: *const c_void,
|
|
28
28
|
pub shape: Vec<i64>,
|
|
29
29
|
pub dtype: ort::ONNXTensorElementDataType,
|
|
30
|
-
///
|
|
31
|
-
#[allow(dead_code)]
|
|
30
|
+
/// ORT-reported element count, used to bound typed initializer reads safely.
|
|
32
31
|
pub count: usize,
|
|
33
32
|
/// Owned copy of the bytes (control-flow body initializers), keeping `data` valid.
|
|
34
33
|
#[allow(dead_code)]
|
|
@@ -137,6 +136,7 @@ pub struct NodeDesc {
|
|
|
137
136
|
pub op_type: String,
|
|
138
137
|
pub domain: String,
|
|
139
138
|
pub since_version: i32,
|
|
139
|
+
pub compile_shape_safety: crate::registry::CompileShapeSafety,
|
|
140
140
|
pub ints: HashMap<String, i64>,
|
|
141
141
|
pub floats: HashMap<String, f32>,
|
|
142
142
|
pub int_arrays: HashMap<String, Vec<i64>>,
|
|
@@ -152,13 +152,19 @@ pub struct NodeDesc {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
impl NodeDesc {
|
|
155
|
-
pub fn new(
|
|
155
|
+
pub fn new(
|
|
156
|
+
op_type: String,
|
|
157
|
+
domain: String,
|
|
158
|
+
since_version: i32,
|
|
159
|
+
compile_shape_safety: crate::registry::CompileShapeSafety,
|
|
160
|
+
) -> Self {
|
|
156
161
|
NodeDesc {
|
|
157
162
|
node_id: 0,
|
|
158
163
|
name: String::new(),
|
|
159
164
|
op_type,
|
|
160
165
|
domain,
|
|
161
166
|
since_version,
|
|
167
|
+
compile_shape_safety,
|
|
162
168
|
ints: HashMap::new(),
|
|
163
169
|
floats: HashMap::new(),
|
|
164
170
|
int_arrays: HashMap::new(),
|
|
@@ -1217,6 +1223,22 @@ impl<'a> TranslationContext<'a> {
|
|
|
1217
1223
|
Ok(self.keep(res))
|
|
1218
1224
|
}
|
|
1219
1225
|
|
|
1226
|
+
/// Shape-preserving cumulative sum.
|
|
1227
|
+
///
|
|
1228
|
+
/// MLX 0.32's backing `Scan` primitive lacks `Primitive::output_shapes`, so every registered
|
|
1229
|
+
/// handler that can reach this helper must declare `CompileShapeSafety::ShapeKeyedOnly`.
|
|
1230
|
+
pub fn cumsum(
|
|
1231
|
+
&mut self,
|
|
1232
|
+
a: mlxsys::mlx_array,
|
|
1233
|
+
axis: i32,
|
|
1234
|
+
reverse: bool,
|
|
1235
|
+
inclusive: bool,
|
|
1236
|
+
) -> Result<mlxsys::mlx_array, MlxError> {
|
|
1237
|
+
self.emit(|res, stream| unsafe {
|
|
1238
|
+
mlxsys::mlx_cumsum(res, a, axis, reverse, inclusive, stream)
|
|
1239
|
+
})
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1220
1242
|
// ---- array introspection (borrowed raw handles; ownership stays with the arena/cache) ---------
|
|
1221
1243
|
|
|
1222
1244
|
pub fn shape_of(&self, a: mlxsys::mlx_array) -> Vec<i32> {
|
|
@@ -2191,7 +2213,12 @@ mod tests {
|
|
|
2191
2213
|
}
|
|
2192
2214
|
|
|
2193
2215
|
fn node(op_type: &str, inputs: &[&str], output_name: &str) -> NodeDesc {
|
|
2194
|
-
let mut node = NodeDesc::new(
|
|
2216
|
+
let mut node = NodeDesc::new(
|
|
2217
|
+
op_type.to_string(),
|
|
2218
|
+
String::new(),
|
|
2219
|
+
17,
|
|
2220
|
+
crate::registry::CompileShapeSafety::Shapeless,
|
|
2221
|
+
);
|
|
2195
2222
|
node.inputs = inputs.iter().map(|name| input(name)).collect();
|
|
2196
2223
|
node.outputs = vec![output(output_name)];
|
|
2197
2224
|
node
|