mod-trace 0.3.1__tar.gz → 0.3.2__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.
Files changed (37) hide show
  1. {mod_trace-0.3.1 → mod_trace-0.3.2}/Cargo.lock +1 -1
  2. {mod_trace-0.3.1 → mod_trace-0.3.2}/Cargo.toml +1 -1
  3. {mod_trace-0.3.1 → mod_trace-0.3.2}/PKG-INFO +1 -1
  4. {mod_trace-0.3.1 → mod_trace-0.3.2}/pyproject.toml +1 -1
  5. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/main.rs +113 -4
  6. {mod_trace-0.3.1 → mod_trace-0.3.2}/.github/workflows/release.yml +0 -0
  7. {mod_trace-0.3.1 → mod_trace-0.3.2}/.gitignore +0 -0
  8. {mod_trace-0.3.1 → mod_trace-0.3.2}/LICENSE +0 -0
  9. {mod_trace-0.3.1 → mod_trace-0.3.2}/README.md +0 -0
  10. {mod_trace-0.3.1 → mod_trace-0.3.2}/benchmarks/tiny_pytorch.py +0 -0
  11. {mod_trace-0.3.1 → mod_trace-0.3.2}/docs/ARCHITECTURE.md +0 -0
  12. {mod_trace-0.3.1 → mod_trace-0.3.2}/docs/REAL_MODELS.md +0 -0
  13. {mod_trace-0.3.1 → mod_trace-0.3.2}/docs/tensor-lab.md +0 -0
  14. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/broken_shape.json +0 -0
  15. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/lightgbm/README.md +0 -0
  16. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/lightgbm/clf_v1.txt +0 -0
  17. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/lightgbm/clf_v2.txt +0 -0
  18. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/lightgbm/generate_demo_models.py +0 -0
  19. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/make_sample_catboost.py +0 -0
  20. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/mlp.json +0 -0
  21. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/onnx/README.md +0 -0
  22. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/onnx/generate_demo_models.py +0 -0
  23. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/onnx/mlp_retrain_a.onnx +0 -0
  24. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/onnx/mlp_retrain_b.onnx +0 -0
  25. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/onnx/mlp_v1.onnx +0 -0
  26. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/onnx/mlp_v2.onnx +0 -0
  27. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/tiny_attention.json +0 -0
  28. {mod_trace-0.3.1 → mod_trace-0.3.2}/examples/tiny_attention_plan.json +0 -0
  29. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/catboost_deep_diff.py +0 -0
  30. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/catboost_explain.py +0 -0
  31. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/cbm.rs +0 -0
  32. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/demo.rs +0 -0
  33. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/explain.rs +0 -0
  34. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/lgbm.rs +0 -0
  35. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/model.rs +0 -0
  36. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/onnx.rs +0 -0
  37. {mod_trace-0.3.1 → mod_trace-0.3.2}/src/tensor.rs +0 -0
@@ -16,7 +16,7 @@ checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
16
16
 
17
17
  [[package]]
18
18
  name = "mod-trace"
