Metadata-Version: 2.4
Name: matscope
Version: 0.1.0
Summary: Diagnostic toolkit for scientific foundation models. Understand what your model learned, where it will fail, and how representations evolve across layers.
Author: Josh
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/joshযযyyy/matscope
Project-URL: Documentation, https://matscope.readthedocs.io
Project-URL: Repository, https://github.com/joshyyyyyyy/matscope
Project-URL: Issues, https://github.com/joshyyyyyyy/matscope/issues
Keywords: machine-learning,foundation-models,interpretability,probing,atomistic-simulation,materials-science,representation-learning
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: scikit-learn>=1.3
Provides-Extra: torch
Requires-Dist: torch>=2.0; extra == "torch"
Provides-Extra: mace
Requires-Dist: mace-torch>=0.3; extra == "mace"
Provides-Extra: chgnet
Requires-Dist: chgnet>=0.3; extra == "chgnet"
Provides-Extra: viz
Requires-Dist: matplotlib>=3.7; extra == "viz"
Requires-Dist: seaborn>=0.12; extra == "viz"
Provides-Extra: full
Requires-Dist: torch>=2.0; extra == "full"
Requires-Dist: mace-torch>=0.3; extra == "full"
Requires-Dist: chgnet>=0.3; extra == "full"
Requires-Dist: matplotlib>=3.7; extra == "full"
Requires-Dist: seaborn>=0.12; extra == "full"
Requires-Dist: pandas>=2.0; extra == "full"
Requires-Dist: scipy>=1.11; extra == "full"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: mypy>=1.5; extra == "dev"
Requires-Dist: pre-commit>=3.0; extra == "dev"
Dynamic: license-file

# 🔬 MatScope

**Diagnostic toolkit for scientific foundation models.**

MatScope brings mechanistic interpretability to scientific ML — atomistic foundation models, molecular FMs, and beyond. Understand what your model learned, where it will fail, and how representations evolve across layers.

---

## Why MatScope?

Foundation models for atomistic simulation (MACE, CHGNet, Orb, EquiformerV2) are transforming computational chemistry and materials science. But we have almost no tools to understand *what these models have actually learned*.

- Does your model understand bond types? At which layer?
- Will it generalize from bulk crystals to surface catalysis?
- How do two models' representations compare?
- Where in the network does distribution shift hit hardest?

MatScope answers these questions with standardized, reproducible diagnostics.

## Installation

```bash
pip install matscope                    # core (numpy + sklearn only)
pip install matscope[torch]             # + PyTorch
pip install matscope[mace]              # + MACE support
pip install matscope[full]              # everything
```

## Quick Start

### Probe a property across layers

```python
from matscope import MatScope

# Load a scientific FM
pk = MatScope.from_model("mace-mp-0")

# Extract representations
reps = pk.extract(my_atoms_list, layers=["interaction_0", "interaction_1", "readout_0"])

# Probe: can the model linearly decode bond types?
result = pk.probe(
    target_property="bond_type",
    representations=reps,
    labels=bond_labels,
    task="classification",
)

print(f"Best layer: {result.best_layer}")
print(f"Accuracy:   {result.summary['accuracy']:.3f}")

# Publication-quality figure
pk.report(result, save="bond_probe.png")
```

### Property emergence map

```python
# Probe multiple properties at once
results = pk.layerwise_analysis(
    dataset=my_atoms,
    properties=["element", "coordination", "bond_type", "oxidation_state"],
    labels_dict={
        "element": element_labels,
        "coordination": coord_labels,
        "bond_type": bond_labels,
        "oxidation_state": ox_labels,
    },
)

# Heatmap: which properties emerge at which depth
pk.report(results, save="emergence_map.png")
```

### Compare two models

```python
pk_mace = MatScope.from_model("mace-mp-0")
pk_chgnet = MatScope.from_model("chgnet")

# Extract from both on same data
pk_mace.extract(atoms_list)
pk_chgnet.extract(atoms_list)

# CKA similarity across all layers
sim = pk_mace.compare(pk_chgnet, method="cka")
pk_mace.report(sim, save="mace_vs_chgnet_cka.png")
```

### Detect distribution shift

```python
shift = pk.detect_shift(
    train_data=bulk_crystals,
    deploy_data=surface_catalysis,
    method="mmd",
)

print(f"Most affected layer: {shift.most_affected_layer}")
print(f"Shift magnitude:     {shift.shift_scores[shift.most_affected_layer]:.4f}")
pk.report(shift, save="shift_analysis.png")
```

## Architecture

```
matscope/
├── core.py              # MatScope orchestrator + result containers
├── probes/
│   ├── linear.py        # Linear probes (LogReg/Ridge) + selectivity
│   └── nonlinear.py     # MLP probes + linear-nonlinear gap analysis
├── analysis/
│   ├── similarity.py    # CKA, CCA, Procrustes
│   ├── shift.py         # MMD, Fisher divergence, cosine drift
│   └── layerwise.py     # Effective dim, isotropy, entropy, separability
├── models/
│   ├── registry.py      # Model backends (MACE, CHGNet, generic)
│   └── torch_wrapper.py # Wrap any nn.Module
└── utils/
    ├── extraction.py    # Hook-based representation extraction
    └── reporting.py     # Publication-quality figures
```

## Supported Models

| Model | Status | Backend |
|-------|--------|---------|
| MACE-MP-0 | ✅ Supported | `mace` |
| MACE-OFF | ✅ Supported | `mace` |
| CHGNet | ✅ Supported | `chgnet` |
| Any PyTorch model | ✅ via `from_torch()` | `generic` |
| Orb | 🔜 v0.2 | — |
| EquiformerV2 | 🔜 v0.2 | — |
| GNoME | 🔜 v0.2 | — |
| SchNet / DimeNet++ | 🔜 v0.2 | — |

## Adding a Custom Backend

```python
from matscope.models.registry import register_backend, ModelBackend

@register_backend("my-model")
class MyModelBackend(ModelBackend):
    def load(self, model_name_or_path, device="cpu", **kwargs):
        # Load and return your model
        ...

    def get_hooks(self, model):
        # Return {layer_name: hook_fn} for each layer to probe
        ...
```

## Roadmap

### v0.1 (current)
- Linear and MLP probes with cross-validation
- CKA, CCA, Procrustes similarity
- MMD, Fisher, cosine shift detection
- Layerwise analysis (effective dim, isotropy, entropy, separability)
- MACE and CHGNet backends
- Publication-quality reporting

### v0.2
- Orb, EquiformerV2, GNoME backends
- Sparse autoencoder (SAE) analysis for scientific FMs
- Causal probing (intervention-based)
- Fine-tuning impact analysis (before/after probing)
- Interactive HTML reports

### v0.3
- Hosted dashboard for continuous model monitoring
- Benchmarking suite across standard chemistry datasets
- Integration with ASE and PyMatGen workflows
- Automated "model health check" reports

## Citation

If you use MatScope in your research, please cite:

```bibtex
@software{matscope2026,
  title={MatScope: Diagnostic Toolkit for Scientific Foundation Models},
  author={Josh},
  year={2026},
  url={https://github.com/joshyyyyyyy/matscope},
}
```

## License

Apache 2.0
