case-explainer 0.1.1__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 (34) hide show
  1. case_explainer-0.1.1/LICENSE +21 -0
  2. case_explainer-0.1.1/MANIFEST.in +22 -0
  3. case_explainer-0.1.1/PKG-INFO +424 -0
  4. case_explainer-0.1.1/README.md +380 -0
  5. case_explainer-0.1.1/assets/README.md +52 -0
  6. case_explainer-0.1.1/assets/data/hardware_trojan.csv +3 -0
  7. case_explainer-0.1.1/case_explainer/__init__.py +13 -0
  8. case_explainer-0.1.1/case_explainer/explainer.py +318 -0
  9. case_explainer-0.1.1/case_explainer/explanation.py +233 -0
  10. case_explainer-0.1.1/case_explainer/indexing.py +108 -0
  11. case_explainer-0.1.1/case_explainer/metrics.py +111 -0
  12. case_explainer-0.1.1/case_explainer.egg-info/SOURCES.txt +31 -0
  13. case_explainer-0.1.1/docs/DEPLOYMENT.md +104 -0
  14. case_explainer-0.1.1/docs/Makefile +22 -0
  15. case_explainer-0.1.1/docs/README.md +59 -0
  16. case_explainer-0.1.1/docs/api/explainer.rst +189 -0
  17. case_explainer-0.1.1/docs/api/explanation.rst +221 -0
  18. case_explainer-0.1.1/docs/api/metrics.rst +201 -0
  19. case_explainer-0.1.1/docs/citation.rst +44 -0
  20. case_explainer-0.1.1/docs/conf.py +91 -0
  21. case_explainer-0.1.1/docs/index.rst +115 -0
  22. case_explainer-0.1.1/docs/license.rst +52 -0
  23. case_explainer-0.1.1/notebooks/01_iris_tutorial.ipynb +663 -0
  24. case_explainer-0.1.1/notebooks/02_breast_cancer_tutorial.ipynb +752 -0
  25. case_explainer-0.1.1/notebooks/03_fraud_detection_tutorial.ipynb +859 -0
  26. case_explainer-0.1.1/notebooks/04_hardware_trojan_tutorial.ipynb +911 -0
  27. case_explainer-0.1.1/notebooks/README.md +113 -0
  28. case_explainer-0.1.1/pyproject.toml +61 -0
  29. case_explainer-0.1.1/setup.cfg +4 -0
  30. case_explainer-0.1.1/setup.py +50 -0
  31. case_explainer-0.1.1/tests/test_explainer.py +406 -0
  32. case_explainer-0.1.1/tests/test_explanation.py +278 -0
  33. case_explainer-0.1.1/tests/test_indexing.py +121 -0
  34. case_explainer-0.1.1/tests/test_metrics.py +195 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Paul Whitten, Francis Wolff, Chris Papachristou
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ # Include essential top-level files
2
+ include README.md
3
+ include LICENSE
4
+ include pyproject.toml
5
+
6
+ # Include notebooks
7
+ recursive-include notebooks *.ipynb *.md
8
+
9
+ # Include assets (data files used in examples)
10
+ recursive-include assets *.csv *.md
11
+
12
+ # Include documentation source
13
+ recursive-include docs *.rst *.py *.md Makefile
14
+
15
+ # Exclude build artifacts and caches
16
+ global-exclude __pycache__
17
+ global-exclude *.py[cod]
18
+ global-exclude *.so
19
+ global-exclude .DS_Store
20
+ prune dist
21
+ prune build
22
+ prune *.egg-info
@@ -0,0 +1,424 @@
1
+ Metadata-Version: 2.4
2
+ Name: case-explainer
3
+ Version: 0.1.1
4
+ Summary: General-purpose case-based explainability for machine learning
5
+ Home-page: https://github.com/paulwhitten/case-explainer
6
+ Author: Paul Whitten
7
+ Author-email: Paul Whitten <pcw@case.edu>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/paulwhitten/case-explainer
10
+ Project-URL: Documentation, https://paulwhitten.github.io/case-explainer/
11
+ Project-URL: Repository, https://github.com/paulwhitten/case-explainer
12
+ Project-URL: Bug Tracker, https://github.com/paulwhitten/case-explainer/issues
13
+ Keywords: explainability,interpretability,machine-learning,case-based-reasoning,nearest-neighbors
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: numpy>=1.20.0
28
+ Requires-Dist: scipy>=1.7.0
29
+ Requires-Dist: scikit-learn>=1.0.0
30
+ Requires-Dist: matplotlib>=3.3.0
31
+ Requires-Dist: pandas>=1.3.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
35
+ Requires-Dist: black>=22.0.0; extra == "dev"
36
+ Requires-Dist: flake8>=4.0.0; extra == "dev"
37
+ Requires-Dist: mypy>=0.950; extra == "dev"
38
+ Requires-Dist: build>=0.10.0; extra == "dev"
39
+ Requires-Dist: twine>=4.0.0; extra == "dev"
40
+ Dynamic: author
41
+ Dynamic: home-page
42
+ Dynamic: license-file
43
+ Dynamic: requires-python
44
+
45
+ # Case-Explainer: General-Purpose Case-Based Explainability
46
+
47
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
48
+ [![PyPI version](https://img.shields.io/pypi/v/case-explainer.svg)](https://pypi.org/project/case-explainer/)
49
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
50
+ [![Documentation](https://img.shields.io/badge/docs-online-blue.svg)](https://paulwhitten.github.io/case-explainer/)
51
+ [![CI](https://github.com/paulwhitten/case-explainer/actions/workflows/ci.yml/badge.svg)](https://github.com/paulwhitten/case-explainer/actions/workflows/ci.yml)
52
+
53
+ Provides model-agnostic explanations through training set precedent and nearest neighbor correspondence.
54
+
55
+ **[Read the full documentation](https://paulwhitten.github.io/case-explainer/)**
56
+
57
+ ## What is Case-Based Explainability?
58
+
59
+ While some explainability methods provide feature importance scores, case-based explainability answers: **"Why was this prediction made?"** by showing similar training examples.
60
+
61
+ Instead of: *"Feature X has importance 0.45"*
62
+ You get: *"This sample is classified as X because it resembles these 5 training examples"*
63
+
64
+ ## Features
65
+
66
+ - **Model-agnostic**: Works with any classifier (sklearn, XGBoost, neural networks, etc.)
67
+ - **Correspondence metric**: Quantifies agreement between prediction and neighbors
68
+ - **Multiple indexing strategies**: K-D Tree, Ball Tree, or brute force
69
+ - **Automatic scaling**: Optional feature standardization
70
+ - **Metadata tracking**: Attach provenance data to training samples
71
+ - **Sklearn-compatible API**: Familiar interface for ML practitioners
72
+ - **Batch explanations**: Explain multiple predictions efficiently
73
+
74
+ ## Installation
75
+
76
+ ```bash
77
+ pip install case-explainer
78
+ ```
79
+
80
+ Or, to install the latest development version from source:
81
+
82
+ ```bash
83
+ git clone https://github.com/paulwhitten/case-explainer.git
84
+ cd case-explainer
85
+ pip install -e .
86
+
87
+ # With development/test dependencies (pytest, pytest-cov, etc.)
88
+ pip install -e ".[dev]"
89
+ ```
90
+
91
+ ## Quick Start
92
+
93
+ ```python
94
+ from case_explainer import CaseExplainer
95
+ from sklearn.datasets import load_iris
96
+ from sklearn.model_selection import train_test_split
97
+ from sklearn.ensemble import RandomForestClassifier
98
+
99
+ # Load data
100
+ X, y = load_iris(return_X_y=True)
101
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
102
+
103
+ # Train classifier
104
+ clf = RandomForestClassifier()
105
+ clf.fit(X_train, y_train)
106
+
107
+ # Create explainer
108
+ explainer = CaseExplainer(
109
+ X_train=X_train,
110
+ y_train=y_train,
111
+ feature_names=['sepal_len', 'sepal_width', 'petal_len', 'petal_width'],
112
+ algorithm='kd_tree'
113
+ )
114
+
115
+ # Explain a prediction
116
+ explanation = explainer.explain_instance(X_test[0], k=5, model=clf)
117
+ print(f"Correspondence: {explanation.correspondence:.2%}")
118
+ print(explanation.summary())
119
+ ```
120
+
121
+ ## Core Concepts
122
+
123
+ ### Correspondence Metric
124
+
125
+ Quantifies agreement between prediction and retrieved neighbors using inverse-cubed distance weighting:
126
+
127
+ ```
128
+ w(c) = sum[ 1 / (distance + 1)^3 ] for neighbors with class c
129
+ Correspondence = w(predicted_class) / sum( w(all_classes) )
130
+ ```
131
+
132
+ The `+1` offset in the denominator prevents division by zero when a test sample is identical to a training sample (distance = 0). In that case the weight is simply `1 / 1 = 1`.
133
+
134
+ **Example Interpretation Thresholds** (domain-dependent, not universal standards):
135
+ - **High (≥85%)**: Strong agreement with training precedent
136
+ - **Medium (70-85%)**: Moderate agreement
137
+ - **Low (<70%)**: Weak agreement, prediction may be uncertain
138
+
139
+ *Note: These thresholds are illustrative examples. Appropriate thresholds should be determined empirically for each specific domain and use case based on validation studies.*
140
+
141
+ ### Indexing Strategies
142
+
143
+ - **`kd_tree`**: Fast for low-dimensional data (<20 features)
144
+ - **`ball_tree`**: Better for high-dimensional data
145
+ - **`brute`**: Exact search for small datasets (<10k samples)
146
+
147
+ ## Examples
148
+
149
+ See `quickstart.py` for a complete working example:
150
+
151
+ ```bash
152
+ python quickstart.py
153
+ ```
154
+
155
+ ### Tutorial Notebooks
156
+
157
+ Interactive Jupyter notebooks for each validated domain:
158
+
159
+ - [Iris Classification](notebooks/01_iris_tutorial.ipynb) - Introductory multi-class example
160
+ - [Breast Cancer Diagnosis](notebooks/02_breast_cancer_tutorial.ipynb) - Medical diagnosis domain
161
+ - [Fraud Detection](notebooks/03_fraud_detection_tutorial.ipynb) - Financial security with extreme class imbalance
162
+ - [Hardware Trojan Detection](notebooks/04_hardware_trojan_tutorial.ipynb) - Large-scale security domain
163
+
164
+ ### Benchmarking
165
+
166
+ Comprehensive performance benchmarks across multiple datasets:
167
+
168
+ ```bash
169
+ python benchmark.py # Full benchmark including MNIST
170
+ python benchmark.py --no-mnist # Skip MNIST (faster)
171
+ python benchmark.py --help # See all options
172
+ ```
173
+
174
+ Results (single run on reference hardware):
175
+ - **Speed**: 14-37 ms per explanation depending on dataset size
176
+ - **Memory**: <1 MB to 131 MB (scales with data size and dimensionality)
177
+ - **Correspondence**: 87-100% neighbor agreement across validated domains
178
+ - **Scalability**: Tested up to 200k training samples
179
+
180
+ **Note on Correspondence**: This metric measures agreement between predictions and retrieved neighbors, not prediction accuracy or quality. High correspondence indicates consistency with training data patterns, not necessarily correct predictions.
181
+
182
+ ### Documentation
183
+
184
+ **[View full API documentation online](https://paulwhitten.github.io/case-explainer/)**
185
+
186
+ Build and view documentation locally:
187
+
188
+ ```bash
189
+ # Build documentation
190
+ cd docs
191
+ make html
192
+
193
+ # View documentation locally
194
+ python3 -m http.server 8000 --directory docs/_build/html
195
+ # Then open http://localhost:8000 in your browser
196
+ ```
197
+
198
+ The documentation includes:
199
+ - Complete API reference for all classes and functions
200
+ - Usage examples and code snippets
201
+ - Theory and mathematical foundations
202
+ - Configuration guides and best practices
203
+
204
+ ## Security & Privacy Considerations
205
+
206
+ **IMPORTANT:** Case-based explanations expose actual training samples as evidence. This can leak sensitive information:
207
+
208
+ - **Medical domains:** Patient records, diagnoses, treatments
209
+ - **Financial domains:** Account details, transaction patterns
210
+ - **Security domains:** Attack signatures, system vulnerabilities
211
+ - **Personal data:** User behavior, preferences, demographics
212
+
213
+ **Before using in production with sensitive data:**
214
+ 1. Implement feature masking for sensitive columns
215
+ 2. Consider differential privacy mechanisms
216
+ 3. Apply anonymization to metadata
217
+ 4. Set up access control and audit logging
218
+ 5. Review legal/regulatory requirements (GDPR, HIPAA, etc.)
219
+
220
+ **Privacy protection features are planned for Phase 2.** For now, use only with non-sensitive data or in controlled research environments.
221
+
222
+ Unlike LIME/SHAP which only show feature importance, case-explainer exposes training sample features. Evaluate whether this trade-off is acceptable for your use case.
223
+
224
+ ---
225
+
226
+ ## API Overview
227
+
228
+ ### CaseExplainer
229
+
230
+ ```python
231
+ explainer = CaseExplainer(
232
+ X_train, # Training features
233
+ y_train, # Training labels
234
+ feature_names=None, # Optional feature names
235
+ class_names=None, # Optional class names {0: 'cat', 1: 'dog'}
236
+ algorithm='kd_tree', # Indexing strategy
237
+ scale_data=True, # Standardize features
238
+ metadata=None # Optional provenance data
239
+ )
240
+ ```
241
+
242
+ ### Explain Single Instance
243
+
244
+ ```python
245
+ explanation = explainer.explain_instance(
246
+ test_sample, # Sample to explain
247
+ k=5, # Number of neighbors
248
+ model=clf, # Trained classifier
249
+ true_class=None, # Optional true label
250
+ distance_weighted=True # Use distance weighting
251
+ )
252
+ ```
253
+
254
+ ### Explain Batch
255
+
256
+ ```python
257
+ explanations = explainer.explain_batch(
258
+ X_test, # Test samples
259
+ k=5, # Number of neighbors
260
+ y_test=None, # Optional true labels
261
+ model=clf # Trained classifier
262
+ )
263
+ ```
264
+
265
+ ### Explanation Object
266
+
267
+ ```python
268
+ explanation.correspondence # Correspondence score [0, 1]
269
+ explanation.correspondence_interpretation # 'high', 'medium', 'low'
270
+ explanation.neighbors # List of Neighbor objects
271
+ explanation.predicted_class # Predicted class
272
+ explanation.is_correct() # True if prediction matches label
273
+ explanation.summary() # Text summary
274
+ explanation.to_dict() # Export as dictionary
275
+ explanation.plot() # Visualize (bar plot)
276
+ ```
277
+
278
+ ## Validated Domains
279
+
280
+ **Hardware Trojan Detection** (56,959 samples, 5 features)
281
+ - 99.3% average correspondence across indexing methods
282
+ - High neighbor agreement on imbalanced security data
283
+ - 25.7 ms/sample explanation time (single run, reference hardware)
284
+
285
+ **Credit Card Fraud Detection** (284,807 samples, 30 features)
286
+ - 100% average correspondence (complete agreement with retrieved neighbors)
287
+ - Highly imbalanced dataset (268:1 normal:fraud ratio)
288
+ - 36.4 ms/sample explanation time (single run, reference hardware)
289
+
290
+ **Medical Diagnosis - Breast Cancer** (569 samples, 30 features)
291
+ - 93.3% average correspondence
292
+ - Correct predictions: 96.2% correspondence vs 47.3% for incorrect predictions
293
+ - 25.9 ms/sample explanation time (single run, reference hardware)
294
+
295
+ **Also Validated On:**
296
+ - Iris (92.7%), Wine (91.8%), Digits (94.9%), MNIST (87.5%)
297
+ - See `benchmark.py` for full results across 7 datasets
298
+
299
+ *Note: Correspondence measures neighbor agreement, not prediction quality. High correspondence with incorrect predictions indicates the model has learned incorrect patterns in the training data.*
300
+
301
+ ## When to Use Case-Based Explainability
302
+
303
+ **Case-Explainer is well-suited for scenarios where:**
304
+ - Domain experts need to verify predictions against known training cases
305
+ - Precedent-based reasoning is valued (medical diagnosis, legal decisions, security analysis)
306
+ - Concrete examples are more intuitive than feature importance scores
307
+ - Training data has provenance or metadata worth surfacing to users
308
+ - Fast explanation generation is needed for real-time or interactive systems
309
+
310
+ **Alternative approaches (LIME, SHAP) may be preferable when:**
311
+ - Feature contributions are more relevant than training precedents
312
+ - Training data cannot be exposed due to privacy/security constraints
313
+ - Model debugging requires understanding feature-level behavior
314
+
315
+ ### Comparison with LIME and SHAP
316
+
317
+ | Aspect | Case-Explainer | LIME | SHAP |
318
+ |--------|---------------|------|------|
319
+ | Explanation type | Training precedents (similar cases) | Local surrogate model (feature importance) | Shapley values (feature importance) |
320
+ | Output | k nearest neighbors + correspondence score | Per-feature importance for one prediction | Per-feature importance (local and global) |
321
+ | Privacy risk | High -- exposes actual training samples | Low -- uses synthetic perturbations | Low -- no sample exposure |
322
+ | Speed (pipeline, HW trojan) | ~13 ms/sample | ~25 ms/sample | ~1 ms/sample (TreeSHAP) |
323
+ | Model-agnostic | Yes | Yes | Yes (KernelSHAP); tree-specific variants are faster |
324
+ | Best for | Precedent-based reasoning, domain expert verification | Local feature contributions, model debugging | Global + local feature analysis, theoretical guarantees |
325
+
326
+ *Timing from the hardware trojan detection pipeline (XGBoost classifier, 5 features, ~57k samples). SHAP uses TreeSHAP which exploits tree structure for speed; KernelSHAP (model-agnostic) is substantially slower. LIME and Case-Explainer speeds are model-agnostic. Results will vary with dataset size, dimensionality, and hardware.*
327
+
328
+ ## Limitations
329
+
330
+ **Privacy and Security**
331
+ - Exposes actual training samples, which may contain sensitive information
332
+ - Not suitable for sensitive data without additional privacy protection mechanisms
333
+ - Privacy-preserving features are planned for future releases
334
+
335
+ **Correspondence Metric**
336
+ - Measures neighbor agreement, not prediction correctness or quality
337
+ - High correspondence can occur with incorrect predictions if training data contains systematic errors
338
+ - Thresholds for "high/medium/low" must be validated per domain
339
+
340
+ **Performance Benchmarks**
341
+ - Timing and memory results are from single runs on reference hardware
342
+ - No statistical error bars or confidence intervals provided
343
+ - Results may vary significantly on different hardware and with different parameters
344
+
345
+ **Scalability**
346
+ - Memory usage scales linearly with training set size
347
+ - Very large datasets (>1M samples) may require approximate nearest neighbor methods (not yet implemented)
348
+
349
+ **Interpretability**
350
+ - Assumes users can meaningfully interpret feature values of retrieved neighbors
351
+ - Multi-feature patterns may be difficult to assess without domain expertise
352
+ - High-dimensional data may require dimensionality reduction for effective interpretation
353
+
354
+ ## Development Status
355
+
356
+ ### Core Functionality MVP
357
+ - [x] CaseExplainer class with sklearn-compatible API
358
+ - [x] Correspondence metric with distance weighting
359
+ - [x] Multiple indexing strategies (K-D tree, Ball tree, brute force)
360
+ - [x] Explanation object with summary and visualization
361
+ - [x] Metadata/provenance tracking
362
+ - [x] Batch explanation support
363
+
364
+ ### Phase 1: Multi-Domain Validation
365
+ - [x] Hardware trojan detection (validated in JETTA paper)
366
+ - [x] Medical diagnosis (UCI Breast Cancer)
367
+ - [x] Fraud detection (Credit Card Fraud)
368
+ - [x] Benchmarking (time, memory, correspondence)
369
+
370
+ ### Phase 2: Documentation - IN PROGRESS
371
+ - [x] API reference
372
+ - [x] Tutorial notebooks (4 domains)
373
+ - [x] Comparison guide (vs LIME/SHAP)
374
+ - [x] Code coverage >90%
375
+
376
+ ### Phase 3: Testing & Quality
377
+ - [x] Unit test suite (pytest, >90% coverage)
378
+ - [x] Multi-Python version compatibility (3.8–3.12)
379
+ - [x] Integration tests across validated domains
380
+ - [ ] Privacy-preserving features (feature masking, differential privacy)
381
+ - [ ] Approximate nearest neighbors (Annoy, FAISS) for large-scale data
382
+
383
+ ### Phase 4: Release & Distribution
384
+ - [x] PyPI package (`pip install case-explainer`)
385
+ - [x] GitHub Pages documentation (https://paulwhitten.github.io/case-explainer/)
386
+ - [x] CI/CD pipeline (GitHub Actions: test matrix, publish to PyPI)
387
+ - [ ] Zenodo DOI
388
+
389
+ ## Citation
390
+
391
+ If you use this module in academic work, please cite:
392
+
393
+ ```bibtex
394
+ @software{case_explainer2025,
395
+ author = {Whitten, Paul and Wolff, Francis and Papachristou, Chris},
396
+ title = {Case-Explainer: General-Purpose Case-Based Explainability},
397
+ year = {2025},
398
+ url = {https://github.com/paulwhitten/case-explainer}
399
+ }
400
+ ```
401
+
402
+ ## License
403
+
404
+ MIT License - see LICENSE file for details.
405
+
406
+ ## Contributing
407
+
408
+ Contributions welcome! Core functionality and release infrastructure are complete.
409
+
410
+ **Priority areas:**
411
+ - Additional distance metrics (Manhattan, Cosine)
412
+ - Approximate nearest neighbors (Annoy, FAISS) for large-scale data
413
+ - Radar and parallel coordinate visualizations
414
+ - More comprehensive unit tests
415
+
416
+ ## Contact
417
+
418
+ Questions? Issues? Open a GitHub issue or contact pcw@case.edu.
419
+
420
+ ## Acknowledgments
421
+
422
+ - Inspired by Caruana et al. (1999) "Case-based explanation of non-case-based learning"
423
+ - Validated on hardware trojan detection research
424
+ - Built with scikit-learn, scipy, and matplotlib