Reorganized as a Python package
This commit is contained in:
parent
19b3b250b4
commit
d397ff93c2
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,12 +1,10 @@
|
||||
.venv
|
||||
__pycache__
|
||||
src/build
|
||||
src/packsim.c
|
||||
.venv/
|
||||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
|
||||
src/packsim.c
|
||||
*.so
|
||||
|
||||
figures
|
||||
simulations
|
||||
old_simulations
|
||||
|
||||
*.json
|
||||
4
build.sh
4
build.sh
@ -1,4 +0,0 @@
|
||||
rm -f packsim_core*
|
||||
cd src
|
||||
python3 setup.py build_ext --inplace --quiet
|
||||
mv *.so ../
|
||||
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
7
docs/source/api.rst
Normal file
7
docs/source/api.rst
Normal file
@ -0,0 +1,7 @@
|
||||
API
|
||||
===
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
|
||||
lumache
|
||||
35
docs/source/conf.py
Normal file
35
docs/source/conf.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
|
||||
# -- Project information
|
||||
|
||||
project = 'Lumache'
|
||||
copyright = '2021, Graziella'
|
||||
author = 'Graziella'
|
||||
|
||||
release = '0.1'
|
||||
version = '0.1.0'
|
||||
|
||||
# -- General configuration
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.duration',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.autosummary',
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
|
||||
}
|
||||
intersphinx_disabled_domains = ['std']
|
||||
|
||||
templates_path = ['_templates']
|
||||
|
||||
# -- Options for HTML output
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# -- Options for EPUB output
|
||||
epub_show_urls = 'footnote'
|
||||
22
docs/source/index.rst
Normal file
22
docs/source/index.rst
Normal file
@ -0,0 +1,22 @@
|
||||
Welcome to Lumache's documentation!
|
||||
===================================
|
||||
|
||||
**Lumache** (/lu'make/) is a Python library for cooks and food lovers
|
||||
that creates recipes mixing random ingredients.
|
||||
It pulls data from the `Open Food Facts database <https://world.openfoodfacts.org/>`_
|
||||
and offers a *simple* and *intuitive* API.
|
||||
|
||||
Check out the :doc:`usage` section for further information, including
|
||||
how to :ref:`installation` the project.
|
||||
|
||||
.. note::
|
||||
|
||||
This project is under active development.
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
|
||||
usage
|
||||
api
|
||||
34
docs/source/usage.rst
Normal file
34
docs/source/usage.rst
Normal file
@ -0,0 +1,34 @@
|
||||
Usage
|
||||
=====
|
||||
|
||||
.. _installation:
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To use Lumache, first install it using pip:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(.venv) $ pip install lumache
|
||||
|
||||
Creating recipes
|
||||
----------------
|
||||
|
||||
To retrieve a list of random ingredients,
|
||||
you can use the ``lumache.get_random_ingredients()`` function:
|
||||
|
||||
.. autofunction:: lumache.get_random_ingredients
|
||||
|
||||
The ``kind`` parameter should be either ``"meat"``, ``"fish"``,
|
||||
or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients`
|
||||
will raise an exception.
|
||||
|
||||
.. autoexception:: lumache.InvalidKindError
|
||||
|
||||
For example:
|
||||
|
||||
>>> import lumache
|
||||
>>> lumache.get_random_ingredients()
|
||||
['shells', 'gorgonzola', 'parsley']
|
||||
|
||||
1
packsim/__init__.py
Normal file
1
packsim/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
__all__ = ["simulation"]
|
||||
@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
import sys, numpy as np
|
||||
|
||||
@ -5,7 +5,7 @@ from shutil import which
|
||||
from pathlib import Path
|
||||
from simulation import Diagram, Flow, Search, Shrink
|
||||
|
||||
from packsim_core import RadialTEnergy
|
||||
from _packsim import RadialTEnergy
|
||||
|
||||
dia_presets = {
|
||||
"animate": [["voronoi"]],
|
||||
@ -91,6 +91,10 @@ def config_sim(args):
|
||||
|
||||
points = None
|
||||
if "points" in dmn_params:
|
||||
if type(dmn_params["points"]) is str:
|
||||
with open(Path(dmn_params["points"]), 'rb') as f:
|
||||
points = np.load(f)
|
||||
else:
|
||||
points = np.asarray(dmn_params["points"])
|
||||
|
||||
check_params(sim_params, ["mode", "step_size", "threshold", "save_sim"], {
|
||||
@ -122,8 +122,8 @@ class Diagram():
|
||||
ax.title.set_text('Energy vs. Time')
|
||||
max_value = round(self.sim[0].energy)
|
||||
min_value = round(self.sim[-1].energy)
|
||||
diff = max_value-min_value
|
||||
ax.set_yticks(np.arange(int(min_value-diff/5), int(max_value+diff/5), diff/25))
|
||||
#diff = max_value-min_value
|
||||
#ax.set_yticks(np.arange(int(min_value-diff/5), int(max_value+diff/5), diff/25))
|
||||
ax.set_xlabel("Iterations")
|
||||
ax.set_ylabel("Energy")
|
||||
ax.grid()
|
||||
@ -494,7 +494,7 @@ class Simulation:
|
||||
if filename is None:
|
||||
path = gen_filepath(self, "sim", "simulations")
|
||||
else:
|
||||
path = f'new_simulations/{filename}.sim'
|
||||
path = f'simulations/{filename}.sim'
|
||||
|
||||
all_info = []
|
||||
for frame in self.frames:
|
||||
@ -521,8 +521,11 @@ class Simulation:
|
||||
frames = []
|
||||
with open(filename, 'rb') as data:
|
||||
all_info, sim_class = pickle.load(data)
|
||||
if type(sim_class) == str:
|
||||
sim_class = {"flow": Flow, "search": Search, "shrink": Shrink}[sim_class]
|
||||
sim = sim_class(*all_info[0]["params"], all_info[0]["energy"], 0,0,0,0)
|
||||
|
||||
|
||||
sim = sim_class(*all_info[0]["params"], "radial-t", 0,0)
|
||||
for frame_info in all_info:
|
||||
frames.append(sim.energy(*frame_info["params"], frame_info["arr"]))
|
||||
#frames[-1].stats = frame_info["stats"]
|
||||
@ -575,35 +578,37 @@ class Flow(Simulation):
|
||||
print(f'Find - N = {self.n}, R = {self.r}, {self.w} X {self.h}', flush=True)
|
||||
i, grad_norm = 0, float('inf')
|
||||
|
||||
trial = 2
|
||||
while grad_norm > self.thres: # Get to threshold.
|
||||
# Iterate and generate next frame using RK-3
|
||||
#trial = 2
|
||||
j = 1
|
||||
while i*self.step_size <= 500:
|
||||
#while grad_norm > self.thres: # Get to threshold.
|
||||
# Iterate and generate next frame using RK-2
|
||||
start = timer()
|
||||
change, grad = self.frames[i].iterate(self.step_size)
|
||||
change, grad = self.frames[-1].iterate(self.step_size)
|
||||
new_frame = self.energy(self.n, self.w, self.h, self.r,
|
||||
self.frames[i].add_sites(change))
|
||||
grad_norm = np.sum(np.absolute(grad))/self.n
|
||||
self.frames[-1].add_sites(change))
|
||||
grad_norm = np.linalg.norm(grad)
|
||||
end = timer()
|
||||
|
||||
if new_frame.energy < self.frames[i].energy: # If energy decreases.
|
||||
if trial < 20: # Try increasing step size for 10 times.
|
||||
factor = 1 + .1**trial
|
||||
# if new_frame.energy < self.frames[i].energy: # If energy decreases.
|
||||
# if trial < 20: # Try increasing step size for 10 times.
|
||||
# factor = 1 + .1**trial
|
||||
|
||||
test_frame = self.energy(self.n, self.w, self.h, self.r,
|
||||
self.frames[i].add_sites(change*factor))
|
||||
# If increased step has less energy than original step.
|
||||
if test_frame.energy < new_frame.energy:
|
||||
self.step_size *= factor
|
||||
trial = max(2, trial-1)
|
||||
new_frame = test_frame
|
||||
else: # Otherwise, increases trials, and use original.
|
||||
trial += 1
|
||||
else: # Step size too large, decrease and reset trial counter.
|
||||
trial = 2
|
||||
shrink_factor = 1.5
|
||||
new_frame = self.energy(self.n, self.w, self.h, self.r,
|
||||
self.frames[i].add_sites(change/shrink_factor))
|
||||
self.step_size /= shrink_factor
|
||||
# test_frame = self.energy(self.n, self.w, self.h, self.r,
|
||||
# self.frames[i].add_sites(change*factor))
|
||||
# # If increased step has less energy than original step.
|
||||
# if test_frame.energy < new_frame.energy:
|
||||
# self.step_size *= factor
|
||||
# trial = max(2, trial-1)
|
||||
# new_frame = test_frame
|
||||
# else: # Otherwise, increases trials, and use original.
|
||||
# trial += 1
|
||||
# else: # Step size too large, decrease and reset trial counter.
|
||||
# trial = 2
|
||||
# shrink_factor = 1.5
|
||||
# new_frame = self.energy(self.n, self.w, self.h, self.r,
|
||||
# self.frames[i].add_sites(change/shrink_factor))
|
||||
# self.step_size /= shrink_factor
|
||||
|
||||
#self.step_size *= abs(.01/np.linalg.norm(error))**(1/3)
|
||||
#self.step_size = max(10e-4, self.step_size)
|
||||
@ -611,10 +616,17 @@ class Flow(Simulation):
|
||||
|
||||
i += 1
|
||||
if(log and i % log_steps == 0):
|
||||
print(f'Iteration: {i:05} | Energy: {self.frames[i].energy: .5f}' + \
|
||||
print(f'Iteration: {i:05} | Energy: {self.frames[-1].energy: .5f}' + \
|
||||
f' | Gradient: {grad_norm:.8f} | Step: {self.step_size: .5f} | ' + \
|
||||
f'Time: {end-start: .3f}', flush=True)
|
||||
|
||||
if i % 5000 == 0:
|
||||
new_frames = [self.frames[-1]]
|
||||
self.frames = self.frames[:-1]
|
||||
self.save(f"N200-{self.step_size}-part{j}")
|
||||
j += 1
|
||||
self.frames = new_frames
|
||||
|
||||
|
||||
class Search(Simulation):
|
||||
"""
|
||||
@ -677,8 +689,6 @@ class Search(Simulation):
|
||||
if zero_eigs != 2:
|
||||
print("WARNING, 0 EIGS NOT 2", zero_eigs)
|
||||
|
||||
if i == self.iter-1:
|
||||
break
|
||||
|
||||
if len(ns) <= 2:
|
||||
new_sites = dim * np.random.random_sample((self.n, 2))
|
||||
@ -687,9 +697,12 @@ class Search(Simulation):
|
||||
new_sites = self.frames[i].add_sites(self.kernel_step*vec.reshape((self.n, 2)))
|
||||
|
||||
new_sites += (center - new_sites[fixed]) % dim # Offset
|
||||
|
||||
if i < self.iter-1:
|
||||
self.frames.append(None)
|
||||
|
||||
|
||||
|
||||
class Shrink(Simulation):
|
||||
"""
|
||||
Class for traversing to other equilibriums from an equilbrium.
|
||||
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 42", "wheel", "Cython", "numpy"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
36
setup.cfg
Normal file
36
setup.cfg
Normal file
@ -0,0 +1,36 @@
|
||||
[metadata]
|
||||
name = packsim-ksjdragon
|
||||
version = 0.1
|
||||
author = Kenneth Jao
|
||||
author_email = ksjdragon@gmail.com
|
||||
description = PackSim is a Python package that handles the simulations for Voronoi cells undergoing a gradient flow.
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/ksjdragon/packsim
|
||||
project_urls =
|
||||
Bug Tracker = https://github.com/ksjdragon/packsim/issues
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
||||
Operating System :: OS Independent
|
||||
|
||||
[options]
|
||||
zip_safe = False
|
||||
package_dir =
|
||||
= packsim
|
||||
packages = find:
|
||||
python_requires = >= 3.8
|
||||
install_requires =
|
||||
numpy == 1.21.2
|
||||
scipy == 1.7.1
|
||||
matplotlib == 3.4.3
|
||||
|
||||
[options.packages.find]
|
||||
where = packsim
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
simulate = packsim.scripts.simulate:main
|
||||
shrink_energy_comparison = packsim.scripts.shrink_energy_comparison:main
|
||||
@ -2,19 +2,17 @@ from setuptools import Extension, setup
|
||||
from Cython.Build import cythonize
|
||||
import numpy
|
||||
|
||||
MODULE_NAME = "packsim_core"
|
||||
|
||||
ext_modules = [
|
||||
Extension(
|
||||
MODULE_NAME,
|
||||
[f'{MODULE_NAME}.pyx'],
|
||||
"_packsim",
|
||||
["src/_packsim.pyx"],
|
||||
extra_compile_args=['-fopenmp'],
|
||||
extra_link_args=['-fopenmp']
|
||||
)
|
||||
]
|
||||
|
||||
setup(
|
||||
name=MODULE_NAME,
|
||||
name="packsim",
|
||||
ext_modules = cythonize(ext_modules, compiler_directives={
|
||||
'language_level': 3, 'boundscheck' : False, 'wraparound': False, 'cdivision' : True
|
||||
}),
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ from cpython cimport array
|
||||
from libc.stdlib cimport malloc, realloc, calloc, free
|
||||
from libc.math cimport isnan, NAN, pi as PI, M_PI_2 as PI_2, \
|
||||
sqrt, log, sin, cos, tan, acos, fabs
|
||||
from packsim_core cimport INT_T, FLOAT_T, Init, IArray, FArray, BitSet, Vector2D, Matrix2x2, \
|
||||
from _packsim cimport INT_T, FLOAT_T, Init, IArray, FArray, BitSet, Vector2D, Matrix2x2, \
|
||||
VectorSelfOps, VectorCopyOps, MatrixSelfOps, MatrixCopyOps, \
|
||||
SiteCacheMap, EdgeCacheMap, VoronoiInfo, Site, HalfEdge
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from packsim_core cimport SiteCacheMap, EdgeCacheMap, VoronoiInfo, Site, HalfEdge
|
||||
from _packsim cimport SiteCacheMap, EdgeCacheMap, VoronoiInfo, Site, HalfEdge
|
||||
|
||||
#### Constants ####
|
||||
|
||||
@ -405,12 +405,6 @@ cdef class VoronoiContainer:
|
||||
self.common_cache()
|
||||
self.precompute()
|
||||
self.calc_grad()
|
||||
self.get_statistics()
|
||||
|
||||
# #print(np.asarray(self.site_cache[0]))
|
||||
# print(np.asarray(self.edges[:6]))
|
||||
# #print(np.asarray(self.edge_cache[:6]))
|
||||
# print(self.gradient)
|
||||
|
||||
|
||||
cdef void calculate_voronoi(VoronoiContainer self,
|
||||
@ -622,10 +616,7 @@ cdef class VoronoiContainer:
|
||||
cache = self.site_cache[:self.n, :]
|
||||
|
||||
self.stats["site_areas"] = np.asarray(cache[:, SITE_CACHE_MAP.iarea])
|
||||
#edge_count = self.sites[:, 2]np.empty((self.n,))
|
||||
# for i in range(self.n):
|
||||
# edge_count[i] = len(self.vor_data.regions[self.vor_data.point_region[i]])
|
||||
self.stats["site_edge_count"] = self.sites[:self.n, 2]
|
||||
self.stats["site_edge_count"] = np.asarray(self.sites[:self.n, 2])
|
||||
|
||||
self.stats["site_isos"] = np.asarray(cache[:, SITE_CACHE_MAP.iisoparam])
|
||||
self.stats["site_energies"] = np.asarray(cache[:, SITE_CACHE_MAP.ienergy])
|
||||
@ -661,21 +652,29 @@ cdef class VoronoiContainer:
|
||||
|
||||
def iterate(self, FLOAT_T step):
|
||||
k1 = self.gradient
|
||||
|
||||
k2 = self.__class__(self.n, self.w, self.h, self.r,
|
||||
self.add_sites(step*k1/2)
|
||||
self.add_sites(step*k1)
|
||||
).gradient
|
||||
|
||||
lower = step*(-k1+ 2*k2)
|
||||
k3 = self.__class__(self.n, self.w, self.h, self.r,
|
||||
self.add_sites(lower)
|
||||
).gradient
|
||||
return (step/2)*(k1+k2), k1
|
||||
# k1 = self.gradient
|
||||
|
||||
# k2 = self.__class__(self.n, self.w, self.h, self.r,
|
||||
# self.add_sites(step*k1/2)
|
||||
# ).gradient
|
||||
|
||||
# lower = step*(-k1+ 2*k2)
|
||||
# k3 = self.__class__(self.n, self.w, self.h, self.r,
|
||||
# self.add_sites(lower)
|
||||
# ).gradient
|
||||
|
||||
# higher = (step/6)*(k1+2*k2+k3)
|
||||
|
||||
|
||||
higher = (step/6)*(k1+2*k2+k3)
|
||||
#new_sites = self.add_sites(higher)
|
||||
#error = higher - lower
|
||||
|
||||
return higher, k1
|
||||
#return higher, k1
|
||||
|
||||
def hessian(self, d: float) -> np.ndarray:
|
||||
"""
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
{
|
||||
"domain": {
|
||||
"n_objects": 100,
|
||||
"width": 10.0,
|
||||
"height": 10.0,
|
||||
"natural_radius": 4.0,
|
||||
"energy": "radial-t"
|
||||
},
|
||||
"simulation": {
|
||||
"mode": "flow",
|
||||
"step_size": 0.05,
|
||||
"threshold": 0.00001,
|
||||
"save_sim": true
|
||||
},
|
||||
"diagram": {
|
||||
"filetype": "mp4",
|
||||
"figures": "energy"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user