diff --git a/equadratures/__init__.py b/equadratures/__init__.py index 48555162..44169ff4 100644 --- a/equadratures/__init__.py +++ b/equadratures/__init__.py @@ -9,6 +9,7 @@ from equadratures.weight import Weight from equadratures.poly import evaluate_model, evaluate_model_gradients, vector_to_2D_grid from equadratures.polytree import PolyTree +from equadratures.plot import Plot from equadratures.scalers import * import numpy as np import os, sys diff --git a/equadratures/parameter.py b/equadratures/parameter.py index 8c7bd3b1..afa616a6 100644 --- a/equadratures/parameter.py +++ b/equadratures/parameter.py @@ -17,10 +17,11 @@ from equadratures.distributions.gumbel import Gumbel from equadratures.distributions.chi import Chi from equadratures.distributions.analytical import Analytical +from equadratures.plot import Plot import numpy as np import scipy as sc -class Parameter(object): +class Parameter(Plot): """ This class defines a univariate parameter. Below are details of its constructor. diff --git a/equadratures/plot.py b/equadratures/plot.py new file mode 100644 index 00000000..8ca2053f --- /dev/null +++ b/equadratures/plot.py @@ -0,0 +1,134 @@ +import seaborn as sns +import matplotlib.pyplot as plt +from equadratures.datasets import score +import numpy as np +sns.set(font_scale=1.5) +sns.set_style("white") +sns.set_style("ticks") + +class Plot: + """ + Plotting utilities. + """ + def plot_pdf(self, data=None, save=False, xlim=None, ylim=None): + """ + Plots the probability density function for a Parameter. + """ + s_values, pdf = self.get_pdf() + fig = plt.figure(figsize=(8,6)) + ax = fig.add_subplot(1,1,1) + plt.fill_between(s_values, pdf*0.0, pdf, color="gold" , label='Density', interpolate=True, hatch="\\\\\\\\", edgecolor="grey", linewidth=0.5,alpha=0.5) + if data is not None: + plt.hist(data, 50, density=True, facecolor='dodgerblue', alpha=0.7, label='Data', edgecolor='white') + plt.xlabel(self.variable.capitalize()) + plt.ylabel('PDF') + if xlim is not None: + plt.xlim([xlim[0], xlim[1]]) + if ylim is not None: + plt.ylim([ylim[0], ylim[1]]) + plt.legend() + sns.despine(offset=10, trim=True) + if save: + plt.savefig('pdf_plot.png', dpi=140, bbox_inches='tight') + else: + plt.show() + def plot_orthogonal_polynomials(self, order_limit=None, number_of_points=200, save=False, xlim=None, ylim=None): + """ + Plots the first K orthogonal polynomials. + + :param Parameter self: An instance of the Parameter class. + """ + Xi = np.linspace(self.distribution.x_range_for_pdf[0], \ + self.distribution.x_range_for_pdf[-1], number_of_points).reshape(number_of_points, 1) + P, _, _ = self._get_orthogonal_polynomial(Xi) + fig = plt.figure(figsize=(8,6)) + ax = fig.add_subplot(1,1,1) + if order_limit is None: + max_order = P.shape[0] + else: + max_order = order_limit + for i in range(0, max_order): + plt.plot(Xi, P[i,:], '-', lw=2, label='order %d'%(i)) + if xlim is not None: + ax.set_xlim([xlim[0], xlim[1]]) + if ylim is not None: + ax.set_ylim([ylim[0], ylim[1]]) + plt.legend() + sns.despine(offset=10, trim=True) + plt.xlabel(self.name.capitalize()+' parameter') + plt.ylabel('Orthogonal polynomials') + if save: + plt.savefig('polyfit_1D_plot.png', dpi=140, bbox_inches='tight') + else: + plt.show() + def plot_polyfit_1D(self, uncertainty=True, output_variances=None, number_of_points=200, save=False, xlim=None, ylim=None): + """ + Plots a univariate polynomial. + """ + if self.dimensions != 1: + raise(ValueError, 'plot_polyfit_1D is only meant for univariate polynomials.') + Xi = np.linspace(self.parameters[0].distribution.x_range_for_pdf[0], \ + self.parameters[0].distribution.x_range_for_pdf[-1], number_of_points).reshape(number_of_points, 1) + if uncertainty: + if output_variances is None: + y, ystd = self.get_polyfit(Xi,uq=True) + else: + self.output_variances = output_variances + y, ystd = self.get_polyfit(Xi,uq=True) + ystd = ystd.squeeze() + else: + y = self.get_polyfit(Xi) + y = y.squeeze() + X = self.get_points() + y_truth = self._model_evaluations + fig = plt.figure(figsize=(8,6)) + ax = fig.add_subplot(1,1,1) + plt.plot(Xi, y, '-',label='Polynomial fit', color='navy') + plt.plot(X.flatten(), y_truth.flatten(), 'o', color='dodgerblue', ms=10, markeredgecolor='k',lw=1, alpha=0.6, label='Data') + if uncertainty: + ax.fill_between(Xi.flatten(), y+ystd, y-ystd, alpha=.10, color='deepskyblue',label='Polynomial $\sigma$') + if xlim is not None: + ax.set_xlim([xlim[0], xlim[1]]) + if ylim is not None: + ax.set_ylim([ylim[0], ylim[1]]) + plt.legend() + sns.despine(offset=10, trim=True) + plt.xlabel(self.parameters[0].variable.capitalize()) + plt.ylabel('Polynomial fit') + if save: + plt.savefig('polyfit_1D_plot.png', dpi=140, bbox_inches='tight') + else: + plt.show() + + def plot_model_vs_data(self, sample_data=None, metric='adjusted_r2', save=False, xlim=None, ylim=None): + """ + Plots the polynomial approximation against the true data. + + :param Poly self: An instance of the Poly class. + + """ + if sample_data is None: + X = self.get_points() + y_truth = self._model_evaluations + y_model = self.get_polyfit(X) + else: + X, y_truth = sample_data[0], sample_data[1] + y_model = self.get_polyfit(X) + R2score = score(y_truth, y_model, metric, X) + fig = plt.figure(figsize=(8,6)) + ax = fig.add_subplot(1,1,1) + plt.plot(y_model, y_truth, 'o', color='dodgerblue', ms=10, markeredgecolor='k',lw=1, alpha=0.6) + plt.xlabel('Polynomial model') + plt.ylabel('True data') + displaytext = '$R^2$ = '+str(np.round(float(R2score), 2)) + if xlim is not None: + plt.xlim([xlim[0], xlim[1]]) + if ylim is not None: + plt.ylim([ylim[0], ylim[1]]) + plt.text(0.3, 0.9, displaytext, transform=ax.transAxes, \ + horizontalalignment='center', verticalalignment='center', fontsize=14, color='grey') + sns.despine(offset=10, trim=True) + if save: + plt.savefig('model_vs_data_plot.png', dpi=140, bbox_inches='tight') + else: + plt.show() diff --git a/equadratures/poly.py b/equadratures/poly.py index d28687f1..0e1a9d3f 100644 --- a/equadratures/poly.py +++ b/equadratures/poly.py @@ -6,11 +6,13 @@ from equadratures.subsampling import Subsampling from equadratures.quadrature import Quadrature from equadratures.datasets import score +from equadratures.plot import Plot import scipy.stats as st import numpy as np from copy import deepcopy MAXIMUM_ORDER_FOR_STATS = 8 -class Poly(object): + +class Poly(Plot): """ Definition of a polynomial object. @@ -815,7 +817,7 @@ def get_poly_hess(self, stack_of_points): return H def get_polyscore(self,X_test=None,y_test=None,metric='adjusted_r2'): """ - Evaluates the accuracy of the polynomial approximation using the selected accuracy metric. Training accuracy is evaluated on the data used for fitting the polynomial. Testing accuracy is evaluated on new data if it is provided by the ``X_test`` and ``y_test`` arguments (both must be provided together). + Evaluates the accuracy of the polynomial approximation using the selected accuracy metric. Training accuracy is evaluated on the data used for fitting the polynomial. Testing accuracy is evaluated on new data if it is provided by the ``X_test`` and ``y_test`` arguments (both must be provided together). :param Poly self: An instance of the Poly class. @@ -824,7 +826,7 @@ def get_polyscore(self,X_test=None,y_test=None,metric='adjusted_r2'): :param numpy.ndarray y_test: An ndarray with shape (number_of_observations, 1) containing new ``y_test`` data (optional). :param string metric: - An optional string containing the scoring metric to use. Avaliable options are: ``adjusted_r2``, ``r2``, ``mae``, ``rmse``, or ``normalised_mae`` (default: ``adjusted_r2``). + An optional string containing the scoring metric to use. Avaliable options are: ``adjusted_r2``, ``r2``, ``mae``, ``rmse``, or ``normalised_mae`` (default: ``adjusted_r2``). :return: **score_train**: The training score of the model, output as a float. @@ -840,7 +842,6 @@ def get_polyscore(self,X_test=None,y_test=None,metric='adjusted_r2'): return train_score, test_score else: return train_score - def _get_polystd(self, stack_of_points): """ Private function to evaluate the uncertainty of the polynomial approximation at prescribed points, following the approach from [7]. @@ -853,8 +854,8 @@ def _get_polystd(self, stack_of_points): **y_std**: A numpy.ndarray of shape (number_of_observations,1) corresponding to the uncertainty (one standard deviation) of the polynomial approximation at each point. """ # Training data - X_train = self.inputs - y_train = self.outputs + X_train = self._quadrature_points + y_train = self._model_evaluations # Define covariance matrix - TODO: allow non-diagonal matrix? # Empirical variance @@ -882,7 +883,7 @@ def _get_polystd(self, stack_of_points): # Propagate the uncertainties Sigma_X = np.dot( np.dot(Q, Sigma), Q.T) - Sigma_F = np.dot( np.dot(Ao, Sigma_X), Ao.T) + Sigma_F = np.dot( np.dot(Ao, Sigma_X), Ao.T) std_F = np.sqrt( np.diag(Sigma_F) ) return std_F.reshape(-1,1) diff --git a/source/_documentation/introduction.txt b/source/_documentation/introduction.txt deleted file mode 100644 index 3cb3efca..00000000 --- a/source/_documentation/introduction.txt +++ /dev/null @@ -1,92 +0,0 @@ - -Effective Quadratures -************************* - -Effective Quadratures is an open-source library for *uncertainty quantification*, *machine learning*, *optimisation*, *numerical integration* and *dimension reduction* -- all using orthogonal polynomials. It is particularly useful for models / problems where output quantities of interest are smooth and continuous; to this extent it has found widespread applications in computational engineering models (finite elements, computational fluid dynamics, etc). It is built on the latest research within these areas and has both deterministic and randomized algorithms. Effective Quadratures is actively being developed by researchers at the `University of Cambridge `__ , `Stanford University `__, `The University of Utah `__, `The Alan Turing Institute `__ and the `University of Cagliari `__. - -**Key words associated with this code**: polynomial surrogates, polynomial chaos, polynomial variable projection, Gaussian quadrature, Clenshaw Curtis, polynomial least squares, compressed sensing, gradient-enhanced surrogates, supervised learning. - -Code -*************** - -The latest version of the code is version 9.0 and was released in August 2020. - -.. image:: https://travis-ci.org/Effective-Quadratures/Effective-Quadratures.svg?branch=master - :target: https://travis-ci.org/Effective-Quadratures/ - -.. image:: https://coveralls.io/repos/github/Effective-Quadratures/Effective-Quadratures/badge.svg?branch=master - :target: https://coveralls.io/github/Effective-Quadratures/Effective-Quadratures?branch=master - -.. image:: https://badge.fury.io/py/equadratures.svg - :target: https://pypi.org/project/equadratures/ - -.. image:: https://joss.theoj.org/papers/10.21105/joss.00166/status.svg - :target: https://doi.org/10.21105/joss.00166 - -.. image:: https://img.shields.io/pypi/pyversions/ansicolortags.svg - -\ - -.. image:: https://img.shields.io/github/stars/Effective-Quadratures/Effective-Quadratures.svg?style=flat-square&logo=github&label=Stars&logoColor=white - :target: https://github.com/Effective-Quadratures/Effective-Quadratures - -.. image:: https://static.pepy.tech/badge/equadratures/week - :target: https://pepy.tech/project/equadratures - -To download and install the code please use the python package index command: - -.. code:: - - pip install equadratures - -or if you are using python3, then - -.. code:: - - pip3 install equadratures - -Alternatively you can visit our `GitHub page `__ and click either on the **Fork Code** button or **Clone**. For issues with the code, please do *raise an issue* on our Github page; do make sure to add the relevant bits of code and specifics on package version numbers. We welcome contributions and suggestions from both users and folks interested in developing the code further. - -Our code is designed to require minimal dependencies; current package requirements include ``numpy``, ``scipy`` and ``matplotlib``. - - -Workshops -*********** - -We routinely organize workshops covering the theory, algorithms and applications of Effective Quadratures. Some of the salient topics include: - -* probability distributions and orthogonal polynomials -* supervised machine learning: regression and compressive sensing -* numerical quadrature and high-dimensional sampling -* transforms for correlated parameters -* computing moments from models and data-sets -* sensitivity analysis and Sobol' indices -* data-driven dimension reduction -* ridge approximations and neural networks -* surrogate-based design optimisation - -If this is of interest, please do get in touch. Upcoming workshops include: - -- 23rd August 2019, Rolls-Royce, Derbyshire. -- 16th September 2019, United Kingdom Atomic Energy Authority, Oxfordshire. - - -Papers (theory and applications) -*************************************** - -- Wong, C. Y., Seshadri, P., Parks, G. T., (2019) Extremum Global Sensitivity Analysis with Least Squares Polynomials and their Ridges. `Preprint `__. - -- Wong, C. Y., Seshadri, P., Parks, G. T., Girolami, M., (2019) Embedded Ridge Approximations: Constructing Ridge Approximations Over Localized Scalar Fields For Improved Simulation-Centric Dimension Reduction. `Preprint `__. - -- Seshadri, P., Iaccarino, G., Ghisu, T., (2019) Quadrature Strategies for Constructing Polynomial Approximations. *Uncertainty Modeling for Engineering Applications*. Springer, Cham, 2019. 1-25. `Paper `__. `Preprint `__. - -- Seshadri, P., Narayan, A., Sankaran M., (2017) Effectively Subsampled Quadratures for Least Squares Polynomial Approximations." *SIAM/ASA Journal on Uncertainty Quantification* 5.1 : 1003-1023. `Paper `__. - -- Seshadri, P., Parks, G. T., (2017) Effective-Quadratures (EQ): Polynomials for Computational Engineering Studies, *Journal of Open Source Software*, 2(11), 166, `Paper `__. - -- Kalra, T. S., Aretxabaleta, A., Seshadri, P., Ganju, N. K., Beudin, A. (2017). Sensitivity Analysis of a Coupled Hydrodynamic-Vegetation Model Using the Effectively Subsampled Quadratures Method (ESQM v5. 2). *Geoscientific Model Development*, 10(12), 4511. `Paper `__. - -Get in touch -*************** - -Feel free to follow us via `Twitter `__ or email us at contact@effective-quadratures.org. diff --git a/source/_documentation/tutorial_1.txt b/source/_documentation/tutorial_1.txt index c8f663e0..9095ce68 100644 --- a/source/_documentation/tutorial_1.txt +++ b/source/_documentation/tutorial_1.txt @@ -1,6 +1,6 @@ Foundations I: Parameter =========================== -A **parameter** is one of the main building blocks in equadratures. Let :math:`s` be a parameter defined on a domain :math:`\mathcal{D} \in \mathbb{R}`. The support of the domain :math:`\mathcal{D}` may be: +A **parameter** is one of the main building blocks in :code:`equadratures`. Let :math:`s` be a parameter defined on a domain :math:`\mathcal{D} \in \mathbb{R}`. The support of the domain :math:`\mathcal{D}` may be: * closed :math:`[a,b]`; * semi-infinite :math:`(-\infty, b)` or :math:`[a, \infty)`; @@ -12,6 +12,7 @@ Further, let us assume that this parameter is characterized by a positive weight \int_{\mathcal{D}}\rho\left(s\right)ds=1. + We now demonstrate some basic functionality of this parameter. First consider the case where :math:`\rho(s) = \mathcal{N} (0, 1)` is a standard Gaussian distribution with a mean of 0.0 and a variance of 1.0. We then plot its PDF and cumulative density function (CDF) and demonstrate how we can generate random samples from this distribution. .. code:: diff --git a/source/index.txt b/source/index.txt index 9eb92214..ac208dfc 100644 --- a/source/index.txt +++ b/source/index.txt @@ -2,7 +2,11 @@ equadratures ============= -:code:`equadratures` is an open-source python code tailored for tackling problems in **uncertainty quantification**, **surrogate-based optimisation**, **numerical integration**, and **data-driven dimension reduction**. +.. meta:: + :description: equadratures is an open-source python code for uncertainty quantification, surrogate-based optimisation, numerical integration and data-driven dimension reduction. It is built on orthogonal polynomial approximations. + :keywords: polynomials, polynomial chaos, optimisation, orthogonal polynomials, dimension reduction + +:code:`equadratures` is an open-source python code tailored for **uncertainty quantification**, **surrogate-based optimisation**, **numerical integration**, and **data-driven dimension reduction**. It does this by constructing orthogonal polynomials. It is particularly useful for problems where output quantities of interest are smooth and continuous and to this extent it has found widespread applications in computational engineering models.