Metadata-Version: 2.4
Name: scip_toolbox
Version: 0.1.1
Summary: Pure-Python notebook API for SCIP run analytics (.statistics + .vbc)
Project-URL: Homepage, https://gitlab.uni-hannover.de/lars.jaeger/scip_toolbox
Project-URL: Repository, https://gitlab.uni-hannover.de/lars.jaeger/scip_toolbox
Project-URL: Issues, https://gitlab.uni-hannover.de/lars.jaeger/scip_toolbox/-/issues
Author: Lars Jaeger
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.11
Requires-Dist: matplotlib>=3.8
Requires-Dist: nbformat>=4.2.0
Requires-Dist: networkx>=3.2
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.2
Requires-Dist: plotly>=5.22
Provides-Extra: dev
Requires-Dist: ipykernel>=6.29; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: twine>=7.0.0; extra == 'dev'
Provides-Extra: gurobi
Requires-Dist: grblogtools>=2.2.0; extra == 'gurobi'
Description-Content-Type: text/markdown

# SCIP Toolbox

A clean, **notebook-first Python API** for analysing SCIP solver runs. Two analysis tracks are
exposed as one importable package:

| Module                      | What it does                                         |
| ---------------------------- | ---------------------------------------------------- |
| `scip_toolbox.statistics`   | Parse `.statistics` files into pandas DataFrames     |
| `scip_toolbox.vbc`          | Parse `.vbc` files & build Plotly B&B-tree figures   |

There is **no CLI and no web UI** — everything is plain Python you call
from a Jupyter notebook (or any script). All visualisations are returned
as Plotly `Figure` objects so they render inline and can be exported to
HTML/PNG with a single method call.

- **Source (GitLab):** <https://gitlab.uni-hannover.de/lars.jaeger/scip_toolbox>
- **Package (PyPI):** <https://pypi.org/project/scip-toolbox/>

## Install

Install the published package with `uv` or `pip`:

```powershell
uv add scip_toolbox
# or
pip install scip_toolbox
```

To work on the repo itself:

```powershell
# inside the repo root
uv sync
# or, with editable install + dev tools:
uv sync --extra dev
```

## Notebook quick start

A single import gets you the full API:

```python
from scip_toolbox import (
    # statistics
    load_directory, aggregate, extract_columns,
    GenericIdParser, RegexIdParser, TemplateIdParser,
    # vbc
    VBCParser, build_graph,
    plot_tree_plotly, plot_tree_at_step, plot_realistic_depth_animated,
    BoundsPlot, GapPlot,
)
```

### 1. Aggregate `.statistics` files

```python
runs = load_directory("path/to/runs/", pattern="*.statistics")

df = aggregate(runs, simple={
    "status":     ("SCIP Status", "Status"),
    "total_time": ("Total Time",  "Total"),
    "primal":     ("Solution",    "Primal Bound"),
    "dual":       ("Solution",    "Dual Bound"),
    "gap":        ("Solution",    "Gap"),
    "nodes":      ("B&B Tree",    "nodes"),
})
df.head()
```

`load_directory` caches the parsed bundle next to the folder as a pickle.
Pass `cache=False` to disable, or `reload=True` to force a re-parse.

### 2. Custom instance-name parsers

Instance IDs often encode parameters
(`Instance_15_1_DEU_NLD_3_wj_zk_Config_1_1_1_0_1_0_0`). Three pluggable
parsers ship with the toolbox:

| Parser              | When to use it                                                 |
| ------------------- | -------------------------------------------------------------- |
| `GenericIdParser`   | Just split on `_` and store tokens as `token_0`, `token_1`, … |
| `RegexIdParser`     | You want full regex control with named groups.                 |
| `TemplateIdParser`  | Friendly `{name}` placeholder template, loadable from a file.  |

Template parser, in code:

```python
parser = TemplateIdParser(
    "Instance_{n_tasks}_{version}_{country:[A-Z]+_[A-Z]+}"
    "_{n_instance}_{weather}_{teams}"
    "_Config_{c1}_{c2}_{c3}_{c4}_{c5}_{c6}_{c7}",
    numeric=("n_tasks", "version", "n_instance"),
)
runs = load_directory("path/to/runs/", id_parser=parser)
```

Or, externalise it to a small text file
([examples/instance_id_template.txt](examples/instance_id_template.txt))
and load it without writing code:

```python
parser = TemplateIdParser.from_file("examples/instance_id_template.txt")
runs = load_directory("path/to/runs/", id_parser=parser)
```

### 3. Visualise a single `.vbc` run

```python
parser = VBCParser(filepath="run.vbc")
parser.parse()

g = build_graph(parser)

# Full B&B tree with realistic-depth (dual-bound) Y axis:
plot_tree_plotly(g, realistic_depth=True).show()

# Primal vs reconstructed global dual bound + relative gap:
BoundsPlot(parser, show_gap=True).build().show()

# Optimality gap over time:
GapPlot(parser).build().show()
```

Step-by-step replay:

```python
snapshots = parser.build_snapshots()
plot_tree_at_step(g, snapshots[42]).show()
```

Animated realistic-depth view:

```python
plot_realistic_depth_animated(g, parser).show()
```

Export any Plotly figure with `fig.write_html("tree.html")` /
`fig.write_image("tree.png")`.

## Testing

```powershell
uv run pytest -q
```

## Layout

```
src/scip_toolbox/
├── __init__.py            # flat re-exports for one-line notebook imports
├── statistics/            # .statistics file parsing & aggregation
│   ├── id_parser.py       # GenericIdParser, RegexIdParser, TemplateIdParser
│   ├── loader.py          # read_statistics_file, load_directory
│   └── summary.py         # aggregate, extract_columns
└── vbc/                   # .vbc file parsing & visualisation
    ├── models/            # NodeData, BoundEvent, layout helpers
    ├── parser/            # VBCParser, classifier, snapshot builder, graph builder
    └── viz/               # tree.py, bounds_plot.py, gap_plot.py, last_bound_scatter.py (all return Plotly/matplotlib figures)
```

See [examples/example.ipynb](examples/example.ipynb) for an end-to-end,
heavily-commented walkthrough that starts from raw SCIP output files and
ends with publication-ready tables and figures.

The `.statistics`/`.stats` and `.vbc` files used by that example (a vehicle routing problem solved with
branch-and-price, column generation, and a compact MIP model) come from
[vrp_example_scip_cpp](https://gitlab.uni-hannover.de/lars.jaeger/vrp_example_scip_cpp), which also serves
as a standalone teaching example of how to implement a branch-and-price algorithm with SCIP/SCIP-SoPlex in
C++. Check it out if you want to see how the analysed runs were produced, or are looking to implement your
own branch-and-price solver.

## License

Licensed under the [Apache License, Version 2.0](LICENSE).

## Citing

If you use `scip_toolbox` in your research, please cite it - see [CITATION.cff](CITATION.cff).

This toolbox only *analyses* output produced by the SCIP Optimization Suite. If you publish results
obtained by running SCIP (with or without this toolbox), please also cite SCIP itself, e.g. the original
SCIP paper:

```bibtex
@article{Achterberg2009,
  author  = {Tobias Achterberg},
  title   = {{SCIP}: solving constraint integer programs},
  journal = {Mathematical Programming Computation},
  year    = {2009},
  volume  = {1},
  number  = {1},
  pages   = {1--41},
  doi     = {10.1007/s12532-008-0001-1}
}
```

See [scipopt.org](https://scipopt.org) for the up-to-date recommended citation for the specific SCIP
Optimization Suite version you used.
