Skip to content

Commit

Permalink
sphinx docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Joe-Vincent committed Jun 28, 2024
1 parent 6537bb0 commit 952197f
Show file tree
Hide file tree
Showing 22 changed files with 986 additions and 316 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/sphinx_docs_to_gh_pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Sphinx docs to gh-pages

on: [push, workflow_dispatch]

jobs:
sphinx_docs_to_gh-pages:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]

name: Sphinx docs to gh-pages
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Installing the Documentation requirements
run: |
python -m pip install --upgrade pip
pip install .[docs]
- name: Installing the library
run: |
python setup.py install
- name: Running the Sphinx to gh-pages Action
uses: uibcdf/action-sphinx-docs-to-gh-pages@v2.1.0
4 changes: 2 additions & 2 deletions binomial_cis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .binomial_helper import binom_coeff, binom_pmf, binom_cdf
from .conf_intervals import accept_prob, llc_accept_prob, binom_ci, CDF
from .conf_intervals import accept_prob_cp, llc_accept_prob_cp, get_ps_cp
from .conf_intervals import accept_prob, llc_accept_prob, binom_ci
from .conf_intervals import accept_prob_cp, get_ps_cp
from .conf_intervals import accept_prob_2_sided, llc_accept_prob_2_sided, max_accept_prob_2_sided, UMAU_lb, UMAU_ub
from .mixed_monotonic import Interval, mmp_solve
from .plotting import plot_expected_shortage_mixed_monotonic, plot_shortage_curve
Expand Down
116 changes: 79 additions & 37 deletions binomial_cis/conf_intervals.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,27 @@ def F(t): return CDF(t, p_0, n)


