├── pycollocation ├── tests │ ├── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── solow.py │ │ └── ramsey_cass_koopmans.py │ ├── test_spence_model.py │ ├── test_solow_model.py │ └── test_ramsey_model.py ├── problems │ ├── __init__.py │ ├── ivp.py │ └── bvp.py ├── basis_functions │ ├── basis_functions.py │ ├── __init__.py │ ├── basis_splines.py │ └── polynomials.py ├── __init__.py └── solvers │ ├── __init__.py │ ├── over_identified.py │ ├── solutions.py │ └── solvers.py ├── MANIFEST.in ├── setup.cfg ├── .coveralls.yml ├── .codeclimate.yml ├── docs ├── source │ ├── modules.rst │ ├── index.rst │ ├── LICENSE.rst │ ├── pycollocation.rst │ └── conf.py ├── rtd-pip-requirements.txt ├── Makefile └── make.bat ├── appveyor.yml ├── CITATION ├── .gitignore ├── .travis.yml ├── LICENSE ├── setup.py ├── README.rst └── examples ├── basic-new-keynesian-model.ipynb └── heat-exchanger.ipynb /pycollocation/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst LICENSE CITATION -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | repo_token: L5wIIz7Vkan2HQxODrAoqgYB3OEHuAxtI 3 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | Python: true 3 | 4 | exclude_paths: 5 | - "pycollocation/tests/*" -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | pycollocation 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | pycollocation 8 | -------------------------------------------------------------------------------- /docs/rtd-pip-requirements.txt: -------------------------------------------------------------------------------- 1 | # These are the required packages for the pycollocation package to build 2 | Sphinx 3 | ipython 4 | numpy 5 | numpydoc 6 | scipy 7 | matplotlib 8 | sympy 9 | pandas -------------------------------------------------------------------------------- /pycollocation/problems/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Objects imported here will live in the `pycollocation.problems` namespace 3 | 4 | """ 5 | from . bvp import TwoPointBVP, TwoPointBVPLike 6 | from . ivp import IVP 7 | 8 | __all__ = ["IVP", "TwoPointBVP", "TwoPointBVPLike"] 9 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - ps: curl https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe 3 | - Miniconda3-latest-Windows-x86_64.exe 4 | 5 | build_script: 6 | - "python setup.py build" 7 | 8 | test_script: 9 | - "python setup.py nosetests" 10 | -------------------------------------------------------------------------------- /pycollocation/tests/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Objects imported here will live in the `pycollocation.tests.models` namespace 3 | 4 | """ 5 | from . solow import SolowModel 6 | from . ramsey_cass_koopmans import RamseyCassKoopmansModel 7 | 8 | __all__ = ["RamseyCassKoopmansModel", "SolowModel"] 9 | -------------------------------------------------------------------------------- /pycollocation/basis_functions/basis_functions.py: -------------------------------------------------------------------------------- 1 | class BasisFunctionLike(object): 2 | 3 | @classmethod 4 | def derivatives_factory(cls, coef, *args, **kwargs): 5 | raise NotImplementedError 6 | 7 | @classmethod 8 | def functions_factory(cls, coef, *args, **kwargs): 9 | raise NotImplementedError 10 | -------------------------------------------------------------------------------- /CITATION: -------------------------------------------------------------------------------- 1 | @Manual{pyCollocation, 2 | title = {\pkg{pyCollocation}: \proglang{Python} library for solving boundary value problems}, 3 | author = {David R. Pugh}, 4 | organization = {pyCollocation}, 5 | year = {2015}, 6 | doi = {10.5281/zenodo.16761}, 7 | url = {https://github.com/davidrpugh/pyCollocation} 8 | } -------------------------------------------------------------------------------- /pycollocation/basis_functions/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Objects imported here will live in the `pycollocation.basis_functions` 3 | namespace. 4 | 5 | """ 6 | from . basis_functions import BasisFunctionLike 7 | from . basis_splines import BSplineBasis 8 | from . polynomials import PolynomialBasis 9 | 10 | __all__ = ["BasisFunctionLike", "BSplineBasis", "PolynomialBasis"] 11 | -------------------------------------------------------------------------------- /pycollocation/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Objects imported here will live in the `pycollocation` namespace 3 | 4 | """ 5 | from pkg_resources import get_distribution 6 | 7 | from . import basis_functions 8 | from . import problems 9 | from . import solvers 10 | 11 | __all__ = ["basis_functions", "problems", "solvers"] 12 | __version__ = get_distribution('pyCollocation').version 13 | -------------------------------------------------------------------------------- /pycollocation/solvers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Objects imported here will live in the `pycollocation.solvers` namespace 3 | 4 | """ 5 | from . over_identified import LeastSquaresSolver 6 | from . solvers import Solver, SolverLike 7 | from . solutions import SolutionLike, Solution 8 | 9 | __all__ = ["LeastSquaresSolver", "Solver", "Solution", "SolutionLike", 10 | "SolverLike"] 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore all .pyc files 2 | *.pyc 3 | 4 | # ignore ipython checkpoints 5 | .ipynb_checkpoints/ 6 | 7 | # ignore coverage statistics 8 | .coverage 9 | 10 | # ignore build info 11 | dist/ 12 | 13 | # ignore *.egg-info 14 | *.egg-info/ 15 | 16 | # ignore version file 17 | pycollocation/version.py 18 | 19 | #ignore MANIFEST 20 | MANIFEST 21 | 22 | # ignore egg-info 23 | pycollocation.egg-info 24 | 25 | # ignore docs/build 26 | docs/build 27 | 28 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. pyCollocation documentation master file, created by 2 | sphinx-quickstart on Thu Apr 30 21:02:38 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to pyCollocation's documentation! 7 | ========================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | LICENSE 15 | modules 16 | 17 | 18 | 19 | Indices and tables 20 | ================== 21 | 22 | * :ref:`genindex` 23 | * :ref:`modindex` 24 | * :ref:`search` 25 | 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: python 4 | 5 | python: 6 | - 2.7 7 | - 3.5 8 | 9 | notifications: 10 | email: false 11 | 12 | branches: 13 | only: 14 | - master 15 | - develop 16 | 17 | before_install: 18 | - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh 19 | - chmod +x miniconda.sh 20 | - ./miniconda.sh -b -p /home/travis/miniconda 21 | - export PATH=/home/travis/miniconda/bin:$PATH 22 | - conda update --yes conda 23 | 24 | install: 25 | - conda install --yes python=$TRAVIS_PYTHON_VERSION numpy scipy nose pip 26 | - pip install coveralls coverage 27 | - python setup.py install 28 | 29 | script: 30 | - nosetests --with-coverage --cover-package=pycollocation 31 | 32 | after_success: 33 | - coveralls 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David R. Pugh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /pycollocation/problems/ivp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes for modeling initial value problems. 3 | 4 | @author : David R. Pugh 5 | 6 | """ 7 | from . bvp import TwoPointBVP 8 | 9 | 10 | class IVP(TwoPointBVP): 11 | r""" 12 | Class for modeling Initial Value Problems (IVPs). 13 | 14 | Attributes 15 | ---------- 16 | bcs_lower : function 17 | Function that calculates the difference between the lower boundary 18 | conditions and the current values of the model dependent variables. 19 | number_bcs_lower : int 20 | The number of lower boundary conditions (BCS). 21 | number_odes : int 22 | The number of Ordinary Differential Equations (ODEs) in the system. 23 | params : dict(str: float) 24 | A dictionary of model parameters. 25 | rhs : function 26 | Function which calculates the value of the right-hand side of a 27 | system of Ordinary Differential Equations (ODEs). 28 | 29 | """ 30 | 31 | def __init__(self, bcs_lower, number_bcs_lower, number_odes, params, rhs): 32 | super(IVP, self).__init__(bcs_lower, None, number_bcs_lower, 33 | number_odes, params, rhs) 34 | -------------------------------------------------------------------------------- /docs/source/LICENSE.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015 David R. Pugh 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | -------------------------------------------------------------------------------- /pycollocation/basis_functions/basis_splines.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class for approximating the solution to two-point boundary value problems using 3 | B-splines as basis functions. 4 | 5 | @author: davidrpugh 6 | 7 | """ 8 | import functools 9 | 10 | from scipy import interpolate 11 | 12 | from . import basis_functions 13 | 14 | 15 | class BSplineBasis(basis_functions.BasisFunctionLike): 16 | 17 | @staticmethod 18 | def _basis_spline_factory(coef, degree, knots, der, ext): 19 | """Return a B-Spline given some coefficients.""" 20 | return functools.partial(interpolate.splev, tck=(knots, coef, degree), der=der, ext=ext) 21 | 22 | @classmethod 23 | def derivatives_factory(cls, coef, degree, knots, ext, **kwargs): 24 | """ 25 | Given some coefficients, return a the derivative of a B-spline. 26 | 27 | """ 28 | return cls._basis_spline_factory(coef, degree, knots, 1, ext) 29 | 30 | @classmethod 31 | def fit(cls, *args, **kwargs): 32 | """Possibly just wrap interpolate.splprep?""" 33 | return interpolate.splprep(*args, **kwargs) 34 | 35 | @classmethod 36 | def functions_factory(cls, coef, degree, knots, ext, **kwargs): 37 | """ 38 | Given some coefficients, return a B-spline. 39 | 40 | """ 41 | return cls._basis_spline_factory(coef, degree, knots, 0, ext) 42 | -------------------------------------------------------------------------------- /pycollocation/solvers/over_identified.py: -------------------------------------------------------------------------------- 1 | """ 2 | Solvers for over-identified systems. 3 | 4 | @author : davidrpugh 5 | 6 | """ 7 | from scipy import optimize 8 | 9 | from . import solvers 10 | 11 | 12 | class LeastSquaresSolver(solvers.Solver): 13 | 14 | def solve(self, basis_kwargs, boundary_points, coefs_array, nodes, problem, 15 | **solver_options): 16 | """ 17 | Solve a boundary value problem using the collocation method. 18 | 19 | Parameters 20 | ---------- 21 | basis_kwargs : dict 22 | Dictionary of keyword arguments used to build basis functions. 23 | coefs_array : numpy.ndarray 24 | Array of coefficients for basis functions defining the initial 25 | condition. 26 | problem : bvp.TwoPointBVPLike 27 | A two-point boundary value problem (BVP) to solve. 28 | solver_options : dict 29 | Dictionary of options to pass to the non-linear equation solver. 30 | 31 | Return 32 | ------ 33 | solution: solutions.SolutionLike 34 | An instance of the SolutionLike class representing the solution to 35 | the two-point boundary value problem (BVP) 36 | 37 | Notes 38 | ----- 39 | 40 | """ 41 | result = optimize.leastsq(self._compute_residuals, 42 | x0=coefs_array, 43 | args=(basis_kwargs, boundary_points, nodes, problem), 44 | **solver_options) 45 | solution = self._solution_factory(basis_kwargs, result[0], nodes, 46 | problem, result) 47 | return solution 48 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from distutils.core import setup 4 | 5 | 6 | def read(*paths): 7 | """Build a file path from *paths* and return the contents.""" 8 | with open(os.path.join(*paths), 'r') as f: 9 | return f.read() 10 | 11 | # Meta information 12 | DESCRIPTION = ("Python package for solving initial value problems (IVP) " + 13 | "and two-point boundary value problems (2PBVP) using the " + 14 | "collocation method.") 15 | 16 | CLASSIFIERS = ['Development Status :: 3 - Alpha', 17 | 'Intended Audience :: Education', 18 | 'Intended Audience :: Science/Research', 19 | 'License :: OSI Approved :: MIT License', 20 | 'Operating System :: OS Independent', 21 | 'Programming Language :: Python', 22 | 'Programming Language :: Python :: 2', 23 | 'Programming Language :: Python :: 3', 24 | 'Programming Language :: Python :: 2.7', 25 | 'Programming Language :: Python :: 3.3', 26 | 'Programming Language :: Python :: 3.4', 27 | 'Topic :: Scientific/Engineering', 28 | ] 29 | 30 | PACKAGES = ['pycollocation', 'pycollocation.basis_functions', 31 | 'pycollocation.problems', 'pycollocation.solvers', 32 | 'pycollocation.tests', 'pycollocation.tests.models'] 33 | 34 | setup( 35 | name="pycollocation", 36 | packages=PACKAGES, 37 | version='0.6.0-alpha', 38 | description=DESCRIPTION, 39 | long_description=read('README.rst'), 40 | license="MIT License", 41 | author="davidrpugh", 42 | author_email="david.pugh@maths.ox.ac.uk", 43 | url='https://github.com/davidrpugh/pyCollocation', 44 | classifiers=CLASSIFIERS, 45 | ) 46 | -------------------------------------------------------------------------------- /pycollocation/tests/models/solow.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from ... import problems 4 | 5 | 6 | class SolowModel(problems.IVP): 7 | """ 8 | Class representing a generic Solow growth model. 9 | 10 | Attributes 11 | ---------- 12 | equilibrium_capital : function 13 | Equilibrium value for capital (per unit effective labor). 14 | intensive_output : function 15 | Output (per unit effective labor supply). 16 | params : dict(str: float) 17 | Dictionary of model parameters. 18 | 19 | """ 20 | 21 | def __init__(self, f, k_star, params): 22 | """ 23 | Initialize an instance of the SolowModel class. 24 | 25 | Parameters 26 | ---------- 27 | f : function 28 | Output (per unit effective labor supply). 29 | k_star : function 30 | Equilibrium value for capital (per unit effective labor). 31 | params : dict(str: float) 32 | Dictionary of model parameters. 33 | 34 | """ 35 | rhs = self._rhs_factory(f) 36 | self._equilbrium_capital = k_star 37 | self._intensive_output = f 38 | super(SolowModel, self).__init__(self._initial_condition, 1, 1, params, rhs) 39 | 40 | @property 41 | def equilibrium_capital(self): 42 | return self._equilbrium_capital 43 | 44 | @property 45 | def intensive_output(self): 46 | return self._intensive_output 47 | 48 | @staticmethod 49 | def _initial_condition(t, k, k0, **params): 50 | return [k - k0] 51 | 52 | @classmethod 53 | def _solow_model(cls, t, k, f, delta, g, n, s, **params): 54 | return [s * f(k, **params) - (g + n + delta) * k] 55 | 56 | @classmethod 57 | def _rhs_factory(cls, f): 58 | return functools.partial(cls._solow_model, f=f) 59 | -------------------------------------------------------------------------------- /docs/source/pycollocation.rst: -------------------------------------------------------------------------------- 1 | pycollocation package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pycollocation.boundary_value_problems module 8 | -------------------------------------------- 9 | 10 | .. automodule:: pycollocation.boundary_value_problems 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pycollocation.differential_equations module 16 | ------------------------------------------- 17 | 18 | .. automodule:: pycollocation.differential_equations 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pycollocation.models module 24 | --------------------------- 25 | 26 | .. automodule:: pycollocation.models 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pycollocation.orthogonal_polynomials module 32 | ------------------------------------------- 33 | 34 | .. automodule:: pycollocation.orthogonal_polynomials 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pycollocation.solvers module 40 | ---------------------------- 41 | 42 | .. automodule:: pycollocation.solvers 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pycollocation.symbolics module 48 | ------------------------------ 49 | 50 | .. automodule:: pycollocation.symbolics 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | pycollocation.version module 56 | ---------------------------- 57 | 58 | .. automodule:: pycollocation.version 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | pycollocation.visualizers module 64 | -------------------------------- 65 | 66 | .. automodule:: pycollocation.visualizers 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | 72 | Module contents 73 | --------------- 74 | 75 | .. automodule:: pycollocation 76 | :members: 77 | :undoc-members: 78 | :show-inheritance: 79 | -------------------------------------------------------------------------------- /pycollocation/solvers/solutions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes for representing solutions to boundary value problems. 3 | 4 | @author : davidrpugh 5 | 6 | """ 7 | 8 | 9 | class SolutionLike(object): 10 | 11 | @property 12 | def basis_kwargs(self): 13 | return self._basis_kwargs 14 | 15 | @property 16 | def functions(self): 17 | return self._functions 18 | 19 | @property 20 | def nodes(self): 21 | return self._nodes 22 | 23 | @property 24 | def problem(self): 25 | return self._problem 26 | 27 | @property 28 | def residual_function(self): 29 | return self._residual_function 30 | 31 | @property 32 | def result(self): 33 | return self._result 34 | 35 | 36 | class Solution(SolutionLike): 37 | """Class representing the solution to a Boundary Value Problem (BVP).""" 38 | 39 | def __init__(self, basis_kwargs, functions, nodes, problem, residual_function, result): 40 | """ 41 | Initialize an instance of the Solution class. 42 | 43 | Parameters 44 | ---------- 45 | basis_kwargs : dict 46 | functions : list 47 | nodes : numpy.ndarray 48 | problem : TwoPointBVPLike 49 | residual_function : callable 50 | result : OptimizeResult 51 | 52 | """ 53 | self._basis_kwargs = basis_kwargs 54 | self._functions = functions 55 | self._nodes = nodes 56 | self._problem = problem 57 | self._residual_function = residual_function 58 | self._result = result 59 | 60 | def evaluate_residual(self, points): 61 | return self.residual_function(points) 62 | 63 | def evaluate_solution(self, points): 64 | return [f(points) for f in self.functions] 65 | 66 | def normalize_residuals(self, points): 67 | """Normalize residuals by the level of the variable.""" 68 | residuals = self.evaluate_residual(points) 69 | solutions = self.evaluate_solution(points) 70 | return [resid / soln for resid, soln in zip(residuals, solutions)] 71 | -------------------------------------------------------------------------------- /pycollocation/basis_functions/polynomials.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class for approximating the solution to two-point boundary value problems using 3 | either standard or orthogonal polynomials as basis functions. 4 | 5 | @author: davidrpugh 6 | 7 | """ 8 | import numpy as np 9 | 10 | from . import basis_functions 11 | 12 | 13 | class PolynomialBasis(basis_functions.BasisFunctionLike): 14 | 15 | _valid_kinds = ['Polynomial', 'Chebyshev', 'Legendre', 'Laguerre', 'Hermite'] 16 | 17 | @staticmethod 18 | def _basis_monomial_coefs(degree): 19 | """Return coefficients for a monomial of a given degree.""" 20 | return np.append(np.zeros(degree), 1) 21 | 22 | @classmethod 23 | def _basis_polynomial_factory(cls, kind): 24 | """Return a polynomial given some coefficients.""" 25 | valid_kind = cls._validate(kind) 26 | basis_polynomial = getattr(np.polynomial, valid_kind) 27 | return basis_polynomial 28 | 29 | @classmethod 30 | def _validate(cls, kind): 31 | """Validate the kind argument.""" 32 | if kind not in cls._valid_kinds: 33 | mesg = "'kind' must be one of {}, {}, {}, or {}." 34 | raise ValueError(mesg.format(*cls._valid_kinds)) 35 | else: 36 | return kind 37 | 38 | @classmethod 39 | def derivatives_factory(cls, coef, domain, kind, **kwargs): 40 | """ 41 | Given some coefficients, return a the derivative of a certain kind of 42 | orthogonal polynomial defined over a specific domain. 43 | 44 | """ 45 | basis_polynomial = cls._basis_polynomial_factory(kind) 46 | return basis_polynomial(coef, domain).deriv() 47 | 48 | @classmethod 49 | def fit(cls, ts, xs, degree, domain, kind): 50 | basis_polynomial = cls._basis_polynomial_factory(kind) 51 | return basis_polynomial.fit(ts, xs, degree, domain) 52 | 53 | @classmethod 54 | def functions_factory(cls, coef, domain, kind, **kwargs): 55 | """ 56 | Given some coefficients, return a certain kind of orthogonal polynomial 57 | defined over a specific domain. 58 | 59 | """ 60 | basis_polynomial = cls._basis_polynomial_factory(kind) 61 | return basis_polynomial(coef, domain) 62 | 63 | @classmethod 64 | def roots(cls, degree, domain, kind): 65 | """Return optimal collocation nodes for some orthogonal polynomial.""" 66 | basis_coefs = cls._basis_monomial_coefs(degree) 67 | basis_poly = cls.functions_factory(basis_coefs, domain, kind) 68 | return basis_poly.roots() 69 | -------------------------------------------------------------------------------- /pycollocation/tests/test_spence_model.py: -------------------------------------------------------------------------------- 1 | from nose import tools 2 | 3 | import numpy as np 4 | from scipy import stats 5 | 6 | from .. import basis_functions 7 | from .. import problems 8 | from .. import solvers 9 | 10 | 11 | def analytic_solution(y, nL, alpha): 12 | """ 13 | Analytic solution to the differential equation describing the signaling 14 | equilbrium of the Spence (1974) model. 15 | 16 | """ 17 | D = ((1 + alpha) / 2) * (nL / yL(nL, alpha)**-alpha)**2 - yL(nL, alpha)**(1 + alpha) 18 | return y**(-alpha) * (2 * (y**(1 + alpha) + D) / (1 + alpha))**0.5 19 | 20 | 21 | def spence_model(y, n, alpha, **params): 22 | return [(n**-1 - alpha * n * y**(alpha - 1)) / y**alpha] 23 | 24 | 25 | def initial_condition(y, n, nL, alpha, **params): 26 | return [n - nL] 27 | 28 | 29 | def yL(nL, alpha): 30 | return (nL**2 * alpha)**(1 / (1 - alpha)) 31 | 32 | 33 | def initial_mesh(yL, yH, num, problem): 34 | ys = np.linspace(yL, yH, num=num) 35 | ns = problem.params['nL'] + np.sqrt(ys) 36 | return ys, ns 37 | 38 | 39 | random_seed = np.random.randint(2147483647) 40 | params = {'nL': 1.0, 'alpha': 0.15} 41 | test_problem = problems.IVP(initial_condition, 1, 1, params, spence_model) 42 | 43 | 44 | def test_bspline_collocation(): 45 | """Tests B-spline collocation.""" 46 | bspline_basis = basis_functions.BSplineBasis() 47 | solver = solvers.Solver(bspline_basis) 48 | 49 | boundary_points = (yL(**params), 10) 50 | ys, ns = initial_mesh(*boundary_points, num=250, problem=test_problem) 51 | 52 | tck, u = bspline_basis.fit([ns], u=ys, k=5, s=0) 53 | knots, coefs, k = tck 54 | initial_coefs = np.hstack(coefs) 55 | 56 | basis_kwargs = {'knots': knots, 'degree': k, 'ext': 2} 57 | nodes = np.linspace(*boundary_points, num=249) 58 | solution = solver.solve(basis_kwargs, boundary_points, initial_coefs, 59 | nodes, test_problem) 60 | 61 | # check that solver terminated successfully 62 | msg = "Solver failed!\nSeed: {}\nModel params: {}\n" 63 | tools.assert_true(solution.result.success, 64 | msg=msg.format(random_seed, test_problem.params)) 65 | 66 | # compute the residuals 67 | normed_residuals = solution.normalize_residuals(ys) 68 | 69 | # check that residuals are close to zero on average 70 | tools.assert_true(np.mean(normed_residuals) < 1e-6, 71 | msg=msg.format(random_seed, test_problem.params)) 72 | 73 | # check that the numerical and analytic solutions are close 74 | numeric_soln = solution.evaluate_solution(ys) 75 | analytic_soln = analytic_solution(ys, **test_problem.params) 76 | tools.assert_true(np.mean(numeric_soln - analytic_soln) < 1e-6) 77 | -------------------------------------------------------------------------------- /pycollocation/problems/bvp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes for representing boundary value problems. 3 | 4 | @author : David R. Pugh 5 | 6 | """ 7 | 8 | 9 | class TwoPointBVPLike(object): 10 | 11 | @property 12 | def bcs_lower(self): 13 | """ 14 | Function that calculates the difference between the lower boundary 15 | conditions and the current values of the model dependent variables. 16 | 17 | :getter: Return the current boundary condition. 18 | :type: function 19 | 20 | """ 21 | return self._bcs_lower 22 | 23 | @property 24 | def bcs_upper(self): 25 | """ 26 | Function that calculates the difference between the upper boundary 27 | conditions and the current values of the model dependent variables. 28 | 29 | :getter: Return the current boundary condition. 30 | :type: function 31 | 32 | """ 33 | return self._bcs_upper 34 | 35 | @property 36 | def number_bcs_lower(self): 37 | """ 38 | The number of lower boundary conditions (BCS). 39 | 40 | :getter: Return the number of lower BCS. 41 | :type: int 42 | 43 | """ 44 | return self._number_bcs_lower 45 | 46 | @property 47 | def number_odes(self): 48 | """ 49 | The number of Ordinary Differential Equations (ODEs) in the system. 50 | 51 | :getter: Return the number of ODEs. 52 | :type: int 53 | 54 | """ 55 | return self._number_odes 56 | 57 | @property 58 | def params(self): 59 | """ 60 | A dictionary of parameters. 61 | 62 | :getter: Return the current parameters. 63 | :type: function 64 | 65 | """ 66 | return self._params 67 | 68 | @property 69 | def rhs(self): 70 | """ 71 | Function which calculates the value of the right-hand side of a 72 | system of Ordinary Differential Equations (ODEs). 73 | 74 | :getter: Return the current rhs. 75 | :type: function 76 | 77 | """ 78 | return self._rhs 79 | 80 | 81 | class TwoPointBVP(TwoPointBVPLike): 82 | r""" 83 | Class for representing Two-Point Boundary Value Problems (BVP). 84 | 85 | Attributes 86 | ---------- 87 | bcs_lower : function 88 | Function that calculates the difference between the lower boundary 89 | conditions and the current values of the model dependent variables. 90 | bcs_upper : function 91 | Function that calculates the difference between the upper boundary 92 | conditions and the current values of the model dependent variables. 93 | number_bcs_lower : int 94 | The number of lower boundary conditions (BCS). 95 | number_odes : int 96 | The number of Ordinary Differential Equations (ODEs) in the system. 97 | params : dict(str: float) 98 | A dictionary of parameters. 99 | rhs : function 100 | Function which calculates the value of the right-hand side of a 101 | system of Ordinary Differential Equations (ODEs). 102 | 103 | """ 104 | 105 | def __init__(self, bcs_lower, bcs_upper, number_bcs_lower, number_odes, 106 | params, rhs): 107 | self._bcs_lower = bcs_lower 108 | self._bcs_upper = bcs_upper 109 | self._number_bcs_lower = number_bcs_lower 110 | self._number_odes = number_odes 111 | self._params = dict(params) # shallow copy! 112 | self._rhs = rhs 113 | -------------------------------------------------------------------------------- /pycollocation/tests/test_solow_model.py: -------------------------------------------------------------------------------- 1 | from nose import tools 2 | 3 | import numpy as np 4 | from scipy import stats 5 | 6 | from . import models 7 | from .. import basis_functions 8 | from .. import solvers 9 | 10 | 11 | def analytic_solution(t, k0, alpha, delta, g, n, s, **params): 12 | """Analytic solution for model with Cobb-Douglas production.""" 13 | lmbda = (g + n + delta) * (1 - alpha) 14 | ks = (((s / (g + n + delta)) * (1 - np.exp(-lmbda * t)) + 15 | k0**(1 - alpha) * np.exp(-lmbda * t))**(1 / (1 - alpha))) 16 | return ks 17 | 18 | 19 | def cobb_douglas_output(k, alpha, **params): 20 | """Intensive output has Cobb-Douglas functional form.""" 21 | return k**alpha 22 | 23 | 24 | def equilibrium_capital(alpha, delta, g, n, s, **params): 25 | """Equilibrium value of capital (per unit effective labor supply).""" 26 | return (s / (g + n + delta))**(1 / (1 - alpha)) 27 | 28 | 29 | def generate_random_params(scale, seed): 30 | np.random.seed(seed) 31 | 32 | # random g, n, delta such that sum of these params is positive 33 | g, n = stats.norm.rvs(0.05, scale, size=2) 34 | delta, = stats.lognorm.rvs(scale, loc=g + n, size=1) 35 | assert g + n + delta > 0 36 | 37 | # s and alpha must be on (0, 1) (but lower values are more reasonable) 38 | s, alpha = stats.beta.rvs(a=1, b=3, size=2) 39 | 40 | # choose k0 so that it is not too far from equilibrium 41 | kstar = equilibrium_capital(alpha, delta, g, n, s) 42 | k0, = stats.uniform.rvs(0.5 * kstar, 1.5 * kstar, size=1) 43 | assert k0 > 0 44 | 45 | params = {'g': g, 'n': n, 'delta': delta, 's': s, 'alpha': alpha, 46 | 'k0': k0} 47 | 48 | return params 49 | 50 | 51 | def initial_mesh(t, T, num, problem): 52 | ts = np.linspace(t, T, num) 53 | kstar = equilibrium_capital(**problem.params) 54 | ks = kstar - (kstar - problem.params['k0']) * np.exp(-ts) 55 | return ts, ks 56 | 57 | 58 | random_seed = np.random.randint(2147483647) 59 | random_params = generate_random_params(0.1, random_seed) 60 | test_problem = models.SolowModel(cobb_douglas_output, equilibrium_capital, 61 | random_params) 62 | 63 | polynomial_basis = basis_functions.PolynomialBasis() 64 | solver = solvers.Solver(polynomial_basis) 65 | 66 | 67 | def _test_polynomial_collocation(basis_kwargs, boundary_points, num=1000): 68 | """Helper function for testing various kinds of polynomial collocation.""" 69 | ts, ks = initial_mesh(*boundary_points, num=num, problem=test_problem) 70 | k_poly = polynomial_basis.fit(ts, ks, **basis_kwargs) 71 | initial_coefs = k_poly.coef 72 | nodes = polynomial_basis.roots(**basis_kwargs) 73 | 74 | solution = solver.solve(basis_kwargs, boundary_points, initial_coefs, 75 | nodes, test_problem) 76 | 77 | # check that solver terminated successfully 78 | msg = "Solver failed!\nSeed: {}\nModel params: {}\n" 79 | tools.assert_true(solution.result.success, 80 | msg=msg.format(random_seed, test_problem.params)) 81 | 82 | # compute the residuals 83 | normed_residuals = solution.normalize_residuals(ts) 84 | 85 | # check that residuals are close to zero on average 86 | tools.assert_true(np.mean(normed_residuals) < 1e-6, 87 | msg=msg.format(random_seed, test_problem.params)) 88 | 89 | # check that the numerical and analytic solutions are close 90 | numeric_soln = solution.evaluate_solution(ts) 91 | analytic_soln = analytic_solution(ts, **test_problem.params) 92 | tools.assert_true(np.mean(numeric_soln - analytic_soln) < 1e-6) 93 | 94 | 95 | def test_chebyshev_collocation(): 96 | """Test collocation solver using Chebyshev polynomials for basis.""" 97 | boundary_points = (0, 100) 98 | basis_kwargs = {'kind': 'Chebyshev', 'degree': 50, 'domain': boundary_points} 99 | _test_polynomial_collocation(basis_kwargs, boundary_points) 100 | 101 | 102 | def test_legendre_collocation(): 103 | """Test collocation solver using Legendre polynomials for basis.""" 104 | boundary_points = (0, 100) 105 | basis_kwargs = {'kind': 'Legendre', 'degree': 50, 'domain': boundary_points} 106 | _test_polynomial_collocation(basis_kwargs, boundary_points) 107 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pyCollocation 2 | ============= 3 | 4 | |Build Status| |Coverage Status| |Documentation Status| |Code Climate| |Latest Version| |Downloads| |DOI| 5 | 6 | .. |Build Status| image:: https://travis-ci.org/davidrpugh/pyCollocation.svg?branch=master 7 | :target: https://travis-ci.org/davidrpugh/pyCollocation 8 | .. |Coverage Status| image:: https://coveralls.io/repos/davidrpugh/pyCollocation/badge.svg?branch=master 9 | :target: https://coveralls.io/r/davidrpugh/pyCollocation?branch=master 10 | .. |Code Climate| image:: https://codeclimate.com/github/davidrpugh/pyCollocation/badges/gpa.svg 11 | :target: https://codeclimate.com/github/davidrpugh/pyCollocation 12 | .. |Latest Version| image:: https://img.shields.io/pypi/v/pyCollocation.svg 13 | :target: https://pypi.python.org/pypi/pyCollocation/ 14 | .. |Downloads| image:: https://img.shields.io/pypi/dm/pyCollocation.svg 15 | :target: https://pypi.python.org/pypi/pyCollocation/ 16 | .. |DOI| image:: https://zenodo.org/badge/doi/10.5281/zenodo.33724.svg 17 | :target: http://dx.doi.org/10.5281/zenodo.33724 18 | .. |Documentation Status| image:: https://readthedocs.org/projects/pycollocation/badge/?version=latest 19 | :target: https://readthedocs.org/projects/pycollocation/?badge=latest 20 | 21 | 22 | Python package for solving initial value problems (IVP) and two-point boundary value problems (2PBVP) using the collocation method with various basis functions. Currently I have implemented the following basis functions: 23 | 24 | - Polynomials: Standard_, Chebyshev_, Laguerre_, Legendre_, and Hermite_. 25 | 26 | .. _Standard: https://en.wikipedia.org/wiki/Polynomial 27 | .. _Chebyshev: http://en.wikipedia.org/wiki/Chebyshev_polynomials 28 | .. _Laguerre: http://en.wikipedia.org/wiki/Laguerre_polynomials 29 | .. _Legendre: http://en.wikipedia.org/wiki/Legendre_polynomials 30 | .. _Hermite: http://en.wikipedia.org/wiki/Hermite_polynomials 31 | 32 | Installation 33 | ------------ 34 | 35 | Assuming you have `pip`_ on your computer (as will be the case if you've `installed Anaconda`_) you can install the latest stable release of ``pycollocation`` by typing 36 | 37 | .. code:: bash 38 | 39 | pip install pycollocation 40 | 41 | at a terminal prompt. 42 | 43 | .. _pip: https://pypi.python.org/pypi/pip 44 | .. _`installed Anaconda`: http://quant-econ.net/getting_started.html#installing-anaconda 45 | 46 | Example notebooks 47 | ----------------- 48 | 49 | Economics 50 | ~~~~~~~~~ 51 | 52 | There are a number of example notebooks that demonstrate how to use the library to solve seminal models in the economics literature. 53 | 54 | - `Solow model`_ of economic growth 55 | - `Ramsey model`_ of optimal savings 56 | - `Spence model`_ of costly signaling 57 | - Various `auction models`_ (currently symmetric and asymmetric IPVP) 58 | 59 | .. _`Solow model` : https://github.com/davidrpugh/pyCollocation/blob/master/examples/solow-model.ipynb 60 | .. _`Ramsey model`: https://github.com/davidrpugh/pyCollocation/blob/master/examples/ramsey-cass-koopmans-model.ipynb 61 | .. _`Spence model`: https://github.com/davidrpugh/pyCollocation/blob/master/examples/spence-model.ipynb 62 | .. _`auction models` : https://github.com/davidrpugh/pyCollocation/blob/master/examples/auction-models.ipynb 63 | 64 | 65 | Physics 66 | ~~~~~~~ 67 | 68 | - `A simple heat exchanger`_ 69 | 70 | .. _`A simple heat exchanger`: https://github.com/davidrpugh/pyCollocation/blob/master/examples/heat-exchanger.ipynb 71 | 72 | More notebooks will be added in the near future (hopefully!)...and suggestions for example notebooks are very welcome! 73 | 74 | Roadmap to 1.0 75 | -------------- 76 | Ultimately I am hoping to contribute this package to either SciPy or QuantEcon, depending. Ideally, version 1.0 of pyCollocation would include the following functionality... 77 | 78 | - Support for at least two additional classes of basis functions: B-Splines, what else? Next obvious candidate would be some basis functions specifically used to approximate periodic functions. 79 | 80 | - Support a solver for over-identified systems of equations. Currently the Solver class requires the system of equations defined by the collocation residuals to be exactly identified. Many economic applications, particularly models of optimal bidding functions for various types of auctions, naturally lead to over-identified systems. 81 | 82 | - Built-in support for computing Jacobian matrix for the system of equations defined by the collocation residuals. Given a user-supplied Jacobian for the BVP, one can apply the chain rule to construct the Jacobian matrix for system of equations defined by the collocation residuals. 83 | 84 | - Support for solving models with unknown parameters (similar to `scikits.bvp_solver`_). This would allow for the possibility to simultaneously solve and calibrate a model. 85 | 86 | - Support for free boundary conditions. This comes up a lot in auction theory applications where the upper bounds on the bidder valuation distributions are unknown. 87 | 88 | .. _`scikits.bvp_solver` : https://github.com/jsalvatier/scikits.bvp_solver 89 | -------------------------------------------------------------------------------- /pycollocation/tests/models/ramsey_cass_koopmans.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | import numpy as np 4 | 5 | from ... import problems 6 | 7 | 8 | class RamseyCassKoopmansModel(problems.TwoPointBVP): 9 | """ 10 | Class representing a generic Solow growth model. 11 | 12 | Attributes 13 | ---------- 14 | equilibrium_capital : function 15 | Equilibrium value for capital (per unit effective labor). 16 | equilibrium_consumption : function 17 | Equilibrium value for consumption (per unit effective labor). 18 | intensive_output : function 19 | Output (per unit effective labor supply). 20 | marginal_product_capital : function 21 | Marginal product of capital (i.e., first derivative of intensive output). 22 | params : dict(str: float) 23 | Dictionary of model parameters. 24 | pratt_arrow_risk_aversion : function 25 | Pratt-Arrow relative risk aversion function. 26 | 27 | """ 28 | 29 | def __init__(self, ARA, f, k_star, mpk, params): 30 | """ 31 | Initialize an instance of the RamseyCassKoopmans class. 32 | 33 | Parameters 34 | ---------- 35 | ARA : function 36 | Pratt-Arrow absolute risk aversion function. 37 | f : function 38 | Output (per unit effective labor supply). 39 | k_star : function 40 | Equilibrium (i.e., steady-state) value for capital stock (per unit 41 | effective labor supply). 42 | mpk : function 43 | Marginal product of capital (per unit effective labor supply). 44 | params : dict(str: float) 45 | Dictionary of model parameters 46 | 47 | """ 48 | self._equilibrium_capital = k_star 49 | self._intensive_output = f 50 | self._marginal_product_capital = mpk 51 | self._pratt_arrow_risk_aversion = ARA 52 | 53 | # construct the terminal condition 54 | c_star = self._c_star_factory(k_star) 55 | terminal_condition = self._terminal_condition_factory(c_star) 56 | self._equilibrium_consumption = c_star 57 | 58 | # construct the RHS of the system of ODEs 59 | rhs = self._rhs_factory(ARA, f, mpk) 60 | 61 | super(RamseyCassKoopmansModel, self).__init__(self._initial_condition, 62 | terminal_condition, 1, 2, 63 | params, rhs) 64 | 65 | @property 66 | def equilibrium_capital(self): 67 | return self._equilibrium_capital 68 | 69 | @property 70 | def equilibrium_consumption(self): 71 | return self._equilibrium_consumption 72 | 73 | @property 74 | def intensive_output(self): 75 | return self._intensive_output 76 | 77 | @property 78 | def marginal_product_capital(self): 79 | return self._marginal_product_capital 80 | 81 | @property 82 | def pratt_arrow_risk_aversion(self): 83 | return self._pratt_arrow_risk_aversion 84 | 85 | @staticmethod 86 | def _actual_investment(k_tilde, c_tilde, f, **params): 87 | return f(k_tilde, **params) - c_tilde 88 | 89 | @staticmethod 90 | def _breakeven_investment(k_tilde, delta, g, n, **params): 91 | return (g + n + delta) * k_tilde 92 | 93 | @classmethod 94 | def _c_tilde_dot(cls, t, k_tilde, c_tilde, ARA, mpk, A0, delta, g, rho, **params): 95 | A = cls._technology(t, A0, g) 96 | return ((mpk(k_tilde, **params) - delta - rho) / (A * ARA(t, A * c_tilde, **params))) - g * c_tilde 97 | 98 | @staticmethod 99 | def _initial_condition(t, k_tilde, c_tilde, A0, K0, N0, **params): 100 | return [k_tilde - (K0 / (A0 * N0))] 101 | 102 | @staticmethod 103 | def _technology(t, A0, g): 104 | return A0 * np.exp(g * t) 105 | 106 | @classmethod 107 | def _k_dot(cls, t, k_tilde, c_tilde, f, delta, g, n, **params): 108 | k_dot = (cls._actual_investment(k_tilde, c_tilde, f, **params) - 109 | cls._breakeven_investment(k_tilde, delta, g, n)) 110 | return k_dot 111 | 112 | @classmethod 113 | def _ramsey_model(cls, t, k_tilde, c_tilde, ARA, f, mpk, A0, delta, g, n, rho, **params): 114 | out = [cls._k_dot(t, k_tilde, c_tilde, f, delta, g, n, **params), 115 | cls._c_tilde_dot(t, k_tilde, c_tilde, ARA, mpk, A0, delta, g, rho, **params)] 116 | return out 117 | 118 | @classmethod 119 | def _rhs_factory(cls, ARA, f, mpk): 120 | return functools.partial(cls._ramsey_model, ARA=ARA, f=f, mpk=mpk) 121 | 122 | @staticmethod 123 | def _terminal_condition(t, k_tilde, c_tilde, c_star, **params): 124 | return [c_tilde - c_star(**params)] 125 | 126 | @classmethod 127 | def _terminal_condition_factory(cls, c_star): 128 | return functools.partial(cls._terminal_condition, c_star=c_star) 129 | 130 | def _c_star(self, k_star, **params): 131 | k_tilde = k_star(**params) 132 | c_star = (self.intensive_output(k_tilde, **params) - 133 | self._breakeven_investment(k_tilde, **params)) 134 | return c_star 135 | 136 | def _c_star_factory(self, k_star): 137 | return functools.partial(self._c_star, k_star=k_star) 138 | -------------------------------------------------------------------------------- /pycollocation/tests/test_ramsey_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test case using the Ramsey-Cass-Koopmans model of optimal savings. 3 | 4 | Model assumes Cobb-Douglas production technology and Constant Relative Risk 5 | Aversion (CRRA) preferences. I then generate random parameter values 6 | consistent with a constant consumption-output ratio (i.e., constant savings 7 | rate). 8 | 9 | """ 10 | from nose import tools 11 | 12 | import numpy as np 13 | from scipy import stats 14 | 15 | from . import models 16 | from .. import basis_functions 17 | from .. import solvers 18 | 19 | 20 | def analytic_solution(t, A0, alpha, delta, g, K0, n, N0, theta, **params): 21 | """Analytic solution for model with Cobb-Douglas production.""" 22 | k0 = K0 / (A0 * N0) 23 | lmbda = (g + n + delta) * (1 - alpha) 24 | ks = ((k0**(1 - alpha) * np.exp(-lmbda * t) + 25 | (1 / (theta * (g + n + delta))) * (1 - np.exp(-lmbda * t)))**(1 / (1 - alpha))) 26 | ys = cobb_douglas_output(ks, alpha, **params) 27 | cs = ((theta - 1) / theta) * ys 28 | 29 | return [ks, cs] 30 | 31 | 32 | def cobb_douglas_output(k, alpha, **params): 33 | """Cobb-Douglas output (per unit effective labor supply).""" 34 | return k**alpha 35 | 36 | 37 | def cobb_douglas_mpk(k, alpha, **params): 38 | """Marginal product of capital with Cobb-Douglas production.""" 39 | return alpha * k**(alpha - 1) 40 | 41 | 42 | def equilibrium_capital(alpha, delta, g, n, rho, theta, **params): 43 | """Steady state value for capital stock (per unit effective labor).""" 44 | return (alpha / (delta + rho + theta * g))**(1 / (1 - alpha)) 45 | 46 | 47 | def generate_random_params(scale, seed): 48 | np.random.seed(seed) 49 | 50 | # random g, n, delta such that sum of these params is positive 51 | g, n = stats.norm.rvs(0.05, scale, size=2) 52 | delta, = stats.lognorm.rvs(scale, loc=g + n, size=1) 53 | assert g + n + delta > 0 54 | 55 | # choose alpha consistent with theta > 1 56 | upper_bound = 1 if (g + delta) < 0 else min(1, (g + delta) / (g + n + delta)) 57 | alpha, = stats.uniform.rvs(scale=upper_bound, size=1) 58 | assert 0 < alpha < upper_bound 59 | 60 | lower_bound = delta / (alpha * (g + n + delta) - g) 61 | theta, = stats.lognorm.rvs(scale, loc=lower_bound, size=1) 62 | rho = alpha * theta * (g + n + delta) - (delta * theta * g) 63 | assert rho > 0 64 | 65 | # normalize A0 and N0 66 | A0 = 1.0 67 | N0 = A0 68 | 69 | # choose K0 so that it is not too far from balanced growth path 70 | kstar = equilibrium_capital(g, n, alpha, delta, rho, theta) 71 | K0, = stats.uniform.rvs(0.5 * kstar, 1.5 * kstar, size=1) 72 | assert K0 > 0 73 | 74 | params = {'g': g, 'n': n, 'delta': delta, 'rho': rho, 'alpha': alpha, 75 | 'theta': theta, 'K0': K0, 'A0': A0, 'N0': N0} 76 | 77 | return params 78 | 79 | 80 | def initial_mesh(t, T, num, problem): 81 | # compute equilibrium values 82 | cstar = problem.equilibrium_consumption(**problem.params) 83 | kstar = problem.equilibrium_capital(**problem.params) 84 | ystar = problem.intensive_output(kstar, **problem.params) 85 | 86 | # create the mesh for capital 87 | ts = np.linspace(t, T, num) 88 | k0 = problem.params['K0'] / (problem.params['A0'] * problem.params['N0']) 89 | ks = kstar - (kstar - k0) * np.exp(-ts) 90 | 91 | # create the mesh for consumption 92 | s = 1 - (cstar / ystar) 93 | y0 = cobb_douglas_output(k0, **problem.params) 94 | c0 = (1 - s) * y0 95 | cs = cstar - (cstar - c0) * np.exp(-ts) 96 | 97 | return ts, ks, cs 98 | 99 | 100 | def pratt_arrow_risk_aversion(t, c, theta, **params): 101 | """Assume constant relative risk aversion""" 102 | return theta / c 103 | 104 | 105 | random_seed = np.random.randint(2147483647) 106 | random_params = generate_random_params(0.1, random_seed) 107 | test_problem = models.RamseyCassKoopmansModel(pratt_arrow_risk_aversion, 108 | cobb_douglas_output, 109 | equilibrium_capital, 110 | cobb_douglas_mpk, 111 | random_params) 112 | 113 | polynomial_basis = basis_functions.PolynomialBasis() 114 | solver = solvers.Solver(polynomial_basis) 115 | 116 | 117 | def _test_polynomial_collocation(basis_kwargs, boundary_points, num=1000): 118 | """Helper function for testing various kinds of polynomial collocation.""" 119 | ts, ks, cs = initial_mesh(*boundary_points, num=num, problem=test_problem) 120 | k_poly = polynomial_basis.fit(ts, ks, **basis_kwargs) 121 | c_poly = polynomial_basis.fit(ts, cs, **basis_kwargs) 122 | initial_coefs = np.hstack([k_poly.coef, c_poly.coef]) 123 | nodes = polynomial_basis.roots(**basis_kwargs) 124 | 125 | solution = solver.solve(basis_kwargs, boundary_points, initial_coefs, 126 | nodes, test_problem) 127 | 128 | # check that solver terminated successfully 129 | msg = "Solver failed!\nSeed: {}\nModel params: {}\n".format(random_seed, test_problem.params) 130 | tools.assert_true(solution.result.success, msg=msg) 131 | 132 | # compute the residuals 133 | normed_residuals = solution.normalize_residuals(ts) 134 | 135 | # check that residuals are close to zero on average 136 | for normed_residual in normed_residuals: 137 | tools.assert_true(np.mean(normed_residual) < 1e-6, msg=msg) 138 | 139 | # check that the numerical and analytic solutions are close 140 | numeric_solns = solution.evaluate_solution(ts) 141 | analytic_solns = analytic_solution(ts, **test_problem.params) 142 | 143 | for numeric_soln, analytic_soln in zip(numeric_solns, analytic_solns): 144 | error = numeric_soln - analytic_soln 145 | tools.assert_true(np.mean(error) < 1e-6, msg=msg) 146 | 147 | 148 | def test_chebyshev_collocation(): 149 | """Test collocation solver using Chebyshev polynomials for basis.""" 150 | boundary_points = (0, 100) 151 | basis_kwargs = {'kind': 'Chebyshev', 'degree': 50, 'domain': boundary_points} 152 | _test_polynomial_collocation(basis_kwargs, boundary_points) 153 | 154 | 155 | def test_legendre_collocation(): 156 | """Test collocation solver using Legendre polynomials for basis.""" 157 | boundary_points = (0, 100) 158 | basis_kwargs = {'kind': 'Legendre', 'degree': 50, 'domain': boundary_points} 159 | _test_polynomial_collocation(basis_kwargs, boundary_points) 160 | -------------------------------------------------------------------------------- /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 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyCollocation.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyCollocation.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pyCollocation" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyCollocation" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /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% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 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. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyCollocation.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyCollocation.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /pycollocation/solvers/solvers.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | import numpy as np 4 | from scipy import optimize 5 | 6 | from . import solutions 7 | 8 | 9 | class SolverLike(object): 10 | """ 11 | Class describing the protocol the all SolverLike objects should satisfy. 12 | 13 | Notes 14 | ----- 15 | Subclasses should implement `solve` method as described below. 16 | 17 | """ 18 | 19 | @property 20 | def basis_functions(self): 21 | r""" 22 | Functions used to approximate the solution to a boundary value problem. 23 | 24 | :getter: Return the current basis functions. 25 | :type: `basis_functions.BasisFunctions` 26 | 27 | """ 28 | return self._basis_functions 29 | 30 | @staticmethod 31 | def _array_to_list(coefs_array, indices_or_sections, axis=0): 32 | """Split an array into a list of arrays.""" 33 | return np.split(coefs_array, indices_or_sections, axis) 34 | 35 | @staticmethod 36 | def _evaluate_functions(funcs, points): 37 | """Evaluate a list of functions at some points.""" 38 | return [func(points) for func in funcs] 39 | 40 | @classmethod 41 | def _evaluate_rhs(cls, funcs, nodes, problem): 42 | """ 43 | Compute the value of the right-hand side of the system of ODEs. 44 | 45 | Parameters 46 | ---------- 47 | basis_funcs : list(function) 48 | nodes : numpy.ndarray 49 | problem : TwoPointBVPLike 50 | 51 | Returns 52 | ------- 53 | evaluated_rhs : list(float) 54 | 55 | """ 56 | evald_funcs = cls._evaluate_functions(funcs, nodes) 57 | evald_rhs = problem.rhs(nodes, *evald_funcs, **problem.params) 58 | return evald_rhs 59 | 60 | @classmethod 61 | def _lower_boundary_residual(cls, funcs, problem, ts): 62 | evald_funcs = cls._evaluate_functions(funcs, ts) 63 | return problem.bcs_lower(ts, *evald_funcs, **problem.params) 64 | 65 | @classmethod 66 | def _upper_boundary_residual(cls, funcs, problem, ts): 67 | evald_funcs = cls._evaluate_functions(funcs, ts) 68 | return problem.bcs_upper(ts, *evald_funcs, **problem.params) 69 | 70 | @classmethod 71 | def _compute_boundary_residuals(cls, boundary_points, funcs, problem): 72 | boundary_residuals = [] 73 | if problem.bcs_lower is not None: 74 | residual = cls._lower_boundary_residual_factory(funcs, problem) 75 | boundary_residuals.append(residual(boundary_points[0])) 76 | if problem.bcs_upper is not None: 77 | residual = cls._upper_boundary_residual_factory(funcs, problem) 78 | boundary_residuals.append(residual(boundary_points[1])) 79 | return boundary_residuals 80 | 81 | @classmethod 82 | def _compute_interior_residuals(cls, derivs, funcs, nodes, problem): 83 | interior_residuals = cls._interior_residuals_factory(derivs, funcs, problem) 84 | residuals = interior_residuals(nodes) 85 | return residuals 86 | 87 | @classmethod 88 | def _interior_residuals(cls, derivs, funcs, problem, ts): 89 | evaluated_lhs = cls._evaluate_functions(derivs, ts) 90 | evaluated_rhs = cls._evaluate_rhs(funcs, ts, problem) 91 | return [lhs - rhs for lhs, rhs in zip(evaluated_lhs, evaluated_rhs)] 92 | 93 | @classmethod 94 | def _interior_residuals_factory(cls, derivs, funcs, problem): 95 | return functools.partial(cls._interior_residuals, derivs, funcs, problem) 96 | 97 | @classmethod 98 | def _lower_boundary_residual_factory(cls, funcs, problem): 99 | return functools.partial(cls._lower_boundary_residual, funcs, problem) 100 | 101 | @classmethod 102 | def _upper_boundary_residual_factory(cls, funcs, problem): 103 | return functools.partial(cls._upper_boundary_residual, funcs, problem) 104 | 105 | def _assess_approximation(self, boundary_points, derivs, funcs, nodes, problem): 106 | """ 107 | Parameters 108 | ---------- 109 | basis_derivs : list(function) 110 | basis_funcs : list(function) 111 | problem : TwoPointBVPLike 112 | 113 | Returns 114 | ------- 115 | resids : numpy.ndarray 116 | 117 | """ 118 | interior_residuals = self._compute_interior_residuals(derivs, funcs, 119 | nodes, problem) 120 | boundary_residuals = self._compute_boundary_residuals(boundary_points, 121 | funcs, problem) 122 | return np.hstack(interior_residuals + boundary_residuals) 123 | 124 | def _compute_residuals(self, coefs_array, basis_kwargs, boundary_points, nodes, problem): 125 | """ 126 | Return collocation residuals. 127 | 128 | Parameters 129 | ---------- 130 | coefs_array : numpy.ndarray 131 | basis_kwargs : dict 132 | problem : TwoPointBVPLike 133 | 134 | Returns 135 | ------- 136 | resids : numpy.ndarray 137 | 138 | """ 139 | coefs_list = self._array_to_list(coefs_array, problem.number_odes) 140 | derivs, funcs = self._construct_approximation(basis_kwargs, coefs_list) 141 | resids = self._assess_approximation(boundary_points, derivs, funcs, 142 | nodes, problem) 143 | return resids 144 | 145 | def _construct_approximation(self, basis_kwargs, coefs_list): 146 | """ 147 | Construct a collection of derivatives and functions that approximate 148 | the solution to the boundary value problem. 149 | 150 | Parameters 151 | ---------- 152 | basis_kwargs : dict(str: ) 153 | coefs_list : list(numpy.ndarray) 154 | 155 | Returns 156 | ------- 157 | basis_derivs : list(function) 158 | basis_funcs : list(function) 159 | 160 | """ 161 | derivs = self._construct_derivatives(coefs_list, **basis_kwargs) 162 | funcs = self._construct_functions(coefs_list, **basis_kwargs) 163 | return derivs, funcs 164 | 165 | def _construct_derivatives(self, coefs, **kwargs): 166 | """Return a list of derivatives given a list of coefficients.""" 167 | return [self.basis_functions.derivatives_factory(coef, **kwargs) for coef in coefs] 168 | 169 | def _construct_functions(self, coefs, **kwargs): 170 | """Return a list of functions given a list of coefficients.""" 171 | return [self.basis_functions.functions_factory(coef, **kwargs) for coef in coefs] 172 | 173 | def _solution_factory(self, basis_kwargs, coefs_array, nodes, problem, result): 174 | """ 175 | Construct a representation of the solution to the boundary value problem. 176 | 177 | Parameters 178 | ---------- 179 | basis_kwargs : dict(str : ) 180 | coefs_array : numpy.ndarray 181 | problem : TwoPointBVPLike 182 | result : OptimizeResult 183 | 184 | Returns 185 | ------- 186 | solution : SolutionLike 187 | 188 | """ 189 | soln_coefs = self._array_to_list(coefs_array, problem.number_odes) 190 | soln_derivs = self._construct_derivatives(soln_coefs, **basis_kwargs) 191 | soln_funcs = self._construct_functions(soln_coefs, **basis_kwargs) 192 | soln_residual_func = self._interior_residuals_factory(soln_derivs, 193 | soln_funcs, 194 | problem) 195 | solution = solutions.Solution(basis_kwargs, soln_funcs, nodes, problem, 196 | soln_residual_func, result) 197 | return solution 198 | 199 | def solve(self, basis_kwargs, boundary_points, coefs_array, nodes, problem, 200 | **solver_options): 201 | """ 202 | Solve a boundary value problem using the collocation method. 203 | 204 | Parameters 205 | ---------- 206 | basis_kwargs : dict 207 | Dictionary of keyword arguments used to build basis functions. 208 | coefs_array : numpy.ndarray 209 | Array of coefficients for basis functions defining the initial 210 | condition. 211 | problem : bvp.TwoPointBVPLike 212 | A two-point boundary value problem (BVP) to solve. 213 | solver_options : dict 214 | Dictionary of options to pass to the non-linear equation solver. 215 | 216 | Return 217 | ------ 218 | solution: solutions.SolutionLike 219 | An instance of the SolutionLike class representing the solution to 220 | the two-point boundary value problem (BVP) 221 | 222 | Notes 223 | ----- 224 | 225 | """ 226 | raise NotImplementedError 227 | 228 | 229 | class Solver(SolverLike): 230 | 231 | def __init__(self, basis_functions): 232 | self._basis_functions = basis_functions 233 | 234 | def solve(self, basis_kwargs, boundary_points, coefs_array, nodes, problem, 235 | **solver_options): 236 | """ 237 | Solve a boundary value problem using the collocation method. 238 | 239 | Parameters 240 | ---------- 241 | basis_kwargs : dict 242 | Dictionary of keyword arguments used to build basis functions. 243 | coefs_array : numpy.ndarray 244 | Array of coefficients for basis functions defining the initial 245 | condition. 246 | problem : bvp.TwoPointBVPLike 247 | A two-point boundary value problem (BVP) to solve. 248 | solver_options : dict 249 | Dictionary of options to pass to the non-linear equation solver. 250 | 251 | Return 252 | ------ 253 | solution: solutions.SolutionLike 254 | An instance of the SolutionLike class representing the solution to 255 | the two-point boundary value problem (BVP) 256 | 257 | Notes 258 | ----- 259 | 260 | """ 261 | result = optimize.root(self._compute_residuals, 262 | x0=coefs_array, 263 | args=(basis_kwargs, boundary_points, nodes, problem), 264 | **solver_options) 265 | solution = self._solution_factory(basis_kwargs, result.x, nodes, 266 | problem, result) 267 | return solution 268 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # pyCollocation documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Apr 30 21:02:38 2015. 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 | import sys 16 | import os 17 | import shlex 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | sys.path.insert(0, os.path.abspath('../../pycollocation')) 24 | 25 | # numpydoc settings 26 | numpydoc_class_members_toctree = False 27 | numydoc_show_class_members = True 28 | 29 | # -- General configuration ------------------------------------------------ 30 | 31 | # If your documentation needs a minimal Sphinx version, state it here. 32 | #needs_sphinx = '1.0' 33 | 34 | # Add any Sphinx extension module names here, as strings. They can be 35 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 36 | # ones. 37 | extensions = [ 38 | 'sphinx.ext.autodoc', 39 | 'sphinx.ext.intersphinx', 40 | 'sphinx.ext.doctest', 41 | 'sphinx.ext.todo', 42 | 'sphinx.ext.coverage', 43 | 'sphinx.ext.mathjax', 44 | 'sphinx.ext.viewcode', 45 | 'sphinx.ext.ifconfig', 46 | 'sphinx.ext.extlinks', 47 | 'sphinx.ext.autosummary', 48 | 'numpydoc', 49 | 'IPython.sphinxext.ipython_directive', 50 | 'IPython.sphinxext.ipython_console_highlighting', 51 | 'matplotlib.sphinxext.only_directives', 52 | 'matplotlib.sphinxext.plot_directive', 53 | ] 54 | 55 | # Add any paths that contain templates here, relative to this directory. 56 | templates_path = ['_templates'] 57 | 58 | # The suffix(es) of source filenames. 59 | # You can specify multiple suffix as a list of string: 60 | # source_suffix = ['.rst', '.md'] 61 | source_suffix = '.rst' 62 | 63 | # The encoding of source files. 64 | #source_encoding = 'utf-8-sig' 65 | 66 | # The master toctree document. 67 | master_doc = 'index' 68 | 69 | # General information about the project. 70 | project = u'pyCollocation' 71 | copyright = u'2015, David R. Pugh' 72 | author = u'David R. Pugh' 73 | 74 | # The version info for the project you're documenting, acts as replacement for 75 | # |version| and |release|, also used in various other places throughout the 76 | # built documents. 77 | # 78 | # The short X.Y version. 79 | version = '0.3' 80 | # The full version, including alpha/beta/rc tags. 81 | release = '0.3.0-alpha' 82 | 83 | # The language for content autogenerated by Sphinx. Refer to documentation 84 | # for a list of supported languages. 85 | # 86 | # This is also used if you do content translation via gettext catalogs. 87 | # Usually you set "language" from the command line for these cases. 88 | language = None 89 | 90 | # There are two options for replacing |today|: either, you set today to some 91 | # non-false value, then it is used: 92 | #today = '' 93 | # Else, today_fmt is used as the format for a strftime call. 94 | #today_fmt = '%B %d, %Y' 95 | 96 | # List of patterns, relative to source directory, that match files and 97 | # directories to ignore when looking for source files. 98 | exclude_patterns = [] 99 | 100 | # The reST default role (used for this markup: `text`) to use for all 101 | # documents. 102 | #default_role = None 103 | 104 | # If true, '()' will be appended to :func: etc. cross-reference text. 105 | #add_function_parentheses = True 106 | 107 | # If true, the current module name will be prepended to all description 108 | # unit titles (such as .. function::). 109 | #add_module_names = True 110 | 111 | # If true, sectionauthor and moduleauthor directives will be shown in the 112 | # output. They are ignored by default. 113 | #show_authors = False 114 | 115 | # The name of the Pygments (syntax highlighting) style to use. 116 | pygments_style = 'sphinx' 117 | 118 | # A list of ignored prefixes for module index sorting. 119 | #modindex_common_prefix = [] 120 | 121 | # If true, keep warnings as "system message" paragraphs in the built documents. 122 | #keep_warnings = False 123 | 124 | # If true, `todo` and `todoList` produce output, else they produce nothing. 125 | todo_include_todos = True 126 | 127 | 128 | # -- Options for HTML output ---------------------------------------------- 129 | 130 | # The theme to use for HTML and HTML Help pages. See the documentation for 131 | # a list of builtin themes. 132 | html_theme = 'sphinx_rtd_theme' 133 | 134 | # Theme options are theme-specific and customize the look and feel of a theme 135 | # further. For a list of options available for each theme, see the 136 | # documentation. 137 | #html_theme_options = {} 138 | 139 | # Add any paths that contain custom themes here, relative to this directory. 140 | #html_theme_path = [] 141 | 142 | # The name for this set of Sphinx documents. If None, it defaults to 143 | # " v documentation". 144 | #html_title = None 145 | 146 | # A shorter title for the navigation bar. Default is the same as html_title. 147 | #html_short_title = None 148 | 149 | # The name of an image file (relative to this directory) to place at the top 150 | # of the sidebar. 151 | #html_logo = None 152 | 153 | # The name of an image file (within the static path) to use as favicon of the 154 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 155 | # pixels large. 156 | #html_favicon = None 157 | 158 | # Add any paths that contain custom static files (such as style sheets) here, 159 | # relative to this directory. They are copied after the builtin static files, 160 | # so a file named "default.css" will overwrite the builtin "default.css". 161 | html_static_path = ['_static'] 162 | 163 | # Add any extra paths that contain custom files (such as robots.txt or 164 | # .htaccess) here, relative to this directory. These files are copied 165 | # directly to the root of the documentation. 166 | #html_extra_path = [] 167 | 168 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 169 | # using the given strftime format. 170 | #html_last_updated_fmt = '%b %d, %Y' 171 | 172 | # If true, SmartyPants will be used to convert quotes and dashes to 173 | # typographically correct entities. 174 | #html_use_smartypants = True 175 | 176 | # Custom sidebar templates, maps document names to template names. 177 | #html_sidebars = {} 178 | 179 | # Additional templates that should be rendered to pages, maps page names to 180 | # template names. 181 | #html_additional_pages = {} 182 | 183 | # If false, no module index is generated. 184 | #html_domain_indices = True 185 | 186 | # If false, no index is generated. 187 | #html_use_index = True 188 | 189 | # If true, the index is split into individual pages for each letter. 190 | #html_split_index = False 191 | 192 | # If true, links to the reST sources are added to the pages. 193 | #html_show_sourcelink = True 194 | 195 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 196 | #html_show_sphinx = True 197 | 198 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 199 | #html_show_copyright = True 200 | 201 | # If true, an OpenSearch description file will be output, and all pages will 202 | # contain a tag referring to it. The value of this option must be the 203 | # base URL from which the finished HTML is served. 204 | #html_use_opensearch = '' 205 | 206 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 207 | #html_file_suffix = None 208 | 209 | # Language to be used for generating the HTML full-text search index. 210 | # Sphinx supports the following languages: 211 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 212 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 213 | #html_search_language = 'en' 214 | 215 | # A dictionary with options for the search language support, empty by default. 216 | # Now only 'ja' uses this config value 217 | #html_search_options = {'type': 'default'} 218 | 219 | # The name of a javascript file (relative to the configuration directory) that 220 | # implements a search results scorer. If empty, the default will be used. 221 | #html_search_scorer = 'scorer.js' 222 | 223 | # Output file base name for HTML help builder. 224 | htmlhelp_basename = 'pyCollocationdoc' 225 | 226 | # -- Options for LaTeX output --------------------------------------------- 227 | 228 | latex_elements = { 229 | # The paper size ('letterpaper' or 'a4paper'). 230 | #'papersize': 'letterpaper', 231 | 232 | # The font size ('10pt', '11pt' or '12pt'). 233 | #'pointsize': '10pt', 234 | 235 | # Additional stuff for the LaTeX preamble. 236 | #'preamble': '', 237 | 238 | # Latex figure (float) alignment 239 | #'figure_align': 'htbp', 240 | } 241 | 242 | # Grouping the document tree into LaTeX files. List of tuples 243 | # (source start file, target name, title, 244 | # author, documentclass [howto, manual, or own class]). 245 | latex_documents = [ 246 | (master_doc, 'pyCollocation.tex', u'pyCollocation Documentation', 247 | u'David R. Pugh', 'manual'), 248 | ] 249 | 250 | # The name of an image file (relative to this directory) to place at the top of 251 | # the title page. 252 | #latex_logo = None 253 | 254 | # For "manual" documents, if this is true, then toplevel headings are parts, 255 | # not chapters. 256 | #latex_use_parts = False 257 | 258 | # If true, show page references after internal links. 259 | #latex_show_pagerefs = False 260 | 261 | # If true, show URL addresses after external links. 262 | #latex_show_urls = False 263 | 264 | # Documents to append as an appendix to all manuals. 265 | #latex_appendices = [] 266 | 267 | # If false, no module index is generated. 268 | #latex_domain_indices = True 269 | 270 | 271 | # -- Options for manual page output --------------------------------------- 272 | 273 | # One entry per manual page. List of tuples 274 | # (source start file, name, description, authors, manual section). 275 | man_pages = [ 276 | (master_doc, 'pycollocation', u'pyCollocation Documentation', 277 | [author], 1) 278 | ] 279 | 280 | # If true, show URL addresses after external links. 281 | #man_show_urls = False 282 | 283 | 284 | # -- Options for Texinfo output ------------------------------------------- 285 | 286 | # Grouping the document tree into Texinfo files. List of tuples 287 | # (source start file, target name, title, author, 288 | # dir menu entry, description, category) 289 | texinfo_documents = [ 290 | (master_doc, 'pyCollocation', u'pyCollocation Documentation', 291 | author, 'pyCollocation', 'One line description of project.', 292 | 'Miscellaneous'), 293 | ] 294 | 295 | # Documents to append as an appendix to all manuals. 296 | #texinfo_appendices = [] 297 | 298 | # If false, no module index is generated. 299 | #texinfo_domain_indices = True 300 | 301 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 302 | #texinfo_show_urls = 'footnote' 303 | 304 | # If true, do not generate a @detailmenu in the "Top" node's menu. 305 | #texinfo_no_detailmenu = False 306 | 307 | 308 | # Example configuration for intersphinx: refer to the Python standard library. 309 | intersphinx_mapping = {'https://docs.python.org/': None} 310 | -------------------------------------------------------------------------------- /examples/basic-new-keynesian-model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": { 18 | "collapsed": false 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "import numpy as np\n", 23 | "import seaborn as sn\n", 24 | "\n", 25 | "import models\n", 26 | "import orthogonal_collocation" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 15, 32 | "metadata": { 33 | "collapsed": false 34 | }, 35 | "outputs": [], 36 | "source": [ 37 | "# define some variables\n", 38 | "t, X, i, pi = sym.symbols('t, X, i, pi')\n", 39 | "\n", 40 | "# define some parameters\n", 41 | "r = sym.symbols('r')\n", 42 | "\n", 43 | "# assumes logarithmic utility from consumption\n", 44 | "X_dot = i - pi - r " 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 16, 50 | "metadata": { 51 | "collapsed": false 52 | }, 53 | "outputs": [], 54 | "source": [ 55 | "# define some parameters\n", 56 | "epsilon, theta, rho, psi = sym.symbols('epsilon, theta, rho, psi')\n", 57 | "\n", 58 | "# assumes convex adjustment costs a la Rotemberg\n", 59 | "pi_dot = rho * pi - ((epsilon - 1) / theta) * (X**(1 + psi) - 1)" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 17, 65 | "metadata": { 66 | "collapsed": false 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "# define some parameters\n", 71 | "istar, phi_pi, phi_X = sym.symbols('istar, phi_pi, phi_X')\n", 72 | "\n", 73 | "# define some Taylor-esque rule for setting the nominal interest rate\n", 74 | "taylor_rule = istar + phi_pi * pi + phi_X * sym.log(X)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 14, 80 | "metadata": { 81 | "collapsed": false 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "rhs = {X: X_dot.subs({i: taylor_rule}), pi: pi_dot}\n", 86 | "\n", 87 | "# define some boundary conditions\n", 88 | "X0 = 5.0\n", 89 | "pi0 = 1.0\n", 90 | "bcs = {'lower': [X - X0, pi - pi0], 'upper': None}\n", 91 | "\n", 92 | "basic_NK = models.BoundaryValueProblem(dependent_vars=[X, pi],\n", 93 | " independent_var=t,\n", 94 | " rhs=rhs,\n", 95 | " boundary_conditions=bcs)\n", 96 | "\n", 97 | "params = {'rho': 0.03, 'r': 0.04, 'epsilon': 1.04, 'theta': 0.5, 'psi': 0.5,\n", 98 | " 'istar': 1.0, 'phi_pi': 0.5, 'phi_X': 1.05}\n", 99 | "\n", 100 | "basic_NK_solver = orthogonal_collocation.OrthogonalCollocationSolver(basic_NK, params)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 17, 106 | "metadata": { 107 | "collapsed": false 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "# specify an initial guess\n", 112 | "def kstar(g, n, alpha, delta, rho, theta, sigma): \n", 113 | " \"\"\"Steady-state level of capital (per unit effective labor).\"\"\"\n", 114 | " gamma = (sigma - 1) / sigma\n", 115 | " if gamma == 0:\n", 116 | " return (alpha / (delta + rho + theta * g))**(1 / (1 - alpha))\n", 117 | " else:\n", 118 | " return (1 - alpha)**(1 / gamma) * ((alpha / (delta + rho + theta * g))**(gamma / (gamma - 1)) - alpha)**(-1 / gamma)\n", 119 | "\n", 120 | "def cstar(g, n, alpha, delta, rho, theta, sigma):\n", 121 | " \"\"\"Steady-state level of consumption (per unit effective labor).\"\"\"\n", 122 | " return kstar(g, n, alpha, delta, rho, theta, sigma)**alpha - (g + n + delta) * kstar(g, n, alpha, delta, rho, theta, sigma)\n", 123 | "\n", 124 | "domain = [0, 200]\n", 125 | "ts = np.linspace(domain[0], domain[1], 1000)\n", 126 | "ks = kstar(**ramsey_params) - (kstar(**ramsey_params) - k0) * np.exp(-ts)\n", 127 | "initial_capital_poly = np.polynomial.Chebyshev.fit(ts, ks, 50, domain)\n", 128 | "\n", 129 | "cs = np.log(initial_capital_poly(ks)) # guess that consumption is concave function of capital\n", 130 | "initial_consumption_poly = np.polynomial.Chebyshev.fit(ts, cs, 50, domain)\n", 131 | "initial_ramsey_coefs = {k: initial_capital_poly.coef, c: initial_consumption_poly.coef}" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 18, 137 | "metadata": { 138 | "collapsed": false 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "ramsey_solution = ramsey_solver.solve(kind=\"Chebyshev\",\n", 143 | " coefs_dict=initial_ramsey_coefs,\n", 144 | " domain=domain)\n" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 19, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "data": { 156 | "text/plain": [ 157 | "array([,\n", 158 | " ], dtype=object)" 159 | ] 160 | }, 161 | "execution_count": 19, 162 | "metadata": {}, 163 | "output_type": "execute_result" 164 | }, 165 | { 166 | "data": { 167 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeEAAAE+CAYAAAC6Iqj0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xt8VPWd//HXZBIIkIQiBEHlptWPKN7AlqqIRSu6peza\nrrVrvdcq4u7+WunWoq796dZWq1t3bbvVLeKtKu3Py1ovLSpKVaRo1xti9SOICiJiRAiXQEgy5/fH\nmWjEJDPJTHLmzLyfjwePzMyZOefD4wP5zPd7zvl+EkEQICIiIr2vLOoARERESpWKsIiISERUhEVE\nRCKiIiwiIhIRFWEREZGIqAiLiIhEpDybN5nZ80B9+ulKdz+7zbYLgLOBuvRLM9z99bxGKSIiUoQy\nFmEzqwRw9ykdvGU8cJq7v5DPwERERIpdItNiHWY2EbgVeJuwaF/s7s+02f5X4BVgGPCQu1/Vc+GK\niIgUj2zOCW8FrnH344DzgDvMrO3n5gEzgKOBSWY2Lf9hioiIFJ9szgm/DqwAcPflZrYeGA6sSW+/\nzt03AZjZQ8AhwEMd7SwIgiCRSOQUtIiISIx0WPSyKcJnAQcC/2hmuwE1wHsAZjYQWGpm+wENhKPh\nuZ1GkkhQV7c5y7ilkNTWVit3Mab8xZdyF2+1tdUdbstmOnouUGNmTwK/JSzKJ5nZOe5eD8wGFgJP\nAsvcfX7uIYuIiBS/jBdm9YBA3+jiSd/G4035iy/lLt5qa6s7nI7WYh0iIiIRUREWERGJiIqwiIhI\nRFSERUREIqIiLCIiEhEVYRERkYjko4vSdOBSoBm4yd1vzHuUIiIiBeAPf3iAVave5rzz/ikv+8up\ni5KZVQDXAocSrpj1tJnd7+7v5yU6ERGRDgy47F/p+8B9ed1n4/QT2HrZFR1uz/eyy9lMRx8E9Dez\nh83ssXRXpVZjgRXuXu/uTcAiYHJeIxQRESkwGzZsYObMs3n++f/NaT/ZTEe3dlGaa2Z7A380s33c\nPUW4jnR9m/duBgZ2ure774ajjutuvCIiIgBsveyKTketPeXDD9dz0UXf4zvf+R5jx+6f075y7aJU\nD7Rdmboa2NDp3pYsofbEE7sVrESvs4XIpfApf/Gl3BWGqqq+/OUvSxg6dCg1NZU55yWnLkrAa8De\nZjaIcMQ8Gbim0729957WQI0prV8bb8pffCl3hWPLlkaOO24axx33ZWbPns2cObdRWVnZ6Wd6sotS\nEzALeBhYDMx197Wd7u299zrdLCIiUsgSiQRjxuzJ1Klf5uc//1lu++r1LkrjxgV1C//cu8eUvNC3\n8XhT/uJLuYu3wuqipJGwiIgIEEURXr8eduzo9cOKiIgUmkiWrSz7oC6Kw4qIiBSUaIrw++uiOKyI\niEhBUREWERGJSDRFuE7T0SIiItl2URoKPAcc4+6vt3n9AuBsoLWqzmi7vSMaCYuIiGTXRakC+G/C\nFbF2Nh44zd1f6MpBVYRFRESym46+BrgeaG8lrAnAxWb2lJnNzvqg76vToYiISKdF2MzOBOrc/ZH0\nSzuv+jEPmAEcDUwys2mZj1imkbCIiAgZlq00syeAIP3nYMCBv3X399Pba9x9U/rxTGCwu3feV2r4\n8ICqKli+PD9/AxERkcLW4bKVnZ4TdvejWh+b2ULCC69aC/BAYKmZ7Qc0EI6G52YMZdgwUstXsF7r\noMaO1q+NN+UvvpS7eMu1i1JbCTM7Od1BqR6YDSwEngSWufv8jHsYNoyyrVtgy5YuHlpERKS4ZHWL\nEoC7T2l92Oa1eYTnhbM3bBgAZXXvk6qq6tJHRUREiknvL9bRWoTX6eIsEREpbb1fhIcPByC5rr07\nnkREREpH7xfhPfYID7z23V4/tIiISCGJrgi/qyIsIiKlTSNhERGRiPR+Ed51V4JkkuS7a3r90CIi\nIoUk1y5K04FLgWbgJne/MePOkklSw4ZrJCwiIiUv40i4oy5K6devBY4FjgLOTRfrjFLDd6PsvbXQ\n0tL1iEVERIpELl2UxgIr3L3e3ZuARcDkbA7astvuJJqbKfugLvObRUREilQuXZRqgPo2zzcDA7M5\naGr4buHBdV5YRERKWKZzwmcBgZl9ibCL0q1m1tpFqR5ouyp1NbAhm4P232dPAAZt3QCdLGwthaez\nhcil8Cl/8aXcFadud1ECXgP2NrNBhOeLJxNOXWe0qWYwNcDm11awXZ1BYkOdXOJN+Ysv5S7eOvsC\nlXUDh7SEmZ0MVLn7HDObBTxMOK09192zWouyZfjuACS1YIeIiJSwXLsoPQg82NWDpnYPi3DZmtVd\n/aiIiEjR6P3FOoDUsOEE5eUkV6sIi4hI6YqkCJNMktp9D8pWvR3J4UVERApBNEUYaBk5iuT762Db\ntqhCEBERiVR0RXjESACSa96JKgQREZFIRVaEU+kirClpEREpVdGPhFeviioEERGRSGW8RcnMksAc\nYB8gAM5z91fabL8AOBtoXQh6RttOSx1pGTkaUBEWEZHSlc19wl8BUu4+ycyOAn4MnNBm+3jgNHd/\noSsHTo1snY5+qysfExERKRoZp6Pd/ffAjPTT0Xx6fegJwMVm9pSZzc72wKldhxFUVGgkLCIiJSur\nc8Lu3mJmtwA/B+7cafM8wiJ9NDDJzKZldeT0vcLJt3VhloiIlKZEEARZv9nMdgWeAca6+7b0azXu\nvin9eCYw2N2v6GQ3Hx9w6lR49FHYtAmq1SFERESKUqKjDdlcmHUasIe7XwlsA1KkC6mZDQSWmtl+\nQAPhaHhupn22dgOpGjGafsCGv7xE8wEHZf5rSKTUySXelL/4Uu7irbMuStlMR98NHGxmTwDzge8A\nXzWzc9y9HpgNLASeBJa5+/xsA2vZcy8AkivfyPYjIiIiRSPjSDg97fyNTrbPIzwv3GUfFeE3VnTn\n4yIiIrEW2WIdoJGwiIiUtmiL8IhRBMmkirCIiJSkSIswFRVhN6U3VYRFRKT0RFuECaeky9avJ1G/\nMepQREREelX0RXivzwK6OEtEREpPARThvQFILs/Y80FERKSo5KOL0nTgUqAZuMndb+xKAC22bxiI\nv0ZjVz4oIiISc9mMhD/qogT8K2EXJQDMrAK4FjgWOAo418yGdiWAZhsLQNJf7crHREREYi/XLkpj\ngRXuXu/uTcAiYHJXAggGDyY1pJZyf60rHxMREYm9XLso1QD1bZ5vBgZ2NYjmfceSXPU2bNnS1Y+K\niIjEVsZzwq3c/Uwz+wHwjJm1dlGqB9quTF3Np/sNf8qnFrM++EBY9CS1H7wDYz6XbUgSgc4WIpfC\np/zFl3JXnHLqogS8BuxtZoOArYRT0ddk2ufO3UAqR+5FNbBpyXM0jt63S38B6T3q5BJvyl98KXfx\n1pNdlJqAWcDDwGJgrruv7WqALfuGF2eVv6aLs0REpHTko4vSg8CDuQTRPHa/MJi/LstlNyIiIrES\n+WIdAMHAz9AyajTlL78EQZD5AyIiIkWgIIowQPOBB1O2fj1l766JOhQREZFeUTBFuOnAgwAof+nF\niCMRERHpHQVThJsPSBfhpSrCIiJSGgqnCB94MEB4XlhERKQEFEwRDoYMoWX3PcLpaF2cJSIiJaDT\nW5TSDRpuAkYBfYEr3P2BNtsvAM4G6tIvzXD3bvckbD54PH0fup+yd1aTGjGyu7sRERGJhUz3CZ8C\n1Ln7aelVsV4EHmizfTxwmru/kI9gmj43kb4P3U/FX56hUUVYRESKXKbp6LuAH7Z5b/NO2ycAF5vZ\nU2Y2O9dgmj4/EYCKZ5fkuisREZGC12kRdvet7r7FzKoJC/IlO71lHmGbw6OBSWY2LZdgmg84iKBv\nX8r/8mwuuxEREYmFRJDhIigzGwHcC/yXu9+y07Yad9+UfjwTGOzuV2Q4ZucHPPJIWLwYNm6EanUN\nERGR2Et0tCHThVm7Ao8A57v7wp22DQSWmtl+QAPhaHhuNtF01g1kwMGH0n/RIjbOf5ymLx6dze6k\nl6iTS7wpf/Gl3MVbLl2ULgYGAj80s4XpP99Md1CqB2YDC4EngWXuPj/XYJuOmARAn6eeyHVXIiIi\nBS3jdHQPCDr9Rrd1K0NsFM377sfGBU/2XlSSkb6Nx5vyF1/KXbzV1lZ3OB1dMIt1fGTAAJo+N5Hy\nl18isX591NGIiIj0mMIrwkDTUVNIBAF9nvpT1KGIiIj0mIIswjuOmgJAxcLHIo5ERESk5xRkEW4+\n6BBStUPp++h8aGmJOhwREZEeUZBFmLIyGo+fRtkHH2j1LBERKVqFWYSBxmlfAaDPHx7I8E4REZF4\nyrWL0nTgUsI1pW9y9xvzFVjTpKNIVdfQ96EH2PpvV0Kiwyu8RUREYinTSLi1i9Jk4Hjgl60b0gX6\nWuBY4CjgXDMbmrfI+vRhx99MI/nOaiqWLM7bbkVERApFLl2UxgIr3L3e3ZuARcDkfAa3/R9OAaDv\nb+/I525FREQKQi5dlGqA+jbPNxMucZk3TYdPomXkKCp//z+wZUs+dy0iIhK5Ts8Jw6e6KP22zaZ6\noO2q1NXAhmwO2tli1p9y1plw+eXULngQzjkn+89Jj+hS7qTgKH/xpdwVp07Xjk53UfoT7XdRqgBe\nASYCW4HFwHR3X5vhmJ2vHb2TsrXvssuEcbTsuRcbnnpWF2hFSOvXxpvyF1/KXbx1tnZ0ppFw2y5K\nreeG5wAD3H2Omc0CHiac1p6bRQHustTw3Wg84e+pvPt39Hn8UXYcMzXfhxAREYlE4XVRakfy5aXs\ncswkmiYexsb752s0HBF9G4835S++lLt4i1cXpXa0HHAgjcd/mYpn/kyfBQ9HHY6IiEhexKIIA2y9\n6IcEiQQDrrgcmpszf0BERKTAxaYIt4zdj+0nn0r5q6/Q79fXRx2OiIhIzmJThAG2XvpvpAYPZsBP\nr6DszZVRhyMiIpKTWBXhYPBgtvz4ahLbtlFzzpmwfXvUIYmIiHRbrIowQOPXvs62b55GxdIXqb7w\nAuj9q7tFRETyIuOKWQBmNhG4yt2n7PT6BcDZQF36pRnu/np+Q/y0LVf+O+WvLKPyt3eQqh3K1n+9\nTLctiYhI7GSzbOWFwKlAe4s3jwdOc/cX8h1Yp/r1o/7Ou/nM9Kn0/8V/kNi0iS1XXgPlWX2nEBER\nKQjZTEevAL4GtDfUnABcbGZPmdnsvEaWQVBbS/19f6B5/wPod+tcBn7zRMrWvdebIYiIiOQkYxF2\n93v5ZAvDtuYBM4CjgUlmNi2PsWWUGjacjff/kcYvTaXPnx5n0OSJVN5xm+4jFhGRWMhq2UozGw3M\nc/fDdnq9xt03pR/PBAa7+xUZdpf/K6mCAK6/Hr7/fWhogLFj4YIL4OSToaoq74cTERHpgg4vWup2\nETazgcBSYD+gAfh/hE0c5mfYXZfXjs5W2dp36f/vV1F5529ItLSQqqpmx/FfZsexx7Fj8hSCwYN7\n5LilQuvXxpvyF1/KXbzl0kWprQDAzE4GqtJdlGYDC4FGYEEWBbhHpYbvxpaf/ZyG7/2Ayjtuo/LO\n31B59++ovPt3ALSMHE3TIeNp2XsfWsbsScvoMaSG70Zq8BDo1y/K0EVEpATFootS948UkFz2Mn0f\nnU/Fs0sof+E5yjZsaP+t/QeQGjKEoLqGoF8/gv4DCPr3I+jfn6Bff6iogGSSoLwckuXpx8nwcXk5\nQTIJibLwVqnW26U6/Emn24OdX2fn59Gorq5k82YtkBJXyl98KXfxVv2DWblNR+dZ7xXhTx05oGz1\nKpIr3yD51psk31xJ2fvrKFv/AYn168OfmzeTaNhKoqUlmhhFRKS4BEFepqPjL5EgNXIUqZGjaMr0\n3h07SGxrINHQQKJhKzS3QHMziVT4k+bmsFC3pJ+3NJNIpT5eweujn+z0PMuffPJ5ogBWBqup6cem\nTduiDkO6SfmLL+Uu3mo62VZaRbgr+vQh6NOHYOBnoo6kcNRW06iLQ+JL+Ysv5a5oxW7taBERkWKh\nIiwiIhIRFWEREZGIZFWEzWyimS1s5/XpZvasmS02s2/nPzwREZHilbEIp7sozQH67vR6BXAtcCxw\nFHCumQ3tiSBFRESKUS5dlMYCK9y93t2bgEXA5DzHJyIiUrRy6aJUA9S3eb4ZGJinuERERIpeLvcJ\n1wPVbZ5XA+2vCflJidra6szvkoKk3MWb8hdfyl1xyqUIvwbsbWaDgK2EU9HX5CUqERGREpBrF6VZ\nwMOE09pz3X1tD8QoIiJSlKJo4CAiIiJosQ4REZHIqAiLiIhEREVYREQkIirCIiIiEVERFhERiYiK\nsIiISERUhEVERCKiIiwiIhIRFWEREZGIqAiLiIhEREVYREQkIirCIiIiEVERFhERiYiKsIiISERU\nhEVERCKiIiwiIhIRFWEREZGIlGfzJjObCFzl7lPavLYr8Ns2bzsY+IG7/zq/IYqIiBSnjEXYzC4E\nTgW2tH3d3dcBU9LvOQz4ETCnB2IUEREpStlMR68AvgYk2ttoZgng58BMdw/yGJuIiEhRy1iE3f1e\noLmTt0wHlrn78rxFJSIiUgKyOiecwSnAf2b75iAIgkSi3UG1iIhIMeqw6OWjCB/q7n/OOpJEgrq6\nzXk4rPS22tpq5S7GlL/4Uu7irba2usNtXblFKQAws5PN7Jz041qgPqfoRERESlQiCHr9WqpA3+ji\nSd/G4035iy/lLt5qa6s7nI7u9cU66up6+4giIiKFqdeL8A039PYRRUREClOvF+E33+ztI4qIiBSm\nXi/Cq1f39hFFREQKk4qwiIhIRCIpwr1/QbaIiEjh6fUi3NAAGzf29lFFREQKTyT9hNesURtjERGR\nbvcTTr/+OeBnhOtirgFOd/cdmfa3Zk2CceO6Ea2IiAhw2WV9eeCBfKy8/LHp05u57LLGDrc3Nm7n\nJz+5nHXr1tHU1MQFF1zIuHEH5HTMjEPSdD/hOUDfnV5PAL8GznT3I4HHgDHZHFQjYRERiZv77ruH\n3XbbgxtuuInLL/8Jf/3rspz3mc3XiNZ+wr/Z6fV9gPXALDMbBzzk7p7NQd99V12URESk+y67rLHT\nUWtPWL16FV/4wuEA7LHHCE466eSc95lLP+EhwOHAL4AvAceY2ZR23vcpGgmLiEjcjBo1hldf/SsA\na9a8w49+dGnO+8xlQn09sKJ19Gtm84FDgYWdfSiRgLq6CmprK3I4tESls5ZcUviUv/hS7qL37W+f\nwUUXXcSsWefT0tLCJZdcknNecinCK4EqM9vL3d8AjgRuzPShYcPgrbdS1NVtzeHQEgV1cok35S++\nlLvCMXv2ZZ94nk1eeqSfcPoq6LOBO83sWWCVu/8x005GjIC1axOkUl04soiISBHq9X7CJ55IcM89\n8PLLW9h1Vy2dFSf6Nh5vyl98KXfxVlD9hEeMCH/qCmkRESl1kRVhXSEtIiKlrtcr4ciR4c81azQS\nFhGR0qaRsIiISER0TlhERCQivV6Ehw2DioqAd97RSFhEREpbr1fCsjLYY4+AVas0EhYRkdIWyXB0\n5MgUH3xQxpYtURxdRESkMOTaT/gCwlWz6tIvzXD31zPtb+TIcLms1avLGDtWS2eJiEhpyliE0/2E\nTwXaG7eOB05z9xe6ctBRo8KVslatSjB2bFc+KSIiUjyymY5u7Sfc3kncCcDFZvaUmc3O9qCtI+FV\nq3RxloiIlK5c+gkDzANmAEcDk8xsWjYHVREWERHJrZUhwHXuvgnAzB4CDgEeyvSh8eMHALBuXR9q\na/vkGIL0JvU0jTflL76Uu+LU7SJsZgOBpWa2H9BAOBqem81ng2Az/ftXsXx5irq6hu6GIL1MnVzi\nTfmLL+Uu3jr7AtWVIvxRP2Ggyt3npM8DLwQagQXuPj+bHSUS4ZT0qlVlBEH4XEREpNT0ej9hIKir\n28ypp/bjkUfKcd/MoEG9HYJ0h76Nx5vyF1/KXbwVVD/hVro4S0RESp2KsIiISEQiLMLhNPjbb+uE\nsIiIlCaNhEVERCISWQUcNerj9aNFRERKUWQVsLoaBg1SS0MRESldkQ5DW+8VTqmRkoiIlKBIi/Ce\ne6ZobEzw7rsaDYuISOnJqgib2UQzW9jJ9l+b2ZVdPfiYMeEQ+I03dF5YRERKT8bql+4nPAfo28H2\nGcA40stadsWee4ZFeOVKFWERESk9OfUTNrPDgc8D/93e9kxUhEVEpJRlbODg7vea2eidXzez4cAP\nga8C3+jKQVs7SkycGD5fs0YtDeNC7dTiTfmLL+WuOOXST/hEYAjwB2AY0N/MXnX32zJ9sO1C5IMG\nVfHqqwF1dVtzCEV6gxaRjzflL76Uu3jLVyvDT3D3XwC/ADCzM4B9synAO9tzzxQvvVRGczOU5/KV\nQEREJGa6cjL2o37CZnZOR9u7asyYFM3NCVav1m1KIiJSWrIae7r7W8Dh6cfz2tl+a3cD2Guv8OKs\nN98sY8yYlu7uRkREJHYivyy59Qpp3SssIiKlJvLKp9uURESkVEVe+TQSFhGRUhV55auuhmHDUqxY\nEXkoIiIivaogKt8++6R4550ytmyJOhIREZHeUxBF2Cyckl6+vCDCERER6RUFUfX22Scswu4FEY6I\niEivyOo+YTObCFzl7lN2ev3vgR8QLtRxh7v/vDtBtI6E3ZNAc3d2ISIiEjvdbmVoZkngSuAY4DDg\nfDPbpTtB7LNPuEjH669rJCwiIqWj260M3b2FcL3ozUAtkAR2dCeIXXaB2tqUpqNFRKSkZKx67n4v\nHcwRu3vKzL4GvAAsBBq6G4hZitWrE2xVMyURESkROfctSvcb/h/gFuD09M9OtdfW6aCDYNEi+PDD\nakaPzjUq6SnqaRpvyl98KXfFqdtF2MxqgAeAY919h5ltBbLqwNBeX8wRIyqASpYs2caIEbo4qxCp\np2m8KX/xpdzFW776CX/UyhCocvc5ZnY78KSZNQEvAbd3N8jWK6Rfe03nhUVEpDQkgqBbbYBzEbT3\njW7DBjCr5phjmpk3b1tvxyRZ0LfxeFP+4ku5i7fa2upER9sKZtg5aBDsvnuKZcsKJiQREZEeVVAV\nb9y4FOvWlfH++x1+aRARESkaBVWE998/vK7rlVcKKiwREZEeUVDVbty48OKsZcuSEUciIiLS8wqs\nCGskLCIipaOgqt3IkQHV1YEuzhIRkZJQUNWurCw8L7xiRRkN3V4AU0REJB4KqghDeF44lUpo0Q4R\nESl6ufYTPhn4DmGDh5eB8909p9U/DjwwPC/84otJxo9P5bIrERGRgpZLP+F+wI+AL7r7JGAg8JVc\nA2otvM8/ryukRUSkuHW7nzCwHTjM3benn5cDOa83+dnPpqipCXjuORVhEREpbt3uJ+zugbvXAZjZ\nPwMD3H1BzgGVwSGHtPDGG2Vs2JDr3kRERApXTv2EzawMuBr4LPD32X4uU1/MI4+EJ56AlSurOf74\nXCKUfFNP03hT/uJLuStOORVh4L8Jp6W/2pULsjJ1Axk7Ngn057HHGpkwYUduEUreqJNLvCl/8aXc\nxVuP9BMG/hf4FvAk8LiZAVzn7vd1O9K0Qw7RxVkiIlL8sirC7v4WcHj68bw2m3qkSg4ZEjB6dIrn\nn08SBJBQUyURESlCBbsixoQJLWzcmOD11ws2RBERkZwUbIU74ohw0Y5FizQlLSIixamAi3B4V9TT\nT6sIi4hIcSrYIjx6dMDuu6dYvDhJSqtXiohIESrYIpxIhFPSH35YxquvFmyYIiIi3VbQ1W3SJE1J\ni4hI8SroIqyLs0REpJgVdBEeMSJg1KgUTz9dzg4tnCUiIkWmS0XYzCaa2cIOtvU3s6ctvXRWvkyd\n2szmzQmWLNFoWEREikvWRbijvsLpbYcSLl85hvTylvkydWp4Xvjhh3Nd5lpERKSwdGUk3FFfYYA+\nwAmA5yOotg47rIXq6oCHHy4nyGt5FxERiVbWRbijvsLpbYvd/Z28RdVGnz5w9NHNrFpVhntBn8IW\nERHpkkjmeLvaF/PEE+H3v4ennx7AkUf2UFCSFfU0jTflL76Uu+IUSRHual/Mz38eysurmDcvxdln\nN/RQVJKJeprGm/IXX8pdvHX2Bao787sf9RU2s3O6G1RXDBoEX/xiC0uXJlm+XFPSIiJSHBJB71/t\nFHTnG90995Qzc2Y/Zs1qZPZs3TQcBX0bjzflL76Uu3irra1u74JmoMAX62jr+OOb6d8/4O67K3SV\ntIiIFIXYFOEBA+ArXwmvkn7qKS3cISIi8RebIgxwxhnhNPTNN1dEHImIiEjuYlWEDz00xf77tzB/\nfjlr13Y4xS4iIhILsSrCiQR861tNtLQkuPFGjYZFRCTeYlWEAb7+9SaGDk1x88192Lgx6mhERES6\nL3ZFuLISZs7cwZYtCW68sU/U4YiIiHRb7IowwBlnNDF4cIpf/aoP77+vc8MiIhJPWRXhjvoIm9l0\nM3vWzBab2bfzH177qqrgX/4lHA1ffbVGwyIiEk8Zi3BHfYTNrAK4FjgWOAo418yG9kSQ7Tn99Cb2\n3ruF22+v4LnnYjmgFxGREpdN9eqoj/BYYIW717t7E7AImJzn+DpUUQFXX91IKpXgu9+tpLGxt44s\nIiKSHxm7KLn7vWY2up1NNUB9m+ebgYHZHDRfLblOOAFmzoTrr0/y059W84tf5GW30gm1U4s35S++\nlLvilEsrw3qg7b+KamBDNh/M50LkF14ICxf255e/TLLvvts46aTmvO1bPkmLyMeb8hdfyl285buV\nYavXgL3NbJCZ9SGciv5zDvvrlgED4Oabt1FdHfDd71by6KNaV1pEROKhK0X4E32E0+eBZwEPA4uB\nue6+tgdizGivvQLuuGMbFRXwrW/1Y8ECFWIRESl8seknnI2FC5Ocfno/mprg8ssbOffcJhK6jThv\nNCUWb8pffCl38VYU/YSzMWVKC/fd18CQIQGXXlrJKaf0Y80aVWERESlMRVWEASZMSPHIIw1MntzM\nggXlHHHEAH784z5syOqSMRERkd5TdEUYYLfdAu66axvXXbeNqqqA667ry0EHVTFzZiVPPpmkqSnq\nCEVERIrsnHB7GhrgttsquOWWPqxcGX7nqKoKOPLIZiZMSHHwwS3sv3+KXXYJdP44A52XijflL76U\nu3jr7Jzchy87AAAHdUlEQVRw0Rfhjw4awJIlSe6/v5zHHivnrbc+OQlQVRUwalSKkSNT1NYG7LJL\nwKBB4Z+aGqisDOjXL/xZWfnx87IySCYhmQwoK2v7PPxTVkbRFHf9Iog35S++lLt4UxFux+rVCV58\nMclLL5XhnmTVqgRvv11GQ0P+K2YiEbRbjHN5num9n44hc5yZJBIJIvj3Inmi/MWXchdvW7Z0/Bu4\nZItwe4IA1q9PsH59gg0bEnz4Yfhz0ybYvj3B9u2wbVv4c/v2BI2N0NIS/kmlIJVKfPS8pSXc38fb\nE586VmfP24st2892dd/ZKi9P0tzckp+dSa9T/uJLuYu3l15KdliEO1220szKgF8BBwKNwLfd/Y02\n208Gvg9sB+5y9//IS8QRSSRgyJCAIUP0jbM94ZRYQ9RhSDcpf/Gl3MVd95etPAHo4+6HA7OBn7Vu\nMLPBwE+Ao4EjgL8zs0NyjlVERKREZCrCRwDzAdz9GeDQNtv2Al5y943uHgBL6MVWhiIiInGXqQjX\nAJvaPG9JT1EDLAf2N7OhZtYfOAbo3wMxioiIFKVMrQw38cnJ7DJ3TwG4+wYzuwC4B1gPPA98kMUx\nE+qLGV/KXbwpf/Gl3BWnTCPhp4EvA5jZF4ClrRvMrBw41N2PBL4BHAQ81kNxioiIFJ1Ob1EyswQf\nXx0NcBYwAahy9zlmdinhxVstwA3uflMPxysiIlI0orhPWERERCjSBg4iIiJxoCIsIiISERVhERGR\niKgIS7vSF+WJiEgP6pEibGZnmtlVZnZMT+xfeo6ZDQNIr4ImIiJZMLNkdz6X16uj06OnHxLe0vQb\nwluannb3q/N2EOkRZjYCuAwYCvweeMLdl5tZQgU5HtIr1+0HvO3udWb20eI6UtiUu/gys0rgGsLF\nrZa5+7yufD7vtyiZ2TXAU+5+v5ntD/wROMTd1+f1QJIX6WVIBxF+YaoGbgFOIyzGl7h7fXTRSbbM\n7Fjgv4DHCRfO+bq7vxNtVJINM5sK/BLlLnbMrB9wORAAvwVuBi4GHnf37dnsI6/T0emRcD0w0Myq\n3f0V4CHg3/N5HMkPM/smsAA4CRgPzHP3N4E7gQbgzOiik2yZWQUwDTjf3c8jXLnun81sdKSBSafM\n7DPph19GuYuV1tN2QBPweeBWd38BuBr4W8IGR1nJaxFOT1s+DhwMjEi/fBGwj5ntms9jSfeZWV8z\nuwv4EnCSu18P1AHfSb/lHcJfBqPTLSulwJjZSDP7ZzMzd28iXLXusPTmawlnMsan36uL7AqImY0y\ns7mEM04AfQh/kYNyV9DMbISZ3QjMMbMZwO7AvcDfAbj7nUAK+Fz6/Rnzl/cLs9x9MeEvhK+Y2VA+\nbnm4Lt/Hku5x90bgfcLCe5aZ3QmMBk43s7HpaZR1QCWwRb8ICouZnQg8SJizfzGzfwSeAKrMbC93\n/xB4EjgddJFdITGz8wm/4N5HOAUN8Ciwi3IXC2cCawkHLEOBC4ENQLWZHZ5+z4PAtyC7/PXULUpX\nAwnC+fHrgD/30HGk+24gnMLsB5wKLE4/vtrMxgLHAoMJO2fpF0EBMLOD0g9HALPd/XvA7YRfdA8B\nVhE2U8HdbwYqzGyXKGKVTzKzA9IPdwC/BrYDt5jZGYTXZKxEuStIZnaWmd1qZj8E9gRudveVwO8I\nOwgeALwGfC/9kV2Ap9JNjjLK6k1d5e4fAD81swXA0vR0mRSWN4GfAY+lr8L8qZmdArwKnA8MBL7j\n7tsijFHSzGxvYJ6ZHUH4i2Ag8AfgRWAIcBzwJ+BSM+sLTAL+l0/2A5cIpHP3OzObQvj/7quE082/\nAQz4G8Kra2elr7Q9AuUucukZwCsJ/79dBVxCmLs6whHwamARMJVwduMgM/sdMJzwHH9zNsdRA4cS\nlv5HNgLYTPhL/QfA/yEc/TZGGZt8LH3/4U+AU4C5wPXAc8D+7r7RzCYQnl+8ivAK90nAFne/K6KQ\nJa1N7k4mvHp2dvr5be7+1za3t9wDvA18EeWuYJjZDcDd7r4g/SXqa4SFeLq7v2Bm44Fz3f289AWS\ng939va4co0dGwhIbVYRFdzzhqYmbNGtRsDYDk4EbgTuAmwjPKZ4KLCc8P9yY/gWwPKIYpX2bCYvr\nHOBz7j7bzAamtwWEX4DfcPfVhCNlKQDp2zfvAZ5Jv/QPhGsoLAP+08zOBY4hPJ/f390bgC4VYNBI\nWAAz+wLwnApw4TKzoe7+vpmdR/gf/xuE09GvAIcTXszzfwkLsf5TF5A2uTsHOMHdp5nZ/cBbhAsb\nPUe4yFGDcld40jOG1YRTzn/r7mvN7BLCa2aGAt9397Xd3b+KsEiMpFdWugO4y93vTJ8jDtJ3JUgB\nS+fudmAe4dXRRwBN7v50pIFJRumLVU8HbgV+RDga/kk+Bi4qwiIxY2bTgH8i/Fau2YsYUe7iKT0D\n9SvgEeB2d789X/tWERaJITNLuntL1HFI1yl38WNmZwG7AVfn+8uTirCIiEgnerKRjYqwiIhIRHpq\nxSwRERHJQEVYREQkIirCIiIiEVERFhERiYiKsIiISERUhEVERCLy/wEnT1VVq2IjogAAAABJRU5E\nrkJggg==\n", 168 | "text/plain": [ 169 | "" 170 | ] 171 | }, 172 | "metadata": {}, 173 | "output_type": "display_data" 174 | } 175 | ], 176 | "source": [ 177 | "ramsey_solution.number_knots = 1000\n", 178 | "ramsey_solution.solution.plot(subplots=True, style=['r', 'b'])" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 20, 184 | "metadata": { 185 | "collapsed": false 186 | }, 187 | "outputs": [ 188 | { 189 | "data": { 190 | "text/plain": [ 191 | "array([,\n", 192 | " ], dtype=object)" 193 | ] 194 | }, 195 | "execution_count": 20, 196 | "metadata": {}, 197 | "output_type": "execute_result" 198 | }, 199 | { 200 | "data": { 201 | "image/png": "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\nFt27h6hWzeK111wlFoSJsCyYPt2NplkMHJhciK2uw333BTFNLeVc23AYJk3y4HRaDB+e3IlKLSpA\nMKilHFEdDsPYsVK7Lm2GjUWDBhZDhshJ9OmnU/v7vv9e58UXpYnvrrsS/54yLcyPrluMHOktkSmR\niE8/dZCTI1O/BgxIvEAbODBIs2YRcnLcrF6dvJCfNcvFmjWy4cgVV8S/H4cNgzPPjDB7tivpIihv\nveVk1SonV1wRpnPn2M/WRRdF6NEjxPr1Dl57LfECc+1anX/8w0XTppGYGQhNm5r07Rti61ad2bPj\njzlnjosdO3T69w9FXeQMGRKkWjWL555zx9QgbW23RYsibdfGtja99FLZ8zh4EBYtkvfVpZcW7Xfd\ndWWLAlkWLFnipEoViz//OULt2tLF+emnDgIBOb9s367TooXJ6aebhEJJlv61LCvlf02aNHmqSZMm\n1xd7vz3e9mBZPp9lgWUtWmRZQlhWrVqWZVmW9c478vOnnpLv/X75vn17qwSdO8vPwbIeeKDo86ZN\nLSs727IikZLbr18vtx00yIrKfffJ7z//PPr306bJ73Nyon9vWZa1YIHcZsqU2NtYlmWddppl1asX\nfxvLsqy//lWO9/HHibc99VTLqls38XaWZVnz5slxJ01Kbvv27eX2u3cnt/3mzXL7bt2S296yLKtF\nC8tyuSzr11+T32fqVHmcqVOT3+fJJ5O7RqUZNkzuN3du8vusXCn3ueaa1I4VDstnwuWyrG3bkt9v\n9mx5vDvvTP14zZtblqZZ1oYNye83fbo83h13pHa83FzLql/fstxuea8kg2kW3YfvvJPa8e69V+43\nfnxy2wcCltWsmdxn9erkj/PZZ/I3FELOW4nYulXOgyefnPyztWyZPK8LL5TXLR6//irn1YwMeaxE\n7Nol586TTrKsvXtjbxeJWFarVvI8Pvoo/ph791pW1apyzP37o29z8KBl1ahhWVWqWNa+fbHHevRR\neczHHov+fc+e8vvFi6N/b98/X39d8nP7Pi497m+/WZbHI2WKacrPbDnSq1fRdkOGyM9WrrSsDz6Q\nr8eMsayHHy6SUWBZVhyZmG50wCqgKzBfCNEaWJ9oB5fLAjQOHMjH7/fgcMDevbn4/bI/7v79sj+u\n1ECz8HhC7N1btGQdNMjBhx9mUKOGRZ8+eYW9cc85x8umTS4+++wwZ55ZtHKSLfh8VK8eve9u1aqy\n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\n", 202 | "text/plain": [ 203 | "" 204 | ] 205 | }, 206 | "metadata": {}, 207 | "output_type": "display_data" 208 | } 209 | ], 210 | "source": [ 211 | "ramsey_solution.residuals.plot(subplots=True, style=['r', 'b'])\n" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": null, 217 | "metadata": { 218 | "collapsed": false 219 | }, 220 | "outputs": [], 221 | "source": [] 222 | } 223 | ], 224 | "metadata": { 225 | "kernelspec": { 226 | "display_name": "Python 2", 227 | "language": "python", 228 | "name": "python2" 229 | }, 230 | "language_info": { 231 | "codemirror_mode": { 232 | "name": "ipython", 233 | "version": 2 234 | }, 235 | "file_extension": ".py", 236 | "mimetype": "text/x-python", 237 | "name": "python", 238 | "nbconvert_exporter": "python", 239 | "pygments_lexer": "ipython2", 240 | "version": "2.7.10" 241 | } 242 | }, 243 | "nbformat": 4, 244 | "nbformat_minor": 0 245 | } 246 | -------------------------------------------------------------------------------- /examples/heat-exchanger.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": { 18 | "collapsed": false 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "import matplotlib.pyplot as plt\n", 23 | "import numpy as np\n", 24 | "import seaborn as sn\n", 25 | "\n", 26 | "import pycollocation" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "Consider the following boundary value problem:\n", 34 | "\\begin{align}\n", 35 | " \\frac{d T_1}{d_A} =& -q,\\ T_1(0) = T_{1,0} \\\\\n", 36 | " \\frac{d T_2}{d_A} =& -\\frac{1}{2}q,\\ T_2(A_{hx}) = T_{2,A_{hx}} \\\\\n", 37 | "\\end{align}\n", 38 | "where $q = (T_1 - T_2)U$.\n", 39 | "\n", 40 | "This boundary value problem describes a counter current heat exchanger; a hot liquid enters a device and exchanges heat across a metal plate with a cold liquid traveling through the device in the opposite direction. Here, $T_1$ is the temperature of the hot stream, $T_2$ is the temperature of the cold stream, $q$ is the rate of heat transfer from the hot fluid to the cold fluid, $U$ is the overall heat transfer coefficient and $A_hx$ is the total area of the heat exchanger." 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 4, 46 | "metadata": { 47 | "collapsed": false 48 | }, 49 | "outputs": [], 50 | "source": [ 51 | "# provide numerical values for the parameters\n", 52 | "params = {'T10': 130, 'T2Ahx': 70, 'U': 1.0}\n", 53 | "\n", 54 | "def q(T1, T2, U):\n", 55 | " return (T1 - T2) * U\n", 56 | "\n", 57 | "def rhs(A, T1, T2, U, **params):\n", 58 | " return [-q(T1, T2, U), -0.5 * q(T1, T2, U)]\n", 59 | "\n", 60 | "def bcs_lower(A, T1, T2, T10, **params):\n", 61 | " return [T1 - T10]\n", 62 | "\n", 63 | "def bcs_upper(A, T1, T2, T2Ahx, **params):\n", 64 | " return [T2 - T2Ahx]" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 5, 70 | "metadata": { 71 | "collapsed": false 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "problem = pycollocation.problems.TwoPointBVP(bcs_lower, bcs_upper, 1, 2, params, rhs)" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 8, 81 | "metadata": { 82 | "collapsed": true 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "def initial_mesh(domain, num, problem):\n", 87 | " As = np.linspace(domain[0], domain[1], num)\n", 88 | " T1s = np.repeat(0.5 * (problem.params['T10'] + problem.params['T2Ahx']), num)\n", 89 | " return As, T1s, T1s\n" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 10, 95 | "metadata": { 96 | "collapsed": false 97 | }, 98 | "outputs": [], 99 | "source": [ 100 | "polynomial_basis = pycollocation.basis_functions.PolynomialBasis()\n", 101 | "solver = pycollocation.solvers.Solver(polynomial_basis)\n", 102 | "\n", 103 | "basis_kwargs = {'kind': 'Chebyshev', 'domain': [0, 5.0], 'degree': 15}\n", 104 | "\n", 105 | "As, T1s, T2s = initial_mesh(basis_kwargs['domain'], 1000, problem)\n", 106 | "T1_poly = polynomial_basis.fit(As, T1s, **basis_kwargs)\n", 107 | "T2_poly = polynomial_basis.fit(As, T2s, **basis_kwargs)\n", 108 | "initial_coefs = np.hstack([T1_poly.coef, T2_poly.coef])\n", 109 | "\n", 110 | "solution = solver.solve(basis_kwargs, initial_coefs, problem)\n" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 11, 116 | "metadata": { 117 | "collapsed": false 118 | }, 119 | "outputs": [ 120 | { 121 | "data": { 122 | "image/png": "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\nUk58ATqtFubqP3iPAAAAAElFTkSuQmCC\n", 123 | "text/plain": [ 124 | "" 125 | ] 126 | }, 127 | "metadata": {}, 128 | "output_type": "display_data" 129 | } 130 | ], 131 | "source": [ 132 | "T1_soln, T2_soln = solution.evaluate_solution(As)\n", 133 | "plt.plot(As, T1_soln)\n", 134 | "plt.plot(As, T2_soln)\n", 135 | "plt.show()" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 12, 141 | "metadata": { 142 | "collapsed": false 143 | }, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "image/png": "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\nYNAmIiJqE/8fsBZlPfjmi3IAAAAASUVORK5CYII=\n", 148 | "text/plain": [ 149 | "" 150 | ] 151 | }, 152 | "metadata": {}, 153 | "output_type": "display_data" 154 | } 155 | ], 156 | "source": [ 157 | "T1_normalized_resids, T2_normalized_resids = solution.normalize_residuals(As)\n", 158 | "plt.plot(As, np.abs(T1_normalized_resids))\n", 159 | "plt.plot(As, np.abs(T2_normalized_resids))\n", 160 | "plt.yscale('log')\n", 161 | "plt.show()" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": { 168 | "collapsed": true 169 | }, 170 | "outputs": [], 171 | "source": [] 172 | } 173 | ], 174 | "metadata": { 175 | "kernelspec": { 176 | "display_name": "Python 2", 177 | "language": "python", 178 | "name": "python2" 179 | }, 180 | "language_info": { 181 | "codemirror_mode": { 182 | "name": "ipython", 183 | "version": 2 184 | }, 185 | "file_extension": ".py", 186 | "mimetype": "text/x-python", 187 | "name": "python", 188 | "nbconvert_exporter": "python", 189 | "pygments_lexer": "ipython2", 190 | "version": "2.7.10" 191 | } 192 | }, 193 | "nbformat": 4, 194 | "nbformat_minor": 0 195 | } 196 | --------------------------------------------------------------------------------