Skip to content

Commit 193413a

Browse files
authored
adding dependency tutorial (#329)
* adding dependency tutorial * fixing caching * remove n-dim mom approx * rearanging docs
1 parent 634a361 commit 193413a

25 files changed

+820
-733
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
command: |
7777
source /home/circleci/venv/bin/activate
7878
coverage run --source=chaospy/ --module pytest --nbval-lax --doctest-modules \
79-
chaospy/ tests/ docs/*/*.ipynb docs/*/*.rst README.rst
79+
chaospy/ tests/ docs/*/*.ipynb docs/index.rst docs/*/*.rst
8080
bash <(curl -s https://codecov.io/bash)
8181
deploy:
8282
executor: python-container

README.rst

Lines changed: 67 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -18,127 +18,98 @@
1818
.. |binder| image:: https://mybinder.org/badge_logo.svg
1919
:target: https://mybinder.org/v2/gh/jonathf/chaospy/master?filepath=docs%2Ftutorials
2020

21-
Chaospy is a numerical tool for performing uncertainty quantification using
22-
polynomial chaos expansions and advanced Monte Carlo methods implemented in
23-
Python.
24-
2521
* `Documentation <https://chaospy.readthedocs.io/en/master>`_
2622
* `Interactive tutorials with Binder <https://mybinder.org/v2/gh/jonathf/chaospy/master?filepath=docs%2Ftutorials>`_
27-
* `Source code <https://github.com/jonathf/chaospy>`_
28-
* `Issue tracker <https://github.com/jonathf/chaospy/issues>`_
29-
* `Code of Conduct <https://github.com/jonathf/chaospy/blob/master/CODE_OF_CONDUCT.md>`_
30-
* `Contribution Guideline <https://github.com/jonathf/chaospy/blob/master/CONTRIBUTING.md>`_
31-
* `Changelog <https://github.com/jonathf/chaospy/blob/master/CHANGELOg.md>`_
23+
* `Code of conduct <https://github.com/jonathf/chaospy/blob/master/CODE_OF_CONDUCT.md>`_
24+
* `Contribution guideline <https://github.com/jonathf/chaospy/blob/master/CONTRIBUTING.md>`_
25+
* `Changelog <https://github.com/jonathf/chaospy/blob/master/CHANGELOG.md>`_
26+
* `License <https://github.com/jonathf/chaospy/blob/master/LICENCE.txt>`_
27+
28+
Chaospy is a numerical toolbox for performing uncertainty quantification using
29+
polynomial chaos expansions, advanced Monte Carlo methods implemented in
30+
Python. It also include a full suite of tools for doing low-discrepancy
31+
sampling, quadrature creation, polynomial manipulations, and a lot more.
32+
33+
The philosophy behind ``chaospy`` is not to be a single tool that solves every
34+
uncertainty quantification problem, but instead be a specific tools to aid to
35+
let the user solve problems themselves. This includes both well established
36+
problems, but also to be a foundry for experimenting with new problems, that
37+
are not so well established. To do this, emphasis is put on the following:
38+
39+
* Focus on an easy to use interface that embraces the `pythonic code style
40+
<https://docs.python-guide.org/writing/style/>`_.
41+
* Make sure the code is "composable", such a way that changing one part of the
42+
code with something user defined should be easy and encouraged.
43+
* Try to support a broad width of the various methods for doing uncertainty
44+
quantification where that makes sense to involve ``chaospy``.
45+
* Make sure that ``chaospy`` plays nice with a large set of of other other
46+
similar projects. This includes `numpy <https://numpy.org/>`_, `scipy
47+
<https://scipy.org/>`_, `scikit-learn <https://scikit-learn.org>`_,
48+
`statsmodels <https://statsmodels.org/>`_, `openturns
49+
<https://openturns.org/>`_, and `gstools <https://geostat-framework.org/>`_
50+
to mention a few.
51+
* Contribute all code to the community open source.
3252

3353
Installation
34-
------------
54+
============
3555

36-
Installation should be straight forward using `pip <https://pypi.org/>`_:
56+
Installation should be straight forward from `pip <https://pypi.org/>`_:
3757

3858
.. code-block:: bash
3959
40-
$ pip install chaospy
41-
42-
For more installation details, see the `installation guide
43-
<https://chaospy.readthedocs.io/en/master/installation.html>`_.
44-
45-
Example Usage
46-
-------------
47-
48-
``chaospy`` is created to work well inside numerical Python ecosystem. You
49-
therefore typically need to import `Numpy <https://numpy.org/>`_ along side
50-
``chaospy``:
51-
52-
.. code-block:: python
53-
54-
>>> import numpy
55-
>>> import chaospy
56-
57-
``chaospy`` is problem agnostic, so you can use your own code using any means
58-
you find fit. The only requirement is that the output is compatible with
59-
`numpy.ndarray` format:
60-
61-
.. code-block:: python
62-
63-
>>> coordinates = numpy.linspace(0, 10, 100)
64-
65-
>>> def forward_solver(coordinates, parameters):
66-
... """Function to do uncertainty quantification on."""
67-
... param_init, param_rate = parameters
68-
... return param_init*numpy.e**(-param_rate*coordinates)
60+
pip install chaospy
6961
70-
We here assume that ``parameters`` contains aleatory variability with known
71-
probability. We formalize this probability in ``chaospy`` as a joint
72-
probability distribution. For example:
62+
Or if `Conda <https://conda.io/>`_ is more to your liking:
7363

74-
.. code-block:: python
75-
76-
>>> distribution = chaospy.J(chaospy.Uniform(1, 2), chaospy.Normal(0, 2))
77-
78-
>>> print(distribution)
79-
J(Uniform(lower=1, upper=2), Normal(mu=0, sigma=2))
80-
81-
Most probability distributions have an associated expansion of orthogonal
82-
polynomials. These can be automatically constructed:
83-
84-
.. code-block:: python
85-
86-
>>> expansion = chaospy.generate_expansion(8, distribution)
87-
88-
>>> print(expansion[:5].round(8))
89-
[1.0 q1 q0-1.5 q0*q1-1.5*q1 q0**2-3.0*q0+2.16666667]
90-
91-
Here the polynomial is defined positional, such that ``q0`` and ``q1`` refers
92-
to the uniform and normal distribution respectively.
64+
.. code-block:: bash
9365
94-
The distribution can also be used to create (pseudo-)random samples and
95-
low-discrepancy sequences. For example to create Sobol sequence samples:
66+
conda install -c conda-forge chaospy
9667
97-
.. code-block:: python
68+
Then go over to the
69+
`tutorial collection <https://chaospy.readthedocs.io/en/master/tutorials>`_
70+
to see how to use the toolbox.
9871

99-
>>> samples = distribution.sample(1000, rule="sobol")
72+
Development
73+
===========
10074

101-
>>> print(samples[:, :4].round(8))
102-
[[ 1.5 1.75 1.25 1.375 ]
103-
[ 0. -1.3489795 1.3489795 -0.63727873]]
75+
Chaospy uses `poetry`_ to manage its development installation. Assuming
76+
`poetry`_ installed on your system, installing ``chaospy`` for development can
77+
be done from the repository root with the command::
10478

105-
We can evaluating the forward solver using these samples:
79+
poetry install
10680

107-
.. code-block:: python
81+
This will install all required dependencies and chaospy into a virtual
82+
environment. If you are not already managing your own virtual environment, you
83+
can use poetry to activate and deactivate with::
10884

109-
>>> evaluations = numpy.array([forward_solver(coordinates, sample)
110-
... for sample in samples.T])
85+
poetry shell
86+
exit
11187

112-
>>> print(evaluations[:3, :5].round(8))
113-
[[1.5 1.5 1.5 1.5 1.5 ]
114-
[1.75 2.00546578 2.29822457 2.63372042 3.0181921 ]
115-
[1.25 1.09076905 0.95182169 0.83057411 0.72477163]]
88+
.. _poetry: https://poetry.eustace.io/
11689

117-
Having all these components in place, we have enough components to perform
118-
point collocation. Or in other words, we can create a polynomial approximation
119-
of ``forward_solver``:
90+
Testing
91+
-------
12092

121-
.. code-block:: python
93+
To ensure that the code run on your local system, run the following:
12294

123-
>>> approx_solver = chaospy.fit_regression(expansion, samples, evaluations)
95+
.. code-block:: bash
12496
125-
>>> print(approx_solver[:2].round(4))
126-
[q0 -0.0002*q0*q1**3+0.0051*q0*q1**2-0.101*q0*q1+q0]
97+
poetry run pytest --nbval-lax --doctest-modules \
98+
chaospy/ tests/ docs/*/*.{rst,ipynb}
12799
128-
Since the model approximations are polynomials, we can do inference on them
129-
directly. For example:
100+
Documentation
101+
-------------
130102

131-
.. code-block:: python
103+
The documentation build assumes that ``pandoc`` is installed on your
104+
system and available in your path.
132105

133-
>>> expected = chaospy.E(approx_solver, distribution)
134-
>>> deviation = chaospy.Std(approx_solver, distribution)
106+
To build documentation locally on your system, use ``make`` from the ``docs/``
107+
folder:
135108

136-
>>> print(expected[:5].round(8))
137-
[1.5 1.53092356 1.62757217 1.80240142 2.07915608]
138-
>>> print(deviation[:5].round(8))
139-
[0.28867513 0.43364958 0.76501802 1.27106355 2.07110879]
109+
.. code-block:: bash
140110
141-
For more extensive guides on this approach an others, see the `tutorial
142-
collection`_.
111+
cd docs/
112+
make html
143113
144-
.. _tutorial collection: https://chaospy.readthedocs.io/en/master/tutorials
114+
Run ``make`` without argument to get a list of build targets.
115+
The HTML target stores output to the folder ``doc/.build/html``.

chaospy/distributions/approximation.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,14 @@ def approximate_moment(
186186
7.0
187187
188188
"""
189-
if order is None:
190-
order = int(1e5**(1./len(distribution)))
191-
assert isinstance(order, int)
192189
assert isinstance(distribution, chaospy.Distribution)
193190
k_loc = tuple(numpy.asarray(k_loc).tolist())
194191
assert len(k_loc) == len(distribution), "incorrect size of exponents"
195192
assert all([isinstance(k, int) for k in k_loc]), (
196193
"exponents must be integers: %s found" % type(k_loc[0]))
194+
assert len(distribution) == 1, "only 1-D distributions support approximate moment."
197195

196+
order = int(1e5 if order is None else order)
198197
if (distribution, order) not in MOMENTS_QUADS:
199198
MOMENTS_QUADS[distribution, order] = chaospy.generate_quadrature(
200199
order, distribution, rule=rule, **kwargs)

chaospy/distributions/baseclass/distribution.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ def _get_cache(self, idx, cache, get=None):
657657
The cached values are as follows:
658658
659659
----------- ------------- -------------
660-
Context Get 1 Get 2
660+
Context Get 0 Get 1
661661
----------- ------------- -------------
662662
pdf Input values Output values
663663
cdf/fwd Input values Output values
@@ -681,22 +681,13 @@ def _get_cache(self, idx, cache, get=None):
681681
682682
"""
683683
if (idx, self) in cache:
684-
out = cache[idx, self]
685-
else:
686-
out = self._cache(idx, cache)
687-
if not isinstance(out, Distribution) and get is not None:
688684
assert get in (0, 1)
689-
out = out[get]
690-
return out
691-
if self in cache:
692-
out = cache[idx, self]
685+
out = cache[idx, self][get]
693686
else:
694-
out = self._cache(idx, cache)
695-
if not isinstance(out, Distribution):
696-
out = out[1]
687+
out = self._cache(idx=idx, cache=cache, get=get)
697688
return out
698689

699-
def _cache(self, idx, cache):
690+
def _cache(self, idx, cache, get):
700691
"""Backend function of retrieving cache values."""
701692
return self
702693

chaospy/distributions/baseclass/lower_upper.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(
3232
repr_args=None,
3333
):
3434
assert isinstance(dist, Distribution), "'dist' should be a distribution"
35+
assert len(dist) == 1
3536
if repr_args is None:
3637
repr_args = dist._repr_args[:]
3738
repr_args += chaospy.format_repr_kwargs(lower=(lower, 0), upper=(upper, 1))
@@ -43,6 +44,9 @@ def __init__(
4344
rotation=rotation,
4445
extra_parameters=dict(dist=dist),
4546
)
47+
assert len(dependencies) == 1
48+
assert len(parameters["lower"]) == 1
49+
assert len(parameters["upper"]) == 1
4650
super(LowerUpperDistribution, self).__init__(
4751
parameters=parameters,
4852
dependencies=dependencies,
@@ -57,16 +61,12 @@ def get_parameters(self, idx, cache, assert_numerical=True):
5761
lower = parameters["lower"]
5862
if isinstance(lower, Distribution):
5963
lower = lower._get_cache(idx, cache, get=0)
60-
if idx is not None and len(lower) > 1:
61-
lower = lower[idx]
6264
upper = parameters["upper"]
6365
if isinstance(upper, Distribution):
6466
upper = upper._get_cache(idx, cache, get=0)
65-
if idx is not None and len(upper) > 1:
66-
upper = upper[idx]
6767
assert not assert_numerical or not (isinstance(lower, Distribution) or
6868
isinstance(upper, Distribution))
69-
assert numpy.all(upper.ravel() > lower.ravel()), (
69+
assert numpy.all(upper > lower), (
7070
"condition not satisfied: `upper > lower`")
7171
lower0 = self._dist._get_lower(idx, cache.copy())
7272
upper0 = self._dist._get_upper(idx, cache.copy())

chaospy/distributions/baseclass/operator.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,8 @@ def get_parameters(self, idx, cache, assert_numerical=True):
5959
if idx is None:
6060
del parameters["idx"]
6161
return parameters
62+
63+
def _cache(self, idx, cache, get):
64+
assert get == 0
65+
parameters = self.get_parameters(idx, cache)
66+
return self._operator(parameters["left"], parameters["right"])

chaospy/distributions/baseclass/shift_scale.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,15 @@ def get_parameters(self, idx, cache, assert_numerical=True):
7070
shift = shift._get_cache(idx, cache, get=0)
7171
elif idx is not None and len(shift) > 1:
7272
shift = shift[idx]
73+
assert not isinstance(shift, Distribution), shift
7374

7475
scale = self._parameters["scale"]
7576
if isinstance(scale, Distribution):
7677
scale = scale._get_cache(idx, cache, get=0)
7778
elif idx is not None and len(scale) > 1:
7879
scale = scale[idx]
79-
assert scale > 0, "condition not satisfied: `scale > 0`"
80+
assert not isinstance(scale, Distribution), scale
81+
assert numpy.all([scale]) > 0, "condition not satisfied: `scale > 0`"
8082

8183
assert not assert_numerical or not (isinstance(shift, Distribution) or
8284
isinstance(scale, Distribution))

chaospy/distributions/baseclass/slice_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ def _mom(self, kloc, parent, parameters):
5252
def _ttr(self, kloc, parent, parameters):
5353
raise chaospy.StochasticallyDependentError("TTR not supported")
5454

55-
def _cache(self, idx, cache):
55+
def _cache(self, idx, cache, get):
5656
if idx is None:
5757
return self
5858
assert idx == 0
5959
idx = int(self._parameters["index"])
6060
parent = self._parameters["parent"]
61-
return parent._get_cache(idx, cache)
61+
return parent._get_cache(idx, cache, get)

chaospy/distributions/operators/addition.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@
6262
array([[3.01, 3.5 , 3.99],
6363
[5.11, 6. , 6.89]])
6464
65-
Raw moments::
66-
67-
>>> joint1.mom([(0, 1, 1), (1, 0, 1)]).round(4)
68-
array([ 6. , 2.5 , 15.0834])
69-
>>> joint2.mom([(0, 1, 1), (1, 0, 1)]).round(4)
70-
array([ 6. , 3.5 , 21.0834])
7165
"""
7266
from __future__ import division
7367
from scipy.special import comb
@@ -80,6 +74,8 @@
8074
class Add(OperatorDistribution):
8175
"""Addition operator."""
8276

77+
_operator = lambda self, left, right: (left.T+right.T).T
78+
8379
def __init__(self, left, right):
8480
super(Add, self).__init__(
8581
left=left,
@@ -103,7 +99,7 @@ def _lower(self, idx, left, right, cache):
10399
left = left._get_lower(idx, cache=cache)
104100
if isinstance(right, Distribution):
105101
right = right._get_lower(idx, cache=cache)
106-
return left+right
102+
return self._operator(left, right)
107103

108104
def _upper(self, idx, left, right, cache):
109105
"""
@@ -122,7 +118,7 @@ def _upper(self, idx, left, right, cache):
122118
left = left._get_upper(idx, cache=cache)
123119
if isinstance(right, Distribution):
124120
right = right._get_upper(idx, cache=cache)
125-
return (left.T+right.T).T
121+
return self._operator(left, right)
126122

127123
def _cdf(self, xloc, idx, left, right, cache):
128124
if isinstance(right, Distribution):
@@ -165,7 +161,8 @@ def _ppf(self, uloc, idx, left, right, cache):
165161
if isinstance(right, Distribution):
166162
left, right = right, left
167163
xloc = left._get_inv(uloc, idx, cache=cache)
168-
return (xloc.T+numpy.asfarray(right).T).T
164+
right = numpy.asfarray(right)
165+
return self._operator(xloc, right)
169166

170167
def _mom(self, keys, left, right, cache):
171168
"""

0 commit comments

Comments
 (0)