Skip to content

Releases: RemiPelloux/Modelly

Modelly 1.3.1

Choose a tag to compare

@RemiPelloux RemiPelloux released this 24 Oct 20:45

No score is slower than 1.2.0 any more: benchmarked against the 1.2.0 wheel with identical
dependencies (core operations, the playbooks and a research notebook, in paired alternating
runs; docs/performance.md, "No regression from release to release").

Performance

  • Reading a Column attribute costs what it did in 1.2.0 again. 1.3.0's getter kept the
    column's name for the error of a removed agent, which cost about 2.5% per read; it now looks
    the name up only when the error is raised. The error is unchanged.
  • On regular (GIL) builds, the neighbourhood walk (Cell.get_neighborhood) and the cell move
    are 1.2.0's code again, without the free-threading safety 1.3.0 added to every call (a
    reference count per neighbour, a cache-generation check per move). Free-threaded builds
    keep their safe paths.
  • FixedAgent.remove reads its cell once.

Fixed

  • A timing test of modelly profile --sizes compares populations 16 times apart, so it no
    longer fails on a slow runner.

Modelly 1.3.0

Choose a tag to compare

@RemiPelloux RemiPelloux released this 24 Oct 18:38

The research release: calibrate, explain, scale and publish a model, on Windows, macOS and
Linux. Everything from 1.2 keeps working; what 2.0 will change now warns (see
docs/deprecations.md). The plan behind it is in docs/roadmap/1.3.0.md.

Answers

  • modelly.experiments: a study as one object. ScenarioSpace (ranges, log and integer
    ranges, choices, fixed values, checked against the model's Scenario), designs (full
    factorial, Latin hypercube, Sobol, Halton, custom), replicates with one seed per run, a
    Parquet results store with a DuckDB view that resumes after a crash, and executors:
    serial, threads, processes, Dask, Ray, SLURM and PBS job arrays (pluggable through the
    modelly.executors entry points). The same experiment gives the same results on every
    executor. modelly experiment run|status spec.toml.
  • modelly.calibrate: ABCRejection, ABCSMC (adaptive tolerances, resumable),
    SimulatedMoments and a basic BayesOpt; priors, targets and distances; a Posterior
    with summaries, HDIs, posterior predictive runs and plots. Verified by recovering the
    parameters of SIR and predator-prey models from synthetic data. modelly calibrate.
  • modelly.sensitivity: one-at-a-time, Morris, Sobol (Saltelli sampling, bootstrap intervals;
    matches the Ishigami indices) and PAWN, on a model, a function or a Gaussian-process
    emulator. modelly sensitivity.
  • DataCollector.to_parquet, to_arrow and query (DuckDB).

Trust

  • Provenance: modelly run writes the outputs and a manifest (code hash, versions, compiled
    or pure build, platform, scenario, seeds, input and output hashes); modelly verify reruns
    it and names the first differing output; modelly bundle packs a byte-identical zip for
    Zenodo; modelly odd writes an ODD protocol skeleton from the code.
  • modelly.timetravel: Recorder keeps checkpoints within a memory or disk budget;
    at(step) rewinds, a replay is bit-identical with a straight run, branch forks futures,
    and diff finds the first step, agent and attribute where two runs part.
  • A determinism suite: every example, compiled and pure-Python, straight and resumed from a
    checkpoint, against stored digests that every platform must reproduce
    (python -m modelly.dev golden). Property-based tests for columns, cohorts, cell spaces,
    checkpoints and designs.

Reach

  • modelly.space.geo (extra geo): GeoSpace with a coordinate reference system, regions,
    contiguity and distance queries in metres, GeoParquet and GeoJSON export; RasterLayer
    (GeoTIFF); RoadNetwork with routing and travel times; synthetic populations by
    iterative proportional fitting.
  • modelly.space.networks: temporal networks (O(changed edges) updates), multilayer networks,
    contact matrices, network collectors; NetworkX, igraph and CSV import.
  • modelly.llm: language-model agents with a provider-agnostic client (Anthropic,
    OpenAI-compatible and local endpoints, a fake provider), a deterministic SQLite response
    cache with offline replay, token and cost budgets, retries, structured decisions with
    schema validation, and LLMAgent. API keys never reach caches, checkpoints or logs.
  • modelly.zoo: twelve validated reference models (age-structured SEIR, Nagel-Schreckenberg
    traffic, Bass diffusion, Deffuant and Hegselmann-Krause opinions, Axelrod culture, kinetic
    and Bouchaud-Mezard wealth, a double-auction order book, forest fire, an energy-budget
    population, vector-borne transmission), each with a validation test against an analytic or
    known result. modelly zoo list|info|copy, modelly new --from-zoo.
  • New examples: a commuting city and a three-layer epidemic (spatial), persona agents
    deciding through modelly.llm.

