GPy/GPy/mappings/piecewise_linear.py
Eric Kalosa-Kenyon fa909768bd
v1.10.0 (#908)
* Update self.num_data in GP when X is updated

* Update appveyor.yml

* Update setup.cfg

* Stop using legacy bdist_wininst

* fix: reorder brackets to avoid an n^2 array

* Minor fix to multioutput regression example, to clarify code + typo.

* added missing import

* corrected typo in function name

* fixed docstring and added more explanation

* changed ordering of explanation to get to the point fast and provide additional details after

* self.num_data and self.input_dim are set dynamically in class GP() after the shape of X. In MRD, the user-specific values are passed around until X is defined.

* fixed technical description of gradients_X()

* brushed up wording

* fix normalizer

* fix ImportError in likelihood.py

in function log_predictive_density_sampling

* Update setup.py

bump min require version of scipy to 1.3.0

* Add cython into installation requirement

* Coregionalized regression bugfix (#824)

* route default arg W_rank correctly (Addresses #823)

* Drop Python 2.7 support (fix #833)

* travis, appveyor: Add Python 3.8 build

* README: Fix scipy version number

* setup.py: Install scipy < 1.5.0 when using Python 3.5

* plotting_tests.py: Use os.makedirs instead of matplotlib.cbook.mkdirs (fix #844)

* Use super().__init__ consistently, instead of sometimes calling base class __init__ directly

* README.md: Source formatting, one badge per line

* README.md: Remove broken landscape badge (fix #831)

* README.md: Badges for devel and deploy (fix #830)

* ignore itermediary sphinx restructured text

* ignore vs code project settings file

* add yml config for readthedocs

* correct path

* drop epub and pdf builds (as per main GPy)

* typo

* headings and structure

* update copyright

* restructuring and smartening

* remove dead links

* reorder package docs

* rst "markup"

* change rst syntax

* makes sense for core to go first

* add placeholder

* initial core docs, class diagram

* lower level detail

* higher res diagrams

* layout changes for diagrams

resolve conflict

* better syntax

* redunant block

* introduction

* inheritance diagrams

* more on models

* kernel docs to kern.src

* moved doc back from kern.src to kern

* kern not kern.src in index

* better kernel description

* likelihoods

* placeholder

* add plotting to docs index

* summarise plotting

* clarification

* neater contents

* architecture diagram

* using pods

* build with dot

* more on examples

* introduction for utils package

* compromise formatting for sphinx

* correct likelihod definition

* parameterization of priors

* latent function inference intro and format

* maint: Remove tabs (and some trailing spaces)

* dpgplvm.py: Wrap long line + remove tabs

* dpgplvm.py: Fix typo in the header

* maint: Wrap very long lines (> 450 chars)

* maint: Wrap very long lines (> 400 chars)

* Add the link to the api doc on the readme page.

* remove deprecated parameter

* Update README.md

* new: Added to_dict() method to Ornstein-Uhlenbeck (OU) kernel

* fix: minor typos in README !minor

* added python 3.9 build following 4aa2ea9f5e to address https://github.com/SheffieldML/GPy/issues/881

* updated cython-generated c files for python 3.9 via `pyenv virtualenv 3.9.1 gpy391 && pyenv activate gpy391 && python setup.py build --force

* updated osx to macOS 10.15.7, JDK to 14.0.2, and XCode to Xcode 12.2 (#904)

The CI  was broken. This commit fixes the CI. The root cause is reported in more detail in issue #905.

In short, the default macOS version (10.13, see the TravisCI docs) used in TravisCI isn't supported by brew which caused the brew install pandoc in the download_miniconda.sh pre-install script to hang and time out the build. It failed even on inert PRs (adding a line to README, e.g.). Now, with the updated macOS version (from 10.13 to 10.15), brew is supported and the brew install pandoc command succeeds and allows the remainder of the CI build and test sequence to succeed.

* incremented version

Co-authored-by: Masha Naslidnyk 🦉 <naslidny@amazon.co.uk>
Co-authored-by: Zhenwen Dai <zhenwendai@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
Co-authored-by: Mark McLeod <mark.mcleod@mindfoundry.ai>
Co-authored-by: Sigrid Passano Hellan <sighellan@gmail.com>
Co-authored-by: Antoine Blanchard <antoine@sand-lab-gpu.mit.edu>
Co-authored-by: kae_mihara <rukamihara@outlook.com>
Co-authored-by: lagph <49130858+lagph@users.noreply.github.com>
Co-authored-by: Julien Bect <julien.bect@centralesupelec.fr>
Co-authored-by: Neil Lawrence <ndl21@cam.ac.uk>
Co-authored-by: bobturneruk <bob.turner.uk@gmail.com>
Co-authored-by: bobturneruk <r.d.turner@sheffield.ac.uk>
Co-authored-by: gehbiszumeis <16896724+gehbiszumeis@users.noreply.github.com>
2021-05-11 20:12:38 -07:00

94 lines
3.6 KiB
Python

from GPy.core.mapping import Mapping
from GPy.core import Param
import numpy as np
class PiecewiseLinear(Mapping):
"""
A piecewise-linear mapping.
The parameters of this mapping are the positions and values of the function where it is broken (self.breaks, self.values).
Outside the range of the breaks, the function is assumed to have gradient 1
"""
def __init__(self, input_dim, output_dim, values, breaks, name='piecewise_linear'):
assert input_dim==1
assert output_dim==1
super(PiecewiseLinear, self).__init__(input_dim, output_dim, name)
values, breaks = np.array(values).flatten(), np.array(breaks).flatten()
assert values.size == breaks.size
self.values = Param('values', values)
self.breaks = Param('breaks', breaks)
self.link_parameter(self.values)
self.link_parameter(self.breaks)
def parameters_changed(self):
self.order = np.argsort(self.breaks)*1
self.reverse_order = np.zeros_like(self.order)
self.reverse_order[self.order] = np.arange(self.order.size)
self.sorted_breaks = self.breaks[self.order]
self.sorted_values = self.values[self.order]
self.grads = np.diff(self.sorted_values)/np.diff(self.sorted_breaks)
def f(self, X):
x = X.flatten()
y = x.copy()
#first adjus the points below the first value
y[x<self.sorted_breaks[0]] = x[x<self.sorted_breaks[0]] + self.sorted_values[0] - self.sorted_breaks[0]
#now all the points pas the last break
y[x>self.sorted_breaks[-1]] = x[x>self.sorted_breaks[-1]] + self.sorted_values[-1] - self.sorted_breaks[-1]
#loop throught the pairs of points
for low, up, g, v in zip(self. sorted_breaks[:-1], self.sorted_breaks[1:], self.grads, self.sorted_values[:-1]):
i = np.logical_and(x>low, x<up)
y[i] = v + (x[i]-low)*g
return y.reshape(-1,1)
def update_gradients(self, dL_dF, X):
x = X.flatten()
dL_dF = dL_dF.flatten()
dL_db = np.zeros(self.sorted_breaks.size)
dL_dv = np.zeros(self.sorted_values.size)
#loop across each interval, computing the gradient for each of the 4 parameters that define it
for i, (low, up, g, v) in enumerate(zip(self. sorted_breaks[:-1], self.sorted_breaks[1:], self.grads, self.sorted_values[:-1])):
index = np.logical_and(x>low, x<up)
xx = x[index]
grad = dL_dF[index]
span = up-low
dL_dv[i] += np.sum(grad*( (low - xx)/span + 1))
dL_dv[i+1] += np.sum(grad*(xx-low)/span)
dL_db[i] += np.sum(grad*g*(xx-up)/span)
dL_db[i+1] += np.sum(grad*g*(low-xx)/span)
#now the end parts
dL_db[0] -= np.sum(dL_dF[x<self.sorted_breaks[0]])
dL_db[-1] -= np.sum(dL_dF[x>self.sorted_breaks[-1]])
dL_dv[0] += np.sum(dL_dF[x<self.sorted_breaks[0]])
dL_dv[-1] += np.sum(dL_dF[x>self.sorted_breaks[-1]])
#now put the gradients back in the correct order!
self.breaks.gradient = dL_db[self.reverse_order]
self.values.gradient = dL_dv[self.reverse_order]
def gradients_X(self, dL_dF, X):
x = X.flatten()
#outside the range of the breakpoints, the function is just offset by a contant, so the partial derivative is 1.
dL_dX = dL_dF.copy().flatten()
#insude the breakpoints, the partial derivative is self.grads
for low, up, g, v in zip(self. sorted_breaks[:-1], self.sorted_breaks[1:], self.grads, self.sorted_values[:-1]):
i = np.logical_and(x>low, x<up)
dL_dX[i] = dL_dF[i]*g
return dL_dX.reshape(-1,1)