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

Implement (adaptable) memory limits for potentially memory-intense functions #239

Merged
merged 3 commits into from
Nov 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion deerlab/bootstrap_analysis.py
Expand Up @@ -10,7 +10,7 @@
from deerlab.classes import UQResult
from deerlab.utils import isnumeric

def bootstrap_analysis(fcn,Vexp,Vfit, samples=1000, resampling='gaussian', verbose = False, cores=1):
def bootstrap_analysis(fcn,Vexp,Vfit, samples=1000, resampling='gaussian', verbose = False, cores=1, memorylimit=8):
r"""
Bootstrap analysis for uncertainty quantification

Expand Down Expand Up @@ -50,6 +50,9 @@ def bootstrap_analysis(fcn,Vexp,Vfit, samples=1000, resampling='gaussian', verbo
Specifies whether to print the progress of the bootstrap analysis on the
command window, the default is false.

memorylimit :
Memory limit to be allocated for the full boostrap analysis. If the requested analysis exceeds this limit, the
execution will be stopped. The default is 12GB.

Returns
-------
Expand Down Expand Up @@ -122,6 +125,11 @@ def sample():
if not all(isnumeric(x) for x in var):
raise ValueError('Non-numeric output arguments by the analyzed function are not accepted.')

# Check that the full bootstrap analysis will not exceed the memory limits
memory_requirements = np.sum([nSamples*np.atleast_1d(var).size*np.atleast_1d(var).itemsize])/1e9 # GB
if memory_requirements > memorylimit:
raise MemoryError(f'The requested bootstrap analysis requires {memory_requirements:.2f}GB, exceeding the current memory limit {memorylimit:.2f}GB.')

# Get ndarray shapes of all outputs
shapeout = []
evals = []
Expand Down
10 changes: 9 additions & 1 deletion deerlab/dipolarkernel.py
Expand Up @@ -26,7 +26,7 @@ def ω0(g):
return (μ0/2)*μB**2*g[0]*g[1]/h*1e21 # Hz m^3 -> MHz nm^3 -> rad μs^-1 nm^3

def dipolarkernel(t, r, *, pathways=None, mod=None, bg=None, method='fresnel', excbandwidth=inf, orisel=None, g=[ge,ge],
integralop=True, nKnots=5001, clearcache=False):
integralop=True, nKnots=5001, clearcache=False, memorylimit=8):
#===================================================================================================
r"""Compute the dipolar kernel operator which enables the linear transformation from
distance-domain to time-domain data.
Expand Down Expand Up @@ -86,6 +86,10 @@ def dipolarkernel(t, r, *, pathways=None, mod=None, bg=None, method='fresnel', e
clearcache : boolean, optional
Clear the cached dipolar kernels at the beginning of the function. Disabled by default.

memorylimit :
Memory limit to be allocated for the dipolar kernel. If the requested kernel exceeds this limit, the
execution will be stopped. The default is 12GB.

Returns
--------
K : ndarray
Expand Down Expand Up @@ -152,6 +156,10 @@ def dipolarkernel(t, r, *, pathways=None, mod=None, bg=None, method='fresnel', e
# Ensure that inputs are Numpy arrays
r,t,g = np.atleast_1d(r,t,g)

memory_requirement = 8*len(t)*len(r)/1e9 # GB
if memory_requirement > memorylimit:
raise MemoryError(f'The requested kernel requires {memory_requirement:.2f}GB, exceeding the current memory limit {memorylimit:.2f}GB.')

# Check validity of some inputs
if len(g) == 1:
g = [g, g]
Expand Down
13 changes: 13 additions & 0 deletions test/test_bootstrap_analysis.py
Expand Up @@ -170,3 +170,16 @@ def bootfcn(V):

assert all(abs(paruq.mean - fit.nonlin) < 1.5e-2)
# ======================================================================

import pytest
# ======================================================================
def test_memory_limit():
"Check that the memory limit works for too large analyses"
# Request one million samples, approx. 80GB
yfit = np.linspace(0,1,int(1e4))
yexp = yfit + whitegaussnoise(yfit,0.01)
def bootfcn(y):
return y
with pytest.raises(MemoryError):
paruq = bootstrap_analysis(bootfcn,yexp,yfit,1e6)
# ======================================================================
12 changes: 12 additions & 0 deletions test/test_dipolarkernel.py
Expand Up @@ -472,3 +472,15 @@ def test_docstring():
"Check that the docstring includes all variables and keywords."
assert_docstring(dipolarkernel)
# ======================================================================

import pytest

def test_memory_limit():
# ======================================================================
"Check that the memory limiter works"
# Construct 12GB kernel (should fit in RAM)
t = np.linspace(0,5,int(3e4))
r = np.linspace(1,6,int(5e4))
with pytest.raises(MemoryError):
K = dipolarkernel(t,r)
# ======================================================================