Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic Differentiation #24

Merged
merged 54 commits into from Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
2b6ed72
init autodiff implementation
pzivich Jun 30, 2023
07daa67
adding missing trig functions
pzivich Jul 4, 2023
6f5d8ee
initial test suite for autodiff
pzivich Jul 4, 2023
3b96939
adding in deriv method
pzivich Jul 4, 2023
4cf8ba1
fixing tests that had hold over dx option
pzivich Jul 4, 2023
e1045e6
fixing autodiff test class name
pzivich Jul 4, 2023
5604214
fix autodiff to handle 1param estimating functions
pzivich Jul 4, 2023
24a0219
fixing autodiff to handle 1param efs
pzivich Jul 4, 2023
c5ad433
autodiff tests for selected ee
pzivich Jul 4, 2023
1a08edb
better init for wlogit to prevent test fails
pzivich Jul 4, 2023
3380176
fixing autodiff test to have better stability
pzivich Jul 5, 2023
5f7b15c
fixing what Pairs __bool__ returns
pzivich Jul 5, 2023
b25eaed
adding more autodiff ee tests
pzivich Jul 5, 2023
6194465
adding test for robust mean, since uses different funcs
pzivich Jul 6, 2023
c17019f
adjusting N for robust mean
pzivich Jul 6, 2023
fd36acd
improved documentation for derivative
pzivich Jul 6, 2023
4b8a0a3
adding comments and fixing rpow to return array
pzivich Jul 6, 2023
af09939
fixing backslash in docs
pzivich Jul 6, 2023
05779b8
adding better tests for power rule of autodiff
pzivich Jul 6, 2023
a0314be
adding docs for autodiff
pzivich Jul 7, 2023
266e5a9
tweaking some MEstimator docs
pzivich Jul 7, 2023
584e958
more documentation updates
pzivich Jul 7, 2023
1a10220
adding GAM test, changing solver for reg
pzivich Jul 7, 2023
63b050c
adding traceback for github test
pzivich Jul 7, 2023
ec1a630
removing traceback
pzivich Jul 7, 2023
a8ad6e0
Merge branch 'main' into autodiff
pzivich Sep 2, 2023
ce8b9bc
Autodiff polygamma support
pzivich Sep 3, 2023
dba6864
tests for autodiff polygamma
pzivich Sep 3, 2023
3dee31a
defining autodiff transpose operation, adding tests
pzivich Sep 3, 2023
abfd36b
making the transpose operator handle another edge case
pzivich Sep 3, 2023
187e7f6
notes on deriv issue I found
pzivich Sep 4, 2023
da1cc0e
tests to demo, lead works tail doesnt
pzivich Sep 4, 2023
7d2cc7a
updating autodiff to handle np.ndarray tailing
pzivich Sep 6, 2023
6575565
getting polygamma working with autodiff revisions
pzivich Sep 6, 2023
0ebe8fd
importing custom special functions for regression
pzivich Sep 6, 2023
0a2c48c
polygamma and digamma test functions
pzivich Sep 6, 2023
6bfc508
updating default maxiter for the root-finder
pzivich Sep 7, 2023
c3fdc15
updating default root-finder
pzivich Sep 7, 2023
022d2e7
Adding a Basics section to the docs
pzivich Sep 7, 2023
85b413f
updating np.nan recommendations for autodiff
pzivich Sep 7, 2023
c230731
enabling probit and cauchy glm with autodiff
pzivich Sep 7, 2023
5825927
removing unneeded hyperparameter call for glm
pzivich Sep 7, 2023
9f03497
updating derivative class to handle norm
pzivich Sep 7, 2023
c3c67a4
a note to self about the power rule
pzivich Sep 7, 2023
c83e215
fixing docs for dose-response 4pl
pzivich Sep 7, 2023
7ae636d
adding autodiff tests for all built-in EE
pzivich Sep 7, 2023
03b2522
tweaking precision of single unit test
pzivich Sep 7, 2023
e091841
updating dose-response to be easier to maintain
pzivich Sep 7, 2023
12ac321
updating tolerance for cauchy test
pzivich Sep 7, 2023
d0e7c84
Adding check for nan's in bread to give better error back
pzivich Sep 7, 2023
537d08b
autodiff will be v2.0 release
pzivich Sep 7, 2023
449c578
Noting some caveats about autodiff
pzivich Sep 26, 2023
32d8456
Changing np.nan in the bread to a warning
pzivich Sep 26, 2023
099ac09
tweaking autodiff tests
pzivich Sep 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
510 changes: 510 additions & 0 deletions delicatessen/derivative.py

