mod-trace 0.3.0__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.0 → mod_trace-0.3.2}/Cargo.lock +1 -1
  2. {mod_trace-0.3.0 → mod_trace-0.3.2}/Cargo.toml +1 -1
  3. {mod_trace-0.3.0 → mod_trace-0.3.2}/PKG-INFO +1 -1
  4. {mod_trace-0.3.0 → mod_trace-0.3.2}/pyproject.toml +1 -1
  5. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/main.rs +119 -6
  6. {mod_trace-0.3.0 → mod_trace-0.3.2}/.github/workflows/release.yml +0 -0
  7. {mod_trace-0.3.0 → mod_trace-0.3.2}/.gitignore +0 -0
  8. {mod_trace-0.3.0 → mod_trace-0.3.2}/LICENSE +0 -0
  9. {mod_trace-0.3.0 → mod_trace-0.3.2}/README.md +0 -0
  10. {mod_trace-0.3.0 → mod_trace-0.3.2}/benchmarks/tiny_pytorch.py +0 -0
  11. {mod_trace-0.3.0 → mod_trace-0.3.2}/docs/ARCHITECTURE.md +0 -0
  12. {mod_trace-0.3.0 → mod_trace-0.3.2}/docs/REAL_MODELS.md +0 -0
  13. {mod_trace-0.3.0 → mod_trace-0.3.2}/docs/tensor-lab.md +0 -0
  14. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/broken_shape.json +0 -0
  15. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/lightgbm/README.md +0 -0
  16. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/lightgbm/clf_v1.txt +0 -0
  17. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/lightgbm/clf_v2.txt +0 -0
  18. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/lightgbm/generate_demo_models.py +0 -0
  19. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/make_sample_catboost.py +0 -0
  20. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/mlp.json +0 -0
  21. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/onnx/README.md +0 -0
  22. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/onnx/generate_demo_models.py +0 -0
  23. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/onnx/mlp_retrain_a.onnx +0 -0
  24. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/onnx/mlp_retrain_b.onnx +0 -0
  25. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/onnx/mlp_v1.onnx +0 -0
  26. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/onnx/mlp_v2.onnx +0 -0
  27. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/tiny_attention.json +0 -0
  28. {mod_trace-0.3.0 → mod_trace-0.3.2}/examples/tiny_attention_plan.json +0 -0
  29. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/catboost_deep_diff.py +0 -0
  30. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/catboost_explain.py +0 -0
  31. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/cbm.rs +0 -0
  32. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/demo.rs +0 -0
  33. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/explain.rs +0 -0
  34. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/lgbm.rs +0 -0
  35. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/model.rs +0 -0
  36. {mod_trace-0.3.0 → mod_trace-0.3.2}/src/onnx.rs +0 -0
  37. {mod_trace-0.3.0 → 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.0"
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.0"
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.0
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.0"
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"
@@ -192,11 +192,15 @@ fn print_doctor_report(report: &DoctorReport) {
192
192
  println!();
193
193
  println!("Available commands:");
194
194
  println!(
195
- " inspect .cbm/.onnx/.json: {}",
195
+ " inspect .cbm/.lgb/.onnx/.json: {}",
196
196
  available_unavailable(report.commands.inspect_artifacts)
197
197
  );
198
198
  println!(
199
- " diff .cbm/.onnx: {}",
199
+ " diff .cbm/.lgb/.onnx: {}",
200
+ available_unavailable(report.commands.diff_artifacts)
201
+ );
202
+ println!(
203
+ " explain-diff .cbm/.lgb/.onnx: {}",
200
204
  available_unavailable(report.commands.diff_artifacts)
201
205
  );
202
206
  println!(
@@ -1023,6 +1027,12 @@ fn opt_num(value: Option<usize>) -> String {
1023
1027
  .unwrap_or_else(|| "unknown".to_string())
1024
1028
  }
1025
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
+
1026
1036
  fn format_count_human(count: usize) -> String {
1027
1037
  let value = count as f64;
1028
1038
  if value >= 1e9 {
@@ -1157,10 +1167,29 @@ fn explain_diff_onnx(old_path: &str, new_path: &str) -> Result<(), String> {
1157
1167
  Ok(())
1158
1168
  }
1159
1169
 
1170
+ fn same_or_changed(equal: bool) -> &'static str {
1171
+ if equal { "same" } else { "changed" }
1172
+ }
1173
+
1160
1174
  fn explain_diff_lgbm(old_path: &str, new_path: &str) -> Result<(), String> {
1161
1175
  let old = lgbm::inspect(old_path)?;
1162
1176
  let new = lgbm::inspect(new_path)?;
1163
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
+
1164
1193
  println!("Model Change Explanation");
1165
1194
  println!("------------------------");
1166
1195
  println!("Type: LightGBM");
@@ -1179,17 +1208,60 @@ fn explain_diff_lgbm(old_path: &str, new_path: &str) -> Result<(), String> {
1179
1208
  opt_num(old.num_leaves.map(|value| value as usize)),
1180
1209
  opt_num(new.num_leaves.map(|value| value as usize))
1181
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
+ };
1182
1218
  println!(
1183
- " Features: {} -> {}",
1219
+ " Features: {} -> {} ({})",
1184
1220
  opt_num(old.num_features.map(|value| value as usize)),
1185
- opt_num(new.num_features.map(|value| value as usize))
1221
+ opt_num(new.num_features.map(|value| value as usize)),
1222
+ feature_note
1186
1223
  );
1224
+ print_lgbm_feature_list(" added: ", &added, 8);
1225
+ print_lgbm_feature_list(" removed:", &removed, 8);
1226
+ println!();
1227
+ println!("Training config:");
1187
1228
  println!(
1188
- " Objective: {} -> {}",
1229
+ " Objective: {} -> {} ({})",
1189
1230
  old.objective.as_deref().unwrap_or("unknown"),
1190
- 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)
1191
1257
  );
1192
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
+ );
1193
1265
  match (old.estimated_leaf_values(), new.estimated_leaf_values()) {
1194
1266
  (Some(o), Some(n)) => println!(
1195
1267
  "Estimated leaf-slot growth: {}",
@@ -1204,11 +1276,52 @@ fn explain_diff_lgbm(old_path: &str, new_path: &str) -> Result<(), String> {
1204
1276
  println!("Learned state: unchanged (identical leaf values)");
1205
1277
  }
1206
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!();
1207
1303
  println!("Note: parsed natively from the LightGBM text model.");
1208
1304
 
1209
1305
  Ok(())
1210
1306
  }
1211
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
+
1212
1325
  fn explain_diff_catboost(old_path: &str, new_path: &str) -> Result<(), String> {
1213
1326
  let old = cbm::inspect(old_path)?;
1214
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