explainiverse 0.8.4__py3-none-any.whl → 0.8.6__py3-none-any.whl

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.
explainiverse/__init__.py CHANGED
@@ -34,7 +34,7 @@ from explainiverse.adapters.sklearn_adapter import SklearnAdapter
34
34
  from explainiverse.adapters import TORCH_AVAILABLE
35
35
  from explainiverse.engine.suite import ExplanationSuite
36
36
 
37
- __version__ = "0.8.4"
37
+ __version__ = "0.8.6"
38
38
 
39
39
  __all__ = [
40
40
  # Core
@@ -45,6 +45,10 @@ from explainiverse.evaluation.faithfulness_extended import (
45
45
  compute_batch_monotonicity_nguyen,
46
46
  compute_pixel_flipping,
47
47
  compute_batch_pixel_flipping,
48
+ compute_region_perturbation,
49
+ compute_batch_region_perturbation,
50
+ compute_selectivity,
51
+ compute_batch_selectivity,
48
52
  )
49
53
 
50
54
  __all__ = [
@@ -78,4 +82,8 @@ __all__ = [
78
82
  "compute_batch_monotonicity_nguyen",
79
83
  "compute_pixel_flipping",
80
84
  "compute_batch_pixel_flipping",
85
+ "compute_region_perturbation",
86
+ "compute_batch_region_perturbation",
87
+ "compute_selectivity",
88
+ "compute_batch_selectivity",
81
89
  ]
@@ -256,6 +256,421 @@ def compute_batch_faithfulness_estimate(
256
256
  }
257
257
 
258
258
 
259
+ # =============================================================================
260
+ # Metric 6: Selectivity (Montavon et al., 2018)
261
+ # =============================================================================
262
+
263
+ def compute_selectivity(
264
+ model,
265
+ instance: np.ndarray,
266
+ explanation: Explanation,
267
+ baseline: Union[str, float, np.ndarray, Callable] = "mean",
268
+ background_data: np.ndarray = None,
269
+ target_class: int = None,
270
+ n_steps: int = None,
271
+ use_absolute: bool = True,
272
+ return_details: bool = False,
273
+ ) -> Union[float, Dict[str, Union[float, np.ndarray]]]:
274
+ """
275
+ Compute Selectivity score using AOPC (Montavon et al., 2018).
276
+
277
+ Measures how quickly the prediction function drops when removing features
278
+ with the highest attributed values. Computed as the Area Over the
279
+ Perturbation Curve (AOPC), which is the average prediction drop across
280
+ all perturbation steps.
281
+
282
+ AOPC = (1/(K+1)) * Σₖ₌₀ᴷ [f(x) - f(x_{1..k})]
283
+
284
+ where:
285
+ - f(x) is the original prediction for the target class
286
+ - f(x_{1..k}) is the prediction after removing the top-k most important features
287
+ - K is the total number of perturbation steps (default: n_features)
288
+
289
+ Higher AOPC indicates better selectivity - the explanation correctly
290
+ identifies features whose removal causes the largest prediction drop.
291
+
292
+ Args:
293
+ model: Model adapter with predict/predict_proba method
294
+ instance: Input instance (1D array)
295
+ explanation: Explanation object with feature_attributions
296
+ baseline: Baseline for feature removal ("mean", "median", scalar, array, callable)
297
+ background_data: Reference data for computing baseline (required for "mean"/"median")
298
+ target_class: Target class index for probability (default: predicted class)
299
+ n_steps: Number of perturbation steps (default: n_features, max features to remove)
300
+ use_absolute: If True, sort features by absolute attribution value (default: True)
301
+ return_details: If True, return detailed results including prediction drops per step
302
+
303
+ Returns:
304
+ If return_details=False: AOPC score (float, higher is better)
305
+ If return_details=True: Dictionary with:
306
+ - 'aopc': float - Area Over the Perturbation Curve (average drop)
307
+ - 'prediction_drops': np.ndarray - Drop at each step [f(x) - f(x_{1..k})]
308
+ - 'predictions': np.ndarray - Predictions at each step
309
+ - 'feature_order': np.ndarray - Order in which features were removed
310
+ - 'n_steps': int - Number of perturbation steps
311
+
312
+ References:
313
+ Montavon, G., Samek, W., & Müller, K. R. (2018). Methods for Interpreting
314
+ and Understanding Deep Neural Networks. Digital Signal Processing, 73, 1-15.
315
+
316
+ Samek, W., Binder, A., Montavon, G., Lapuschkin, S., & Müller, K. R. (2016).
317
+ Evaluating the Visualization of What a Deep Neural Network has Learned.
318
+ IEEE Transactions on Neural Networks and Learning Systems, 28(11), 2660-2673.
319
+ """
320
+ instance = np.asarray(instance).flatten()
321
+ n_features = len(instance)
322
+
323
+ # Get baseline values
324
+ baseline_values = compute_baseline_values(
325
+ baseline, background_data, n_features
326
+ )
327
+
328
+ # Extract attributions as array
329
+ attr_array = _extract_attribution_array(explanation, n_features)
330
+
331
+ # Determine number of steps (default: all features)
332
+ if n_steps is None:
333
+ n_steps = n_features
334
+ n_steps = min(n_steps, n_features)
335
+
336
+ # Sort features by attribution (descending - most important first)
337
+ if use_absolute:
338
+ sorted_indices = np.argsort(-np.abs(attr_array))
339
+ else:
340
+ sorted_indices = np.argsort(-attr_array)
341
+
342
+ # Determine target class
343
+ if target_class is None:
344
+ pred = get_prediction_value(model, instance.reshape(1, -1))
345
+ if isinstance(pred, np.ndarray) and pred.ndim > 0:
346
+ target_class = int(np.argmax(pred))
347
+ else:
348
+ target_class = 0
349
+
350
+ # Get original prediction for the target class
351
+ original_pred = get_prediction_value(model, instance.reshape(1, -1))
352
+ if isinstance(original_pred, np.ndarray) and original_pred.ndim > 0 and len(original_pred) > target_class:
353
+ original_value = original_pred[target_class]
354
+ else:
355
+ original_value = float(original_pred)
356
+
357
+ # Start with original instance
358
+ current = instance.copy()
359
+
360
+ # Track predictions and drops at each step
361
+ # Step 0: no features removed (drop = 0)
362
+ predictions = [original_value]
363
+ prediction_drops = [0.0] # f(x) - f(x) = 0
364
+
365
+ # Remove features one by one (most important first)
366
+ for k in range(n_steps):
367
+ idx = sorted_indices[k]
368
+ # Remove this feature (replace with baseline)
369
+ current[idx] = baseline_values[idx]
370
+
371
+ # Get prediction
372
+ pred = get_prediction_value(model, current.reshape(1, -1))
373
+ if isinstance(pred, np.ndarray) and pred.ndim > 0 and len(pred) > target_class:
374
+ current_pred = pred[target_class]
375
+ else:
376
+ current_pred = float(pred)
377
+
378
+ predictions.append(current_pred)
379
+ # Prediction drop: f(x) - f(x_{1..k})
380
+ prediction_drops.append(original_value - current_pred)
381
+
382
+ predictions = np.array(predictions)
383
+ prediction_drops = np.array(prediction_drops)
384
+
385
+ # Compute AOPC: average of prediction drops across all steps
386
+ # AOPC = (1/(K+1)) * Σₖ₌₀ᴷ [f(x) - f(x_{1..k})]
387
+ aopc = np.mean(prediction_drops)
388
+
389
+ if return_details:
390
+ return {
391
+ "aopc": float(aopc),
392
+ "prediction_drops": prediction_drops,
393
+ "predictions": predictions,
394
+ "feature_order": sorted_indices[:n_steps],
395
+ "n_steps": n_steps,
396
+ "original_prediction": original_value,
397
+ }
398
+
399
+ return float(aopc)
400
+
401
+
402
+ def compute_batch_selectivity(
403
+ model,
404
+ X: np.ndarray,
405
+ explanations: List[Explanation],
406
+ baseline: Union[str, float, np.ndarray, Callable] = "mean",
407
+ max_samples: int = None,
408
+ n_steps: int = None,
409
+ use_absolute: bool = True,
410
+ ) -> Dict[str, float]:
411
+ """
412
+ Compute average Selectivity (AOPC) over a batch of instances.
413
+
414
+ Args:
415
+ model: Model adapter
416
+ X: Input data (2D array)
417
+ explanations: List of Explanation objects (one per instance)
418
+ baseline: Baseline for feature removal
419
+ max_samples: Maximum number of samples to evaluate
420
+ n_steps: Number of perturbation steps per instance
421
+ use_absolute: If True, sort features by absolute attribution value
422
+
423
+ Returns:
424
+ Dictionary with mean, std, min, max, and count of valid scores
425
+ """
426
+ n_samples = len(explanations)
427
+ if max_samples:
428
+ n_samples = min(n_samples, max_samples)
429
+
430
+ scores = []
431
+
432
+ for i in range(n_samples):
433
+ try:
434
+ score = compute_selectivity(
435
+ model, X[i], explanations[i],
436
+ baseline=baseline, background_data=X,
437
+ n_steps=n_steps,
438
+ use_absolute=use_absolute
439
+ )
440
+ if not np.isnan(score):
441
+ scores.append(score)
442
+ except Exception:
443
+ continue
444
+
445
+ if not scores:
446
+ return {"mean": 0.0, "std": 0.0, "min": 0.0, "max": 0.0, "n_samples": 0}
447
+
448
+ return {
449
+ "mean": float(np.mean(scores)),
450
+ "std": float(np.std(scores)),
451
+ "min": float(np.min(scores)),
452
+ "max": float(np.max(scores)),
453
+ "n_samples": len(scores),
454
+ }
455
+
456
+
457
+ # =============================================================================
458
+ # Metric 5: Region Perturbation (Samek et al., 2015)
459
+ # =============================================================================
460
+
461
+ def compute_region_perturbation(
462
+ model,
463
+ instance: np.ndarray,
464
+ explanation: Explanation,
465
+ baseline: Union[str, float, np.ndarray, Callable] = "mean",
466
+ background_data: np.ndarray = None,
467
+ target_class: int = None,
468
+ region_size: int = None,
469
+ use_absolute: bool = True,
470
+ return_curve: bool = False,
471
+ ) -> Union[float, Dict[str, Union[float, np.ndarray]]]:
472
+ """
473
+ Compute Region Perturbation score (Samek et al., 2015).
474
+
475
+ Similar to Pixel Flipping, but operates on regions (groups) of features
476
+ rather than individual features. Features are divided into non-overlapping
477
+ regions, and regions are perturbed in order of their cumulative importance
478
+ (sum of attributions within the region).
479
+
480
+ This metric is particularly relevant for image data where local spatial
481
+ correlations exist, but is also applicable to tabular data with groups
482
+ of related features.
483
+
484
+ The score is the Area Under the perturbation Curve (AUC), normalized
485
+ to [0, 1]. Lower AUC indicates better faithfulness (faster degradation
486
+ when important regions are removed first).
487
+
488
+ Args:
489
+ model: Model adapter with predict/predict_proba method
490
+ instance: Input instance (1D array)
491
+ explanation: Explanation object with feature_attributions
492
+ baseline: Baseline for feature removal ("mean", "median", scalar, array, callable)
493
+ background_data: Reference data for computing baseline (required for "mean"/"median")
494
+ target_class: Target class index for probability (default: predicted class)
495
+ region_size: Number of features per region. If None, defaults to max(1, n_features // 4)
496
+ For image-like data, this would correspond to patch size.
497
+ use_absolute: If True, sort regions by absolute attribution sum (default: True)
498
+ return_curve: If True, return full degradation curve and details
499
+
500
+ Returns:
501
+ If return_curve=False: AUC score (float, 0 to 1, lower is better)
502
+ If return_curve=True: Dictionary with:
503
+ - 'auc': float - Area under the perturbation curve
504
+ - 'curve': np.ndarray - Normalized prediction values at each step
505
+ - 'predictions': np.ndarray - Raw prediction values
506
+ - 'region_order': list - Order in which regions were perturbed
507
+ - 'regions': list - List of feature indices in each region
508
+ - 'n_regions': int - Number of regions
509
+ - 'region_size': int - Size of each region
510
+
511
+ References:
512
+ Samek, W., Binder, A., Montavon, G., Lapuschkin, S., & Müller, K. R. (2015).
513
+ Evaluating the Visualization of What a Deep Neural Network has Learned.
514
+ arXiv preprint arXiv:1509.06321.
515
+ """
516
+ instance = np.asarray(instance).flatten()
517
+ n_features = len(instance)
518
+
519
+ # Get baseline values
520
+ baseline_values = compute_baseline_values(
521
+ baseline, background_data, n_features
522
+ )
523
+
524
+ # Extract attributions as array
525
+ attr_array = _extract_attribution_array(explanation, n_features)
526
+
527
+ # Determine region size
528
+ if region_size is None:
529
+ # Default: divide features into ~4 regions
530
+ region_size = max(1, n_features // 4)
531
+ region_size = max(1, min(region_size, n_features)) # Clamp to valid range
532
+
533
+ # Create non-overlapping regions
534
+ regions = []
535
+ for start_idx in range(0, n_features, region_size):
536
+ end_idx = min(start_idx + region_size, n_features)
537
+ regions.append(list(range(start_idx, end_idx)))
538
+
539
+ n_regions = len(regions)
540
+
541
+ # Compute region importance (sum of attributions in each region)
542
+ region_importance = []
543
+ for region in regions:
544
+ if use_absolute:
545
+ importance = np.sum(np.abs(attr_array[region]))
546
+ else:
547
+ importance = np.sum(attr_array[region])
548
+ region_importance.append(importance)
549
+
550
+ # Sort regions by importance (descending - most important first)
551
+ sorted_region_indices = np.argsort(-np.array(region_importance))
552
+
553
+ # Determine target class
554
+ if target_class is None:
555
+ pred = get_prediction_value(model, instance.reshape(1, -1))
556
+ if isinstance(pred, np.ndarray) and pred.ndim > 0:
557
+ target_class = int(np.argmax(pred))
558
+ else:
559
+ target_class = 0
560
+
561
+ # Get original prediction for the target class
562
+ original_pred = get_prediction_value(model, instance.reshape(1, -1))
563
+ if isinstance(original_pred, np.ndarray) and original_pred.ndim > 0 and len(original_pred) > target_class:
564
+ original_value = original_pred[target_class]
565
+ else:
566
+ original_value = float(original_pred)
567
+
568
+ # Start with original instance
569
+ current = instance.copy()
570
+
571
+ # Track predictions as regions are perturbed
572
+ predictions = [original_value]
573
+
574
+ # Perturb regions one by one (most important first)
575
+ for region_idx in sorted_region_indices:
576
+ region = regions[region_idx]
577
+
578
+ # Replace all features in this region with baseline
579
+ for feat_idx in region:
580
+ current[feat_idx] = baseline_values[feat_idx]
581
+
582
+ # Get prediction
583
+ pred = get_prediction_value(model, current.reshape(1, -1))
584
+ if isinstance(pred, np.ndarray) and pred.ndim > 0 and len(pred) > target_class:
585
+ predictions.append(pred[target_class])
586
+ else:
587
+ predictions.append(float(pred))
588
+
589
+ predictions = np.array(predictions)
590
+
591
+ # Normalize predictions to [0, 1] relative to original
592
+ # curve[i] = prediction after perturbing i regions / original prediction
593
+ if abs(original_value) > 1e-10:
594
+ curve = predictions / original_value
595
+ else:
596
+ # Handle zero original prediction
597
+ curve = predictions
598
+
599
+ # Compute AUC using trapezoidal rule
600
+ # x-axis: fraction of regions perturbed (0 to 1)
601
+ # y-axis: relative prediction value
602
+ x = np.linspace(0, 1, len(predictions))
603
+ auc = np.trapz(curve, x)
604
+
605
+ if return_curve:
606
+ return {
607
+ "auc": float(auc),
608
+ "curve": curve,
609
+ "predictions": predictions,
610
+ "region_order": sorted_region_indices.tolist(),
611
+ "regions": regions,
612
+ "n_regions": n_regions,
613
+ "region_size": region_size,
614
+ }
615
+
616
+ return float(auc)
617
+
618
+
619
+ def compute_batch_region_perturbation(
620
+ model,
621
+ X: np.ndarray,
622
+ explanations: List[Explanation],
623
+ baseline: Union[str, float, np.ndarray, Callable] = "mean",
624
+ max_samples: int = None,
625
+ region_size: int = None,
626
+ use_absolute: bool = True,
627
+ ) -> Dict[str, float]:
628
+ """
629
+ Compute average Region Perturbation score over a batch of instances.
630
+
631
+ Args:
632
+ model: Model adapter
633
+ X: Input data (2D array)
634
+ explanations: List of Explanation objects (one per instance)
635
+ baseline: Baseline for feature removal
636
+ max_samples: Maximum number of samples to evaluate
637
+ region_size: Number of features per region (default: n_features // 4)
638
+ use_absolute: If True, sort regions by absolute attribution sum
639
+
640
+ Returns:
641
+ Dictionary with mean, std, min, max, and count of valid scores
642
+ """
643
+ n_samples = len(explanations)
644
+ if max_samples:
645
+ n_samples = min(n_samples, max_samples)
646
+
647
+ scores = []
648
+
649
+ for i in range(n_samples):
650
+ try:
651
+ score = compute_region_perturbation(
652
+ model, X[i], explanations[i],
653
+ baseline=baseline, background_data=X,
654
+ region_size=region_size,
655
+ use_absolute=use_absolute
656
+ )
657
+ if not np.isnan(score):
658
+ scores.append(score)
659
+ except Exception:
660
+ continue
661
+
662
+ if not scores:
663
+ return {"mean": 0.0, "std": 0.0, "min": 0.0, "max": 0.0, "n_samples": 0}
664
+
665
+ return {
666
+ "mean": float(np.mean(scores)),
667
+ "std": float(np.std(scores)),
668
+ "min": float(np.min(scores)),
669
+ "max": float(np.max(scores)),
670
+ "n_samples": len(scores),
671
+ }
672
+
673
+
259
674
  # =============================================================================
260
675
  # Metric 4: Pixel Flipping (Bach et al., 2015)
261
676
  # =============================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: explainiverse
3
- Version: 0.8.4
3
+ Version: 0.8.6
4
4
  Summary: Unified, extensible explainability framework supporting 18 XAI methods including LIME, SHAP, LRP, TCAV, GradCAM, and more
5
5
  Home-page: https://github.com/jemsbhai/explainiverse
6
6
  License: MIT
@@ -44,7 +44,7 @@ Description-Content-Type: text/markdown
44
44
  | Feature | Description |
45
45
  |---------|-------------|
46
46
  | **18 Explainers** | LIME, KernelSHAP, TreeSHAP, Integrated Gradients, DeepLIFT, DeepSHAP, SmoothGrad, Saliency Maps, GradCAM/GradCAM++, LRP, TCAV, Anchors, Counterfactual, Permutation Importance, PDP, ALE, SAGE, ProtoDash |
47
- | **13 Evaluation Metrics** | Faithfulness (PGI, PGU, Comprehensiveness, Sufficiency, Correlation, Faithfulness Estimate, Monotonicity, Monotonicity-Nguyen, Pixel Flipping) and Stability (RIS, ROS, Lipschitz) |
47
+ | **15 Evaluation Metrics** | Faithfulness (PGI, PGU, Comprehensiveness, Sufficiency, Correlation, Faithfulness Estimate, Monotonicity, Monotonicity-Nguyen, Pixel Flipping, Region Perturbation, Selectivity) and Stability (RIS, ROS, Lipschitz) |
48
48
  | **Unified API** | Consistent `BaseExplainer` interface with standardized `Explanation` output |
49
49
  | **Plugin Registry** | Filter explainers by scope, model type, data type; automatic recommendations |
50
50
  | **Framework Support** | Adapters for scikit-learn and PyTorch (with gradient computation) |
@@ -100,6 +100,8 @@ Explainiverse includes a comprehensive suite of evaluation metrics based on the
100
100
  | **Monotonicity** | Sequential feature addition shows monotonic prediction increase | [Arya et al., 2019](https://arxiv.org/abs/1909.03012) |
101
101
  | **Monotonicity-Nguyen** | Spearman correlation between attributions and feature removal impact | [Nguyen & Martinez, 2020](https://arxiv.org/abs/2010.07455) |
102
102
  | **Pixel Flipping** | AUC of prediction degradation when removing features by importance | [Bach et al., 2015](https://doi.org/10.1371/journal.pone.0130140) |
103
+ | **Region Perturbation** | AUC of prediction degradation when perturbing feature regions by importance | [Samek et al., 2015](https://arxiv.org/abs/1509.06321) |
104
+ | **Selectivity (AOPC)** | Average prediction drop when sequentially removing features by importance | [Montavon et al., 2018](https://doi.org/10.1016/j.dsp.2017.10.011) |
103
105
 
104
106
  ### Stability Metrics
105
107
 
@@ -1,4 +1,4 @@
1
- explainiverse/__init__.py,sha256=bi_M_46DTXxO2sTGol7RX7LrCajNZSw12CYg7I9WE90,1694
1
+ explainiverse/__init__.py,sha256=KFPDVxlFzpILL_9pdWSDKFtrnVo086_K1KeYcJIT6cc,1694
2
2
  explainiverse/adapters/__init__.py,sha256=HcQGISyp-YQ4jEj2IYveX_c9X5otLcTNWRnVRRhzRik,781
3
3
  explainiverse/adapters/base_adapter.py,sha256=Nqt0GeDn_-PjTyJcZsE8dRTulavqFQsv8sMYWS_ps-M,603
4
4
  explainiverse/adapters/pytorch_adapter.py,sha256=DLQKJ7gB0foPwAmcrru7QdZnPRnhqDKpFCT-EaD3420,15612
@@ -9,10 +9,10 @@ explainiverse/core/explanation.py,sha256=498BbRYrNR-BOql78sENOsyWxgqLsBVZXn14lh-
9
9
  explainiverse/core/registry.py,sha256=6HttL27Ty4jYtugRf-EDIKPy80M8BfvUppAKwwGDyQ8,27207
10
10
  explainiverse/engine/__init__.py,sha256=1sZO8nH1mmwK2e-KUavBQm7zYDWUe27nyWoFy9tgsiA,197
11
11
  explainiverse/engine/suite.py,sha256=G-7OjESisSTaQ1FQrlPl4YydX13uz8Bb70hJZNlcl2M,8918
12
- explainiverse/evaluation/__init__.py,sha256=HicoR2_xVWQO6z7ckQj05jxa7djA7zpKozAwRyURYmA,2233
12
+ explainiverse/evaluation/__init__.py,sha256=bde3iDHvCKlYJ1TgJMhkY1ldCHBZ26eQxQX7u6iCDkY,2497
13
13
  explainiverse/evaluation/_utils.py,sha256=ej7YOPZ90gVHuuIMj45EXHq9Jx3QG7lhaj5sk26hRpg,10519
14
14
  explainiverse/evaluation/faithfulness.py,sha256=_40afOW6vJ3dQguHlJySlgWqiJF_xIvN-uVA3nPKRvI,14841
15
- explainiverse/evaluation/faithfulness_extended.py,sha256=uMcYO6FJmzDFPAr5Y7AGkU7gYbweaPnqEhRoC4URGm0,27264
15
+ explainiverse/evaluation/faithfulness_extended.py,sha256=c1tQazZBR9sdH3Y431VCL00nE8w9p1vMOYsx2mOBzJg,43101
16
16
  explainiverse/evaluation/metrics.py,sha256=snNK9Ua1VzHDT6DlrhYL4m2MmRF3X15vuuVXiHbeicU,9944
17
17
  explainiverse/evaluation/stability.py,sha256=q2d3rpxpp0X1s6ADST1iZA4tzksLJpR0mYBnA_U5FIs,12090
18
18
  explainiverse/explainers/__init__.py,sha256=-ncRXbFKahH3bR0oXM2UQM4LtTdTlvdeprL6cHeqNBs,2549
@@ -39,7 +39,7 @@ explainiverse/explainers/gradient/smoothgrad.py,sha256=COIKZSFcApmMkA62M0AForHiY
39
39
  explainiverse/explainers/gradient/tcav.py,sha256=zc-8wMsc2ZOhUeSZNBJ6H6BPXlVMJ9DRcAMiL25wU9I,32242
40
40
  explainiverse/explainers/rule_based/__init__.py,sha256=gKzlFCAzwurAMLJcuYgal4XhDj1thteBGcaHWmN7iWk,243
41
41
  explainiverse/explainers/rule_based/anchors_wrapper.py,sha256=ML7W6aam-eMGZHy5ilol8qupZvNBJpYAFatEEPnuMyo,13254
42
- explainiverse-0.8.4.dist-info/LICENSE,sha256=28rbHe8rJgmUlRdxJACfq1Sj-MtCEhyHxkJedQd1ZYA,1070
43
- explainiverse-0.8.4.dist-info/METADATA,sha256=-NAqFPbZ_fOqstOEIHUP8CQLplzFqzGGdeVAoP3l7Fg,24894
44
- explainiverse-0.8.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
45
- explainiverse-0.8.4.dist-info/RECORD,,
42
+ explainiverse-0.8.6.dist-info/LICENSE,sha256=28rbHe8rJgmUlRdxJACfq1Sj-MtCEhyHxkJedQd1ZYA,1070
43
+ explainiverse-0.8.6.dist-info/METADATA,sha256=7KtAAsGj0I3PpqYofYwk-OfSaCEs2HyQo9rW3YCxLks,25263
44
+ explainiverse-0.8.6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
45
+ explainiverse-0.8.6.dist-info/RECORD,,