├── .github
└── workflows
│ └── python-publish.yml
├── .gitignore
├── .readthedocs.yml
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── docs
├── Makefile
├── conf.py
├── index.rst
├── make.bat
├── ode_example
│ ├── example_rob.png
│ ├── example_rob_no_noise.png
│ ├── fit_after_pso.png
│ ├── fit_before_pso.png
│ └── ode_example.rst
├── requirements.txt
└── training_earm.gif
├── examples
├── __init__.py
├── data
│ └── EC-RP_IMS-RP_IC-RP_data_for_models.csv
├── example_earm.py
├── robertson_example.py
└── run_schogl_example_ssa.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── simplepso
├── __init__.py
├── logging.py
├── pso.py
└── tests
├── __init__.py
├── test_pso_construction.py
└── test_robertson.py
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | # This workflow uses actions that are not certified by GitHub.
5 | # They are provided by a third-party and are governed by
6 | # separate terms of service, privacy policy, and support
7 | # documentation.
8 |
9 | name: Upload Python Package
10 |
11 | on:
12 | release:
13 | types: [published]
14 |
15 | jobs:
16 | deploy:
17 | environment: release
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Set up Python
23 | uses: actions/setup-python@v2
24 | with:
25 | python-version: '3.x'
26 | - name: Install dependencies
27 | run: |
28 | python -m pip install --upgrade pip
29 | pip install setuptools wheel twine
30 | pip install -r requirements.txt
31 | - name: Build and publish
32 | env:
33 | TWINE_USERNAME: __token__
34 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
35 | run: |
36 | python setup.py sdist bdist_wheel
37 | twine upload --repository pypi dist/*
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Python template
2 | # Byte-compiled / optimized / DLL files
3 | __pycache__/
4 | *.py[cod]
5 | *$py.class
6 | *.pyc
7 | .idea
8 | # C extensions
9 | *.so
10 | docs/_build
11 | # Distribution / packaging
12 | .Python
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *,cover
48 | .hypothesis/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 |
58 | # Flask instance folder
59 | instance/
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # IPython Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # dotenv
80 | .env
81 |
82 | # virtualenv
83 | venv/
84 | ENV/
85 |
86 | # Spyder project settings
87 | .spyderproject
88 |
89 | # Rope project settings
90 | .ropeproject
91 |
92 | # Created by .ignore support plugin (hsz.mobi)
93 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Build documentation in the docs/ directory with Sphinx
9 | sphinx:
10 | configuration: docs/conf.py
11 |
12 | # Build documentation with MkDocs
13 | #mkdocs:
14 | # configuration: mkdocs.yml
15 |
16 | # Optionally build your docs in additional formats such as PDF
17 | formats:
18 | - pdf
19 |
20 | # Optionally set the version of Python and requirements required to build your docs
21 | python:
22 | version: 3.7
23 | install:
24 | - requirements: docs/requirements.txt
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, SimplePSO Developers.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in
13 | the documentation and/or other materials provided with the
14 | distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ANY KIND,
27 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
30 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include examples/data/*
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://zenodo.org/badge/latestdoi/20464524)
2 | [](https://simplepso.readthedocs.io/en/latest/?badge=latest)
3 | # Simple interface to optimize models using Particle Swarm optimization
4 |
5 | Training stochastic or determinisic models encoded in PySB? Looking for an easy to use, effecient Python package? Look no further.
6 |
7 | PySB examples can be found in simplepso/examples
8 |
9 |
10 |
11 | You can email me at james.ch.pino@gmail.com for any questions or comments.
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help
18 | help:
19 | @echo "Please use \`make ' where is one of"
20 | @echo " html to make standalone HTML files"
21 | @echo " dirhtml to make HTML files named index.html in directories"
22 | @echo " singlehtml to make a single large HTML file"
23 | @echo " pickle to make pickle files"
24 | @echo " json to make JSON files"
25 | @echo " htmlhelp to make HTML files and a HTML help project"
26 | @echo " qthelp to make HTML files and a qthelp project"
27 | @echo " applehelp to make an Apple Help Book"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " epub3 to make an epub3"
31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
32 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
34 | @echo " text to make text files"
35 | @echo " man to make manual pages"
36 | @echo " texinfo to make Texinfo files"
37 | @echo " info to make Texinfo files and run them through makeinfo"
38 | @echo " gettext to make PO message catalogs"
39 | @echo " changes to make an overview of all changed/added/deprecated items"
40 | @echo " xml to make Docutils-native XML files"
41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
42 | @echo " linkcheck to check all external links for integrity"
43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
44 | @echo " coverage to run coverage check of the documentation (if enabled)"
45 | @echo " dummy to check syntax errors of document sources"
46 |
47 | .PHONY: clean
48 | clean:
49 | rm -rf $(BUILDDIR)/*
50 |
51 | .PHONY: html
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | .PHONY: dirhtml
58 | dirhtml:
59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
60 | @echo
61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
62 |
63 | .PHONY: singlehtml
64 | singlehtml:
65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
66 | @echo
67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
68 |
69 | .PHONY: pickle
70 | pickle:
71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
72 | @echo
73 | @echo "Build finished; now you can process the pickle files."
74 |
75 | .PHONY: json
76 | json:
77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
78 | @echo
79 | @echo "Build finished; now you can process the JSON files."
80 |
81 | .PHONY: htmlhelp
82 | htmlhelp:
83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
84 | @echo
85 | @echo "Build finished; now you can run HTML Help Workshop with the" \
86 | ".hhp project file in $(BUILDDIR)/htmlhelp."
87 |
88 | .PHONY: qthelp
89 | qthelp:
90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
91 | @echo
92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/simplepso.qhcp"
95 | @echo "To view the help file:"
96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/simplepso.qhc"
97 |
98 | .PHONY: applehelp
99 | applehelp:
100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
101 | @echo
102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
103 | @echo "N.B. You won't be able to view it unless you put it in" \
104 | "~/Library/Documentation/Help or install it in your application" \
105 | "bundle."
106 |
107 | .PHONY: devhelp
108 | devhelp:
109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
110 | @echo
111 | @echo "Build finished."
112 | @echo "To view the help file:"
113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/simplepso"
114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/simplepso"
115 | @echo "# devhelp"
116 |
117 | .PHONY: epub
118 | epub:
119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
120 | @echo
121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
122 |
123 | .PHONY: epub3
124 | epub3:
125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
126 | @echo
127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
128 |
129 | .PHONY: latex
130 | latex:
131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
132 | @echo
133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
135 | "(use \`make latexpdf' here to do that automatically)."
136 |
137 | .PHONY: latexpdf
138 | latexpdf:
139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
140 | @echo "Running LaTeX files through pdflatex..."
141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
143 |
144 | .PHONY: latexpdfja
145 | latexpdfja:
146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
147 | @echo "Running LaTeX files through platex and dvipdfmx..."
148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
150 |
151 | .PHONY: text
152 | text:
153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
154 | @echo
155 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
156 |
157 | .PHONY: man
158 | man:
159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
160 | @echo
161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
162 |
163 | .PHONY: texinfo
164 | texinfo:
165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
166 | @echo
167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
168 | @echo "Run \`make' in that directory to run these through makeinfo" \
169 | "(use \`make info' here to do that automatically)."
170 |
171 | .PHONY: info
172 | info:
173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
174 | @echo "Running Texinfo files through makeinfo..."
175 | make -C $(BUILDDIR)/texinfo info
176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
177 |
178 | .PHONY: gettext
179 | gettext:
180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
181 | @echo
182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
183 |
184 | .PHONY: changes
185 | changes:
186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
187 | @echo
188 | @echo "The overview file is in $(BUILDDIR)/changes."
189 |
190 | .PHONY: linkcheck
191 | linkcheck:
192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
193 | @echo
194 | @echo "Link check complete; look for any errors in the above output " \
195 | "or in $(BUILDDIR)/linkcheck/output.txt."
196 |
197 | .PHONY: doctest
198 | doctest:
199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
200 | @echo "Testing of doctests in the sources finished, look at the " \
201 | "results in $(BUILDDIR)/doctest/output.txt."
202 |
203 | .PHONY: coverage
204 | coverage:
205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
206 | @echo "Testing of coverage in the sources finished, look at the " \
207 | "results in $(BUILDDIR)/coverage/python.txt."
208 |
209 | .PHONY: xml
210 | xml:
211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
212 | @echo
213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
214 |
215 | .PHONY: pseudoxml
216 | pseudoxml:
217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
218 | @echo
219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
220 |
221 | .PHONY: dummy
222 | dummy:
223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
224 | @echo
225 | @echo "Build finished. Dummy builder generates no files."
226 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # simplepso documentation build configuration file, created by
4 | # sphinx-quickstart on Thu Jun 2 15:57:36 2016.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | # If extensions (or modules to document with autodoc) are in another directory,
16 | # add these directories to sys.path here. If the directory is relative to the
17 | # documentation root, use os.path.abspath to make it absolute, like shown here.
18 | #
19 | import os
20 | import sys
21 |
22 | import sphinx_bootstrap_theme
23 |
24 | sys.path.append(os.path.abspath('..'))
25 | # -- General configuration ------------------------------------------------
26 |
27 | # If your documentation needs a minimal Sphinx version, state it here.
28 | #
29 | needs_sphinx = '3.0'
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = [
35 | 'sphinx.ext.autodoc',
36 | 'sphinx.ext.doctest',
37 | 'sphinx.ext.coverage',
38 | 'sphinx.ext.imgmath',
39 | 'sphinx.ext.ifconfig',
40 | 'sphinx.ext.viewcode',
41 | 'sphinx.ext.autosummary',
42 | 'sphinx.ext.todo',
43 | 'sphinx.ext.inheritance_diagram',
44 |
45 | 'numpydoc',
46 |
47 | ]
48 | autodoc_default_options = {
49 | 'members': True,
50 | 'member-order': 'groupwise',
51 | 'undoc-members': True,
52 | 'special-members': '__init__',
53 | }
54 | # Napoleon settings
55 | napoleon_google_docstring = False
56 | napoleon_numpy_docstring = True
57 | napoleon_include_init_with_doc = False
58 | napoleon_include_private_with_doc = False
59 | napoleon_include_special_with_doc = False
60 | napoleon_use_admonition_for_examples = False
61 | napoleon_use_admonition_for_notes = False
62 | napoleon_use_admonition_for_references = False
63 | napoleon_use_ivar = False
64 | napoleon_use_param = True
65 | napoleon_use_rtype = True
66 | napoleon_use_keyword = True
67 |
68 | numpydoc_show_class_members = False
69 | # Add any paths that contain templates here, relative to this directory.
70 | templates_path = ['_templates']
71 |
72 | # The suffix(es) of source filenames.
73 | # You can specify multiple suffix as a list of string:
74 | #
75 | # source_suffix = ['.rst', '.md']
76 | source_suffix = '.rst'
77 |
78 | # The encoding of source files.
79 | #
80 | # source_encoding = 'utf-8-sig'
81 |
82 | # The master toctree document.
83 | master_doc = 'index'
84 |
85 | # General information about the project.
86 | project = u'simplepso'
87 | copyright = u'2016, James C. Pino'
88 | author = u'James C. Pino'
89 |
90 | # The version info for the project you're documenting, acts as replacement for
91 | # |version| and |release|, also used in various other places throughout the
92 | # built documents.
93 | #
94 | # The short X.Y version.
95 | version = '1.1.5'
96 | # The full version, including alpha/beta/rc tags.
97 | release = '1.1.5'
98 |
99 | # The language for content autogenerated by Sphinx. Refer to documentation
100 | # for a list of supported languages.
101 | #
102 | # This is also used if you do content translation via gettext catalogs.
103 | # Usually you set "language" from the command line for these cases.
104 | language = None
105 |
106 | # There are two options for replacing |today|: either, you set today to some
107 | # non-false value, then it is used:
108 | #
109 | # today = ''
110 | #
111 | # Else, today_fmt is used as the format for a strftime call.
112 | #
113 | # today_fmt = '%B %d, %Y'
114 |
115 | # List of patterns, relative to source directory, that match files and
116 | # directories to ignore when looking for source files.
117 | # This patterns also effect to html_static_path and html_extra_path
118 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
119 |
120 | # The reST default role (used for this markup: `text`) to use for all
121 | # documents.
122 | #
123 | # default_role = None
124 |
125 | # If true, '()' will be appended to :func: etc. cross-reference text.
126 | #
127 | # add_function_parentheses = True
128 |
129 | # If true, the current module name will be prepended to all description
130 | # unit titles (such as .. function::).
131 | #
132 | # add_module_names = True
133 |
134 | # If true, sectionauthor and moduleauthor directives will be shown in the
135 | # output. They are ignored by default.
136 | #
137 | # show_authors = False
138 |
139 | # The name of the Pygments (syntax highlighting) style to use.
140 | pygments_style = 'sphinx'
141 |
142 | # A list of ignored prefixes for module index sorting.
143 | # modindex_common_prefix = []
144 |
145 | # If true, keep warnings as "system message" paragraphs in the built documents.
146 | # keep_warnings = False
147 |
148 | # If true, `todo` and `todoList` produce output, else they produce nothing.
149 | todo_include_todos = True
150 |
151 | # -- Options for HTML output ----------------------------------------------
152 |
153 | # The theme to use for HTML and HTML Help pages. See the documentation for
154 | # a list of builtin themes.
155 | #
156 | html_theme = 'default'
157 | html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
158 | # Theme options are theme-specific and customize the look and feel of a theme
159 | # further. For a list of options available for each theme, see the
160 | # documentation.
161 | #
162 | # html_theme_options = {}
163 |
164 | # Add any paths that contain custom themes here, relative to this directory.
165 | # html_theme_path = []
166 |
167 | # The name for this set of Sphinx documents.
168 | # " v documentation" by default.
169 | #
170 | # html_title = u'simplepso v0.0.1'
171 |
172 | # A shorter title for the navigation bar. Default is the same as html_title.
173 | #
174 | # html_short_title = None
175 |
176 | # The name of an image file (relative to this directory) to place at the top
177 | # of the sidebar.
178 | #
179 | # html_logo = None
180 |
181 | # The name of an image file (relative to this directory) to use as a favicon of
182 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
183 | # pixels large.
184 | #
185 | # html_favicon = None
186 |
187 | # Add any paths that contain custom static files (such as style sheets) here,
188 | # relative to this directory. They are copied after the builtin static files,
189 | # so a file named "default.css" will overwrite the builtin "default.css".
190 |
191 | # Add any extra paths that contain custom files (such as robots.txt or
192 | # .htaccess) here, relative to this directory. These files are copied
193 | # directly to the root of the documentation.
194 | #
195 | # html_extra_path = []
196 |
197 | # If not None, a 'Last updated on:' timestamp is inserted at every page
198 | # bottom, using the given strftime format.
199 | # The empty string is equivalent to '%b %d, %Y'.
200 | #
201 | # html_last_updated_fmt = None
202 |
203 | # If true, SmartyPants will be used to convert quotes and dashes to
204 | # typographically correct entities.
205 | #
206 | # html_use_smartypants = True
207 |
208 | # Custom sidebar templates, maps document names to template names.
209 | #
210 | # html_sidebars = {}
211 |
212 | # Additional templates that should be rendered to pages, maps page names to
213 | # template names.
214 | #
215 | # html_additional_pages = {}
216 |
217 | # If false, no module index is generated.
218 | #
219 | # html_domain_indices = True
220 |
221 | # If false, no index is generated.
222 | #
223 | # html_use_index = True
224 |
225 | # If true, the index is split into individual pages for each letter.
226 | #
227 | # html_split_index = False
228 |
229 | # If true, links to the reST sources are added to the pages.
230 | #
231 | # html_show_sourcelink = True
232 |
233 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
234 | #
235 | # html_show_sphinx = True
236 |
237 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
238 | #
239 | # html_show_copyright = True
240 |
241 | # If true, an OpenSearch description file will be output, and all pages will
242 | # contain a tag referring to it. The value of this option must be the
243 | # base URL from which the finished HTML is served.
244 | #
245 | # html_use_opensearch = ''
246 |
247 | # This is the file name suffix for HTML files (e.g. ".xhtml").
248 | # html_file_suffix = None
249 |
250 | # Language to be used for generating the HTML full-text search index.
251 | # Sphinx supports the following languages:
252 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
253 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
254 | #
255 | # html_search_language = 'en'
256 |
257 | # A dictionary with options for the search language support, empty by default.
258 | # 'ja' uses this config value.
259 | # 'zh' user can custom change `jieba` dictionary path.
260 | #
261 | # html_search_options = {'type': 'default'}
262 |
263 | # The name of a javascript file (relative to the configuration directory) that
264 | # implements a search results scorer. If empty, the default will be used.
265 | #
266 | # html_search_scorer = 'scorer.js'
267 |
268 | # Output file base name for HTML help builder.
269 | htmlhelp_basename = 'simplepsodoc'
270 |
271 | # -- Options for LaTeX output ---------------------------------------------
272 |
273 | latex_elements = {
274 | # The paper size ('letterpaper' or 'a4paper').
275 | #
276 | # 'papersize': 'letterpaper',
277 |
278 | # The font size ('10pt', '11pt' or '12pt').
279 | #
280 | # 'pointsize': '10pt',
281 |
282 | # Additional stuff for the LaTeX preamble.
283 | #
284 | # 'preamble': '',
285 |
286 | # Latex figure (float) alignment
287 | #
288 | # 'figure_align': 'htbp',
289 | }
290 |
291 | # Grouping the document tree into LaTeX files. List of tuples
292 | # (source start file, target name, title,
293 | # author, documentclass [howto, manual, or own class]).
294 | latex_documents = [
295 | (master_doc, 'simplepso.tex', u'simplepso Documentation',
296 | u'James C. Pino', 'manual'),
297 | ]
298 |
299 | # The name of an image file (relative to this directory) to place at the top of
300 | # the title page.
301 | #
302 | # latex_logo = None
303 |
304 | # For "manual" documents, if this is true, then toplevel headings are parts,
305 | # not chapters.
306 | #
307 | # latex_use_parts = False
308 |
309 | # If true, show page references after internal links.
310 | #
311 | # latex_show_pagerefs = False
312 |
313 | # If true, show URL addresses after external links.
314 | #
315 | # latex_show_urls = False
316 |
317 | # Documents to append as an appendix to all manuals.
318 | #
319 | # latex_appendices = []
320 |
321 | # If false, no module index is generated.
322 | #
323 | # latex_domain_indices = True
324 |
325 |
326 | # -- Options for manual page output ---------------------------------------
327 |
328 | # One entry per manual page. List of tuples
329 | # (source start file, name, description, authors, manual section).
330 | man_pages = [
331 | (master_doc, 'simplepso', u'simplepso Documentation',
332 | [author], 1)
333 | ]
334 |
335 | # If true, show URL addresses after external links.
336 | #
337 | # man_show_urls = False
338 |
339 |
340 | # -- Options for Texinfo output -------------------------------------------
341 |
342 | # Grouping the document tree into Texinfo files. List of tuples
343 | # (source start file, target name, title, author,
344 | # dir menu entry, description, category)
345 | texinfo_documents = [
346 | (master_doc, 'simplepso', u'simplepso Documentation',
347 | author, 'simplepso', 'One line description of project.',
348 | 'Miscellaneous'),
349 | ]
350 |
351 | # Documents to append as an appendix to all manuals.
352 | #
353 | # texinfo_appendices = []
354 |
355 | # If false, no module index is generated.
356 | #
357 | # texinfo_domain_indices = True
358 |
359 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
360 | #
361 | # texinfo_show_urls = 'footnote'
362 |
363 | # If true, do not generate a @detailmenu in the "Top" node's menu.
364 | #
365 | # texinfo_no_detailmenu = False
366 |
367 |
368 | # Example configuration for intersphinx: refer to the Python standard library.
369 | intersphinx_mapping = {'https://docs.python.org/': None}
370 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to simplepso's documentation!
2 | =====================================
3 |
4 | simplePSO is a full implementation of the PSO algorithm. It requires an
5 | objective function to minimize that returns a scalar value, starting position,
6 | bounds on how far you want to search around the starting position, and max
7 | speeds each particle can travel. Then, you set the number of particles in the
8 | swarm, the number of iterations to run, and the number of processors you want
9 | to use. That's it!
10 |
11 | .. code-block:: python
12 |
13 | # Here we initial the class
14 | # We must provide the cost function and a starting value
15 | optimizer = PSO(start=start_position)
16 |
17 | # We also must set bounds of the parameter space
18 | optimizer.set_bounds(parameter_range)
19 | # set speed limits on updating particles
20 | optimizer.set_speed(speed_min, speed_max)
21 |
22 | # Now we run the pso algorithm
23 | optimizer.run(num_particles, num_iterations, num_processors,
24 | cost_function)
25 | # get best parameter fit
26 | best = optimizer.best
27 | # parameter set
28 | best.pos
29 | # fitness of parameter set
30 | best.fitness
31 |
32 | We provide two examples in the examples
33 | `folder `_ training
34 | PySB models to time course data using ODE simulations and
35 | `one `_
36 | example training a model to a distribution of species concentrations using
37 | stochastic simulations.
38 |
39 | .. image:: training_earm.gif
40 | :width: 400
41 |
42 | .. toctree::
43 | :maxdepth: 2
44 |
45 | ode_example/ode_example.rst
46 |
47 | .. autoclass:: simplepso.pso.PSO
48 | :members:
49 |
50 |
51 | Indices and tables
52 | ==================
53 |
54 | * :ref:`genindex`
55 | * :ref:`modindex`
56 | * :ref:`search`
57 |
58 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. epub3 to make an epub3
31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
32 | echo. text to make text files
33 | echo. man to make manual pages
34 | echo. texinfo to make Texinfo files
35 | echo. gettext to make PO message catalogs
36 | echo. changes to make an overview over all changed/added/deprecated items
37 | echo. xml to make Docutils-native XML files
38 | echo. pseudoxml to make pseudoxml-XML files for display purposes
39 | echo. linkcheck to check all external links for integrity
40 | echo. doctest to run all doctests embedded in the documentation if enabled
41 | echo. coverage to run coverage check of the documentation if enabled
42 | echo. dummy to check syntax errors of document sources
43 | goto end
44 | )
45 |
46 | if "%1" == "clean" (
47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
48 | del /q /s %BUILDDIR%\*
49 | goto end
50 | )
51 |
52 |
53 | REM Check if sphinx-build is available and fallback to Python version if any
54 | %SPHINXBUILD% 1>NUL 2>NUL
55 | if errorlevel 9009 goto sphinx_python
56 | goto sphinx_ok
57 |
58 | :sphinx_python
59 |
60 | set SPHINXBUILD=python -m sphinx.__init__
61 | %SPHINXBUILD% 2> nul
62 | if errorlevel 9009 (
63 | echo.
64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
65 | echo.installed, then set the SPHINXBUILD environment variable to point
66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
67 | echo.may add the Sphinx directory to PATH.
68 | echo.
69 | echo.If you don't have Sphinx installed, grab it from
70 | echo.http://sphinx-doc.org/
71 | exit /b 1
72 | )
73 |
74 | :sphinx_ok
75 |
76 |
77 | if "%1" == "html" (
78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
79 | if errorlevel 1 exit /b 1
80 | echo.
81 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
82 | goto end
83 | )
84 |
85 | if "%1" == "dirhtml" (
86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
87 | if errorlevel 1 exit /b 1
88 | echo.
89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
90 | goto end
91 | )
92 |
93 | if "%1" == "singlehtml" (
94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
95 | if errorlevel 1 exit /b 1
96 | echo.
97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
98 | goto end
99 | )
100 |
101 | if "%1" == "pickle" (
102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
103 | if errorlevel 1 exit /b 1
104 | echo.
105 | echo.Build finished; now you can process the pickle files.
106 | goto end
107 | )
108 |
109 | if "%1" == "json" (
110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
111 | if errorlevel 1 exit /b 1
112 | echo.
113 | echo.Build finished; now you can process the JSON files.
114 | goto end
115 | )
116 |
117 | if "%1" == "htmlhelp" (
118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
119 | if errorlevel 1 exit /b 1
120 | echo.
121 | echo.Build finished; now you can run HTML Help Workshop with the ^
122 | .hhp project file in %BUILDDIR%/htmlhelp.
123 | goto end
124 | )
125 |
126 | if "%1" == "qthelp" (
127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
128 | if errorlevel 1 exit /b 1
129 | echo.
130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
131 | .qhcp project file in %BUILDDIR%/qthelp, like this:
132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\simplepso.qhcp
133 | echo.To view the help file:
134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\simplepso.ghc
135 | goto end
136 | )
137 |
138 | if "%1" == "devhelp" (
139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
140 | if errorlevel 1 exit /b 1
141 | echo.
142 | echo.Build finished.
143 | goto end
144 | )
145 |
146 | if "%1" == "epub" (
147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
148 | if errorlevel 1 exit /b 1
149 | echo.
150 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
151 | goto end
152 | )
153 |
154 | if "%1" == "epub3" (
155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
156 | if errorlevel 1 exit /b 1
157 | echo.
158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
159 | goto end
160 | )
161 |
162 | if "%1" == "latex" (
163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
164 | if errorlevel 1 exit /b 1
165 | echo.
166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
167 | goto end
168 | )
169 |
170 | if "%1" == "latexpdf" (
171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
172 | cd %BUILDDIR%/latex
173 | make all-pdf
174 | cd %~dp0
175 | echo.
176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
177 | goto end
178 | )
179 |
180 | if "%1" == "latexpdfja" (
181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
182 | cd %BUILDDIR%/latex
183 | make all-pdf-ja
184 | cd %~dp0
185 | echo.
186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
187 | goto end
188 | )
189 |
190 | if "%1" == "text" (
191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
192 | if errorlevel 1 exit /b 1
193 | echo.
194 | echo.Build finished. The text files are in %BUILDDIR%/text.
195 | goto end
196 | )
197 |
198 | if "%1" == "man" (
199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
200 | if errorlevel 1 exit /b 1
201 | echo.
202 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
203 | goto end
204 | )
205 |
206 | if "%1" == "texinfo" (
207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
208 | if errorlevel 1 exit /b 1
209 | echo.
210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
211 | goto end
212 | )
213 |
214 | if "%1" == "gettext" (
215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
216 | if errorlevel 1 exit /b 1
217 | echo.
218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
219 | goto end
220 | )
221 |
222 | if "%1" == "changes" (
223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
224 | if errorlevel 1 exit /b 1
225 | echo.
226 | echo.The overview file is in %BUILDDIR%/changes.
227 | goto end
228 | )
229 |
230 | if "%1" == "linkcheck" (
231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
232 | if errorlevel 1 exit /b 1
233 | echo.
234 | echo.Link check complete; look for any errors in the above output ^
235 | or in %BUILDDIR%/linkcheck/output.txt.
236 | goto end
237 | )
238 |
239 | if "%1" == "doctest" (
240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
241 | if errorlevel 1 exit /b 1
242 | echo.
243 | echo.Testing of doctests in the sources finished, look at the ^
244 | results in %BUILDDIR%/doctest/output.txt.
245 | goto end
246 | )
247 |
248 | if "%1" == "coverage" (
249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
250 | if errorlevel 1 exit /b 1
251 | echo.
252 | echo.Testing of coverage in the sources finished, look at the ^
253 | results in %BUILDDIR%/coverage/python.txt.
254 | goto end
255 | )
256 |
257 | if "%1" == "xml" (
258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
259 | if errorlevel 1 exit /b 1
260 | echo.
261 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
262 | goto end
263 | )
264 |
265 | if "%1" == "pseudoxml" (
266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
267 | if errorlevel 1 exit /b 1
268 | echo.
269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
270 | goto end
271 | )
272 |
273 | if "%1" == "dummy" (
274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
275 | if errorlevel 1 exit /b 1
276 | echo.
277 | echo.Build finished. Dummy builder generates no files.
278 | goto end
279 | )
280 |
281 | :end
282 |
--------------------------------------------------------------------------------
/docs/ode_example/example_rob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoLab-MSM/simplePSO/37e5eb22da920d30780984dcea327686d6eacbfe/docs/ode_example/example_rob.png
--------------------------------------------------------------------------------
/docs/ode_example/example_rob_no_noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoLab-MSM/simplePSO/37e5eb22da920d30780984dcea327686d6eacbfe/docs/ode_example/example_rob_no_noise.png
--------------------------------------------------------------------------------
/docs/ode_example/fit_after_pso.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoLab-MSM/simplePSO/37e5eb22da920d30780984dcea327686d6eacbfe/docs/ode_example/fit_after_pso.png
--------------------------------------------------------------------------------
/docs/ode_example/fit_before_pso.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoLab-MSM/simplePSO/37e5eb22da920d30780984dcea327686d6eacbfe/docs/ode_example/fit_before_pso.png
--------------------------------------------------------------------------------
/docs/ode_example/ode_example.rst:
--------------------------------------------------------------------------------
1 | Example using PySB with ODE simulations
2 | =======================================
3 |
4 |
5 |
6 | .. code-block:: python
7 |
8 | import matplotlib.pyplot as plt
9 | import numpy as np
10 |
11 | from pysb.examples.robertson import model
12 | from pysb.simulator import ScipyOdeSimulator
13 | from simplepso.logging import get_logger
14 | from simplepso.pso import PSO
15 |
16 | For this example, we are going to train the model to some made up data.
17 | Please refer to a PySB tutorial if you are new to PySB. This tutorial assumes
18 | you have knowledge of PySB.
19 |
20 | .. code-block:: python
21 |
22 | # setup model and simulator
23 | t = np.linspace(0, 50, 51)
24 | # observables of the model to train
25 | obs_names = ['A_total', 'C_total']
26 |
27 | # create pysb simulator instance
28 | solver = ScipyOdeSimulator(
29 | model,
30 | t,
31 | integrator='lsoda',
32 | integrator_options={'rtol': 1e-8, 'atol': 1e-8}
33 | )
34 |
35 | .. code-block:: python
36 |
37 | # Defining a helper functions to use
38 | def normalize(trajectories):
39 | """Rescale a matrix of model trajectories to 0-1"""
40 | ymin = trajectories.min(0)
41 | ymax = trajectories.max(0)
42 | return (trajectories - ymin) / (ymax - ymin)
43 |
44 | .. code-block:: python
45 |
46 | traj = solver.run()
47 | ysim_array = traj.dataframe[obs_names].values
48 | # normalize the values from 0-1 (see function definition below)
49 | ysim_norm = normalize(ysim_array)
50 |
51 | plt.figure(figsize=(6, 4))
52 | if title is not None:
53 | plt.title(title)
54 | plt.plot(t, ysim_norm[:, 0], '^r', linestyle='--', label='A')
55 | plt.plot(t, ysim_norm[:, 1], 'ok', linestyle='--', label='C')
56 | plt.ylabel('Normalized concentration')
57 | plt.xlabel('Time (s)')
58 |
59 | .. image:: example_rob_no_noise.png
60 |
61 | For training purposes, we will use the trajectories above and add some random
62 | noise.
63 |
64 | .. code-block:: python
65 |
66 | noise = 0.05
67 | noisy_data_A = norm_data[:, 0] + np.random.uniform(-1 * noise, noise, len(t))
68 | norm_noisy_data_A = normalize(noisy_data_A)
69 |
70 | noisy_data_C = norm_data[:, 1] + np.random.uniform(-noise, noise, len(t))
71 | norm_noisy_data_C = normalize(noisy_data_C)
72 |
73 | ydata_norm = np.column_stack((norm_noisy_data_A, norm_noisy_data_C))
74 |
75 | .. code-block:: python
76 |
77 | plt.figure(figsize=(6, 4))
78 | plt.plot(t, ysim_norm[:, 0], '^r', linestyle='--', label='A')
79 | plt.plot(t, ysim_norm[:, 1], 'ok', linestyle='--', label='C')
80 | plt.plot(t, norm_noisy_data_A, 'r-', label='Noisy A')
81 | plt.plot(t, norm_noisy_data_C, 'k-', label='Noisy C')
82 | plt.legend(loc=0)
83 | plt.ylabel('Normalized concentration')
84 | plt.xlabel('Time (s)')
85 |
86 | .. image:: example_rob.png
87 |
88 | Next, we define the cost function. The cost function should take a parameter
89 | set as an argument and return a scalar value, where the ultimate goal is to
90 | minimize this value. To efficiently sample across large parameter space, we
91 | use log10 space for the parameters. This means before you
92 | pass them back to the simulator, you must convert them to linear space.
93 | We are also only optimizing the rate parameters from the model, not the
94 | initial conditions, so we must create a mask to identify which parameters in
95 | the model are rate versus initial conditions.
96 | .. code-block:: python
97 |
98 | rate_params = model.parameters_rules()
99 | rate_mask = np.array([p in rate_params for p in model.parameters])
100 | param_values = np.array([p.value for p in model.parameters])
101 | log_original_values = np.log10(param_values[rate_mask])
102 |
103 |
104 | Here we use the chi square metric to determine the distances between the
105 | trajectory of the parameter sets and our training data.
106 |
107 | .. code-block:: python
108 |
109 | def obj_function(params):
110 | # create copy of parameters
111 | params_tmp = np.copy(params)
112 | # convert back into regular base
113 | param_values[rate_mask] = 10 ** params_tmp
114 | traj = solver.run(param_values=param_values)
115 | ysim_array = traj.dataframe[obs_names].values
116 | ysim_norm = normalize(ysim_array)
117 | # chi^2 error
118 | err = np.sum((ydata_norm - ysim_norm) ** 2)
119 | # if there are nans, return a really large number
120 | if np.isnan(err):
121 | return 1000
122 | return err
123 |
124 |
125 |
126 | .. code-block:: python
127 |
128 | # make up a random starting point
129 | start_position = log_original_values + \
130 | np.random.uniform(-1, 1,
131 | size=len(log_original_values))
132 |
133 | We can see that these are not ideal.
134 |
135 | .. code-block:: python
136 |
137 | param_values[rate_mask] = 10 ** start_position
138 | traj = solver.run(param_values=param_values)
139 | ysim_array = traj.dataframe[obs_names].values
140 | ysim_norm = normalize(ysim_array)
141 |
142 | plt.figure(figsize=(6, 4))
143 | plt.plot(t, ysim_norm[:, 0], '^r', linestyle='--', label='A')
144 | plt.plot(t, ysim_norm[:, 1], 'ok', linestyle='--', label='C')
145 | plt.plot(t, norm_noisy_data_A, 'r-', label='Noisy A')
146 | plt.plot(t, norm_noisy_data_C, 'k-', label='Noisy C')
147 | plt.legend(loc=0)
148 | plt.ylabel('Normalized concentration')
149 | plt.xlabel('Time (s)')
150 |
151 | .. image:: fit_before_pso.png
152 |
153 | Finally, we get to initialize and run the PSO class. The main options to
154 | consider when running the algorithm are the `num_particles`, `num_iterations`,
155 | `num_processors`. The `num_particles` should be a multiple of the `num_processors`.
156 | You can set `num_iterations` as large as you'd like, but if you set it to a large
157 | value, you should consider setting the `max_iter_no_improv` or `stop_threshold`
158 | options.
159 |
160 | .. code-block:: python
161 |
162 | # Here we initial the class
163 | # We must provide the cost function and a starting value
164 | optimizer = PSO(start=start_position, verbose=True, shrink_steps=False)
165 |
166 | # We also must set bounds of the parameter space, and the speed PSO will
167 | # travel (max speed in either direction)
168 | optimizer.set_bounds(parameter_range=4)
169 | optimizer.set_speed(speed_min=-.05, speed_max=.05)
170 |
171 | # Now we run the pso algorithm
172 | optimizer.run(num_particles=48, num_iterations=500, num_processors=12,
173 | cost_function=obj_function, max_iter_no_improv=25)
174 |
175 | Done! We can then use `optimizer.best.pos` to access the best fit parameters.
176 |
177 | .. code-block:: python
178 |
179 | best_params = optimizer.best.pos
180 | param_values[rate_mask] = 10 ** best_params
181 | traj = solver.run(param_values=param_values)
182 | ysim_array = traj.dataframe[obs_names].values
183 | ysim_norm = normalize(ysim_array)
184 |
185 | plt.figure(figsize=(6, 4))
186 | plt.plot(t, ysim_norm[:, 0], '^r', linestyle='--', label='A')
187 | plt.plot(t, ysim_norm[:, 1], 'ok', linestyle='--', label='C')
188 | plt.plot(t, norm_noisy_data_A, 'r-', label='Noisy A')
189 | plt.plot(t, norm_noisy_data_C, 'k-', label='Noisy C')
190 | plt.legend(loc=0)
191 | plt.ylabel('Normalized concentration')
192 | plt.xlabel('Time (s)')
193 |
194 | .. image:: fit_after_pso.png
195 |
196 |
197 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib>=1.5.0
2 | numpy>=1.11.0
3 | scipy>=0.17.1
4 | pandas
5 | numpydoc
6 | sphinx_bootstrap_theme
7 | sphinx==3.1.2
--------------------------------------------------------------------------------
/docs/training_earm.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoLab-MSM/simplePSO/37e5eb22da920d30780984dcea327686d6eacbfe/docs/training_earm.gif
--------------------------------------------------------------------------------
/examples/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Simple interface for particle swarm optimization
3 |
4 | """
5 |
--------------------------------------------------------------------------------
/examples/data/EC-RP_IMS-RP_IC-RP_data_for_models.csv:
--------------------------------------------------------------------------------
1 | Time,IC-RP,norm_IC-RP,nrm_var_IC-RP,IMS-RP,IMS-RP step,VAR,EC-RP,norm_EC-RP,nrm_var_EC-RP
2 | 180,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4451,0.0020,0.0003
3 | 360,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4460,0.0018,0.0003
4 | 540,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4459,0.0015,0.0003
5 | 720,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4458,0.0051,0.0003
6 | 900,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4474,0.0038,0.0002
7 | 1080,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4468,0.0042,0.0002
8 | 1260,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4470,0.0057,0.0003
9 | 1440,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4477,0.0022,0.0004
10 | 1620,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4461,0.0027,0.0004
11 | 1800,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4463,0.0071,0.0004
12 | 1980,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4483,0.0027,0.0005
13 | 2160,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4463,0.0060,0.0005
14 | 2340,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4478,0.0060,0.0005
15 | 2520,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4478,0.0064,0.0006
16 | 2700,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4480,0.0068,0.0007
17 | 2880,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4482,0.0046,0.0007
18 | 3060,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4472,0.0075,0.0008
19 | 3240,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4485,0.0091,0.0012
20 | 3420,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4492,0.0115,0.0009
21 | 3600,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4503,0.0144,0.0010
22 | 3780,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4516,0.0106,0.0009
23 | 3960,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4499,0.0077,0.0012
24 | 4140,0.7063,0.0000,0.0006,0.0000,0.0000,1.0000,0.4486,0.0110,0.0011
25 | 4320,0.7081,0.0035,0.0042,0.0000,0.0000,1.0000,0.4501,0.0086,0.0012
26 | 4500,0.7151,0.0170,0.0047,0.0000,0.0000,1.0000,0.4490,0.0128,0.0010
27 | 4680,0.7159,0.0186,0.0046,0.0000,0.0000,1.0000,0.4509,0.0177,0.0011
28 | 4860,0.7170,0.0207,0.0053,0.0000,0.0000,1.0000,0.4531,0.0194,0.0014
29 | 5040,0.7223,0.0310,0.0051,0.0000,0.0000,1.0000,0.4539,0.0188,0.0015
30 | 5220,0.7245,0.0352,0.0063,0.0000,0.0000,1.0000,0.4536,0.0168,0.0021
31 | 5400,0.7313,0.0484,0.0078,0.0000,0.0000,1.0000,0.4527,0.0199,0.0019
32 | 5580,0.7347,0.0550,0.0082,0.0000,0.0000,1.0000,0.4541,0.0241,0.0049
33 | 5760,0.7414,0.0680,0.0075,0.0000,0.0000,1.0000,0.4560,0.0230,0.0079
34 | 5940,0.7440,0.0730,0.0067,0.0000,0.0000,1.0000,0.4555,0.0252,0.0097
35 | 6120,0.7446,0.0742,0.0142,0.0000,0.0000,1.0000,0.4565,0.0289,0.0103
36 | 6300,0.7614,0.1067,0.0104,0.0000,0.0000,1.0000,0.4582,0.0309,0.0114
37 | 6480,0.7598,0.1036,0.0094,0.0000,0.0000,1.0000,0.4591,0.0362,0.0093
38 | 6660,0.7677,0.1189,0.0121,0.0000,0.0000,1.0000,0.4615,0.0386,0.0096
39 | 6840,0.7812,0.1450,0.0083,0.0000,0.0000,1.0000,0.4626,0.0391,0.0103
40 | 7020,0.7906,0.1632,0.0064,0.0000,0.0000,1.0000,0.4628,0.0446,0.0090
41 | 7200,0.7877,0.1576,0.0056,0.0000,0.0000,1.0000,0.4653,0.0519,0.0084
42 | 7380,0.7982,0.1779,0.0054,0.0000,0.0000,1.0000,0.4686,0.0541,0.0075
43 | 7560,0.8043,0.1897,0.0052,0.0000,0.0000,1.0000,0.4696,0.0583,0.0077
44 | 7740,0.8217,0.2234,0.0063,0.0000,0.0000,1.0000,0.4715,0.0543,0.0087
45 | 7920,0.8239,0.2277,0.0020,0.0000,0.0000,1.0000,0.4697,0.0601,0.0074
46 | 8100,0.8321,0.2436,0.0028,0.0000,0.0000,1.0000,0.4723,0.0634,0.0076
47 | 8280,0.8526,0.2833,0.0037,0.0000,0.0000,1.0000,0.4738,0.0676,0.0067
48 | 8460,0.8571,0.2920,0.0037,0.0000,0.0000,1.0000,0.4757,0.0674,0.0069
49 | 8640,0.8643,0.3059,0.0095,0.0000,0.0000,1.0000,0.4756,0.0718,0.0064
50 | 8820,0.8758,0.3282,0.0105,0.0000,0.0000,1.0000,0.4776,0.0709,0.0085
51 | 9000,0.8819,0.3400,0.0044,0.0000,0.0000,1.0000,0.4772,0.0738,0.0106
52 | 9180,0.9020,0.3789,0.0095,0.0000,0.0000,1.0000,0.4785,0.0875,0.0115
53 | 9360,0.9091,0.3926,0.0061,0.0000,0.0000,1.0000,0.4847,0.0961,0.0113
54 | 9540,0.9165,0.4070,0.0080,0.0025,0.0000,1.0000,0.4886,0.1073,0.0123
55 | 9720,0.9264,0.4261,0.0055,0.1192,0.0000,1.0000,0.4937,0.1182,0.0125
56 | 9900,0.9490,0.4699,0.0062,0.8808,1.0000,1.0000,0.4986,0.2178,0.0122
57 | 10080,0.9783,0.5266,0.0073,0.9975,1.0000,1.0000,0.5437,0.3894,0.0132
58 | 10260,1.0285,0.6238,0.0085,1.0000,1.0000,1.0000,0.6214,0.5512,0.0150
59 | 10440,1.0640,0.6925,0.0077,1.0000,1.0000,1.0000,0.6947,0.6617,0.0140
60 | 10620,1.0995,0.7613,0.0074,1.0000,1.0000,1.0000,0.7447,0.7705,0.0140
61 | 10800,1.1242,0.8091,0.0085,1.0000,1.0000,1.0000,0.7940,0.8390,0.0146
62 | 10980,1.1574,0.8734,0.0090,1.0000,1.0000,1.0000,0.8250,0.9145,0.0130
63 | 11160,1.1848,0.9264,0.0071,1.0000,1.0000,1.0000,0.8592,0.9832,0.0131
64 | 11340,1.2063,0.9681,0.0067,1.0000,1.0000,1.0000,0.8903,1.0000,0.0095
65 | 11520,1.2228,1.0000,0.0063,1.0000,1.0000,1.0000,0.8979,1.0000,0.0052
66 | 11700,1.2305,1.0000,0.0063,1.0000,1.0000,1.0000,0.9218,1.0000,0.0063
67 | 11880,1.2433,1.0000,0.0063,1.0000,1.0000,1.0000,0.9297,1.0000,0.0059
68 | 12060,1.2493,1.0000,0.0063,1.0000,1.0000,1.0000,0.9343,1.0000,0.0053
69 | 12240,1.2633,1.0000,0.0063,1.0000,1.0000,1.0000,0.9508,1.0000,0.0047
70 | 12420,1.2638,1.0000,0.0063,1.0000,1.0000,1.0000,0.9649,1.0000,0.0039
71 | 12600,1.2782,1.0000,0.0063,1.0000,1.0000,1.0000,0.9727,1.0000,0.0034
72 | 12780,1.2764,1.0000,0.0063,1.0000,1.0000,1.0000,0.9803,1.0000,0.0046
73 | 12960,1.2763,1.0000,0.0063,1.0000,1.0000,1.0000,0.9777,1.0000,0.0047
74 | 13140,1.2678,1.0000,0.0063,1.0000,1.0000,1.0000,0.9860,1.0000,0.0057
75 | 13320,1.2664,1.0000,0.0063,1.0000,1.0000,1.0000,0.9930,1.0000,0.0055
76 | 13500,1.2773,1.0000,0.0063,1.0000,1.0000,1.0000,1.0017,1.0000,0.0052
77 | 13680,1.2999,1.0000,0.0063,1.0000,1.0000,1.0000,1.0034,1.0000,0.0053
78 | 13860,1.2912,1.0000,0.0063,1.0000,1.0000,1.0000,1.0061,1.0000,0.0065
79 | 14040,1.2914,1.0000,0.0063,1.0000,1.0000,1.0000,1.0028,1.0000,0.0054
80 | 14220,1.2896,1.0000,0.0063,1.0000,1.0000,1.0000,1.0190,1.0000,0.0061
81 | 14400,1.3143,1.0000,0.0063,1.0000,1.0000,1.0000,1.0266,1.0000,0.0074
82 | 14580,1.3101,1.0000,0.0063,1.0000,1.0000,1.0000,1.0267,1.0000,0.0068
83 | 14760,1.2957,1.0000,0.0063,1.0000,1.0000,1.0000,1.0368,1.0000,0.0078
84 | 14940,1.2769,1.0000,0.0063,1.0000,1.0000,1.0000,1.0407,1.0000,0.0061
85 | 15120,1.2766,1.0000,0.0063,1.0000,1.0000,1.0000,1.0427,1.0000,0.0050
86 | 15300,1.2830,1.0000,0.0063,1.0000,1.0000,1.0000,1.0394,1.0000,0.0058
87 | 15480,1.2824,1.0000,0.0063,1.0000,1.0000,1.0000,1.0415,1.0000,0.0068
88 | 15660,1.2606,1.0000,0.0063,1.0000,1.0000,1.0000,1.0438,1.0000,0.0062
89 | 15840,1.2601,1.0000,0.0063,1.0000,1.0000,1.0000,1.0539,1.0000,0.0064
90 | 16020,1.2720,1.0000,0.0063,1.0000,1.0000,1.0000,1.0537,1.0000,0.0078
91 | 16200,1.2635,1.0000,0.0063,1.0000,1.0000,1.0000,1.0445,1.0000,0.0065
92 | 16380,1.2384,1.0000,0.0063,1.0000,1.0000,1.0000,1.0530,1.0000,0.0066
93 | 16560,1.2434,1.0000,0.0063,1.0000,1.0000,1.0000,1.0614,1.0000,0.0062
94 | 16740,1.2677,1.0000,0.0063,1.0000,1.0000,1.0000,1.0618,1.0000,0.0067
95 | 16920,1.2650,1.0000,0.0063,1.0000,1.0000,1.0000,1.0684,1.0000,0.0074
96 | 17100,1.2595,1.0000,0.0063,1.0000,1.0000,1.0000,1.0688,1.0000,0.0070
97 | 17280,1.2565,1.0000,0.0063,1.0000,1.0000,1.0000,1.0669,1.0000,0.0060
98 | 17460,1.2476,1.0000,0.0063,1.0000,1.0000,1.0000,1.0731,1.0000,0.0062
99 | 17640,1.2432,1.0000,0.0063,1.0000,1.0000,1.0000,1.0749,1.0000,0.0060
100 | 17820,1.2617,1.0000,0.0063,1.0000,1.0000,1.0000,1.0861,1.0000,0.0064
101 | 18000,1.2494,1.0000,0.0063,1.0000,1.0000,1.0000,1.0929,1.0000,0.0063
102 | 18180,1.2561,1.0000,0.0063,1.0000,1.0000,1.0000,1.0973,1.0000,0.0073
103 | 18360,1.2519,1.0000,0.0063,1.0000,1.0000,1.0000,1.1062,1.0000,0.0073
104 | 18540,1.2578,1.0000,0.0063,1.0000,1.0000,1.0000,1.1075,1.0000,0.0073
105 | 18720,1.2568,1.0000,0.0063,1.0000,1.0000,1.0000,1.1091,1.0000,0.0073
106 | 18900,1.2688,1.0000,0.0063,1.0000,1.0000,1.0000,1.1068,1.0000,0.0073
107 | 19080,1.2505,1.0000,0.0063,1.0000,1.0000,1.0000,1.1185,1.0000,0.0073
108 | 19260,1.2490,1.0000,0.0063,1.0000,1.0000,1.0000,1.1227,1.0000,0.0073
109 | 19440,1.2533,1.0000,0.0063,1.0000,1.0000,1.0000,1.1263,1.0000,0.0073
110 | 19620,1.2579,1.0000,0.0063,1.0000,1.0000,1.0000,1.1257,1.0000,0.0073
111 | 19800,1.2531,1.0000,0.0063,1.0000,1.0000,1.0000,1.1248,1.0000,0.0073
112 | 19980,1.2571,1.0000,0.0063,1.0000,1.0000,1.0000,1.1257,1.0000,0.0073
113 | 20160,1.2580,1.0000,0.0063,1.0000,1.0000,1.0000,1.1276,1.0000,0.0073
114 |
--------------------------------------------------------------------------------
/examples/example_earm.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | simplePSO example using the EARM 1.0 model
4 | """
5 | import os
6 |
7 | import matplotlib.pyplot as plt
8 | import numpy as np
9 | import pandas as pd
10 | import scipy.interpolate
11 |
12 | from pysb.examples.earm_1_0 import model
13 | from pysb.simulator import ScipyOdeSimulator
14 | from simplepso.pso import PSO
15 |
16 | # load experimental data
17 | data_path = os.path.join(os.path.dirname(__file__), 'data',
18 | 'EC-RP_IMS-RP_IC-RP_data_for_models.csv')
19 |
20 | exp_data = pd.read_csv(data_path, index_col=False)
21 |
22 | # Mean and variance of Td (delay time) and Ts (switching time) of MOMP, and
23 | # yfinal (the last value of the IMS-RP trajectory)
24 | momp_data = np.array([9810.0, 180.0, model.parameters['mSmac_0'].value])
25 | momp_var = np.array([7245000.0, 3600.0, 1e4])
26 |
27 | # timepoints for simulation. These must match the experimental data.
28 | tspan = exp_data['Time'].values.copy()
29 |
30 | rate_params = model.parameters_rules()
31 | param_values = np.array([p.value for p in model.parameters])
32 | rate_mask = np.array([p in rate_params for p in model.parameters])
33 | starting_position = np.log10(param_values[rate_mask])
34 |
35 | solver = ScipyOdeSimulator(
36 | model,
37 | tspan,
38 | integrator='lsoda',
39 | use_analytic_jacobian=True,
40 | compiler='python',
41 | integrator_options={"rtol": 1e-6, "atol": 1e-6}
42 | )
43 |
44 |
45 | def likelihood(position):
46 | param_values[rate_mask] = 10 ** position.copy()
47 | traj = solver.run(param_values=param_values)
48 |
49 | # normalize trajectories
50 | bid_traj = traj.observables['tBid_total'] / model.parameters['Bid_0'].value
51 | cparp_traj = traj.observables['CPARP_total'] / model.parameters[
52 | 'PARP_0'].value
53 | momp_traj = traj.observables['cSmac_total']
54 |
55 | # calculate chi^2 distance for each time course
56 | e1 = np.sum((exp_data['norm_IC-RP'] - bid_traj) ** 2 /
57 | (2 * exp_data['nrm_var_IC-RP'])) / len(bid_traj)
58 |
59 | e2 = np.sum((exp_data['norm_EC-RP'] - cparp_traj) ** 2 /
60 | (2 * exp_data['nrm_var_EC-RP'])) / len(cparp_traj)
61 |
62 | # Here we fit a spline to find where we get 50% release of MOMP reporter
63 | if np.nanmax(momp_traj) == 0:
64 | print('No aSmac!')
65 | e3 = 10000000
66 | else:
67 | ysim_momp_norm = momp_traj / np.nanmax(momp_traj)
68 | st, sc, sk = scipy.interpolate.splrep(tspan, ysim_momp_norm)
69 | try:
70 | t10 = scipy.interpolate.sproot((st, sc - 0.10, sk))[0]
71 | t90 = scipy.interpolate.sproot((st, sc - 0.90, sk))[0]
72 | except IndexError:
73 | t10 = 0
74 | t90 = 0
75 |
76 | # time of death = halfway point between 10 and 90%
77 | td = (t10 + t90) / 2
78 |
79 | # time of switch is time between 90 and 10 %
80 | ts = t90 - t10
81 |
82 | # final fraction of aSMAC (last value)
83 | momp_sim = [td, ts, momp_traj[-1]]
84 |
85 | e3 = np.sum((momp_data - momp_sim) ** 2 / (2 * momp_var)) / 3
86 |
87 | return e1 + e2 + e3
88 |
89 |
90 | def display(position, save_name):
91 | param_values[rate_mask] = 10 ** position
92 | traj = solver.run(param_values=param_values)
93 |
94 | # normalize trajectories
95 | bid_traj = traj.observables['tBid_total'] / model.parameters['Bid_0'].value
96 | cparp_traj = traj.observables['CPARP_total'] / model.parameters[
97 | 'PARP_0'].value
98 | aSmac_traj = traj.observables['cSmac_total'] / model.parameters[
99 | 'mSmac_0'].value
100 |
101 | # create all plots for each observable
102 | plt.figure(figsize=(3, 9))
103 |
104 | # plot cleaved parp
105 | plt.subplot(311)
106 | plt.plot(tspan, bid_traj, color='r', marker='^', label='tBID sim')
107 | plt.errorbar(exp_data['Time'], exp_data['norm_IC-RP'],
108 | yerr=exp_data['nrm_var_IC-RP'] ** .5,
109 | ecolor='black', color='black', elinewidth=0.5, capsize=0)
110 | plt.legend(loc=0)
111 |
112 | # plot cleaved parp
113 | plt.subplot(312)
114 | plt.plot(tspan, cparp_traj, color='blue', marker='*', label='cPARP sim')
115 | plt.errorbar(exp_data['Time'], exp_data['norm_EC-RP'],
116 | yerr=exp_data['nrm_var_EC-RP'] ** .5,
117 | ecolor='black', color='black', elinewidth=0.5, capsize=0)
118 | plt.legend(loc=0)
119 |
120 | # plot activated SMAC
121 | plt.subplot(313)
122 | plt.plot(tspan, aSmac_traj, color='g', label='aSMAC sim')
123 | plt.axvline(momp_data[0], -0.05, 1.05, color='black', linestyle=':',
124 | label='exp aSMAC')
125 | plt.legend(loc=0)
126 | plt.savefig('{}.png'.format(save_name), bbox_inches='tight')
127 | plt.close()
128 |
129 |
130 | def create_gif_of_model_training(pso_instance):
131 | """
132 | Create a gif showing the improvements of parameter fits from a PSO instance
133 | """
134 |
135 | def create(position, save_name, values):
136 | param_values[rate_mask] = 10 ** position
137 | traj = solver.run(param_values=param_values)
138 |
139 | # normalize trajectories
140 | cparp_traj = traj.observables['CPARP_total'] / \
141 | model.parameters['PARP_0'].value
142 |
143 | # create all plots for each observable
144 | plt.figure(figsize=(4, 6))
145 | time = tspan / 3600
146 | # plot cleaved parp
147 | plt.subplot(211)
148 | plt.plot(time, cparp_traj, color='r', label='CPARP sim')
149 | plt.errorbar(time, exp_data['norm_EC-RP'],
150 | yerr=exp_data['nrm_var_EC-RP'] ** .5, label='CPARP exp',
151 | ecolor='black', color='black', elinewidth=0.5, capsize=0)
152 | plt.legend(loc=2)
153 | plt.xlabel("Time(hr)")
154 | plt.subplot(212)
155 | plt.plot(range(len(values)), np.log10(values), '-k')
156 | plt.plot(range(len(values))[save_name], np.log10(values[save_name]),
157 | 'or')
158 |
159 | plt.xlabel("Iteration number")
160 | plt.ylabel("Objective function value")
161 | plt.subplots_adjust(hspace=.3)
162 | plt.savefig('images/{}.png'.format(save_name),
163 | bbox_inches='tight', dpi=300)
164 | plt.close()
165 |
166 | try:
167 | import imageio
168 | except ImportError:
169 | raise ImportError("Please install imageio to create a gif")
170 |
171 | if not os.path.exists('images'):
172 | os.mkdir('images')
173 | for n, i in enumerate(pso_instance.history):
174 | create(i, n, pso_instance.values)
175 |
176 | images = ['images/{}.png'.format(i) for i in
177 | range(len(pso_instance.history) - 1)]
178 |
179 | imgs = [imageio.imread(i) for i in images]
180 | print("Creating gif")
181 | imageio.mimsave('training_earm.gif', imgs, duration=10 / len(images))
182 | print("Creating mp4")
183 | imageio.mimsave('training_earm.mp4', imgs, fps=len(images) / 10)
184 |
185 |
186 | def run_example():
187 | # create PSO object
188 | pso = PSO(save_sampled=False, verbose=True, shrink_steps=False)
189 | pso.set_start_position(starting_position)
190 |
191 | # allows particles to move +/- 2 orders of magnitude
192 | pso.set_bounds(2)
193 | # sets maximum speed that a particle can travel
194 | pso.set_speed(-.1, .1)
195 |
196 | pso.run(
197 | num_particles=24,
198 | num_iterations=100,
199 | stop_threshold=1e-5,
200 | num_processors=18,
201 | max_iter_no_improv=20,
202 | cost_function=likelihood
203 | )
204 |
205 | display(pso.best.pos, save_name='best_fit')
206 | np.savetxt("pso_fit_for_model.csv", pso.best.pos)
207 | create_gif_of_model_training(pso)
208 |
209 |
210 | if __name__ == '__main__':
211 | # Runs PSO
212 | run_example()
213 |
--------------------------------------------------------------------------------
/examples/robertson_example.py:
--------------------------------------------------------------------------------
1 | """
2 | Example usage of the simplePSO package
3 | """
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 |
7 | from pysb.examples.robertson import model
8 | from pysb.simulator import ScipyOdeSimulator
9 | from simplepso.logging import get_logger
10 | from simplepso.pso import PSO
11 |
12 | logger = get_logger()
13 |
14 |
15 | # Defining a few helper functions to use
16 | def normalize(trajectories):
17 | """Rescale a matrix of model trajectories to 0-1"""
18 | ymin = trajectories.min(0)
19 | ymax = trajectories.max(0)
20 | return (trajectories - ymin) / (ymax - ymin)
21 |
22 |
23 | def display(params, title=None):
24 | local_params = np.copy(params)
25 | param_values[rate_mask] = 10 ** local_params
26 | traj = solver.run(param_values=param_values)
27 | ysim_array = traj.dataframe[obs_names].values
28 | ysim_norm = normalize(ysim_array)
29 |
30 | plt.figure(figsize=(6, 4))
31 | if title is not None:
32 | plt.title(title)
33 | plt.plot(t, ysim_norm[:, 0], '^r', linestyle='--', label='A')
34 | plt.plot(t, ysim_norm[:, 1], 'ok', linestyle='--', label='C')
35 | plt.plot(t, norm_noisy_data_A, 'r-', label='Noisy A')
36 | plt.plot(t, norm_noisy_data_C, 'k-', label='Noisy C')
37 | plt.legend(loc=0)
38 | plt.ylabel('Normalized concentration')
39 | plt.xlabel('Time (s)')
40 |
41 |
42 | '''
43 | Here we define the cost function. We pass it the parameter vector, unlog it,
44 | and pass it to the solver. We choose a chi square cost function, but you can
45 | provide any metric of you choosing
46 | '''
47 |
48 |
49 | def obj_function(params):
50 | # create copy of parameters
51 | params_tmp = np.copy(params)
52 | # convert back into regular base
53 | param_values[rate_mask] = 10 ** params_tmp
54 | traj = solver.run(param_values=param_values)
55 | ysim_array = traj.dataframe[obs_names].values
56 | ysim_norm = normalize(ysim_array)
57 | # chi^2 error
58 | err = np.sum((ydata_norm - ysim_norm) ** 2)
59 | if np.isnan(err):
60 | return 1000
61 | return err
62 |
63 |
64 | # setup model and simulator
65 | t = np.linspace(0, 50, 51)
66 | obs_names = ['A_total', 'C_total']
67 |
68 | # create pysb simulator instance
69 | solver = ScipyOdeSimulator(
70 | model,
71 | t,
72 | integrator='lsoda',
73 | integrator_options={'rtol': 1e-8, 'atol': 1e-8}
74 | )
75 | traj = solver.run()
76 |
77 | # Creating ideal data
78 | ysim_array = traj.dataframe[obs_names].values
79 | norm_data = normalize(ysim_array)
80 |
81 | noise = 0.05
82 | noisy_data_A = norm_data[:, 0] + np.random.uniform(-1 * noise, noise, len(t))
83 | norm_noisy_data_A = normalize(noisy_data_A)
84 |
85 | noisy_data_C = norm_data[:, 1] + np.random.uniform(-noise, noise, len(t))
86 | norm_noisy_data_C = normalize(noisy_data_C)
87 |
88 | ydata_norm = np.column_stack((norm_noisy_data_A, norm_noisy_data_C))
89 |
90 | '''
91 | In this example we are going to optimize the rate parameters of the model.
92 | We do this in log10 so we can span across large parameter spaces easily.
93 | '''
94 | rate_params = model.parameters_rules()
95 | rate_mask = np.array([p in rate_params for p in model.parameters])
96 | param_values = np.array([p.value for p in model.parameters])
97 | log_original_values = np.log10(param_values[rate_mask])
98 |
99 | if '__main__' == __name__:
100 | # We will use a best guess starting position for the model,
101 | start_position = log_original_values + \
102 | np.random.uniform(-1, 1,
103 | size=len(log_original_values))
104 |
105 | display(start_position, "Before optimization")
106 | plt.tight_layout()
107 | plt.savefig("fit_before_pso.png", bbox_inches='tight')
108 | logger.info("Saving pre-fit figure as fit_before_pso.png")
109 |
110 | # Here we initial the class
111 | # We must proivde the cost function and a starting value
112 | optimizer = PSO(start=start_position, verbose=True, shrink_steps=False)
113 |
114 | # We also must set bounds of the parameter space, and the speed PSO will
115 | # travel (max speed in either direction)
116 | optimizer.set_bounds(parameter_range=4)
117 | optimizer.set_speed(speed_min=-.05, speed_max=.05)
118 |
119 | # Now we run the pso algorithm
120 | optimizer.run(num_particles=50, num_iterations=500, num_processors=12,
121 | cost_function=obj_function, max_iter_no_improv=25)
122 |
123 | best_params = optimizer.best.pos
124 | display(best_params, "After optimization")
125 | plt.tight_layout()
126 | plt.savefig("fit_after_pso.png", bbox_inches='tight')
127 | logger.info("Saving post-fit figure as fit_after_pso.png")
128 | plt.show()
129 |
--------------------------------------------------------------------------------
/examples/run_schogl_example_ssa.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import seaborn as sns
4 | from scipy.stats import wasserstein_distance
5 |
6 | from pysb.integrate import odesolve
7 | from pysb.simulator import OpenCLSSASimulator
8 | from simplepso import PSO
9 |
10 |
11 | def run_params_for_plot(params):
12 | simulator.param_values = None
13 | simulator.initials = None
14 | traj = simulator.run(tspan, param_values=params, number_sim=num_sim)
15 | return traj.dataframe[name].unstack(0).values
16 |
17 |
18 | def obj_function(traj_dist):
19 | tmp = traj_dist[name].T.unstack('simulation').values[-1, :]
20 | return wasserstein_distance(tmp, actual)
21 |
22 |
23 | def run_pso():
24 | optimizer = PSO(start=log10_original_values, verbose=True)
25 | optimizer.set_bounds(parameter_range=.2)
26 | optimizer.set_speed(speed_min=-.05, speed_max=.05)
27 | optimizer.run_ssa(model, num_particles=num_particles, num_iterations=200,
28 | cost_function=obj_function, num_sim=num_sim,
29 | simulator=simulator)
30 |
31 | print("Ending parameters")
32 | print(10 ** optimizer.best.pos)
33 | return optimizer.best.pos
34 |
35 |
36 | def add_subplot(traj, title, params):
37 | plt.title(title)
38 | plt.plot(tspan, traj, '0.5', lw=2, alpha=0.25)
39 | plt.plot(tspan, traj.mean(1), 'k-*', lw=3, label="Mean")
40 | plt.plot(tspan, traj.min(1), 'b--', lw=3, label="Minimum")
41 | plt.plot(tspan, traj.max(1), 'r--', lw=3, label="Maximum")
42 | y = odesolve(model, tspan, params)
43 | plt.plot(tspan, y[name], 'g--', lw=3, label="ODE")
44 | plt.ylim(0, 800)
45 | plt.xlabel('Time')
46 | plt.ylabel('X molecules')
47 |
48 |
49 | def run():
50 | fit_params = run_pso()
51 |
52 | plt.figure(figsize=(9, 5))
53 | plt.subplot(231)
54 | add_subplot(actual_traj, "Training data", orig_values)
55 |
56 | plt.subplot(232)
57 | orig_values[rate_mask] = 10 ** noisy_start
58 | start_sim = run_params_for_plot(orig_values)
59 |
60 | add_subplot(start_sim, "Starting state", orig_values)
61 |
62 | orig_values[rate_mask] = 10 ** fit_params
63 | best_fit_sim = run_params_for_plot(orig_values)
64 |
65 | plt.subplot(233)
66 | add_subplot(best_fit_sim, "After training", orig_values)
67 |
68 | plt.subplot(212)
69 | sns.kdeplot(actual, label='actual')
70 | sns.kdeplot(start_sim[-1, :], label='before')
71 | sns.kdeplot(best_fit_sim[-1, :], label='after')
72 |
73 | plt.legend()
74 | plt.tight_layout()
75 | savename = 'trained_model'
76 | plt.savefig("{}.png".format(savename), bbox_inches='tight', dpi=300)
77 | plt.show()
78 | plt.close()
79 |
80 |
81 | if __name__ == '__main__':
82 | from pysb.examples.schloegl import model
83 |
84 | tspan = np.linspace(0, 100, 101)
85 | model.parameters['X_0'].value = 500
86 | name = 'X_total'
87 | num_sim = 100
88 | num_particles = 8
89 | # uncomment to use CUDA device
90 | # simulator = CudaSSASimulator(model, tspan=tspan, verbose=False)
91 |
92 | simulator = OpenCLSSASimulator(model, tspan=tspan, verbose=False)
93 |
94 | actual_traj = run_params_for_plot(None)
95 | actual = actual_traj[-1, :]
96 | rate_mask = np.array(
97 | [p in model.parameters_rules() for p in model.parameters])
98 |
99 | orig_values = np.array([p.value for p in model.parameters])
100 | log10_original_values = np.log10(orig_values[rate_mask])
101 | n_params = len(log10_original_values)
102 | print("Ideal parameters")
103 | print(log10_original_values)
104 |
105 | noisy_start = log10_original_values + np.random.uniform(-.1, .1, n_params)
106 |
107 | run()
108 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib>=1.5.0
2 | numpy>=1.11.0
3 | scipy>=0.17.1
4 | pandas
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
4 | [bdist_wheel]
5 | universal=1
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import find_packages, setup
2 |
3 | from simplepso import __version__
4 |
5 | with open('README.md') as f:
6 | readme = f.read()
7 |
8 |
9 | setup(
10 | name='simplepso',
11 | packages=find_packages(exclude=['examples', 'docs'],),
12 | version=__version__,
13 | description='Simple usage particle swarm optimization',
14 | author='James C. Pino',
15 | author_email='james.ch.pino@gmail.com',
16 | url='https://github.com/LoLab-VU/simplePSO',
17 | keywords=['optimization',
18 | 'systems biology'],
19 | classifiers=['License :: OSI Approved :: BSD License',
20 | 'Operating System :: OS Independent',
21 | 'Programming Language :: Python :: 2',
22 | 'Programming Language :: Python :: 3'],
23 | include_package_data=True,
24 | install_requires=['matplotlib >= 1.5.0',
25 | 'numpy >= 1.11.0',
26 | 'scipy >= 0.17.1',
27 | 'pysb >= 1.1.1'],
28 | )
29 |
--------------------------------------------------------------------------------
/simplepso/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Simple interface for particle swarm optimization
3 |
4 | """
5 | from simplepso.pso import PSO
6 |
7 | _MAJOR = 2
8 | _MINOR = 2
9 | _MICRO = 3
10 | __version__ = '%d.%d.%d' % (_MAJOR, _MINOR, _MICRO)
11 | __release__ = '%d.%d' % (_MAJOR, _MINOR)
12 | __all__ = ['PSO']
13 |
--------------------------------------------------------------------------------
/simplepso/logging.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 |
3 | import logging
4 | import os
5 | import platform
6 | import socket
7 | import time
8 | import warnings
9 |
10 | # code based off of pysb.logging.py
11 |
12 | SECONDS_IN_HOUR = 3600
13 | LOG_LEVEL_ENV_VAR = 'SIMPLEPSO_LOG'
14 | BASE_LOGGER_NAME = 'simplepso'
15 | EXTENDED_DEBUG = 5
16 | NAMED_LOG_LEVELS = {'NOTSET': logging.NOTSET,
17 | 'EXTENDED_DEBUG': EXTENDED_DEBUG,
18 | 'DEBUG': logging.DEBUG,
19 | 'INFO': logging.INFO,
20 | 'WARNING': logging.WARNING,
21 | 'ERROR': logging.ERROR,
22 | 'CRITICAL': logging.CRITICAL}
23 |
24 |
25 | def formatter(time_utc=False):
26 | """
27 | Build a logging formatter using local or UTC time
28 |
29 | Parameters
30 | ----------
31 | time_utc : bool, optional (default: False)
32 | Use local time stamps in log messages if False, or Universal
33 | Coordinated Time (UTC) if True
34 |
35 | Returns
36 | -------
37 | A logging.Formatter object for simplePSO logging
38 | """
39 | log_fmt = logging.Formatter('%(asctime)s.%(msecs).3d - %(name)s - '
40 | '%(levelname)s - %(message)s',
41 | datefmt='%Y-%m-%d %H:%M:%S')
42 | if time_utc:
43 | log_fmt.converter = time.gmtime
44 |
45 | return log_fmt
46 |
47 |
48 | def setup_logger(level=logging.INFO, console_output=True, file_output=False,
49 | time_utc=False, capture_warnings=True):
50 | """
51 | Set up a new logging.Logger for simplePSO logging
52 |
53 | Calling this method will override any existing handlers, formatters etc.
54 | attached to the simplePSO logger. Typically, :func:`get_logger` should be
55 | used instead, which returns the existing simplePSO logger if it has already
56 | been set up, and can handle simplePSO submodule namespaces.
57 |
58 | Parameters
59 | ----------
60 | level : int
61 | Logging level, typically using a constant like logging.INFO or
62 | logging.DEBUG
63 | console_output : bool
64 | Set up a default console log handler if True (default)
65 | file_output : string
66 | Supply a filename to copy all log output to that file, or set to
67 | False to disable (default)
68 | time_utc : bool
69 | Specifies whether log entry time stamps should be in local time
70 | (when False) or UTC (True). See :func:`formatter` for more
71 | formatting options.
72 | capture_warnings : bool
73 | Capture warnings from Python's warnings module if True (default)
74 |
75 | Returns
76 | -------
77 | A logging.Logger object for simplePSO logging.
78 | :func:`get_logger`.
79 | """
80 | log = logging.getLogger(BASE_LOGGER_NAME)
81 |
82 | # Logging level can be overridden with environment variable
83 | if LOG_LEVEL_ENV_VAR in os.environ:
84 | try:
85 | level = int(os.environ[LOG_LEVEL_ENV_VAR])
86 | except ValueError:
87 | # Try parsing as a name
88 | level_name = os.environ[LOG_LEVEL_ENV_VAR]
89 | if level_name in NAMED_LOG_LEVELS.keys():
90 | level = NAMED_LOG_LEVELS[level_name]
91 | else:
92 | raise ValueError(
93 | 'Environment variable {} contains an invalid value "{}". '
94 | 'If set, its value must be one of {} (case-sensitive) or '
95 | 'an integer log level.'.format(
96 | LOG_LEVEL_ENV_VAR, level_name,
97 | ", ".join(NAMED_LOG_LEVELS.keys())
98 | )
99 | )
100 |
101 | log.setLevel(level)
102 |
103 | # Remove default logging handler
104 | log.handlers = []
105 |
106 | log_fmt = formatter(time_utc=time_utc)
107 |
108 | if console_output:
109 | stream_handler = logging.StreamHandler()
110 | stream_handler.setFormatter(log_fmt)
111 | log.addHandler(stream_handler)
112 |
113 | if file_output:
114 | file_handler = logging.FileHandler(file_output)
115 | file_handler.setFormatter(log_fmt)
116 | log.addHandler(file_handler)
117 |
118 | log.info('Logging started on simplepso')
119 | if time_utc:
120 | log.info('Log entry times are in UTC')
121 | else:
122 | utc_offset = time.timezone if (time.localtime().tm_isdst == 0) else \
123 | time.altzone
124 | utc_offset = -(utc_offset / SECONDS_IN_HOUR)
125 | log.info('Log entry time offset from UTC: %.2f hours', utc_offset)
126 |
127 | log.debug('OS Platform: %s', platform.platform())
128 | log.debug('Python version: %s', platform.python_version())
129 | log.debug('Hostname: %s', socket.getfqdn())
130 |
131 | logging.captureWarnings(capture_warnings)
132 |
133 | return log
134 |
135 |
136 | def get_logger(logger_name=BASE_LOGGER_NAME, log_level=None,
137 | **kwargs):
138 | """
139 | Returns (if extant) or creates a simplePSO logger
140 |
141 | If the simplePSO base logger has already been set up, this method will
142 | return it or any of its descendant loggers without overriding the settings
143 | - i.e. any values supplied as kwargs will be ignored.
144 |
145 | Parameters
146 | ----------
147 | logger_name : string
148 | Get a logger for a specific namespace, typically __name__ for code
149 | outside of classes or self.__module__ inside a class
150 | log_level : bool or int
151 | Override the default or preset log level for the requested logger.
152 | None or False uses the default or preset value. True evaluates to
153 | logging.DEBUG. Any integer is used directly.
154 | **kwargs : kwargs
155 | Keyword arguments to supply to :func:`setup_logger`. Only used when
156 | the simplePSO logger hasn't been set up yet (i.e. there have been no
157 | calls to this function or :func:`get_logger` directly).
158 |
159 | Returns
160 | -------
161 | A logging.Logger object with the requested name
162 |
163 | Examples
164 | --------
165 |
166 | >>> from simplepso.logging import get_logger
167 | >>> logger = get_logger(__name__)
168 | >>> logger.debug('Test message')
169 | """
170 | if BASE_LOGGER_NAME not in logging.Logger.manager.loggerDict.keys():
171 | setup_logger(**kwargs)
172 | elif kwargs:
173 | warnings.warn('simplepso logger already exists, ignoring keyword '
174 | 'arguments to setup_logger')
175 |
176 | logger = logging.getLogger(logger_name)
177 |
178 | if log_level is not None and log_level is not False:
179 | if isinstance(log_level, bool):
180 | log_level = logging.DEBUG
181 | elif not isinstance(log_level, int):
182 | raise ValueError('log_level must be a boolean, integer or None')
183 |
184 | if logger.getEffectiveLevel() != log_level:
185 | logger.debug('Changing log_level from %d to %d' % (
186 | logger.getEffectiveLevel(), log_level))
187 | logger.setLevel(log_level)
188 | return logger
189 |
--------------------------------------------------------------------------------
/simplepso/pso.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import os
3 | from concurrent.futures import ProcessPoolExecutor, Executor, Future
4 | from copy import deepcopy
5 |
6 | import numpy as np
7 |
8 | from simplepso.logging import setup_logger
9 |
10 | # set OMP_NUM_THREADS to 1 to prevent multi-processing to expand no each core
11 | os.environ['OMP_NUM_THREADS'] = "1"
12 |
13 |
14 | class Particle(object):
15 | """
16 | Particle to be used in the Swarm
17 | """
18 |
19 | def __init__(self, pos, fitness=None, smin=None, smax=None):
20 | self.pos = pos
21 | self.fitness = fitness
22 | self.smin = smin
23 | self.smax = smax
24 | self.__best = None
25 |
26 | @property
27 | def best(self):
28 | return self.__best
29 |
30 | @best.setter
31 | def best(self, new_best):
32 | self.__best = Particle(
33 | deepcopy(new_best.pos), deepcopy(new_best.fitness),
34 | deepcopy(new_best.smin), deepcopy(new_best.smax)
35 | )
36 |
37 | # Used for printing below
38 | header = '{:<10}' + "\t".join(['{:>12}'] * 5)
39 | stats_header = header.format('iteration', 'best', 'mean', 'min', 'max', 'std')
40 | stats_output = '{:<10}' + '\t'.join(['{:>12.3f}'] * 5)
41 |
42 |
43 | class PSO(object):
44 | """
45 | Simple interface to run particle swarm optimization
46 |
47 | It can optimize parameters for a function using two run methods.
48 | run() : cost function gets a parameter vector and returns a scalar. Can
49 | be used with any callable function.
50 | run_ssa() : cost function gets multiple trajectories of a PySB model
51 | and returns a scalar. These trajectories are provided as
52 | a pandas.DataFrame. PySB dependent function.
53 | """
54 |
55 | def __init__(self, start=None, save_sampled=False, verbose=False,
56 | shrink_steps=True):
57 | """
58 |
59 | Parameters
60 | ----------
61 | start : list
62 | Starting position
63 | save_sampled : bool
64 | Save each particles position and fitness over all iterations.
65 | verbose : bool
66 | shrink_steps : bool
67 | Shrink max distance traveled by each particle as the number of
68 | iterations increases
69 | """
70 | self.save_sampled = save_sampled
71 | if start is not None:
72 | self.set_start_position(start)
73 | else:
74 | self.start = None
75 | """Starting position"""
76 | self.size = None
77 | self.verbose = verbose
78 | self.best = None
79 | """Best Particle of population"""
80 | self.max_speed = None
81 | self.min_speed = None
82 | self.lb = None
83 | self.ub = None
84 | self.bounds_set = False
85 | self.range = 2
86 | self.all_history = None
87 | """History of all particles positions over all iterations. Only saved
88 | if save_sampled=True"""
89 | self.all_fitness = None
90 | """History of all particles finesses over all iterations. Only saved
91 | if save_sampled=True"""
92 | self.population = []
93 | """Population of particles"""
94 | self.population_fitness = []
95 | """Fitness values of population of particles"""
96 | self.values = []
97 | """Fitness values of the best particle for each iteration"""
98 | self.history = []
99 | """Best particle for each iteration"""
100 | self.update_w = shrink_steps
101 | self._is_setup = False
102 | self.log = setup_logger(verbose)
103 | fi = 2.05 + 2.05 # value from kennedy paper
104 | self.w = 2.0 / np.abs(2.0 - fi - np.sqrt(np.power(fi, 2) - 4 * fi))
105 |
106 | def get_best_value(self):
107 | """ Returns the best fitness value of the population """
108 | return self.best.fitness
109 |
110 | def get_history(self):
111 | """ Returns the history of the run"""
112 | return self.history
113 |
114 | def _generate(self):
115 | """ Creates Particles and sets their speed """
116 | part = Particle(np.random.uniform(self.lb, self.ub, self.size))
117 | part.speed = np.random.uniform(self.min_speed, self.max_speed,
118 | self.size)
119 | part.smin = self.min_speed
120 | part.smax = self.max_speed
121 | return part
122 |
123 | def _setup_pso(self):
124 | """ Sets up everything for PSO. Only does once """
125 | if self.max_speed is None or self.min_speed is None:
126 | self.set_speed()
127 |
128 | if not self.bounds_set:
129 | self.set_bounds()
130 |
131 | if self.start is None:
132 | raise Exception("Must provide a starting position\n"
133 | "Use PSO.set_start_position() \n")
134 |
135 | self._is_setup = True
136 |
137 | def _update_connected(self):
138 | """ Update the population of particles"""
139 | for part in self.population:
140 | if part.best is None or part.best.fitness > part.fitness:
141 | part.best = part
142 | # part.best = deepcopy(part)
143 | # part.best.fitness = deepcopy(part.fitness)
144 | if self.best is None or self.best.fitness > part.fitness:
145 | self.best = Particle(
146 | deepcopy(part.pos),
147 | deepcopy(part.fitness),
148 | deepcopy(part.smin),
149 | deepcopy(part.smax)
150 | )
151 | # self.best = deepcopy(part)
152 | # self.best.fitness = deepcopy(part.fitness)
153 |
154 | def _update_particle_position(self, part):
155 | """ Updates an individual particles position """
156 | phi1 = 2.05
157 | phi2 = 2.05
158 | v_u1 = np.random.uniform(0, 1, self.size) * phi1 * (
159 | part.best.pos - part.pos)
160 | v_u2 = np.random.uniform(0, 1, self.size) * phi2 * (
161 | self.best.pos - part.pos)
162 | part.speed = self.w * (part.speed + v_u1 + v_u2)
163 | np.place(part.speed, part.speed < part.smin, part.smin)
164 | np.place(part.speed, part.speed > part.smax, part.smax)
165 | part.pos += part.speed
166 | for i, pos in enumerate(part.pos):
167 | if pos < self.lb[i]:
168 | part.pos[i] = self.lb[i]
169 | elif pos > self.ub[i]:
170 | part.pos[i] = self.ub[i]
171 |
172 | def return_ranked_populations(self):
173 | """ Returns a ranked population of the particles
174 |
175 | Returns
176 | -------
177 | np.array, np.array
178 | """
179 | positions = []
180 | finesses = []
181 | for n, part in enumerate(self.population):
182 | finesses.append(part.best.fitness)
183 | positions.append(part.best.pos)
184 |
185 | positions = np.array(positions)
186 | finesses = np.array(finesses)
187 | idx = np.argsort(finesses)
188 | return finesses[idx], positions[idx]
189 |
190 | def set_start_position(self, position):
191 | """
192 | Set the starting position for the population of particles.
193 |
194 | Parameters
195 | ----------
196 | position : array
197 |
198 | """
199 | self.start = np.array(position)
200 | self.size = len(position)
201 |
202 | def set_speed(self, speed_min=-10000, speed_max=10000):
203 | """ Sets the max and min speed of the particles.
204 |
205 | This is usually a fraction of the bounds set with `set_bounds`. So if
206 | one sets the bound to be +/- 1 order of magnitude, you can set the
207 | speed to be -.1 and .1, allow the particles to update in 1/10 the
208 | parameter space. This keeps particles near their local position rather
209 | than jumping across parameter space quickly.
210 |
211 | Parameters
212 | ----------
213 | speed_min : float
214 | speed_max : float
215 |
216 | """
217 | self.min_speed = speed_min
218 | self.max_speed = speed_max
219 |
220 | def set_bounds(self, parameter_range=None, lower=None, upper=None):
221 | """ Set the search space bounds that the parameters may search.
222 |
223 | This can be a single float, in which the particles can search plus or
224 | minus the starting values around this. It can also be an array that
225 | must be the same length of the starting position.
226 |
227 | Parameters
228 | ----------
229 | parameter_range : float
230 | If provided parameter_range, the range will be set by the starting
231 | position +/- this value. To set each parameter manually, use
232 | `lower` and `upper` args
233 | lower : array
234 | Lower bounds for parameters
235 | upper : array
236 | Upper bounds for parameters
237 |
238 | """
239 | if self.start is None:
240 | raise Exception("Must provide starting array: %r" % self.start)
241 |
242 | if parameter_range is None and upper is None and lower is None:
243 | raise Exception('Need to provide parameter ranges')
244 |
245 | if parameter_range is None:
246 | if self.range is None:
247 | raise Exception("Please provide parameter range")
248 | parameter_range = self.range
249 |
250 | if lower is None:
251 | lower = self.start - parameter_range
252 | else:
253 | if not self.size == len(lower):
254 | raise Exception("Size of bounds must equal size of particle")
255 | if upper is None:
256 | upper = self.start + parameter_range
257 | else:
258 | if not self.size == len(upper):
259 | raise Exception("Size of bounds must equal size of particle")
260 | self.lb = lower
261 | self.ub = upper
262 | self.bounds_set = True
263 |
264 | def run(self, num_particles, num_iterations, cost_function=None,
265 | num_processors=1, save_samples=False, stop_threshold=1e-5,
266 | max_iter_no_improv=None):
267 | """
268 | Run optimization
269 |
270 | Parameters
271 | ----------
272 | num_particles : int
273 | Number of particles in population, ~20 is a good starting place
274 | num_iterations : int
275 | Number of iterations to perform.
276 | cost_function : callable function
277 | Takes a parameter set and returns a scalar (particles fitness)
278 | num_processors : int
279 | Number of processors to run on. If using scipy, note that you may
280 | need to set OMP_NUM_THREADS=1 to prevent each process from using
281 | more than one CPU.
282 | save_samples : bool
283 | Save ALL positions of particles over time, can require large memory
284 | if num_particles, num_iterations, and len(parameters) is large.
285 | stop_threshold : float
286 | Standard deviation of the particles’ cost function at which the
287 | optimization is stopped
288 | max_iter_no_improv: int
289 | Maximum steps allowed without improvement before the optimization
290 | stops.
291 | """
292 | if self._is_setup:
293 | pass
294 | else:
295 | self._setup_pso()
296 |
297 | if not callable(cost_function):
298 | raise TypeError("Provide a callable cost function")
299 |
300 | history = np.zeros((num_iterations, len(self.start)))
301 | if self.save_sampled or save_samples:
302 | self.all_history = np.zeros(
303 | (num_iterations, num_particles, len(self.start)))
304 | self.all_fitness = np.zeros((num_iterations, num_particles))
305 | values = np.zeros(num_iterations)
306 | self.population = [self._generate() for _ in range(num_particles)]
307 | self.population_fitness = np.zeros(len(self.population))
308 | if max_iter_no_improv is None:
309 | max_iter_no_improv = np.inf
310 | iter_without_improvement = 0
311 | best_fitness = np.inf
312 | with SerialExecutor() if num_processors == 1 else \
313 | ProcessPoolExecutor(max_workers=num_processors) as executor:
314 | for g in range(num_iterations):
315 | if self.update_w:
316 | self.w = (num_iterations - g + 1.) / num_iterations
317 | self.population_fitness = np.array(
318 | list(executor.map(
319 | cost_function,
320 | [p.pos for p in self.population],
321 | )
322 | )
323 | )
324 |
325 | for ind, fit in zip(self.population, self.population_fitness):
326 | ind.fitness = fit
327 | self._update_connected()
328 | for part in self.population:
329 | self._update_particle_position(part)
330 | values[g] = self.best.fitness
331 | history[g] = self.best.pos
332 | if self.save_sampled or save_samples:
333 | curr_fit, curr_pop = self.return_ranked_populations()
334 | self.all_history[g, :, :] = curr_pop
335 | self.all_fitness[g, :] = curr_fit
336 |
337 | if self.verbose:
338 | self.print_stats(g + 1, fitness=self.population_fitness)
339 |
340 | if self.population_fitness.std() < stop_threshold:
341 | self.log.info("Stopping. STD < stop_threshold.")
342 | break
343 | if self.best.fitness < best_fitness:
344 | best_fitness = self.best.fitness
345 | iter_without_improvement = 0
346 | else:
347 | iter_without_improvement += 1
348 | if iter_without_improvement > max_iter_no_improv:
349 | self.log.info("Stopping. max_iter_no_improv reached.")
350 | break
351 | self.values = np.array(values[:g])
352 | self.history = np.array(history[:g, :])
353 |
354 | def _calc_fitness_from_array(self, traj, num_sim, cost_function):
355 | index_names = traj.index.names
356 | traj.reset_index(inplace=True)
357 | for i, part in enumerate(self.population):
358 | start = i * num_sim
359 | end = (i + 1) * num_sim
360 | tmp_results = traj.loc[
361 | traj.simulation.isin(list(range(start, end)))]
362 | tmp_results.set_index(index_names, inplace=True)
363 | error = cost_function(tmp_results)
364 | part.fitness = error
365 | self.population_fitness[i] = error
366 |
367 | def _get_parameters_from_population(self, num_sims, total_sims, n_params):
368 | """ Create param_array for GPU based simulators """
369 | rate_vals = np.zeros((total_sims, n_params))
370 | # create parameters for each particle, creates blocks per num_sims
371 | for i, part in enumerate(self.population):
372 | start = i * num_sims
373 | end = (i + 1) * num_sims
374 | rate_vals[start:end, :] = part.pos
375 | return rate_vals
376 |
377 | def run_ssa(self, model, num_particles, num_iterations, num_sim,
378 | cost_function, simulator, save_samples=False,
379 | stop_threshold=0):
380 | """
381 | Run PSO for a stochastic simulator
382 |
383 | Parameters
384 | ----------
385 | model : pysb.Model
386 | num_particles : int
387 | Number of particles in the swarm.
388 | num_iterations : int
389 | Number of iterations to perform
390 | num_sim : int
391 | Number of SSA simulations to run for each particle.
392 | cost_function : function
393 | Takes a pandas dataframe of PySB trajectories created by running
394 | multiple SSA simulations. Function must return a scalar.
395 | simulator : pysb.Simulator
396 | PySB simulator (CudaSSASimulator or OpenclSSASimulator)
397 | save_samples : bool
398 | stop_threshold : float
399 | """
400 | if self._is_setup:
401 | pass
402 | else:
403 | self._setup_pso()
404 | if simulator is None:
405 | raise ValueError(
406 | "Must provide an SSA simulator to use this method")
407 |
408 | history = []
409 | values = []
410 | self.population = [self._generate() for _ in range(num_particles)]
411 | self.population_fitness = np.zeros((len(self.population)))
412 | total_sims = num_particles * num_sim
413 | rate_p = model.parameters_rules()
414 | rate_mask = np.array([p in rate_p for p in model.parameters])
415 | nominal_values = np.array([p.value for p in model.parameters])
416 | log10_original_values = np.log10(nominal_values[rate_mask])
417 | all_param_vals = np.repeat([nominal_values], total_sims, axis=0)
418 |
419 | for g in range(0, num_iterations):
420 | if self.update_w:
421 | self.w = (num_iterations - g) / num_iterations
422 | rate_values = self._get_parameters_from_population(
423 | num_sim, total_sims, len(log10_original_values)
424 | )
425 |
426 | # convert back from log10 space
427 | all_param_vals[:, rate_mask] = 10 ** rate_values
428 | # reset initials and param_values so simulator won't try to
429 | # duplicate any
430 | simulator.initials = None
431 | simulator.param_values = None
432 | traj = simulator.run(param_values=all_param_vals).dataframe
433 | self._calc_fitness_from_array(traj, num_sim, cost_function)
434 | self._update_connected()
435 | for part in self.population:
436 | self._update_particle_position(part)
437 |
438 | values.append(self.best.fitness)
439 | history.append(deepcopy(self.best.pos))
440 |
441 | if self.save_sampled or save_samples:
442 | curr_fit, curr_pop = self.return_ranked_populations()
443 | self.all_history[g, :, :] = curr_pop
444 | self.all_fitness[g, :] = curr_fit
445 |
446 | if self.verbose:
447 | self.print_stats(g + 1, fitness=self.population_fitness)
448 | if self.population_fitness.std() < stop_threshold:
449 | self.log.info("Stopping criteria reached.")
450 | break
451 |
452 | self.values = np.array(values)
453 | self.history = np.array(history)
454 |
455 | def print_stats(self, iteration, fitness):
456 | if iteration == 1:
457 | self.log.info(stats_header)
458 | self.log.info(
459 | stats_output.format(iteration, self.best.fitness, fitness.mean(),
460 | fitness.min(), fitness.max(), fitness.std())
461 | )
462 |
463 |
464 | class SerialExecutor(Executor):
465 | """ Execute tasks in serial (immediately on submission)
466 | Code originally from PySB.
467 | """
468 |
469 | def submit(self, fn, *args, **kwargs):
470 | f = Future()
471 | try:
472 | result = fn(*args, **kwargs)
473 | except BaseException as e:
474 | f.set_exception(e)
475 | else:
476 | f.set_result(result)
477 |
478 | return f
479 |
--------------------------------------------------------------------------------
/simplepso/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoLab-MSM/simplePSO/37e5eb22da920d30780984dcea327686d6eacbfe/simplepso/tests/__init__.py
--------------------------------------------------------------------------------
/simplepso/tests/test_pso_construction.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from nose.tools import raises
3 |
4 | from simplepso.pso import PSO
5 |
6 |
7 | def h1(individual):
8 | """ Simple two-dimensional function containing several local maxima.
9 | Found in deap.benchmarks.h1
10 | Defined range of [-100, 100]
11 | minimum is at f(8.6998, 6.7665) = 2
12 | """
13 | num = (np.sin(individual[0] - individual[1] / 8)) ** 2 + (np.sin(individual[1] + individual[0] / 8)) ** 2
14 | denum = ((individual[0] - 8.6998) ** 2 + (individual[1] - 6.7665) ** 2) ** 0.5 + 1
15 | return -1 * num / denum
16 |
17 |
18 | def himmelblau(individual):
19 | """The Himmelblau's function is multimodal with 4 defined minimums in
20 | :math:`[-6, 6]^2.
21 |
22 | range [-6, 6]
23 | x_1 = (3.0, 2.0), = 0
24 | x_2 = (-2.805118, 3.131312), = 0
25 | x_3 = (-3.779310, -3.283186), = 0
26 | x_4 = (3.584428, -1.848126), = 0
27 | """
28 | return (individual[0] * individual[0] + individual[1] - 11) ** 2 + \
29 | (individual[0] + individual[1] * individual[1] - 7) ** 2
30 |
31 |
32 | def test_population_creation():
33 | known_sol = [8.6998, 6.7665]
34 | pso = PSO(start=[0, 0], verbose=False, shrink_steps=False)
35 | pso.set_bounds(lower=[-100, -100], upper=[100, 100])
36 | pso.set_speed(-10, 10)
37 | pso.run(num_iterations=100, num_particles=10, cost_function=h1, )
38 | pso.return_ranked_populations()
39 | error = np.sum((pso.best.pos - known_sol) ** 2)
40 | print('True value: [8.6998, 6.7665]. Found:{0}. Error^2 = {1}'.format(
41 | pso.best.pos, error))
42 | assert (error < 0.1)
43 |
44 |
45 | def test_himmelblau():
46 | """ test to see if PSO can find simple minimum
47 | """
48 | minimums = [[3.0, 2.0],
49 | [-2.805118, 3.131312],
50 | [-3.779310, -3.283186],
51 | [3.584428, -1.848126]]
52 |
53 | pso = PSO(start=[10, 0], verbose=False)
54 | pso.set_bounds(lower=[-100, -100], upper=[100, 100])
55 | pso.run(num_iterations=100, num_particles=10, cost_function=himmelblau, )
56 | good_min = False
57 | for i in minimums:
58 | if np.sum((pso.best.pos - i) ** 2) < .1:
59 | good_min = True
60 | error = np.sum((pso.best.pos - i) ** 2)
61 | found_min = i
62 | if good_min:
63 | print('Found minimum')
64 | print('True value: {0}. Found:{1}. Error^2 = {2}'.format(
65 | found_min, pso.best.pos, error)
66 | )
67 |
68 |
69 | @raises(Exception)
70 | def test_missing_cost_function():
71 | pso = PSO(start=[10, 0], verbose=False)
72 | pso.set_bounds(lower=[-100, -100], upper=[100, 100])
73 | pso.run(num_iterations=100, num_particles=10)
74 |
75 |
76 | @raises(Exception)
77 | def test_mismatched_bounds():
78 | pso = PSO(start=[10, 0], verbose=False)
79 | pso.set_bounds(lower=[-100, 0, -100], upper=[100, 100])
80 | pso.run(num_iterations=100, num_particles=10, cost_function=himmelblau, )
81 |
82 |
83 | @raises(Exception)
84 | def test_no_bounds():
85 | pso = PSO(start=[10, 0], verbose=False)
86 | pso.run(num_iterations=100, num_particles=10, cost_function=himmelblau, )
87 |
--------------------------------------------------------------------------------
/simplepso/tests/test_robertson.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | from pysb.examples.robertson import model
4 | from pysb.integrate import odesolve
5 | import pysb
6 |
7 |
8 |
9 |
10 | t = np.linspace(0, 40,100)
11 | obs_names = ['A_total', 'C_total']
12 |
13 |
14 |
15 | solver = pysb.integrate.Solver(model, t, integrator='vode',rtol=1e-8, atol=1e-8)
16 |
17 | solver.run()
18 | def normalize(trajectories):
19 | """Rescale a matrix of model trajectories to 0-1"""
20 | ymin = trajectories.min(0)
21 | ymax = trajectories.max(0)
22 | return (trajectories - ymin) / (ymax - ymin)
23 |
24 | def extract_records(recarray, names):
25 | """Convert a record-type array and list of names into a float array"""
26 | return np.vstack([recarray[name] for name in names]).T
--------------------------------------------------------------------------------