Experience

  • Modelly Studio: modelly studio model.py:Model opens a browser app with a parameter panel
    built from the scenario, run controls, space and time-series views, an agent table, a
    timeline scrubber on time travel with replay from any step, profiler and lint panels, and
    exports (figures, data, checkpoints, GIF).
  • modelly new with grid, network, continuous and cohort templates (seeded, tested, linted).
  • modelly lint --fix and --diff for MDL001, MDL002, MDL003 and MDL009; new rules MDL010
    (a lambda or closure sent to spawned workers, which fails on Windows and macOS) and MDL012
    (a network or LLM call that bypasses modelly.llm).
  • python -m modelly.dev gate: the local gate in Python, for contributors without bash.
  • Jupyter: %load_ext modelly.ipython for %modelly lint and %modelly profile, and a rich
    display of a Model.
  • Publication figures (modelly.viz.styles.paper(), journal widths, colour-blind-safe
    palettes) and GIF or MP4 export of any space view (extra video).
  • Plugins: entry-point groups for spaces, executors, samplers, collectors, Studio views and
    lint rules (modelly.plugins).
  • modelly lists its fourteen commands by task: start, run and explore, publish.
  • Docs: six domain tutorials, an eight-session course pack, a gallery, guides for every new
    module, and the roadmap for 1.3 to 2.0.

Performance and platforms

  • fastset.c: AgentSet type filtering, select, attribute reads in agg and weak-set
    listing in C (0.45 to 0.95 of the Python time on 20,000 agents), with Python twins.
  • The four compiled modules run on free-threaded CPython with the GIL off; free-threaded
    wheels are built, as are Windows ARM64 wheels on a native runner.
  • modelly bench: the playbooks in alternating rounds, CPU pinning where the OS allows it, a
    JSON-lines history and a regression check (exit 1).
  • Memory baseline per agent recorded in docs/performance.md.
  • Every text file is opened as UTF-8 (ruff PLW1514), checkpoints are written atomically, and
    hashed text outputs use LF line endings, so results and hashes match on every OS.

Changed

  • Reading or writing a Column on a removed agent raises ColumnMissingError (an
    AttributeError) instead of reading a stale row. Errors for a full cell, a missing agent
    and a removed agent name the agent and the model time.
  • Deprecations raise ModellyDeprecationWarning, both a DeprecationWarning and a
    FutureWarning, naming the replacement and the removal in 2.0.
  • A checkpoint loads only into the Modelly minor version that wrote it: 1.2 checkpoints do not
    load in 1.3.
  • Every public name has a stability level (docs/api_stability.toml): what 1.2 had is stable,
    what 1.3 adds is provisional.

Fixed

  • The Epstein example's scenario declared rng: int = 42, overriding any seed; the virus
    network example built a different network in each process. Both are seeded from the model.
  • The Epstein, prisoner's dilemma and Sugarscape examples can be checkpointed.
  • Browser tests bind free ports, so two test runs can share a machine.

Modelly 1.2.0

Choose a tag to compare

@RemiPelloux RemiPelloux released this 24 Oct 14:33

Added

  • Checkpoints: model.save(path), Model.load(path) and model.checkpoint_every(n, pattern).
    A run resumed from a checkpoint, in a new process, gives exactly the run it would have been:
    both random generators, the event scheduler, spaces, columns and collected data are saved.
    A checkpoint made with the compiled random core loads into the pure-Python one and the other
    way round. Loading runs pickle: only load files you trust. Lambdas and closures are refused
    with an error naming them.
  • Versioned documentation: /Modelly/ is the latest release, /Modelly/dev/ is main and
    each minor release keeps its own folder (/1.0/, /1.1/, /1.2/), with a version switcher
    in the navbar and a banner on older versions.
  • modelly profile --sizes 1000,4000 (and modelly.profile.compare_sizes) profiles a model
    at several population sizes and compares the cost per agent, so the MDL101 hint also works
    for models whose population does not change during a run.
  • MetaAgents.cache_build(score): scores each candidate group once across repeated
    find_combinations calls, with invalidate() and an optional maxsize.
  • Windows wheels (x86-64) with the compiled modules.