19
- version = "0.3.1"
19
+ version = "0.3.2"
20
20
  dependencies = [
21
21
  "serde",
22
22
  "serde_json",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "mod-trace"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  edition = "2024"
5
5
  description = "Rust CLI for inspecting ML model artifacts without loading the framework"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mod-trace
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "mod-trace"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Rust CLI for inspecting ML model artifacts without loading the framework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1027,6 +1027,12 @@ fn opt_num(value: Option<usize>) -> String {
1027
1027
  .unwrap_or_else(|| "unknown".to_string())
1028
1028
  }
1029
1029
 
1030
+ fn opt_float(value: Option<f64>) -> String {
1031
+ value
1032
+ .map(|value| format!("{value}"))
1033
+ .unwrap_or_else(|| "unknown".to_string())
1034
+ }
1035
+
1030
1036
  fn format_count_human(count: usize) -> String {
1031
1037
  let value = count as f64;
1032
1038
  if value >= 1e9 {
@@ -1161,10 +1167,29 @@ fn explain_diff_onnx(old_path: &str, new_path: &str) -> Result<(), String> {
1161
1167
  Ok(())
1162
1168
  }
1163
1169
 
1170
+ fn same_or_changed(equal: bool) -> &'static str {
1171
+ if equal { "same" } else { "changed" }
1172
+ }
1173
+
1164
1174
  fn explain_diff_lgbm(old_path: &str, new_path: &str) -> Result<(), String> {
1165
1175
  let old = lgbm::inspect(old_path)?;
1166
1176
  let new = lgbm::inspect(new_path)?;
1167
1177
 
1178
+ // Feature-name set comparison (not just the count).
1179
+ let old_features = old.feature_names.iter().collect::<BTreeSet<_>>();
1180
+ let new_features = new.feature_names.iter().collect::<BTreeSet<_>>();
1181
+ let added = new_features
1182
+ .difference(&old_features)
1183
+ .map(|name| name.as_str())
1184
+ .collect::<Vec<_>>();
1185
+ let removed = old_features
1186
+ .difference(&new_features)
1187
+ .map(|name| name.as_str())
1188
+ .collect::<Vec<_>>();
1189
+ let names_known = !old.feature_names.is_empty() || !new.feature_names.is_empty();
1190
+
1191
+ let config_same = lgbm_training_config_same(&old, &new);
1192
+
1168
1193
  println!("Model Change Explanation");
1169
1194
  println!("------------------------");
1170
1195
  println!("Type: LightGBM");
@@ -1183,17 +1208,60 @@ fn explain_diff_lgbm(old_path: &str, new_path: &str) -> Result<(), String> {
1183
1208
  opt_num(old.num_leaves.map(|value| value as usize)),
1184
1209
  opt_num(new.num_leaves.map(|value| value as usize))
1185
1210
  );
1211
+ let feature_note = if !names_known {
1212
+ "names not in header".to_string()
1213
+ } else if added.is_empty() && removed.is_empty() {
1214
+ "same set".to_string()
1215
+ } else {
1216
+ format!("+{} added, -{} removed", added.len(), removed.len())
1217
+ };
1186
1218
  println!(
1187
- " Features: {} -> {}",
1219
+ " Features: {} -> {} ({})",
1188
1220
  opt_num(old.num_features.map(|value| value as usize)),
1189
- opt_num(new.num_features.map(|value| value as usize))
1221
+ opt_num(new.num_features.map(|value| value as usize)),
1222
+ feature_note
1190
1223
  );
1224
+ print_lgbm_feature_list(" added: ", &added, 8);
1225
+ print_lgbm_feature_list(" removed:", &removed, 8);
1226
+ println!();
1227
+ println!("Training config:");
1191
1228
  println!(
1192
- " Objective: {} -> {}",
1229
+ " Objective: {} -> {} ({})",
1193
1230
  old.objective.as_deref().unwrap_or("unknown"),
1194
- new.objective.as_deref().unwrap_or("unknown")
1231
+ new.objective.as_deref().unwrap_or("unknown"),
1232
+ same_or_changed(old.objective == new.objective)
1233
+ );
1234
+ println!(
1235
+ " Boosting: {} -> {} ({})",
1236
+ old.boosting.as_deref().unwrap_or("unknown"),
1237
+ new.boosting.as_deref().unwrap_or("unknown"),
1238
+ same_or_changed(old.boosting == new.boosting)
1239
+ );
1240
+ println!(
1241
+ " Metric: {} -> {} ({})",
1242
+ old.metric.as_deref().unwrap_or("unknown"),
1243
+ new.metric.as_deref().unwrap_or("unknown"),
1244
+ same_or_changed(old.metric == new.metric)
1245
+ );
1246
+ println!(
1247
+ " Learning rate: {} -> {} ({})",
1248
+ opt_float(old.learning_rate),
1249
+ opt_float(new.learning_rate),
1250
+ same_or_changed(old.learning_rate == new.learning_rate)
1251
+ );
1252
+ println!(
1253
+ " Max depth: {} -> {} ({})",
1254
+ old.max_depth.map(lgbm_depth_label).as_deref().unwrap_or("unknown"),
1255
+ new.max_depth.map(lgbm_depth_label).as_deref().unwrap_or("unknown"),
1256
+ same_or_changed(old.max_depth == new.max_depth)
1195
1257
  );
1196
1258
  println!();
1259
+ println!(
1260
+ "File size: {} -> {} ({})",
1261
+ format_bytes(old.bytes),
1262
+ format_bytes(new.bytes),
1263
+ growth_label(old.bytes, new.bytes)
1264
+ );
1197
1265
  match (old.estimated_leaf_values(), new.estimated_leaf_values()) {
1198
1266
  (Some(o), Some(n)) => println!(
1199
1267
  "Estimated leaf-slot growth: {}",
@@ -1208,11 +1276,52 @@ fn explain_diff_lgbm(old_path: &str, new_path: &str) -> Result<(), String> {
1208
1276
  println!("Learned state: unchanged (identical leaf values)");
1209
1277
  }
1210
1278
  println!();
1279
+ println!("Summary:");
1280
+ let structure_same = old.num_trees == new.num_trees && old.num_leaves == new.num_leaves;
1281
+ let features_same = added.is_empty() && removed.is_empty();
1282
+ let descriptor = if !features_same {
1283
+ "feature set changed"
1284
+ } else if !config_same {
1285
+ "training config changed"
1286
+ } else if !structure_same {
1287
+ "same spec, retrained with more/fewer trees"
1288
+ } else {
1289
+ "same spec and features, retrained"
1290
+ };
1291
+ println!(
1292
+ " {descriptor}; trees {}, features {}.",
1293
+ growth_label(old.num_trees as usize, new.num_trees as usize),
1294
+ if !names_known {
1295
+ "unknown".to_string()
1296
+ } else if features_same {
1297
+ "unchanged".to_string()
1298
+ } else {
1299
+ format!("+{}/-{}", added.len(), removed.len())
1300
+ }
1301
+ );
1302
+ println!();
1211
1303
  println!("Note: parsed natively from the LightGBM text model.");
1212
1304
 
1213
1305
  Ok(())
1214
1306
  }
1215
1307
 
1308
+ fn print_lgbm_feature_list(label: &str, names: &[&str], limit: usize) {
1309
+ if names.is_empty() {
1310
+ return;
1311
+ }
1312
+ let shown = names
1313
+ .iter()
1314
+ .take(limit)
1315
+ .copied()
1316
+ .collect::<Vec<_>>()
1317
+ .join(", ");
1318
+ if names.len() > limit {
1319
+ println!("{label} {shown}, ... {} more", names.len() - limit);
1320
+ } else {
1321
+ println!("{label} {shown}");
1322
+ }
1323
+ }
1324
+
1216
1325
  fn explain_diff_catboost(old_path: &str, new_path: &str) -> Result<(), String> {
1217
1326
  let old = cbm::inspect(old_path)?;
1218
1327
  let new = cbm::inspect(new_path)?;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes