diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..77964fa --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: Build and publish pypi package + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: 3.11.1 + - name: Install poetry + uses: abatilo/actions-poetry@v2 + with: + poetry-version: 1.2.2 + - name: Configure poetry + # TODO (mis): add this secret to your repository + run: | + poetry config virtualenvs.create false + poetry config pypi-token.pypi ${{ SECRETS.PYPI_TOKEN }} + #---------------------------------------------- + # load cached venv if cache exists + #---------------------------------------------- + - name: Load cached venv + id: cached-dependencies + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} + #---------------------------------------------- + # install dependencies if cache does not exist + #---------------------------------------------- + - name: Install dependencies + if: steps.cached-dependencies.outputs.cache-hit != 'true' + run: | + poetry install + - name: Run Tests + run: | + pytest + - name: Build and publish + if: github.ref == 'refs/heads/main' + run: | + poetry publish --build diff --git a/flock/main.py b/flock/main.py deleted file mode 100644 index db4ffe1..0000000 --- a/flock/main.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/python -import click -import numpy as np - -from flock.model import FlockModel -from flock.vicsek import VicsekModel -from flock.reynolds import ReynoldsModel -from flock.kuravicsek import KuramotoVicsekModel -from util.geometry import EnumBounds, EnumNeighbours -from util.plot import plot_state, plot_trajectories, FlockStyle, savefig - -from typing import Any, Dict, List, Tuple - - -def __run(sim: 'FlockModel', t: int, out: str) -> None: - txtpath = sim.mkdir(out) - while sim.t < t: - sim.save(txtpath) - sim.update() - - return - - -@click.command() -@click.option('--out', default = 'out/txt', help = 'Directory to save trajectories') -@click.option('-s', default = 0, help='Seed to run simulation: will generate a random seed if not given.') -@click.option('-t', default = 100, help='Time to run the simulation') -@click.option('-n', default = 10, help='Number of particles') -@click.option('-l', default = 2.0, help='System size') -@click.option('-e', default = 0.0, help='Perturbation of angular velocity') -@click.option('-v', default = 0.1, help='Absolute velocity') -@click.option('-r', default = 1.0, help='Radius or number of neighbours to follow') -@click.option('-dt', default = 1.0, help='Time step') -@click.option('--bounds', required = True, - type = click.Choice(['PERIODIC', 'REFLECTIVE']), - help = 'How particles behave at the boundary') -@click.option('--neighbours', required = True, - type = click.Choice(['METRIC', 'TOPOLOGICAL']), - help = 'Use neighbours in a radius r or nearest r neighbours') -def vicsek(out: str, - s: int, t: int, n: int, l: float, - e: float, v: float, r: float, - dt: float, bounds: str, neighbours: str - ) -> None: - """ - Create VicsekModel with given params and run it for t timesteps - Dump txt file of the state in each step (and image if the flag is set) - - Run from the root pyflocks/ folder - - python -m flock.main vicsek [flags] - - """ - if not s: - s = np.random.randint(10000) - sim = VicsekModel(s, n, l, EnumBounds[bounds], EnumNeighbours[neighbours], dt, - params = { 'eta': e, 'v' : v, 'r': r }) - __run(sim, t, out) - - -@click.command() -@click.option('--out', default = 'out/txt', help = 'Directory to save trajectories') -@click.option('-s', default = 0, help='Seed to run simulation: will generate a random seed if not given.') -@click.option('-t', default = 10, help='Time to run the simulation') -@click.option('-n', default = 10, help='Number of particles') -@click.option('-l', default = 100.0, help='System size') -@click.option('-a1', default = 0.15, help='Aggregate') -@click.option('-a2', default = 0.05, help='Avoidance') -@click.option('-a3', default = 0.25, help='Alignment') -@click.option('-r', default = 1.0, help='Radius or number of neighbours to follow') -@click.option('-dt', default = 0.1, help='Time step') -@click.option('--bounds', required = True, - type = click.Choice(['PERIODIC', 'REFLECTIVE']), - help = 'How particles behave at the boundary') -@click.option('--neighbours', required = True, - type = click.Choice(['METRIC', 'TOPOLOGICAL']), - help = 'Use neighbours in a radius r or nearest r neighbours') -def reynolds(out: str, - s: int, t: int, n: int, l: float, - a1: float, a2: float, a3: float, r: float, - dt: float, bounds: str, neighbours: str - ) -> None: - """ - Create Reynolds model with given params and run it for t timesteps - Dump txt file of the state in each step (and image if the flag is set) - - Run from the root pyflocks/ folder - - python -m flock.main reynolds [flags] - """ - if not s: - s = np.random.randint(10000) - sim = ReynoldsModel(s, n, l, EnumBounds[bounds], EnumNeighbours[neighbours], dt, - params = { 'aggregate': a1, 'avoidance': a2, 'alignment': a3, 'r': r }) - __run(sim, t, out) - - -@click.command() -@click.option('--out', default = 'out/txt', help = 'Directory to save trajectories') -@click.option('-s', default = 0, help='Seed to run simulation: will generate a random seed if not given.') -@click.option('-t', default = 10, help='Time to run the simulation') -@click.option('-n', default = 10, help='Number of particles') -@click.option('-l', default = 5.0, help='System size') -@click.option('-e', default = 0.5, help='Perturbation of angular velocity') -@click.option('-v', default = 0.3, help='Absolute velocity') -@click.option('-r', default = 1, help='Radius or number of neighbours to follow') -@click.option('-k', default = 0.5, help='Kuramoto coupling parameter') -@click.option('-f', default = 1, help='Intrinsic frequency') -@click.option('-dt', default = 0.1, help='Time step') -@click.option('--bounds', required = True, - type = click.Choice(['PERIODIC', 'REFLECTIVE']), - help = 'How particles behave at the boundary') -@click.option('--neighbours', required = True, - type = click.Choice(['METRIC', 'TOPOLOGICAL']), - help = 'Use neighbours in a radius r or nearest r neighbours') -def kuravicsek(out: str, - s: int, t: int, n: int, l: int, - e: float, v: float, r: float, k: float, f: float, - dt: float, bounds: str, neighbours: str - ) -> None: - """ - Create Kuramoto-Vicsek model with given params and run it for t timesteps - Dump txt file of the state in each step (and image if the flag is set) - - Run from the root pyflocks/ folder - - python -m flocks.main kuravicsek [flags] - """ - if not s: - s = np.random.randint(10000) - sim = KuramotoVicsekModel(s, n, l, EnumBounds[bounds], EnumNeighbours[neighbours], dt, - params = { 'eta': e, 'v': v, 'r': r, 'k': k, 'f': f }) - __run(sim, t, out) - - - -@click.group() -def options(): - pass - -options.add_command(vicsek) -options.add_command(reynolds) -options.add_command(kuravicsek) - -if __name__ == "__main__": - options() diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..e9d3c2b --- /dev/null +++ b/poetry.lock @@ -0,0 +1,718 @@ +[[package]] +name = "click" +version = "8.1.6" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "contourpy" +version = "1.1.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "wurlitzer"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "exceptiongroup" +version = "1.1.2" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fonttools" +version = "4.41.1" +description = "Tools to manipulate font files" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "importlib-resources" +version = "6.0.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "jpype1" +version = "1.4.1" +description = "A Python to Java bridge." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +packaging = "*" + +[package.extras] +docs = ["readthedocs-sphinx-ext", "sphinx", "sphinx-rtd-theme"] +tests = ["pytest"] + +[[package]] +name = "kiwisolver" +version = "1.4.4" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "matplotlib" +version = "3.7.2" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} +kiwisolver = ">=1.0.1" +numpy = ">=1.20" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.3.1,<3.1" +python-dateutil = ">=2.7" +setuptools_scm = ">=7" + +[[package]] +name = "numpy" +version = "1.25.1" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.9" + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pillow" +version = "10.0.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "pluggy" +version = "1.2.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pyhamcrest" +version = "2.0.4" +description = "Hamcrest framework for matcher objects" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["black", "flake8", "pyhamcrest[docs,tests]", "pytest-mypy", "towncrier", "tox", "tox-asdf", "twine"] +docs = ["alabaster (>=0.7,<1.0)", "sphinx (>=4.0,<5.0)"] +tests = ["coverage[toml]", "dataclasses", "mypy (!=0.940)", "pytest (>=5.0)", "pytest-mypy-plugins", "pytest-sugar", "pytest-xdist", "types-dataclasses", "types-mock"] +tests-numpy = ["numpy", "pyhamcrest[tests]"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "scipy" +version = "1.11.1" +description = "Fundamental algorithms for scientific computing in Python" +category = "main" +optional = false +python-versions = "<3.13,>=3.9" + +[package.dependencies] +numpy = ">=1.21.6,<1.28.0" + +[package.extras] +dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "setuptools" +version = "68.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "setuptools-scm" +version = "7.1.0" +description = "the blessed package to manage your versions by scm tags" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +packaging = ">=20.0" +setuptools = "*" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +typing-extensions = "*" + +[package.extras] +test = ["pytest (>=6.2)", "virtualenv (>20)"] +toml = ["setuptools (>=42)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "zipp" +version = "3.16.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.9,<3.13" +content-hash = "ac1794374930ddb90f212b4558152205c180fec34ef65c982b0e6f1ede20f5c2" + +[metadata.files] +click = [ + {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, + {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, +] +colorama = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +contourpy = [ + {file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"}, + {file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, + {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, + {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, + {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, + {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"}, + {file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"}, +] +cycler = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] +exceptiongroup = [ + {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, + {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, +] +fonttools = [ + {file = "fonttools-4.41.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a7bbb290d13c6dd718ec2c3db46fe6c5f6811e7ea1e07f145fd8468176398224"}, + {file = "fonttools-4.41.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec453a45778524f925a8f20fd26a3326f398bfc55d534e37bab470c5e415caa1"}, + {file = "fonttools-4.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2071267deaa6d93cb16288613419679c77220543551cbe61da02c93d92df72f"}, + {file = "fonttools-4.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e3334d51f0e37e2c6056e67141b2adabc92613a968797e2571ca8a03bd64773"}, + {file = "fonttools-4.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cac73bbef7734e78c60949da11c4903ee5837168e58772371bd42a75872f4f82"}, + {file = "fonttools-4.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:edee0900cf0eedb29d17c7876102d6e5a91ee333882b1f5abc83e85b934cadb5"}, + {file = "fonttools-4.41.1-cp310-cp310-win32.whl", hash = "sha256:2a22b2c425c698dcd5d6b0ff0b566e8e9663172118db6fd5f1941f9b8063da9b"}, + {file = "fonttools-4.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:547ab36a799dded58a46fa647266c24d0ed43a66028cd1cd4370b246ad426cac"}, + {file = "fonttools-4.41.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:849ec722bbf7d3501a0e879e57dec1fc54919d31bff3f690af30bb87970f9784"}, + {file = "fonttools-4.41.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38cdecd8f1fd4bf4daae7fed1b3170dfc1b523388d6664b2204b351820aa78a7"}, + {file = "fonttools-4.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ae64303ba670f8959fdaaa30ba0c2dabe75364fdec1caeee596c45d51ca3425"}, + {file = "fonttools-4.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f14f3ccea4cc7dd1b277385adf3c3bf18f9860f87eab9c2fb650b0af16800f55"}, + {file = "fonttools-4.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:33191f062549e6bb1a4782c22a04ebd37009c09360e2d6686ac5083774d06d95"}, + {file = "fonttools-4.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:704bccd69b0abb6fab9f5e4d2b75896afa48b427caa2c7988792a2ffce35b441"}, + {file = "fonttools-4.41.1-cp311-cp311-win32.whl", hash = "sha256:4edc795533421e98f60acee7d28fc8d941ff5ac10f44668c9c3635ad72ae9045"}, + {file = "fonttools-4.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:aaaef294d8e411f0ecb778a0aefd11bb5884c9b8333cc1011bdaf3b58ca4bd75"}, + {file = "fonttools-4.41.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d1f9471134affc1e3b1b806db6e3e2ad3fa99439e332f1881a474c825101096"}, + {file = "fonttools-4.41.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:59eba8b2e749a1de85760da22333f3d17c42b66e03758855a12a2a542723c6e7"}, + {file = "fonttools-4.41.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9b3cc10dc9e0834b6665fd63ae0c6964c6bc3d7166e9bc84772e0edd09f9fa2"}, + {file = "fonttools-4.41.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2c2964bdc827ba6b8a91dc6de792620be4da3922c4cf0599f36a488c07e2b2"}, + {file = "fonttools-4.41.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7763316111df7b5165529f4183a334aa24c13cdb5375ffa1dc8ce309c8bf4e5c"}, + {file = "fonttools-4.41.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b2d1ee95be42b80d1f002d1ee0a51d7a435ea90d36f1a5ae331be9962ee5a3f1"}, + {file = "fonttools-4.41.1-cp38-cp38-win32.whl", hash = "sha256:f48602c0b3fd79cd83a34c40af565fe6db7ac9085c8823b552e6e751e3a5b8be"}, + {file = "fonttools-4.41.1-cp38-cp38-win_amd64.whl", hash = "sha256:b0938ebbeccf7c80bb9a15e31645cf831572c3a33d5cc69abe436e7000c61b14"}, + {file = "fonttools-4.41.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e5c2b0a95a221838991e2f0e455dec1ca3a8cc9cd54febd68cc64d40fdb83669"}, + {file = "fonttools-4.41.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:891cfc5a83b0307688f78b9bb446f03a7a1ad981690ac8362f50518bc6153975"}, + {file = "fonttools-4.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73ef0bb5d60eb02ba4d3a7d23ada32184bd86007cb2de3657cfcb1175325fc83"}, + {file = "fonttools-4.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f240d9adf0583ac8fc1646afe7f4ac039022b6f8fa4f1575a2cfa53675360b69"}, + {file = "fonttools-4.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bdd729744ae7ecd7f7311ad25d99da4999003dcfe43b436cf3c333d4e68de73d"}, + {file = "fonttools-4.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b927e5f466d99c03e6e20961946314b81d6e3490d95865ef88061144d9f62e38"}, + {file = "fonttools-4.41.1-cp39-cp39-win32.whl", hash = "sha256:afce2aeb80be72b4da7dd114f10f04873ff512793d13ce0b19d12b2a4c44c0f0"}, + {file = "fonttools-4.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:1df1b6f4c7c4bc8201eb47f3b268adbf2539943aa43c400f84556557e3e109c0"}, + {file = "fonttools-4.41.1-py3-none-any.whl", hash = "sha256:952cb405f78734cf6466252fec42e206450d1a6715746013f64df9cbd4f896fa"}, + {file = "fonttools-4.41.1.tar.gz", hash = "sha256:e16a9449f21a93909c5be2f5ed5246420f2316e94195dbfccb5238aaa38f9751"}, +] +importlib-resources = [ + {file = "importlib_resources-6.0.0-py3-none-any.whl", hash = "sha256:d952faee11004c045f785bb5636e8f885bed30dc3c940d5d42798a2a4541c185"}, + {file = "importlib_resources-6.0.0.tar.gz", hash = "sha256:4cf94875a8368bd89531a756df9a9ebe1f150e0f885030b461237bc7f2d905f2"}, +] +iniconfig = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] +jpype1 = [ + {file = "JPype1-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:233a6a1a9c7f3633e7d74c14039f7ea35df81e138241f1acc8f94f65a8bd086e"}, + {file = "JPype1-1.4.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0099e77c6e99af13089b1c89cd99681b485fbf74daa492ee38e35d90d6349ffc"}, + {file = "JPype1-1.4.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:213f7154a7cc859dead7143cfee255bd3ce57938e0a5f2250b6768af0cef62c8"}, + {file = "JPype1-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:8d94013dfed3d1c7ee193e86393b2aea756a9910d222b7167ed493f9a0b1b3d5"}, + {file = "JPype1-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:34373696c3457f1d686639d928ef53b6a121203d9c0e651ed44dd3adf78bedb3"}, + {file = "JPype1-1.4.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622d07408e6d9a89e9eb70d4a9a675e51d5411657ac434ccec23cc94b828d9c"}, + {file = "JPype1-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d960ce12c3913242f9e4a11e55afa9c38e1a5410d0a38cc5a21086415c38a02"}, + {file = "JPype1-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5473a89d2cab327e38382fd69d1209517bad44158fb3ee9e699ebfeb5bc1cd51"}, + {file = "JPype1-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d85489a27c58b1b21feb8601406319b6a51d233ae9fa27de9b24c4dfea560b22"}, + {file = "JPype1-1.4.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9ce364d26ccbf7a21e35737f62663ce8d3aeb4e4b0f05d7e71f6126a6eb81059"}, + {file = "JPype1-1.4.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b9e29a0ea763c16d0fb05528785d4ed0fa56a421d600eca87e43398b569d2dea"}, + {file = "JPype1-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8c2fef2d0c298c8e69b2880ff866fa5db5f477ddd78ef310a357d697362c9f89"}, + {file = "JPype1-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6089cd28067d77e5b4a09e272525ecd70f838b92a7c08d91d4fa7d192ec3f3bb"}, + {file = "JPype1-1.4.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:24e601a31fb3b44decd5389ea87cbc39aaa0c61980c39b060561f9dc604f4cc5"}, + {file = "JPype1-1.4.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:000556d5839ffbe61c12f1fa41cbfd4e9a0abe103e0100febacc069d75defa8f"}, + {file = "JPype1-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:7039db1a522af55cf89f6a4a72120f8b074abcde2535543da34616640ecbb3c1"}, + {file = "JPype1-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd8bd76bf8741fa20d44ded776e6a3ea7fe103bcab7156f53ba7a72c50b7dd2f"}, + {file = "JPype1-1.4.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d483a6c3e6cb19065e71d322c4742efcfafc44bf1a67ef8ef75f78626158c3d9"}, + {file = "JPype1-1.4.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ea4be3e9471bf62ccdeccc2417ad6b9a300effcc0e5f6af9534eafa14e3978"}, + {file = "JPype1-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:8692a7b14e807b224673010a648ab12415d3bc32323e351f03e6c64814ee319b"}, + {file = "JPype1-1.4.1.tar.gz", hash = "sha256:dc8ee854073474ad79ae168d90c2f6893854f58936cfa18f3587cadae0d3696d"}, +] +kiwisolver = [ + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, +] +matplotlib = [ + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:2699f7e73a76d4c110f4f25be9d2496d6ab4f17345307738557d345f099e07de"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a8035ba590658bae7562786c9cc6ea1a84aa49d3afab157e414c9e2ea74f496d"}, + {file = "matplotlib-3.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f8e4a49493add46ad4a8c92f63e19d548b2b6ebbed75c6b4c7f46f57d36cdd1"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71667eb2ccca4c3537d9414b1bc00554cb7f91527c17ee4ec38027201f8f1603"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:152ee0b569a37630d8628534c628456b28686e085d51394da6b71ef84c4da201"}, + {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f8dddd1f5939e60aacb8fa08f19551f4b0140fab16a3669d5cd6e9cb28fc8"}, + {file = "matplotlib-3.7.2-cp310-cp310-win32.whl", hash = "sha256:fdbb46fad4fb47443b5b8ac76904b2e7a66556844f33370861b4788db0f8816a"}, + {file = "matplotlib-3.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:23fb1750934e5f0128f9423db27c474aa32534cec21f7b2153262b066a581fd1"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:30e1409b857aa8a747c5d4f85f63a79e479835f8dffc52992ac1f3f25837b544"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:50e0a55ec74bf2d7a0ebf50ac580a209582c2dd0f7ab51bc270f1b4a0027454e"}, + {file = "matplotlib-3.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac60daa1dc83e8821eed155796b0f7888b6b916cf61d620a4ddd8200ac70cd64"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305e3da477dc8607336ba10bac96986d6308d614706cae2efe7d3ffa60465b24"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c308b255efb9b06b23874236ec0f10f026673ad6515f602027cc8ac7805352d"}, + {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60c521e21031632aa0d87ca5ba0c1c05f3daacadb34c093585a0be6780f698e4"}, + {file = "matplotlib-3.7.2-cp311-cp311-win32.whl", hash = "sha256:26bede320d77e469fdf1bde212de0ec889169b04f7f1179b8930d66f82b30cbc"}, + {file = "matplotlib-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4860132c8c05261a5f5f8467f1b269bf1c7c23902d75f2be57c4a7f2394b3e"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:a1733b8e84e7e40a9853e505fe68cc54339f97273bdfe6f3ed980095f769ddc7"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d9881356dc48e58910c53af82b57183879129fa30492be69058c5b0d9fddf391"}, + {file = "matplotlib-3.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f081c03f413f59390a80b3e351cc2b2ea0205839714dbc364519bcf51f4b56ca"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cd120fca3407a225168238b790bd5c528f0fafde6172b140a2f3ab7a4ea63e9"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c1590b90aa7bd741b54c62b78de05d4186271e34e2377e0289d943b3522273"}, + {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d2ff3c984b8a569bc1383cd468fc06b70d7b59d5c2854ca39f1436ae8394117"}, + {file = "matplotlib-3.7.2-cp38-cp38-win32.whl", hash = "sha256:5dea00b62d28654b71ca92463656d80646675628d0828e08a5f3b57e12869e13"}, + {file = "matplotlib-3.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f506a1776ee94f9e131af1ac6efa6e5bc7cb606a3e389b0ccb6e657f60bb676"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6515e878f91894c2e4340d81f0911857998ccaf04dbc1bba781e3d89cbf70608"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:71f7a8c6b124e904db550f5b9fe483d28b896d4135e45c4ea381ad3b8a0e3256"}, + {file = "matplotlib-3.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12f01b92ecd518e0697da4d97d163b2b3aa55eb3eb4e2c98235b3396d7dad55f"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7e28d6396563955f7af437894a36bf2b279462239a41028323e04b85179058b"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbcf59334ff645e6a67cd5f78b4b2cdb76384cdf587fa0d2dc85f634a72e1a3e"}, + {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:318c89edde72ff95d8df67d82aca03861240512994a597a435a1011ba18dbc7f"}, + {file = "matplotlib-3.7.2-cp39-cp39-win32.whl", hash = "sha256:ce55289d5659b5b12b3db4dc9b7075b70cef5631e56530f14b2945e8836f2d20"}, + {file = "matplotlib-3.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:2ecb5be2b2815431c81dc115667e33da0f5a1bcf6143980d180d09a717c4a12e"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdcd28360dbb6203fb5219b1a5658df226ac9bebc2542a9e8f457de959d713d0"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3cca3e842b11b55b52c6fb8bd6a4088693829acbfcdb3e815fa9b7d5c92c1b"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebf577c7a6744e9e1bd3fee45fc74a02710b214f94e2bde344912d85e0c9af7c"}, + {file = "matplotlib-3.7.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:936bba394682049919dda062d33435b3be211dc3dcaa011e09634f060ec878b2"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bc221ffbc2150458b1cd71cdd9ddd5bb37962b036e41b8be258280b5b01da1dd"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35d74ebdb3f71f112b36c2629cf32323adfbf42679e2751252acd468f5001c07"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:717157e61b3a71d3d26ad4e1770dc85156c9af435659a25ee6407dc866cb258d"}, + {file = "matplotlib-3.7.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:20f844d6be031948148ba49605c8b96dfe7d3711d1b63592830d650622458c11"}, + {file = "matplotlib-3.7.2.tar.gz", hash = "sha256:a8cdb91dddb04436bd2f098b8fdf4b81352e68cf4d2c6756fcc414791076569b"}, +] +numpy = [ + {file = "numpy-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77d339465dff3eb33c701430bcb9c325b60354698340229e1dff97745e6b3efa"}, + {file = "numpy-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d736b75c3f2cb96843a5c7f8d8ccc414768d34b0a75f466c05f3a739b406f10b"}, + {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a90725800caeaa160732d6b31f3f843ebd45d6b5f3eec9e8cc287e30f2805bf"}, + {file = "numpy-1.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c6c9261d21e617c6dc5eacba35cb68ec36bb72adcff0dee63f8fbc899362588"}, + {file = "numpy-1.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0def91f8af6ec4bb94c370e38c575855bf1d0be8a8fbfba42ef9c073faf2cf19"}, + {file = "numpy-1.25.1-cp310-cp310-win32.whl", hash = "sha256:fd67b306320dcadea700a8f79b9e671e607f8696e98ec255915c0c6d6b818503"}, + {file = "numpy-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1516db588987450b85595586605742879e50dcce923e8973f79529651545b57"}, + {file = "numpy-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b82655dd8efeea69dbf85d00fca40013d7f503212bc5259056244961268b66e"}, + {file = "numpy-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e8f6049c4878cb16960fbbfb22105e49d13d752d4d8371b55110941fb3b17800"}, + {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41a56b70e8139884eccb2f733c2f7378af06c82304959e174f8e7370af112e09"}, + {file = "numpy-1.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5154b1a25ec796b1aee12ac1b22f414f94752c5f94832f14d8d6c9ac40bcca6"}, + {file = "numpy-1.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38eb6548bb91c421261b4805dc44def9ca1a6eef6444ce35ad1669c0f1a3fc5d"}, + {file = "numpy-1.25.1-cp311-cp311-win32.whl", hash = "sha256:791f409064d0a69dd20579345d852c59822c6aa087f23b07b1b4e28ff5880fcb"}, + {file = "numpy-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:c40571fe966393b212689aa17e32ed905924120737194b5d5c1b20b9ed0fb171"}, + {file = "numpy-1.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d7abcdd85aea3e6cdddb59af2350c7ab1ed764397f8eec97a038ad244d2d105"}, + {file = "numpy-1.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a180429394f81c7933634ae49b37b472d343cccb5bb0c4a575ac8bbc433722f"}, + {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d412c1697c3853c6fc3cb9751b4915859c7afe6a277c2bf00acf287d56c4e625"}, + {file = "numpy-1.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e1266411120a4f16fad8efa8e0454d21d00b8c7cee5b5ccad7565d95eb42dd"}, + {file = "numpy-1.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f76aebc3358ade9eacf9bc2bb8ae589863a4f911611694103af05346637df1b7"}, + {file = "numpy-1.25.1-cp39-cp39-win32.whl", hash = "sha256:247d3ffdd7775bdf191f848be8d49100495114c82c2bd134e8d5d075fb386a1c"}, + {file = "numpy-1.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:1d5d3c68e443c90b38fdf8ef40e60e2538a27548b39b12b73132456847f4b631"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:35a9527c977b924042170a0887de727cd84ff179e478481404c5dc66b4170009"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d3fe3dd0506a28493d82dc3cf254be8cd0d26f4008a417385cbf1ae95b54004"}, + {file = "numpy-1.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:012097b5b0d00a11070e8f2e261128c44157a8689f7dedcf35576e525893f4fe"}, + {file = "numpy-1.25.1.tar.gz", hash = "sha256:9a3a9f3a61480cc086117b426a8bd86869c213fc4072e606f01c4e4b66eb92bf"}, +] +packaging = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] +pillow = [ + {file = "Pillow-10.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891"}, + {file = "Pillow-10.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1"}, + {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3"}, + {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992"}, + {file = "Pillow-10.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485"}, + {file = "Pillow-10.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd"}, + {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, + {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, + {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, + {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223"}, + {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, + {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, + {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, + {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530"}, + {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86"}, + {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7"}, + {file = "Pillow-10.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa"}, + {file = "Pillow-10.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3"}, + {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3"}, + {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017"}, + {file = "Pillow-10.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3"}, + {file = "Pillow-10.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684"}, + {file = "Pillow-10.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3"}, + {file = "Pillow-10.0.0.tar.gz", hash = "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396"}, +] +pluggy = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] +pyhamcrest = [ + {file = "pyhamcrest-2.0.4-py3-none-any.whl", hash = "sha256:60a41d4783b9d56c9ec8586635d2301db5072b3ea8a51c32dd03c408ae2b0f79"}, + {file = "pyhamcrest-2.0.4.tar.gz", hash = "sha256:b5d9ce6b977696286cf232ce2adf8969b4d0b045975b0936ac9005e84e67e9c1"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +scipy = [ + {file = "scipy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aec8c62fbe52914f9cf28d846cf0401dd80ab80788bbab909434eb336ed07c04"}, + {file = "scipy-1.11.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3b9963798df1d8a52db41a6fc0e6fa65b1c60e85d73da27ae8bb754de4792481"}, + {file = "scipy-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e8eb42db36526b130dfbc417609498a6192381abc1975b91e3eb238e0b41c1a"}, + {file = "scipy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:366a6a937110d80dca4f63b3f5b00cc89d36f678b2d124a01067b154e692bab1"}, + {file = "scipy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:08d957ca82d3535b3b9ba6c8ff355d78fe975271874e2af267cb5add5bd78625"}, + {file = "scipy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:e866514bc2d660608447b6ba95c8900d591f2865c07cca0aa4f7ff3c4ca70f30"}, + {file = "scipy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba94eeef3c9caa4cea7b402a35bb02a5714ee1ee77eb98aca1eed4543beb0f4c"}, + {file = "scipy-1.11.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:512fdc18c65f76dadaca139348e525646d440220d8d05f6d21965b8d4466bccd"}, + {file = "scipy-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cce154372f0ebe88556ed06d7b196e9c2e0c13080ecb58d0f35062dc7cc28b47"}, + {file = "scipy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4bb943010203465ac81efa392e4645265077b4d9e99b66cf3ed33ae12254173"}, + {file = "scipy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:249cfa465c379c9bb2c20123001e151ff5e29b351cbb7f9c91587260602c58d0"}, + {file = "scipy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:ffb28e3fa31b9c376d0fb1f74c1f13911c8c154a760312fbee87a21eb21efe31"}, + {file = "scipy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:39154437654260a52871dfde852adf1b93b1d1bc5dc0ffa70068f16ec0be2624"}, + {file = "scipy-1.11.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:b588311875c58d1acd4ef17c983b9f1ab5391755a47c3d70b6bd503a45bfaf71"}, + {file = "scipy-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d51565560565a0307ed06fa0ec4c6f21ff094947d4844d6068ed04400c72d0c3"}, + {file = "scipy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b41a0f322b4eb51b078cb3441e950ad661ede490c3aca66edef66f4b37ab1877"}, + {file = "scipy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:396fae3f8c12ad14c5f3eb40499fd06a6fef8393a6baa352a652ecd51e74e029"}, + {file = "scipy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:be8c962a821957fdde8c4044efdab7a140c13294997a407eaee777acf63cbf0c"}, + {file = "scipy-1.11.1.tar.gz", hash = "sha256:fb5b492fa035334fd249f0973cc79ecad8b09c604b42a127a677b45a9a3d4289"}, +] +setuptools = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] +setuptools-scm = [ + {file = "setuptools_scm-7.1.0-py3-none-any.whl", hash = "sha256:73988b6d848709e2af142aa48c986ea29592bbcfca5375678064708205253d8e"}, + {file = "setuptools_scm-7.1.0.tar.gz", hash = "sha256:6c508345a771aad7d56ebff0e70628bf2b0ec7573762be9960214730de278f27"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +typing-extensions = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] +zipp = [ + {file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, + {file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, +] diff --git a/pyflocks/__init__.py b/pyflocks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyflocks/analysis/__init__.py b/pyflocks/analysis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyflocks/analysis/command.py b/pyflocks/analysis/command.py new file mode 100644 index 0000000..f7b8209 --- /dev/null +++ b/pyflocks/analysis/command.py @@ -0,0 +1,175 @@ +import os +import sys +from typing import List + +import click +import numpy as np +from matplotlib import pyplot as plt +from pyflocks.analysis import plot, order +from pyflocks.analysis.emergence import JVM, MutualInfo, system +from pyflocks.analysis.ensemble import __find_sims, ensemble_avg +from pyflocks.analysis.order import EnumParams, param +from pyflocks.models.factory import FlockFactory +from pyflocks.util.util import load_var, save_param + + +@click.group(name='analysis') +def cli(): + pass + + +@cli.command() +@click.option('--path', default='out/txt/', help='Path to load model data from') +@click.option('--out', default='out/order/', help='Path to save order param data to') +@click.option('--name', default='Vicsek', help='Model type or experiment to load') +@click.option('--ordp', default='ALL', type=click.Choice(order.EnumParams.names()), + help='Order parameter to study, all by default') +@click.option('--conp', '-p', default=['rho', 'eta'], multiple=True, + help='Control parameters by which to aggregate simulations') +@click.option('--skip', default=500, + help='Number of transient states to skip to compute only on steady states') +@click.option('--redo', is_flag=True, + help='If data exists, recompute it, otherwise just redo plot') +def ensemble_command( + path: str, out: str, name: str, ordp: str, conp: List[str], skip: int, redo: bool, +) -> None: + """ + After a large number of simulations or experiment are run, we compute average + order parameters on the trajectories, and then average again for all sims + with the same parameters. Then plot the control parameter vs the averaged + order parameters. + + Warn if an output folder from a single simulation or experiment. + + Resulting plots will be stored in out/plt + + Run from the root pyflocks/ folder + + python -m analysis.ensemble [flags] + """ + + if ordp == "ALL": + ordps = order.EnumParams.members()[1:] + else: + ordps = [order.EnumParams[ordp]] + conp = list(conp) + + print(f"Will calculate order parameters {ordps} for the given trajectories and control parameters {conp}") + + conp_str = '_'.join(conp) + + sims = __find_sims(path, name) + if not len(sims.keys()): + print(f'No directories of type {name} found in {path}') + sys.exit(0) + + pltitle = sims[list(sims.keys())[0]].title + plt.rcParams['figure.figsize'] = 10, 7 + + for ordp in ordps: + fname = f"{out}/{name}_{conp_str}_{str(ordp)}" + if os.path.exists(f"{fname}.npy") and not redo: + stats = np.load(f"{fname}.npy", allow_pickle=True).item() + else: + stats = ensemble_avg(sims, conp, ordp, skip) + np.save(fname, stats) + + print(f"Saving figure to {out}") + if len(conp) == 2: + plot.aggregate_2param(name, stats, conp, ordp, pltitle) + if len(conp) == 3: + plot.aggregate_3param(name, stats, conp, ordp, pltitle) + + +@cli.command() +@click.option('--model', help = 'Directory where system trajectories are stored') +@click.option('--est', type = click.Choice([ 'Gaussian', 'Kraskov1', 'Kraskov2', 'Kernel']), + help = 'Mutual Info estimator to use', required = True) +@click.option('--decomposition', is_flag = True, default = False, + help = 'If true, decompose Psi into the synergy, redundancy, and correction.') +@click.option('--pointwise', is_flag = True, default = False, + help = 'If true, use pointwise mutual information for emergence calculation.') +@click.option('--skip', default = 0, + help = 'Number of timesteps to wait before calculation e.g. for removing transients') +def emergence(model: str, est: str, + decomposition: bool, pointwise: bool, skip: int + ) -> None: + """ + Test the emergence calculator on the trajectories specified in `filename`, or + on a random data stream. + """ + JVM.start() + pth = '' + if model: + m = FlockFactory.load(model) + X = m.traj['X'] + n = m.n + M = order.param(params.CMASS, X, [], m.l, m.r, m.bounds)[params.CMASS] + pth = m.mkdir('out/order') + else: + # generate data for 1000 timesteps for 2 variables + np.random.seed(0) + X = np.random.normal(0, 1, size = (1000, 5)) + M = np.sum(X, axis = 1) + + skip = int(skip) + est = MutualInfo.get(est) + dts = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] + results = system(X[skip:], M[skip:], dts, est, pointwise, pth) + for dt, e in zip(dts, results): + print(f"{dt}: {results[0]} {results[1]}") + JVM.stop() + + +@cli.command(name='order') +@click.option('--path', required = True, help = 'Path to load model data from') +@click.option('--out', required = True, help = 'Path to save data to', default = 'out/order/') +@click.option('--ordp', default = '', help = 'Order parameter to compute, all by default', + type = click.Choice(EnumParams.names())) +@click.option('--redo', default = False, + help = 'If data exists, recompute it, otherwise just redo plot') +def order_command(path: str, out: str, ordp: str, redo: bool) -> None: + """ + After a simulation or experiment is run, compute (and plot) the results by + showing trajectories, order parameters, susceptibilities, and histograms for the + most 'interesting' states of that single run. + + It is assume that the simulation has a directory consistent with the mkdir + method of the Flock abstract class, i.e. the dirname begins with the model + name, followed by underscore, and other model details + + {name}(_{info})+(_{paramname}{paramval})+_{seed}?-id + + Run from the root pyflocks/ folder, will save the order parameters as CSV + files in out/order and the plots in out/plt. + + python -m analysis.order [flags] + """ + exp = FlockFactory.load(path) + parampth = exp.mkdir(out) + + ords = dict() + if ordp: + ordp = EnumParams[ordp] + else: + ordp = EnumParams.ALL + + print(f"Computing order parameter(s) {ordp} for {path}, saving to {parampth}") + + if ordp != EnumParams.ALL: + if os.path.exists(f"{parampth}/{ordp}.txt") and not redo: + ords = { ordp: load_var(f"{parampth}/{ordp}.txt") } + else: + ords = param(ordp, exp.traj['X'], exp.traj['A'], exp.l, 0, exp.bounds) + save_param(ords[ordp], str(ordp), parampth) + else: + for ordp in EnumParams.members()[1:]: + if os.path.exists(f"{parampth}/{ordp}.txt") and not redo: + ords[ordp] = load_var(f"{parampth}/{ordp}.txt") + else: + ords |= param(ordp, exp.traj['X'], exp.traj['A'], exp.l, 0, exp.bounds) + save_param(ords[ordp], str(ordp), parampth) + + plot.order_params(exp, ords) + # TODO: peak detection, plot those states + plot.states([ 0, 50, 100, 150, 200, 300, 400, 499 ], exp) diff --git a/analysis/emergence.py b/pyflocks/analysis/emergence.py similarity index 91% rename from analysis/emergence.py rename to pyflocks/analysis/emergence.py index 05433dc..162eadf 100644 --- a/analysis/emergence.py +++ b/pyflocks/analysis/emergence.py @@ -13,21 +13,12 @@ in Python. """ -import click import jpype as jp -import matplotlib.pyplot as plt import numpy as np import os import sys -from typing import Callable, Dict, Iterable, List, NamedTuple, Tuple, Union - -from analysis import order -from analysis.order import EnumParams as params -from flock.model import Flock, FlockModel -from flock.factory import FlockFactory -from util import util -from util.geometry import EnumBounds +from typing import Callable, List, NamedTuple, Tuple, Union class JVM: @@ -591,45 +582,3 @@ def ensemble( return emstats, mistats -@click.command() -@click.option('--model', help = 'Directory where system trajectories are stored') -@click.option('--est', type = click.Choice([ 'Gaussian', 'Kraskov1', 'Kraskov2', 'Kernel']), - help = 'Mutual Info estimator to use', required = True) -@click.option('--decomposition', is_flag = True, default = False, - help = 'If true, decompose Psi into the synergy, redundancy, and correction.') -@click.option('--pointwise', is_flag = True, default = False, - help = 'If true, use pointwise mutual information for emergence calculation.') -@click.option('--skip', default = 0, - help = 'Number of timesteps to wait before calculation e.g. for removing transients') -def test(model: str, est: str, - decomposition: bool, pointwise: bool, skip: int - ) -> None: - """ - Test the emergence calculator on the trajectories specified in `filename`, or - on a random data stream. - """ - pth = '' - if model: - m = FlockFactory.load(model) - X = m.traj['X'] - n = m.n - M = order.param(params.CMASS, X, [], m.l, m.r, m.bounds)[params.CMASS] - pth = m.mkdir('out/order') - else: - # generate data for 1000 timesteps for 2 variables - np.random.seed(0) - X = np.random.normal(0, 1, size = (1000, 5)) - M = np.sum(X, axis = 1) - - skip = int(skip) - est = MutualInfo.get(est) - dts = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] - results = system(X[skip:], M[skip:], dts, est, pointwise, pth) - for dt, e in zip(dts, results): - print(f"{dt}: {results[0]} {results[1]}") - - -if __name__ == "__main__": - JVM.start() - test() - JVM.stop() diff --git a/analysis/ensemble.py b/pyflocks/analysis/ensemble.py similarity index 53% rename from analysis/ensemble.py rename to pyflocks/analysis/ensemble.py index 1052857..c9d2f61 100644 --- a/analysis/ensemble.py +++ b/pyflocks/analysis/ensemble.py @@ -1,17 +1,11 @@ -import click -import itertools -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import ImageGrid -import numpy as np import os -import sys - -from analysis import order, plot -from flock.model import Flock -from flock.factory import FlockFactory -from util.util import load_var, save_param +from typing import Any, Dict, List -from typing import Any, Dict, List, Tuple +import numpy as np +from pyflocks.analysis import order +from pyflocks.models.factory import FlockFactory +from pyflocks.models.model import Flock +from pyflocks.util.util import load_var, save_param def __find_sims(path: str, name: str) -> Dict[str, 'Flock']: @@ -24,22 +18,21 @@ def __find_sims(path: str, name: str) -> Dict[str, 'Flock']: """ d = os.path.basename(path) if name in d: - return { d: FlockFactory.load(path) } + return {d: FlockFactory.load(path)} dirs = [d for d in os.listdir(path) - if os.path.isdir(os.path.join(path, d)) and d.lower().startswith(name.lower()) ] - models = { d: FlockFactory.load(os.path.join(path, d)) for d in dirs } + if os.path.isdir(os.path.join(path, d)) and d.lower().startswith(name.lower())] + models = {d: FlockFactory.load(os.path.join(path, d)) for d in dirs} return models - def ensemble_avg( - models: Dict[str, Any], + models: Dict[str, Any], control_params: List[str], - order_param: str, - skip: int, - path: str = 'out/order' - ) -> Dict[float, Dict[float, Any]]: + order_param: str, + skip: int, + path: str = 'out/order' +) -> Dict[float, Dict[float, Any]]: """ For all models loaded in the given dict, compute order params, and aggregate them by the control parameters by averaging all values for all steady states @@ -84,7 +77,7 @@ def ensemble_avg( # extract the model hyper parameter names (e.g. eta, rho in Vicsek) and # check if the params passed to the function are correct model_params = set([p for m in models.values() - for p in m.params.keys() ]) + for p in m.params.keys()]) for p in control_params: if p not in model_params: raise ValueError( @@ -92,15 +85,15 @@ def ensemble_avg( exit(0) # group experiments with the same params into batches - batch_names = set([ '_'.join(m.split('_')[:-1]) for m in models.keys() ]) + batch_names = set(['_'.join(m.split('_')[:-1]) for m in models.keys()]) print(batch_names) stats = dict() for batch in batch_names: - exp_in_batch = sorted([ m for m in models.keys() if batch in m ]) + exp_in_batch = sorted([m for m in models.keys() if batch in m]) count = len(exp_in_batch) print(f"Processing {count} experiments for {batch} with params " + - " ".join([ f"{p}: {models[exp_in_batch[0]].params[p]}" for p in control_params ]) ) + " ".join([f"{p}: {models[exp_in_batch[0]].params[p]}" for p in control_params])) # build a hash of hashes grouping model parameters by the param_list tmp = stats @@ -131,77 +124,10 @@ def ensemble_avg( if order_param in tmp.keys(): tmp[order_param].append(np.mean(Vt[skip:])) else: - tmp[order_param] = [ np.mean(Vt[skip:]) ] + tmp[order_param] = [np.mean(Vt[skip:])] # average everything accross all experiments tmp[f'{order_param}_mean'] = np.mean(tmp[order_param]) - tmp[f'{order_param}_std'] = np.std( tmp[order_param]) + tmp[f'{order_param}_std'] = np.std(tmp[order_param]) return stats - - -@click.command() -@click.option('--path', default='out/txt/', help='Path to load model data from') -@click.option('--out', default='out/order/', help='Path to save order param data to') -@click.option('--name', default='Vicsek', help='Model type or experiment to load') -@click.option('--ordp', default='ALL', type=click.Choice(order.EnumParams.names()), - help='Order parameter to study, all by default') -@click.option('--conp', '-p', default=[ 'rho', 'eta' ], multiple = True, - help='Control parameters by which to aggregate simulations') -@click.option('--skip', default = 500, - help='Number of transient states to skip to compute only on steady states') -@click.option('--redo', is_flag=True, - help = 'If data exists, recompute it, otherwise just redo plot') -def main( - path: str, out: str, name: str, ordp: str, conp: List[str], skip: int, redo: bool, - ) -> None: - """ - After a large number of simulations or experiment are run, we compute average - order parameters on the trajectories, and then average again for all sims - with the same parameters. Then plot the control parameter vs the averaged - order parameters. - - Warn if an output folder from a single simulation or experiment. - - Resulting plots will be stored in out/plt - - Run from the root pyflocks/ folder - - python -m analysis.ensemble [flags] - """ - - if ordp == "ALL": - ordps = order.EnumParams.members()[1:] - else: - ordps = [ order.EnumParams[ordp] ] - conp = list(conp) - - print(f"Will calculate order parameters {ordps} for the given trajectories and control parameters {conp}") - - conp_str = '_'.join(conp) - - sims = __find_sims(path, name) - if not len(sims.keys()): - print(f'No directories of type {name} found in {path}') - sys.exit(0) - - pltitle = sims[list(sims.keys())[0]].title - plt.rcParams['figure.figsize'] = 10, 7 - - for ordp in ordps: - fname = f"{out}/{name}_{conp_str}_{str(ordp)}" - if os.path.exists(f"{fname}.npy") and not redo: - stats = np.load(f"{fname}.npy", allow_pickle = True).item() - else: - stats = ensemble_avg(sims, conp, ordp, skip) - np.save(fname, stats) - - print(f"Saving figure to {out}") - if len(conp) == 2: - plot.aggregate_2param(name, stats, conp, ordp, pltitle) - if len(conp) == 3: - plot.aggregate_3param(name, stats, conp, ordp, pltitle) - - -if __name__ == "__main__": - main() diff --git a/analysis/infodynamics.jar b/pyflocks/analysis/infodynamics.jar similarity index 100% rename from analysis/infodynamics.jar rename to pyflocks/analysis/infodynamics.jar diff --git a/analysis/order.py b/pyflocks/analysis/order.py similarity index 82% rename from analysis/order.py rename to pyflocks/analysis/order.py index 391a72b..34460b8 100644 --- a/analysis/order.py +++ b/pyflocks/analysis/order.py @@ -1,16 +1,9 @@ #!/usr/bin/python3 -import click -from enum import Enum -import numpy as np -import os -from analysis import plot -from flock.model import FlockModel -from flock.factory import * -from util.geometry import * -from util.util import * +from pyflocks.util.geometry import * +from pyflocks.util.util import * -from typing import Any, Callable, List, Dict, Tuple +from typing import Any, List, Dict, Tuple class EnumParams(Enum): @@ -356,62 +349,3 @@ def param( raise ValueError(f"Param {param_name} not supported") return m - - -@click.command() -@click.option('--path', required = True, help = 'Path to load model data from') -@click.option('--out', required = True, help = 'Path to save data to', default = 'out/order/') -@click.option('--ordp', default = '', help = 'Order parameter to compute, all by default', - type = click.Choice(EnumParams.names())) -@click.option('--redo', default = False, - help = 'If data exists, recompute it, otherwise just redo plot') -def main(path: str, out: str, ordp: str, redo: bool) -> None: - """ - After a simulation or experiment is run, compute (and plot) the results by - showing trajectories, order parameters, susceptibilities, and histograms for the - most 'interesting' states of that single run. - - It is assume that the simulation has a directory consistent with the mkdir - method of the Flock abstract class, i.e. the dirname begins with the model - name, followed by underscore, and other model details - - {name}(_{info})+(_{paramname}{paramval})+_{seed}?-id - - Run from the root pyflocks/ folder, will save the order parameters as CSV - files in out/order and the plots in out/plt. - - python -m analysis.order [flags] - """ - exp = FlockFactory.load(path) - parampth = exp.mkdir(out) - - ords = dict() - if ordp: - ordp = EnumParams[ordp] - else: - ordp = EnumParams.ALL - - print(f"Computing order parameter(s) {ordp} for {path}, saving to {parampth}") - - if ordp != EnumParams.ALL: - if os.path.exists(f"{parampth}/{ordp}.txt") and not redo: - ords = { ordp: load_var(f"{parampth}/{ordp}.txt") } - else: - ords = param(ordp, exp.traj['X'], exp.traj['A'], exp.l, 0, exp.bounds) - save_param(ords[ordp], str(ordp), parampth) - else: - for ordp in EnumParams.members()[1:]: - if os.path.exists(f"{parampth}/{ordp}.txt") and not redo: - ords[ordp] = load_var(f"{parampth}/{ordp}.txt") - else: - ords |= param(ordp, exp.traj['X'], exp.traj['A'], exp.l, 0, exp.bounds) - save_param(ords[ordp], str(ordp), parampth) - - plot.order_params(exp, ords) - # TODO: peak detection, plot those states - plot.states([ 0, 50, 100, 150, 200, 300, 400, 499 ], exp) - - - -if __name__ == "__main__": - main() diff --git a/analysis/plot.py b/pyflocks/analysis/plot.py similarity index 98% rename from analysis/plot.py rename to pyflocks/analysis/plot.py index 52bb5f1..da1f6fc 100644 --- a/analysis/plot.py +++ b/pyflocks/analysis/plot.py @@ -4,10 +4,9 @@ import numpy as np import os -from flock.model import Flock -from util.plot import plot_trajectories +from pyflocks.models.model import Flock -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List def order_params( diff --git a/pyflocks/main.py b/pyflocks/main.py new file mode 100644 index 0000000..1c3376a --- /dev/null +++ b/pyflocks/main.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +import click +import numpy as np +from pyflocks.analysis.command import cli as analysis_cli +from pyflocks.models.kuravicsek import KuramotoVicsekModel +from pyflocks.models.model import FlockModel +from pyflocks.models.reynolds import ReynoldsModel +from pyflocks.models.vicsek import VicsekModel +from pyflocks.util.geometry import EnumBounds, EnumNeighbours +from pyflocks.util.command import cli as util_cli + +def __run(sim: 'FlockModel', t: int, out: str) -> None: + txtpath = sim.mkdir(out) + while sim.t < t: + sim.save(txtpath) + sim.update() + + return + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.option('--out', default='out/txt', help='Directory to save trajectories') +@click.option('-s', default=0, help='Seed to run simulation: will generate a random seed if not given.') +@click.option('-t', default=100, help='Time to run the simulation') +@click.option('-n', default=10, help='Number of particles') +@click.option('-l', default=2.0, help='System size') +@click.option('-e', default=0.0, help='Perturbation of angular velocity') +@click.option('-v', default=0.1, help='Absolute velocity') +@click.option('-r', default=1.0, help='Radius or number of neighbours to follow') +@click.option('-dt', default=1.0, help='Time step') +@click.option('--bounds', required=True, + type=click.Choice(['PERIODIC', 'REFLECTIVE']), + help='How particles behave at the boundary') +@click.option('--neighbours', required=True, + type=click.Choice(['METRIC', 'TOPOLOGICAL']), + help='Use neighbours in a radius r or nearest r neighbours') +def vicsek(out: str, + s: int, t: int, n: int, l: float, + e: float, v: float, r: float, + dt: float, bounds: str, neighbours: str + ) -> None: + """ + Create VicsekModel with given params and run it for t timesteps. + + Dump txt file of the state in each step (and image if the flag is set) + + Run from the root pyflocks/ folder + + python -m flock.main vicsek [flags] + + """ + if not s: + s = np.random.randint(10000) + sim = VicsekModel(s, n, l, EnumBounds[bounds], EnumNeighbours[neighbours], dt, + params={'eta': e, 'v': v, 'r': r}) + __run(sim, t, out) + + +@cli.command() +@click.option('--out', default='out/txt', help='Directory to save trajectories') +@click.option('-s', default=0, help='Seed to run simulation: will generate a random seed if not given.') +@click.option('-t', default=10, help='Time to run the simulation') +@click.option('-n', default=10, help='Number of particles') +@click.option('-l', default=100.0, help='System size') +@click.option('-a1', default=0.15, help='Aggregate') +@click.option('-a2', default=0.05, help='Avoidance') +@click.option('-a3', default=0.25, help='Alignment') +@click.option('-r', default=1.0, help='Radius or number of neighbours to follow') +@click.option('-dt', default=0.1, help='Time step') +@click.option('--bounds', required=True, + type=click.Choice(['PERIODIC', 'REFLECTIVE']), + help='How particles behave at the boundary') +@click.option('--neighbours', required=True, + type=click.Choice(['METRIC', 'TOPOLOGICAL']), + help='Use neighbours in a radius r or nearest r neighbours') +def reynolds(out: str, + s: int, t: int, n: int, l: float, + a1: float, a2: float, a3: float, r: float, + dt: float, bounds: str, neighbours: str + ) -> None: + """ + Create Reynolds model with given params and run it for t timesteps. + + Dump txt file of the state in each step (and image if the flag is set) + + Run from the root pyflocks/ folder + + python -m flock.main reynolds [flags] + """ + if not s: + s = np.random.randint(10000) + sim = ReynoldsModel(s, n, l, EnumBounds[bounds], EnumNeighbours[neighbours], dt, + params={'aggregate': a1, 'avoidance': a2, 'alignment': a3, 'r': r}) + __run(sim, t, out) + + +@cli.command() +@click.option('--out', default='out/txt', help='Directory to save trajectories') +@click.option('-s', default=0, help='Seed to run simulation: will generate a random seed if not given.') +@click.option('-t', default=10, help='Time to run the simulation') +@click.option('-n', default=10, help='Number of particles') +@click.option('-l', default=5.0, help='System size') +@click.option('-e', default=0.5, help='Perturbation of angular velocity') +@click.option('-v', default=0.3, help='Absolute velocity') +@click.option('-r', default=1, help='Radius or number of neighbours to follow') +@click.option('-k', default=0.5, help='Kuramoto coupling parameter') +@click.option('-f', default=1, help='Intrinsic frequency') +@click.option('-dt', default=0.1, help='Time step') +@click.option('--bounds', required=True, + type=click.Choice(['PERIODIC', 'REFLECTIVE']), + help='How particles behave at the boundary') +@click.option('--neighbours', required=True, + type=click.Choice(['METRIC', 'TOPOLOGICAL']), + help='Use neighbours in a radius r or nearest r neighbours') +def kuravicsek(out: str, + s: int, t: int, n: int, l: int, + e: float, v: float, r: float, k: float, f: float, + dt: float, bounds: str, neighbours: str + ) -> None: + """ + Create Kuramoto-Vicsek model with given params and run it for t timesteps. + + Dump txt file of the state in each step (and image if the flag is set) + + Run from the root pyflocks/ folder + + python -m flocks.main kuravicsek [flags] + """ + if not s: + s = np.random.randint(10000) + sim = KuramotoVicsekModel(s, n, l, EnumBounds[bounds], EnumNeighbours[neighbours], dt, + params={'eta': e, 'v': v, 'r': r, 'k': k, 'f': f}) + __run(sim, t, out) + + +cli.add_command(analysis_cli) +cli.add_command(util_cli) + +if __name__ == "__main__": + cli() diff --git a/pyflocks/models/__init__.py b/pyflocks/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flock/factory.py b/pyflocks/models/factory.py similarity index 84% rename from flock/factory.py rename to pyflocks/models/factory.py index fb516e4..d1f9656 100644 --- a/flock/factory.py +++ b/pyflocks/models/factory.py @@ -1,15 +1,10 @@ #!/usr/bin/python from enum import Enum -import numpy as np -import re -import os -from flock.model import Flock, FlockModel -from flock.vicsek import VicsekModel -from flock.reynolds import ReynoldsModel -from flock.kuravicsek import KuramotoVicsekModel - -from typing import Any, Union +from pyflocks.models.model import Flock, FlockModel +from pyflocks.models.vicsek import VicsekModel +from pyflocks.models.reynolds import ReynoldsModel +from pyflocks.models.kuravicsek import KuramotoVicsekModel class EnumModels(Enum): diff --git a/flock/kuravicsek.py b/pyflocks/models/kuravicsek.py similarity index 98% rename from flock/kuravicsek.py rename to pyflocks/models/kuravicsek.py index fbf4b55..f89adb8 100644 --- a/flock/kuravicsek.py +++ b/pyflocks/models/kuravicsek.py @@ -1,11 +1,10 @@ #!/usr/bin/python -import numpy as np -from flock.model import FlockModel -from util.geometry import * -from util.util import save_var +from pyflocks.models.model import FlockModel +from pyflocks.util.geometry import * +from pyflocks.util.util import save_var -from typing import Any, Dict, List, Tuple +from typing import Dict, Tuple class KuramotoVicsekModel(FlockModel): diff --git a/flock/model.py b/pyflocks/models/model.py similarity index 98% rename from flock/model.py rename to pyflocks/models/model.py index 354a96a..6ba9b56 100644 --- a/flock/model.py +++ b/pyflocks/models/model.py @@ -1,13 +1,12 @@ #!/usr/bin/python from abc import abstractmethod import numpy as np -import re import os -from util.geometry import EnumBounds, EnumNeighbours -from util.util import load_var, proc_params +from pyflocks.util.geometry import EnumBounds, EnumNeighbours +from pyflocks.util.util import load_var, proc_params -from typing import Any, Dict, List, Tuple +from typing import Dict class Flock: diff --git a/flock/reynolds.py b/pyflocks/models/reynolds.py similarity index 98% rename from flock/reynolds.py rename to pyflocks/models/reynolds.py index c400406..70875b8 100644 --- a/flock/reynolds.py +++ b/pyflocks/models/reynolds.py @@ -1,11 +1,10 @@ #!/usr/bin/python -import numpy as np -from flock.model import FlockModel -from util.geometry import * -from util.util import save_var +from pyflocks.models.model import FlockModel +from pyflocks.util.geometry import * +from pyflocks.util.util import save_var -from typing import Any, Dict, List, Tuple +from typing import Dict, List, Tuple class ReynoldsModel(FlockModel): diff --git a/flock/vicsek.py b/pyflocks/models/vicsek.py similarity index 97% rename from flock/vicsek.py rename to pyflocks/models/vicsek.py index ae492c0..ce085f4 100644 --- a/flock/vicsek.py +++ b/pyflocks/models/vicsek.py @@ -1,11 +1,10 @@ #!/usr/bin/python -import numpy as np -from flock.model import FlockModel -from util.geometry import * -from util.util import save_var +from pyflocks.models.model import FlockModel +from pyflocks.util.geometry import * +from pyflocks.util.util import save_var -from typing import Any, Dict, List, Tuple +from typing import Dict, Tuple class VicsekModel(FlockModel): diff --git a/pyflocks/util/__init__.py b/pyflocks/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyflocks/util/animate.py b/pyflocks/util/animate.py new file mode 100644 index 0000000..7eed993 --- /dev/null +++ b/pyflocks/util/animate.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +from pyflocks.util.plot import prepare_state_plot, savefig, plot_trajectories, plot_state, plot_cmass, plot_sumvec, \ + FlockStyle + + +def __plot( + sim: 'FlockModel', t: int, + style: 'FlockStyle', traj: int, cmass: bool, sumvec: bool, + imgpath: str, simple: bool +) -> None: + # bug when saving the first image, so save it again + if (t == 0): + prepare_state_plot(sim.l) + savefig(0, sim.title, sim.subtitle, imgpath) + + coords = sim.traj['X'][t] + angles = sim.traj['A'][t] + # absolute velocity may not change in all models, so there is no trajectory + if 'V' in sim.traj.keys(): + speeds = sim.traj['V'][t] + else: + speeds = sim.V + + # plot phase and frequency of oscillator instead of angle and speed + if 'Kuramoto' in str(type(sim)) and style == FlockStyle.OSCIL: + angles = sim.traj['P'][t] + # absolute velocity may not change in all models, so there is no trajectory + if 'F' in sim.traj.keys(): + speeds = sim.traj['F'][t] + + if traj > 0: + plot_trajectories(t, sim.traj['X'], sim.l, traj, sim.bounds) + + plot_state(style, t, sim.traj['X'][t], angles, speeds, sim.l, sim.dt, simple=simple) + + if cmass: + if traj < 0: + traj = 20 + plot_cmass(t, sim.traj['X'], sim.l, traj, sim.bounds) + if sumvec: + plot_sumvec(t, sim.traj['X'][t], angles, speeds, sim.l, sim.dt, simple=simple) + + savefig(t, sim.title, sim.subtitle, imgpath, simple=simple) diff --git a/util/animate.py b/pyflocks/util/command.py similarity index 57% rename from util/animate.py rename to pyflocks/util/command.py index f0e73d2..151d093 100644 --- a/util/animate.py +++ b/pyflocks/util/command.py @@ -1,57 +1,15 @@ -#!/usr/bin/python3 import click -import numpy as np +from pyflocks.models.factory import FlockFactory +from pyflocks.util.animate import __plot +from pyflocks.util.plot import FlockStyle -from flock.factory import FlockFactory -from util.geometry import EnumBounds, EnumNeighbours -from util.plot import * -from typing import Any, Dict, List, Tuple +@click.group(name='util') +def cli(): + pass -def __plot( - sim: 'FlockModel', t: int, - style: 'FlockStyle', traj: int, cmass: bool, sumvec: bool, - imgpath: str, simple: bool - ) -> None: - - # bug when saving the first image, so save it again - if (t == 0): - prepare_state_plot(sim.l) - savefig(0, sim.title, sim.subtitle, imgpath) - - coords = sim.traj['X'][t] - angles = sim.traj['A'][t] - # absolute velocity may not change in all models, so there is no trajectory - if 'V' in sim.traj.keys(): - speeds = sim.traj['V'][t] - else: - speeds = sim.V - - # plot phase and frequency of oscillator instead of angle and speed - if 'Kuramoto' in str(type(sim)) and style == FlockStyle.OSCIL: - angles = sim.traj['P'][t] - # absolute velocity may not change in all models, so there is no trajectory - if 'F' in sim.traj.keys(): - speeds = sim.traj['F'][t] - - if traj > 0: - plot_trajectories(t, sim.traj['X'], sim.l, traj, sim.bounds) - - plot_state(style, t, sim.traj['X'][t], angles, speeds, sim.l, sim.dt, simple = simple) - - if cmass: - if traj < 0: - traj = 20 - plot_cmass(t, sim.traj['X'], sim.l, traj, sim.bounds) - if sumvec: - plot_sumvec(t, sim.traj['X'][t], angles, speeds, sim.l, sim.dt, simple = simple) - - savefig(t, sim.title, sim.subtitle, imgpath, simple = simple) - - - -@click.command() +@cli.command() @click.option('--path', type = str, required = True, help = 'Directory with position data') @click.option('--out', type = str, required = True, default = 'out/img', @@ -108,7 +66,3 @@ def plot( print(f"{t}: animating system to {imgpath}/") __plot(flock, t, FlockStyle[style], traj, cmass, sumvec, imgpath, simple) t += 1 - - -if __name__ == "__main__": - plot() diff --git a/util/geometry.py b/pyflocks/util/geometry.py similarity index 100% rename from util/geometry.py rename to pyflocks/util/geometry.py diff --git a/util/plot.py b/pyflocks/util/plot.py similarity index 98% rename from util/plot.py rename to pyflocks/util/plot.py index a10ae19..30ef766 100644 --- a/util/plot.py +++ b/pyflocks/util/plot.py @@ -1,11 +1,10 @@ #!/usr/bin/python3 from enum import Enum -import matplotlib.pyplot as plt -import numpy as np -from util.geometry import * +import matplotlib.pyplot as plt +from pyflocks.util.geometry import EnumBounds, centre_of_mass, sum_vec_ang, ang_to_vec -import typing +from pyflocks.util.util import * class FlockStyle(Enum): diff --git a/util/util.py b/pyflocks/util/util.py similarity index 100% rename from util/util.py rename to pyflocks/util/util.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d9ce9a3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[tool.poetry] +name = "pyflocks" +version = "0.1.0" +description = "Library for simulating flocks" +authors = ["mis None: + assert_that(actual, close_to(expected, 1e-5), f"Test {i}: failed! Expected {expected}, got {actual}") + + +@pytest.mark.parametrize('i,actual,expected', [ + [9, ang_to_vec(0), [1, 0]], + [10, ang_to_vec(pi), [-1, 0]], + [11, ang_to_vec(-pi), [-1, 0]], + [12, ang_to_vec(pi / 2), [0, 1]], + [13, ang_to_vec(-pi / 2), [0, -1]], + [14, ang_to_vec(pi / 4), [1 / sqrt(2), 1 / sqrt(2)]], + [15, ang_to_vec(3 * pi / 4), [-1 / sqrt(2), 1 / sqrt(2)]], + [20, neighbours(1, neighbours_in_reflective_bounds, 1, EnumNeighbours.METRIC, EnumBounds.REFLECTIVE), [1, 2, 3]], + [21, neighbours(0, neighbours_in_reflective_bounds, 0.11, EnumNeighbours.METRIC, EnumBounds.REFLECTIVE), [0, 5]], + [22, neighbours(0, neighbours_in_reflective_bounds, 0.1, EnumNeighbours.METRIC, EnumBounds.REFLECTIVE), [0]], + [23, neighbours(1, neighbours_in_reflective_bounds, 3, EnumNeighbours.TOPOLOGICAL, EnumBounds.REFLECTIVE), + [1, 3, 2, 4]], + [24, neighbours(0, neighbours_in_reflective_bounds, 3, EnumNeighbours.TOPOLOGICAL, EnumBounds.REFLECTIVE), + [0, 5, 4, 3]], + [25, neighbours(1, neighbours_in_reflective_bounds, 1, EnumNeighbours.METRIC, EnumBounds.PERIODIC, 5), [1, 2, 3]], + [26, neighbours(0, neighbours_in_reflective_bounds, 1, EnumNeighbours.METRIC, EnumBounds.PERIODIC, 5), [0, 5]], + [27, neighbours(1, neighbours_in_reflective_bounds, 5, EnumNeighbours.TOPOLOGICAL, EnumBounds.PERIODIC, 5), + [1, 3, 2, 4, 6, 0]], + [28, neighbours(0, neighbours_in_reflective_bounds, 5, EnumNeighbours.TOPOLOGICAL, EnumBounds.PERIODIC, 5), + [0, 5, 4, 7, 8, 6]], + [32, sum_vec_ang(A, V), np.mean([ang_to_vec(a) * v for a in A], axis=0)], + [34, centre_of_mass(X, l, EnumBounds.PERIODIC), centre_of_mass(X1, l, EnumBounds.PERIODIC)], + [35, centre_of_mass(X2, l, EnumBounds.PERIODIC), [9.95, 9.95]], + [36, centre_of_mass(X2, l, EnumBounds.REFLECTIVE), [4.95, 4.95]], + [37, periodic_diff(X[0], X[1], l), np.array([2, 2])], + [38, periodic_diff(X[0], X[2], l), np.array([0, -1])], + [39, periodic_diff(X[2], X[0], l), np.array([0, 1])], +]) +def test_vector_e(i, actual, expected) -> None: + assert_that(len(actual), is_(equal_to(len(expected)))) + assert_that(any(abs(a - e) > 1e-5 for a, e, in zip(actual, expected)), is_(equal_to(False)), + f"Test {i}: failed! Expected {expected}, got {actual}") + +# # Not actually used before +# def test_np_array(i, actual, expected) -> None: +# if not np.array_equal(actual, expected): +# print(f"Test {i}: failed! Expected {expected}, got {actual}") +# + +# # show graphically the angles and plotting function +# col = cm.rainbow(np.linspace(0, 1, 30)) +# phi = np.linspace(-2 * pi, pi, 30) +# vec = list(map(ang_to_vec, phi)) +# for i in range(len(phi)): +# plot_vector([0, 0], vec[i], col[i]) +# plt.show() +# phi = list(map(ang_mod, phi)) +# vec = list(map(ang_to_vec, phi)) +# for i in range(len(phi)): +# plot_vector([0, 0], vec[i], col[i]) +# plt.show() diff --git a/util/tests.py b/util/tests.py deleted file mode 100644 index d5379d4..0000000 --- a/util/tests.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python - -# to run, make sure you are in the root of the repo, and -# python -m util.tests - -from util.geometry import * -from util.plot import * - -import numpy as np -import matplotlib.cm as cm - -def test_scalar_e(i, actual, expected) -> None: - if abs(actual - expected) > 1e-5: - print(f"Test {i}: failed! Expected {expected}, got {actual}") - -def test_vector_e(i, actual, expected) -> None: - if len(actual) != len(expected) or any( - [abs(a - e) > 1e-5 for (a, e) in zip(actual, expected)]): - print(f"Test {i}: failed! Expected {expected}, got {actual}") - -def test_np_array(i, actual, expected) -> None: - if not np.array_equal(actual, expected): - print(f"Test {i}: failed! Expected {expected}, got {actual}") - -# test vector to angle and angle to vector conversions -test_scalar_e(1, vec_to_ang([ 1, 0]), 0) -test_scalar_e(2, vec_to_ang([-1, 0]), pi) -test_scalar_e(3, vec_to_ang([ 0, 1]), pi/2) -test_scalar_e(4, vec_to_ang([ 0, -1]), -pi/2) -test_scalar_e(5, vec_to_ang([ 1, 1]), pi/4) -test_scalar_e(6, vec_to_ang([ 1, -1]), -pi/4) -test_scalar_e(7, vec_to_ang([-1, 1]), 3*pi/4) -test_scalar_e(8, vec_to_ang([-1, -1]), -3*pi/4) - -test_vector_e(9, ang_to_vec( 0), [ 1, 0]) -test_vector_e(10, ang_to_vec( pi), [-1, 0]) -test_vector_e(11, ang_to_vec( -pi), [-1, 0]) -test_vector_e(12, ang_to_vec( pi/2), [ 0, 1]) -test_vector_e(13, ang_to_vec( -pi/2), [ 0, -1]) -test_vector_e(14, ang_to_vec( pi/4), [ 1/sqrt(2), 1/sqrt(2)]) -test_vector_e(15, ang_to_vec(3*pi/4), [-1/sqrt(2), 1/sqrt(2)]) - -test_scalar_e(16, vec_to_ang(ang_to_vec( pi/3)), pi/3) -test_scalar_e(17, vec_to_ang(ang_to_vec(-pi/3)), -pi/3) -test_scalar_e(18, vec_to_ang(ang_to_vec(4*pi-pi/3)), -pi/3) -test_scalar_e(19, vec_to_ang(ang_to_vec(-4*pi+pi/3)), pi/3) - -# test neighbours in reflective bounds -X = np.array([ [4, 4], [2, 2], [2, 2.8], [2.5, 2.5], [3, 3], [4, 4.1], [0.1, 0.1], [0, 5], [0, 0] ]) -test_vector_e(20, neighbours(1, X, 1, EnumNeighbours.METRIC, EnumBounds.REFLECTIVE), [ 1, 2, 3 ]) -test_vector_e(21, neighbours(0, X, 0.11,EnumNeighbours.METRIC, EnumBounds.REFLECTIVE), [ 0, 5 ]) -# will fail because of floating-point prcision; implement Decimal instead for this -test_vector_e(22, neighbours(0, X, 0.1, EnumNeighbours.METRIC, EnumBounds.REFLECTIVE), [ 0 ]) - -test_vector_e(23, neighbours(1, X, 3, EnumNeighbours.TOPOLOGICAL, EnumBounds.REFLECTIVE), [ 1, 3, 2, 4 ]) -test_vector_e(24, neighbours(0, X, 3, EnumNeighbours.TOPOLOGICAL, EnumBounds.REFLECTIVE), [ 0, 5, 4, 3 ]) - -# test neighbours in periodic bounds -test_vector_e(25, neighbours(1, X, 1, EnumNeighbours.METRIC, EnumBounds.PERIODIC, 5), [ 1, 2, 3 ]) -test_vector_e(26, neighbours(0, X, 1, EnumNeighbours.METRIC, EnumBounds.PERIODIC, 5), [ 0, 5 ]) - -test_vector_e(27, neighbours(1, X, 5, EnumNeighbours.TOPOLOGICAL, EnumBounds.PERIODIC, 5), [ 1, 3, 2, 4, 6, 0]) -test_vector_e(28, neighbours(0, X, 5, EnumNeighbours.TOPOLOGICAL, EnumBounds.PERIODIC, 5), [ 0, 5, 4, 7, 8, 6 ]) - -# test average direction -test_scalar_e(29, average_angles([ [1], [-1.1] ]), np.average([ 1, -1.1])) -test_scalar_e(30, average_angles([ [2], [1] ]), 1.5) -test_scalar_e(31, average_angles([ [2], [-2] ]), np.pi) - -# test angle sums and averages -n = 10 -v = 0.1 -V = np.ones((n, 1)) * v -A = np.random.uniform(-np.pi, np.pi, size=(n, 1)) - -test_vector_e(32, sum_vec_ang(A, V), - np.mean([ ang_to_vec(a) * v for a in A], axis = 0) * n ) -test_scalar_e(33, average_angles(A), - vec_to_ang(np.mean([ ang_to_vec(a) * v for a in A], axis = 0))) - -# test cmass and distances for periodic bounds -l = 10 -X = np.array([[1, 1], [9, 9], [1, 2]]) -X1 = np.array([[1, 1], [-1,-1], [1, 2]]) -X2 = np.array([[0, 0], [0, 9.9], [9.9, 0], [9.9, 9.9]]) - -test_vector_e(34, centre_of_mass(X, l, EnumBounds.PERIODIC), centre_of_mass(X1, l, EnumBounds.PERIODIC)) -test_vector_e(35, centre_of_mass(X2, l, EnumBounds.PERIODIC), [9.95, 9.95]) -test_vector_e(36, centre_of_mass(X2, l, EnumBounds.REFLECTIVE), [4.95, 4.95]) - -test_vector_e(37, periodic_diff(X[0], X[1], l), np.array([2, 2])) -test_vector_e(38, periodic_diff(X[0], X[2], l), np.array([0, -1])) -test_vector_e(39, periodic_diff(X[2], X[0], l), np.array([0, 1])) -test_scalar_e(40, periodic_diff(X[0], X[1], l, True), 2 * np.sqrt(2)) -test_scalar_e(41, periodic_diff(X[0], X[2], l, True), 1) - -# test angle mod -test_scalar_e(42, ang_mod(1), 1) -test_scalar_e(43, ang_mod(-1), -1) -test_scalar_e(44, ang_mod(pi), pi) -test_scalar_e(45, ang_mod(-pi), -pi) -test_scalar_e(45, ang_mod(-pi+1), -pi+1) - -# test angle to point difference TODO: correctly implement bearing -test_scalar_e(46, bearing_to(np.deg2rad(45), [1, 1]), 0) -test_scalar_e(47, bearing_to(np.deg2rad(45), [0, 1]), np.deg2rad(-135)) - -print("If nothing so far was printed, it means your tests all pass! Good job.") - -# show graphically the angles and plotting function -col = cm.rainbow(np.linspace(0, 1, 30)) -phi = np.linspace(-2*pi, pi, 30) -vec = list(map(ang_to_vec, phi)) -for i in range(len(phi)): - plot_vector([0,0], vec[i], col[i]) -plt.show() -phi = list(map(ang_mod, phi)) -vec = list(map(ang_to_vec, phi)) -for i in range(len(phi)): - plot_vector([0,0], vec[i], col[i]) -plt.show()