Changed

  • modelly lint --help, modelly migrate --help and modelly profile --help name the
    command as it was run (modelly <cmd>, or python -m modelly.<cmd>).
  • MDL107 follows imports of agent classes from other files in the linted tree, so models split
    into several modules are checked.
  • A CellCollection built without a generator takes its cells' or agents' model generator, so
    the same seed gives the same draws.
  • The creation path of meta agents returns MetaAgent instead of Any.

Performance

  • Removing an agent that belongs to meta agents reads its own memberships instead of copying
    the whole membership table: 2,000 removals among 10,000 agents went from 100 s to 0.16 s.
  • fastcell.c uses only the public C API, with the same speed as 1.1.0.
  • The Sugarscape playbook ranks cells only when a forager's best cell is taken: 2.5x faster
    than in 1.1.0, 32x faster than Mesa 2.3 (was 12x), same results.

Fixed

  • A continuous space restored from a pickle kept a copy of the agent positions, so later moves
    were lost; it now keeps a view.
  • The empty-cell index keeps its order through pickling, so a restored grid draws the same
    empty cells.
  • DataCollector reporters are picklable.

Modelly 1.1.0

Choose a tag to compare

@RemiPelloux RemiPelloux released this 24 Oct 12:42

Added

  • modelly.profile: a runtime profiler. with Profiler(model) as prof: model.run_for(n), then
    prof.report() or prof.to_dataframe(), gives the time of each step by agent class and
    method (calls, agents per call, microseconds per agent), data collection and scheduled events,
    with hints that point at the matching lint rule. python -m modelly.profile model.py:MyModel
    runs it from the command line. Results are identical with and without the profiler, and the
    instrumented methods are restored exactly on exit.
  • python -m modelly and a modelly script: one entry point for lint, migrate and profile,
    with --version and a help page listing every command.
  • AgentPortrayalStyle.filled (Altair backend); extra keywords to draw_agents reach
    mark_point instead of being dropped.
  • Three benchmark playbooks, each for Mesa 2.3 and Modelly with agreement and determinism
    checks: schelling (27x), boltzmann_wealth (7.4x) and sugarscape (12x).

Linter

  • New rules: MDL005 wall-clock or OS entropy in model code, MDL006 ordering by id() or
    hash(), MDL007 removing agents while looping over the population, MDL008 global state in
    a model method, MDL009 editing model.agents directly, MDL106 DataFrames built inside step,
    MDL107 a per-agent loop that only writes columns.
  • MDL001 also catches unseeded Random(), SystemRandom and numpy generators without a seed,
    and resolves imports (default_rng(seed=7) is no longer flagged); MDL004 skips __slots__
    and ClassVar.
  • # noqa: MDLxxx suppression, [tool.modelly.lint] in pyproject.toml (select, ignore,
    per-file-ignores), --explain CODE with a bad and a fixed example, --statistics,
    --format github and --quiet.
  • Detailed --help for lint and migrate, with examples and exit codes; docs/lint.md
    lists every rule.

Performance

  • Moving an agent between cells runs in a third compiled module (modelly/_native/fastcell.c):
    one move is 205 ns to 107 ns, the cell-list playbooks 7 to 14% faster, with the same results.
    Subclasses that override add_agent or remove_agent keep their overrides; the pure-Python
    twin is used when the module is not built.
  • import modelly no longer walks the stack to name its loggers (about 30 ms less).

Fixed

  • Matplotlib marker groups are drawn in first-seen order instead of set order, so the
    layering no longer changes from run to run.
  • run_batch no longer emits Python's multi-threaded fork DeprecationWarning for closure
    batches.

CI

  • Release wheels are built on tags and manual runs only; superseded runs are cancelled;
    docs-only changes skip the build; Dependabot groups its updates monthly; the examples job,
    which checked out a missing repository, is gone (the test matrix already runs tests/examples).

Modelly 1.0.0

Choose a tag to compare

@RemiPelloux RemiPelloux released this 24 Oct 11:50

First release of Modelly with the domain package layout (core, space, time, data, behaviour, scenarios, batch, viz). See CHANGELOG.md.

On PyPI this code ships as modelly-abm 1.0.1, because PyPI does not accept a second upload of 1.0.0.