def binom_ci(k, n, alpha, side, verbose=True, randomized=True):
"""
Inputs
k: number of observed successes in n trials
n: number of trials
alpha: miscoverage rate, P(p in CI) = 1-alpha
verbose: whether to print intermediate updates
randomized: if true solves for the UMA bounds, if false then solves for (less efficient) non-randomized bounds
""" Compute a binomial confidence interval.
Parameters
----------
k: int
Number of observed successes in `n` trials.
n: int
Number of trials (samples).
alpha: float
Miscoverage rate, P(p in CI) = 1-alpha.
side: {'lb', 'ub', 'lb,ub'}
Selection of lower, upper, or 2-sided bounds.
verbose: bool, default True
Whether to print intermediate updates.
randomized: bool, default True
If True solves for the UMA bounds, if False then solves for (less efficient) non-randomized bounds.
Returns
CI: either a lower bound, upper bound, or simultaneous lower & upper bounds
-------
CI: float
Either a lower bound, upper bound, or simultaneous lower & upper bounds (returned as a tuple)
"""
if side == "lb":
print("Comuting lower confidence bound") if verbose else None
Expand Down Expand Up @@ -271,8 +282,6 @@ def accept_prob_cp(num_args, args):

return accept_prob

llc_accept_prob_cp = LowLevelCallable(accept_prob_cp.ctypes) # SciPy LowLevelCallable version of accept_prob



def binom_bisection(k, n, alpha):
Expand Down Expand Up @@ -337,13 +346,21 @@ def F(p): return -binom_cdf(k, n, p) # negative so now monotonically increasing

def get_ps_cp(p, n, alpha):
"""
Inputs
p: true success probability
n: number of samples
alpha: miscoverage rate, P(p_ <= p) >= 1-alpha
Compute all possible Clopper-Pearson lower bounds given `n` trials.
Parameters
----------
p: float
True success probability.
n: int
Number of trials (samples).
alpha: float
Miscoverage rate, P(p in CI) = 1-alpha.
Returns
ps: array of possible lower bounds from CP that are cutoff points in the expected shortage integral
-------
ps: array_like
Array of possible Clopper-Pearson lower bounds that are cutoff points in the expected shortage integral.
"""
ps = np.array([0.])

Expand Down Expand Up @@ -416,13 +433,21 @@ def get_lb_ub(num_successes, n, alpha):

def UMAU_lb(t_o, n, alpha, tol=1e-6):
"""
Inputs
t_o: observed test statistic
n: number of trials
alpha: miscoverage rate
Computes the lower bound for a 2-sided UMAU CI.
Parameters
----------
t_o: float
Observed test statistic (number of successes + uniform random number).
n: int
Number of trials (samples).
alpha: float
Miscoverage rate, P(p in CI) = 1-alpha.
Returns
p_lb: lower bound of UMAU confidence interval for p
-------
p_lb: float
Lower bound of UMAU confidence interval for p
"""
# find smallest value of p, such that t_o in [t_lo, t_up]

Expand Down Expand Up @@ -471,13 +496,21 @@ def UMAU_lb(t_o, n, alpha, tol=1e-6):

def UMAU_ub(t_o, n, alpha, tol=1e-6):
"""
Inputs
t_o: observed test statistic
n: number of trials
alpha: miscoverage rate
Computes the upper bound for a 2-sided UMAU CI.
Parameters
----------
t_o: float
Observed test statistic (number of successes + uniform random number).
n: int
Number of trials (samples).
alpha: float
Miscoverage rate, P(p in CI) = 1-alpha.
Returns
p_ub: upper bound of UMAU confidence interval for p
-------
p_ub: float
Upper bound of UMAU confidence interval for p
"""
# find largest value of p, such that t_o in [t_lo, t_up]

Expand Down Expand Up @@ -703,21 +736,30 @@ def accept_prob_2_sided(num_args, args):

def max_accept_prob_2_sided(alpha, n, p, n_grid):
"""
Inputs
alpha: miscoverage rate, P(p_ <= p) >= 1-alpha
n: sample size
p: true probability of success
n_grid: size of discrete grid to search over
Grid search to find the parameter which has the highest probability of being in the 2-sided CI.
Returns
max_ap: maximum acceptance probability
max_p_0: location of maximum acceptance probability
Parameters
----------
alpha: float
Miscoverage rate, P(p in CI) = 1-alpha.
n: int
Number of trials (samples).
p: float
True probability of success.
n_grid: int
Size of discrete grid to search over.
It is unclear whether we can rigorously solve for this using mixed monotonic programming
accept_prob_2_sided is mixed monotonic in p_0, but I'm not sure if we can define the function
with split p_0 inputs.
Returns
-------
max_ap: float
Maximum acceptance probability.
max_p_0: float
Parameter that attains the maximum acceptance probability.
"""

# It is unclear whether we can rigorously solve for this using mixed monotonic programming
# accept_prob_2_sided is mixed monotonic in p_0, but I'm not sure if we can define the function
# with split p_0 inputs.

p_0s = np.linspace(0,1,num=n_grid, endpoint=True)
aps = [accept_prob_2_sided(5, [p_0, alpha, n, p, p]) for p_0 in p_0s]

Expand Down
80 changes: 67 additions & 13 deletions binomial_cis/mixed_monotonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,85 @@
class Interval:
"""
Class for storing and performing common operations on intervals.
Parameters
----------
x_min: float
Lower bound of interval
x_max: float
Upper bound of interval
"""
def __init__(self, x_min, x_max):
self.x_min = x_min
self.x_max = x_max

def get_midpoint(self):
"""
Computes the midpoint of the interval.
Returns
-------
float
Midpoint of interval
"""
return self.x_min + (self.x_max - self.x_min)/2

def volume(self):
"""
Computes the width of the interval.
Returns
-------
float
Width of interval
"""
return self.x_max - self.x_min

def get_feasible(self, F):
"""
Finds a feasible point in the interval.
Parameters
----------
F: function
Mixed monotonic function (increasing in first arg, decreasing in second arg)
Returns
-------
x_feas: float
Midpoint of interval
x_feas_val: float
Value of midpoint on the function `F`
"""
x_feas = self.get_midpoint()
x_feas_val = F(x_feas, x_feas)
return x_feas, x_feas_val

def get_ub(self, F):
"""
Gets a certified upper bound of the function F over the interval.
Computes a certified upper bound of the function `F` over the interval.
Inputs
F: F(x1, x2) mixed monotonic function (increasing in x1, decreasing in x2)
Parameters
----------
F: function
Mixed monotonic function (increasing in first arg, decreasing in second arg)
Outputs
ub: certified upper bound of max(F) over Interval
Returns
-------
ub: float
Certified upper bound of max(F) over Interval
"""
ub = F(self.x_max, self.x_min)
return ub

def split(self):
"""
Split along longest axis, else split along first axis.
Returns
-------
I1, I2: Interval
Intervals resulting from splitting the orignal in half.
"""
midpoint = self.get_midpoint()
I1 = Interval(self.x_min, midpoint)
Expand All @@ -48,16 +95,23 @@ def mmp_solve(F, I, tol=1e-3, max_iters=1000, verbose=True):
"""
Uses mixed-monotonic programming to solve for a certified maximum of the function F over the interval I.
Inputs
F: F(x1, x2) mixed monotonic function (increasing in x1, decreasing in x2)
I: Domain of maximization. Interval object
tol: maximum solve tolerance. terminate when ub - lb <= tol
Parameters
----------
F: function
Mixed monotonic function (increasing in first arg, decreasing in second arg)
I: Interval
Domain to maximize over.
Returns
ub: certified upper bound of max F over Interval R within tol of global max
lb: certified lower bound of max F over Interval R within tol of global max
x_lb: x that achieves lb
num_iters: number of iterations taken for the solve
-------
ub: float
Certified upper bound of max `F` over `I` within `tol` of global max.
lb: float
Certified lower bound of max `F` over `I` within `tol` of global max.
x_lb: float
Input that achieves `lb`.
num_iters: int
Number of iterations taken for the solve.
"""
# initialize set of intervals and associated ubs in each
intervals = [I]
Expand Down
Loading

0 comments on commit 952197f

Please sign in to comment.