Large diffs are not rendered by default.

55 changes: 14 additions & 41 deletions delicatessen/estimating_equations/dose_response.py
Expand Up @@ -85,6 +85,7 @@ def ee_4p_logistic(theta, X, y):
To summarize the recommendations, be sure to examine your data (e.g., scatterplot). This will help to determine the
initial starting values for the root-finding procedure. Otherwise, you may come across a convergence error.


>>> estr = MEstimator(psi, init=[np.min(resp_data),
>>> (np.max(resp_data)+np.min(resp_data)) / 2,
>>> (np.max(resp_data)+np.min(resp_data)) / 2,
Expand Down Expand Up @@ -120,18 +121,19 @@ def ee_4p_logistic(theta, X, y):
# Generalized 4PL model function for y-hat
fx = theta[0] + (theta[3] - theta[0]) / (1 + rho)

# Using a special implementatin of natural log here
nested_log = np.log(X / theta[1], # ... to avoid dose=0 issues only take log
where=0 < X) # ... where dose>0 (otherwise puts zero in place)
# Using a special implementation of natural log here
# nested_log = np.log(X / theta[1], # ... to avoid dose=0 issues only take log
# where=0 < X) # ... where dose>0 (otherwise puts zero in place)
nested_log = np.where(X > 0, np.log(X / theta[1]), 0) # Handling when dose = 0

# Calculate the derivatives for the gradient
deriv = np.array((1 - 1/(1 + rho), # Gradient for lower limit
(theta[3]-theta[0])*theta[2]/theta[1]*rho/(1+rho)**2, # Gradient for steepness
(theta[3] - theta[0]) * nested_log * rho / (1 + rho)**2, # Gradient for ED50
1 / (1 + rho)), ) # Gradient for upper limit
deriv = np.array((1 - 1/(1 + rho), # Gradient for lower limit
(theta[3] - theta[0])*theta[2]/theta[1]*rho/(1 + rho)**2, # Gradient for steepness
(theta[3] - theta[0])*nested_log*rho/(1 + rho)**2, # Gradient for ED50
1 / (1 + rho)), ) # Gradient for upper limit

# Compute gradient and return for each i
return -2*(y-fx)*deriv
return -2*(y - fx)*deriv


def ee_3p_logistic(theta, X, y, lower):
Expand Down Expand Up @@ -229,23 +231,8 @@ def ee_3p_logistic(theta, X, y, lower):
Inderjit, Streibig JC, & Olofsdotter M. (2002). Joint action of phenolic acid mixtures and its significance in
allelopathy research. *Physiologia Plantarum*, 114(3), 422-428.
"""
# Creating rho to cut down on typing
rho = (X / theta[0])**theta[1]

# Generalized 3PL model function for y-hat
fx = lower + (theta[2] - lower) / (1 + rho)

# Using a special implementation of natural log here
nested_log = np.log(X / theta[0], # ... to avoid dose=0 issues only take log
where=0 < X) # ... where dose>0 (otherwise puts zero in place)

# Calculate the derivatives for the gradient
deriv = np.array(((theta[2]-lower)*theta[1]/theta[0]*rho/(1+rho)**2, # Gradient for steepness
(theta[2]-lower) * nested_log * rho / (1+rho)**2, # Gradient for ED50
1 / (1 + rho)), ) # Gradient for upper limit

# Compute gradient and return for each i
return -2*(y - fx)*deriv
ee_dr = ee_4p_logistic(theta=[lower, theta[0], theta[1], theta[2]], X=X, y=y)
return ee_dr[1:, :]


def ee_2p_logistic(theta, X, y, lower, upper):
Expand Down Expand Up @@ -343,22 +330,8 @@ def ee_2p_logistic(theta, X, y, lower, upper):
Inderjit, Streibig JC, & Olofsdotter M. (2002). Joint action of phenolic acid mixtures and its significance in
allelopathy research. *Physiologia Plantarum*, 114(3), 422-428.
"""
# Creating rho to cut down on typing
rho = (X / theta[0])**theta[1]

# Generalized 3PL model function for y-hat
fx = lower + (upper - lower) / (1 + rho)

# Using a special implementatin of natural log here
nested_log = np.log(X / theta[0], # ... to avoid dose=0 issues only take log
where=0 < X) # ... where dose>0 (otherwise puts zero in place)

# Calculate the derivatives for the gradient
deriv = np.array(((upper-lower)*theta[1]/theta[0]*rho/(1+rho)**2, # Gradient for steepness
(upper-lower) * nested_log * rho / (1+rho)**2), ) # Gradient for ED50

# Compute gradient and return for each i
return -2*(y-fx)*deriv
ee_dr = ee_4p_logistic(theta=[lower, theta[0], theta[1], upper], X=X, y=y)
return ee_dr[1:3, :]


def ee_effective_dose_delta(theta, y, delta, steepness, ed50, lower, upper):
Expand Down
18 changes: 11 additions & 7 deletions delicatessen/estimating_equations/regression.py
@@ -1,11 +1,11 @@
import warnings
import numpy as np
from scipy.stats import norm, cauchy
from scipy.special import digamma, polygamma

from delicatessen.utilities import (logit, inverse_logit, identity,
robust_loss_functions,
additive_design_matrix)
additive_design_matrix,
digamma, polygamma, standard_normal_cdf, standard_normal_pdf)
from delicatessen.estimating_equations.processing import generate_weights

#################################################################
Expand Down Expand Up @@ -76,7 +76,7 @@ def ee_regression(theta, X, y, model, weights=None, offset=None):
>>> data['Z'] = np.random.normal(size=n)
>>> data['Y1'] = 0.5 + 2*data['X'] - 1*data['Z'] + np.random.normal(loc=0, size=n)
>>> data['Y2'] = np.random.binomial(n=1, p=logistic.cdf(0.5 + 2*data['X'] - 1*data['Z']), size=n)
>>> data['Y3'] = np.random.poisson(np.exp(lam=0.5 + 2*data['X'] - 1*data['Z']), size=n)
>>> data['Y3'] = np.random.poisson(lam=np.exp(0.5 + 2*data['X'] - 1*data['Z']), size=n)
>>> data['C'] = 1

Note that ``C`` here is set to all 1's. This will be the intercept in the regression.
Expand Down Expand Up @@ -1351,11 +1351,15 @@ def _inverse_link_(betax, link):
py = 1 - np.exp(-1*np.exp(betax)) # Inverse link
dpy = np.exp(betax - np.exp(betax)) # Derivative of inverse link
elif link == 'probit':
py = norm.cdf(betax) # Inverse link
dpy = norm.pdf(betax) # Derivative of inverse link
# py = norm.cdf(betax) # Inverse link
# dpy = norm.pdf(betax) # Derivative of inverse link
py = standard_normal_cdf(x=betax) # Inverse link
dpy = standard_normal_pdf(x=betax) # Derivative of inverse link
elif link in ['cauchit', 'cauchy']:
py = cauchy.cdf(betax) # Inverse link
dpy = cauchy.pdf(betax) # Derivative of inverse link
# py = cauchy.cdf(betax) # Inverse link
# dpy = cauchy.pdf(betax) # Derivative of inverse link
py = (1/np.pi)*np.arctan(betax) + 0.5 # Inverse link
dpy = 1 / (np.pi*(1 + betax**2)) # Derivative of inverse link (by-hand)
elif link in ['square_root', 'sqrt']:
py = betax**2 # Inverse link
dpy = 2 * betax # Derivative of inverse link
Expand Down