From 93c21926e43811bc04335b587d94190cbb8bafbb Mon Sep 17 00:00:00 2001 From: Ahmed Gad Date: Fri, 22 May 2026 17:56:31 -0400 Subject: [PATCH] Add GA.push_to_vilvik() convenience wrapper for the Vilvik SDK --- pygad/pygad.py | 32 ++++++++++++++++++++++++++++++ tests/test_push_to_vilvik.py | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/test_push_to_vilvik.py diff --git a/pygad/pygad.py b/pygad/pygad.py index 7a1ac92..228cbb8 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -188,6 +188,38 @@ def save(self, filename): file.write(cloudpickle_serialized_object) cloudpickle.dump(self, file) + def push_to_vilvik(self, *, api_key=None, **overrides): + """Import this run into Vilvik (https://vilvik.com) as an editable, + continuable cloud record. + + This is a thin convenience wrapper over the Vilvik SDK, which must be + installed separately:: + + pip install vilvik + + After ``ga.run()``, call ``ga.push_to_vilvik()`` (sign in first with the + ``vilvik login`` command or set the ``VILVIK_API_KEY`` environment + variable). All keyword arguments are forwarded to ``vilvik.push`` (for + example ``name=``, ``fitness_source=``, ``callbacks=``, ``preamble=``, + ``dry_run=True``). Returns the created record, or a capture report when + ``dry_run=True``. + """ + try: + import vilvik + except ImportError as exc: + raise ImportError( + "push_to_vilvik requires the Vilvik SDK. Install it with: " + "pip install vilvik" + ) from exc + + import pygad + return vilvik.push( + self, + api_key=api_key, + origin_overrides={"client": "pygad_wrapper", "pygad_version": pygad.__version__}, + **overrides, + ) + def load(filename): """ Reads a saved instance of the genetic algorithm: diff --git a/tests/test_push_to_vilvik.py b/tests/test_push_to_vilvik.py new file mode 100644 index 0000000..959c1d8 --- /dev/null +++ b/tests/test_push_to_vilvik.py @@ -0,0 +1,38 @@ +import sys +import types +import pytest +import pygad + + +def _ga(): + return pygad.GA( + num_generations=1, num_parents_mating=2, sol_per_pop=4, num_genes=3, + fitness_func=lambda ga_instance, solution, solution_idx: 0.0) + + +def test_push_to_vilvik_forwards_to_sdk(monkeypatch): + calls = {} + fake = types.ModuleType("vilvik") + + def fake_push(ga, **kw): + calls["ga"] = ga + calls["kw"] = kw + return "RECORD" + + fake.push = fake_push + monkeypatch.setitem(sys.modules, "vilvik", fake) + + ga = _ga() + out = ga.push_to_vilvik(name="local run") + assert out == "RECORD" + assert calls["ga"] is ga + assert calls["kw"]["name"] == "local run" + assert calls["kw"]["origin_overrides"]["client"] == "pygad_wrapper" + assert calls["kw"]["origin_overrides"]["pygad_version"] == pygad.__version__ + + +def test_push_to_vilvik_missing_sdk(monkeypatch): + monkeypatch.setitem(sys.modules, "vilvik", None) # forces `import vilvik` to raise ImportError + ga = _ga() + with pytest.raises(ImportError, match="pip install vilvik"): + ga.push_to_vilvik()