Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,8 @@ jobs:
- name: Install dependencies
run: uv sync --all-extras

- name: Install Chrome for image export tests
run: uv run kaleido_get_chrome

- name: Run tests
run: uv run pytest
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Bug Fixes

- Raised the minimum Plotly version to 6.1.1 and Kaleido to 1.0.0 to exclude incompatible image-export dependencies. Documented the Chrome/Chromium requirement and added real PNG and rotation GIF export tests, with Chrome installation in CI. `plot_3d()` now passes image dimensions explicitly to the exporter so the minimum supported versions preserve the requested size.
- `LONVisualizer.get_layout()` with a `seed`, and therefore `plot_2d()`, `plot_3d()`, `create_rotation_gif()` and `visualize_all()`, no longer reseeds the global NumPy RNG or leaves a seeded RNG installed in igraph. The seed now applies only to the layout, and igraph's default RNG is restored afterwards. Seeded layouts are unchanged (#53).
- Continuous sampling now handles failed local searches explicitly. A local search fails when `minimize` raises `ValueError` or returns a non-finite fitness. A failed initial local search fails its run, and a failed search after a perturbation is rejected and counts as a non-improving iteration. Previously, runs starting at a `nan` minimum silently vanished, and an `inf` minimum made `LON.from_trace_data()` fail after all sampling had finished. Results with `success=False` and a finite fitness are still used as local optima. Each failed local search emits a warning with its context and reason (#44).
- `StepSizeEstimator` divides each sample's escape count by its number of evaluated perturbations instead of `n_perturbations`, so failed local searches no longer bias the escape rate down. Samples whose initial local search fails are skipped, and `RuntimeError` is raised if no sample can be evaluated, instead of returning `nan` (#44).
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ lonkit is a Python library for constructing, analyzing, and visualizing Local Op
pip install lonkit
```

Exporting 3D plots to images or GIFs also requires Chrome/Chromium. If it is not
installed, run `kaleido_get_chrome` after installing lonkit. See the
[installation guide](https://helix-agh.github.io/lonkit/getting-started/installation/).

Or install from source:

```bash
Expand Down
3 changes: 3 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pre-commit install

### Running Tests

The image export tests require Chrome/Chromium. If it is not installed, run
`uv run kaleido_get_chrome` once before running the suite.

```bash
pytest
```
Expand Down
30 changes: 26 additions & 4 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@ The simplest way to install lonkit is via pip:
pip install lonkit
```

### Chrome for 3D image and GIF export

Saving 3D plots as images, creating rotation GIFs, and calling `visualize_all()`
require Chrome or Chromium. Kaleido 1.x uses an external browser; it does not
bundle one. If Chrome is already installed, Kaleido should find it automatically.
Otherwise, install a compatible browser with:

```bash
kaleido_get_chrome
```

For a source checkout managed with uv, use `uv run kaleido_get_chrome`.
The download requires an internet connection. In CI, run this command before
the visualization tests. 2D plots and creating Plotly figures without image
export do not require Chrome.

See the [Kaleido installation instructions](https://github.com/plotly/Kaleido#installation)
for details.

## Install from Source

For the latest development version:
Expand Down Expand Up @@ -81,14 +100,17 @@ On some systems, you may need to install igraph system dependencies first:

### Plotly/Kaleido Issues

If you encounter issues with static image export:
lonkit requires Plotly >= 6.1.1 and Kaleido >= 1.0.0. If you encounter a version
compatibility error, upgrade both packages together:

```bash
pip install --upgrade kaleido
pip install --upgrade "plotly>=6.1.1" "kaleido>=1.0.0"
```

On some systems, you may need:
If export reports that Chrome is missing, install it with:

```bash
pip install kaleido==0.2.1
kaleido_get_chrome
```

Do not downgrade to Kaleido 0.2.1; it is outside lonkit's supported dependencies.
3 changes: 3 additions & 0 deletions docs/user-guide/visualization.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ description: 2D and 3D visualizations of Local Optima Networks. Export plots as

This guide covers how to create visualizations of Local Optima Networks.

Exporting 3D images and rotation GIFs requires Chrome/Chromium. If needed, install
it with `kaleido_get_chrome`; see the [installation guide](../getting-started/installation.md#chrome-for-3d-image-and-gif-export).

## Quick Start

```python
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ dependencies = [
"pandas>=2.0.0",
"igraph>=0.11.0",
"matplotlib>=3.7.0",
"plotly>=5.15.0",
"kaleido>=0.2.1",
"plotly>=6.1.1",
"kaleido>=1.0.0",
"imageio>=2.31.0",
"joblib>=1.4.0",
"tqdm>=4.67.3",
Expand Down
2 changes: 1 addition & 1 deletion src/lonkit/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ def plot_3d(
)

if output_path:
fig.write_image(str(output_path))
fig.write_image(str(output_path), width=width, height=height)

return fig

Expand Down
28 changes: 28 additions & 0 deletions tests/test_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import matplotlib.pyplot as plt
import numpy as np
import pytest
from PIL import Image

from lonkit import LON, LONVisualizer
from tests.conftest import SEED
Expand All @@ -15,6 +16,33 @@ def visualizer() -> LONVisualizer:
return LONVisualizer()


@pytest.mark.parametrize("extension", ["png", "gif"])
def test_3d_export(visualizer: LONVisualizer, tmp_path: Path, extension: str) -> None:
graph = ig.Graph(n=3, edges=[(0, 1), (1, 2)], directed=True)
graph.vs["Fitness"] = [3.0, 1.0, 0.0]
graph.vs["Count"] = [1, 1, 1]
graph.es["Count"] = [1, 1]
lon = LON(graph=graph, best_fitness=0.0)
path = tmp_path / f"plot.{extension}"

if extension == "png":
visualizer.plot_3d(lon, output_path=path, width=200, height=200, seed=SEED)
else:
visualizer.create_rotation_gif(
lon, path, width=200, height=200, duration=1, fps=3, seed=SEED
)

with Image.open(path) as image:
assert image.format == extension.upper()
assert image.size == (200, 200)
assert image.n_frames == (1 if extension == "png" else 3)
pixels = np.array(image.convert("RGB"))
assert pixels.min() < pixels.max()
if extension == "gif":
image.seek(1)
assert not np.array_equal(pixels, np.array(image.convert("RGB")))


class TestLayoutReproducibility:
def test_layout_same_seed_gives_same_coordinates(
self, rastrigin_lon: LON, visualizer: LONVisualizer
Expand Down
4 changes: 2 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading