Miscellaneous functions¶
set_default_args
¶
Signature¶
set_default_args(func: Callable, inplace: bool = True, **kwargs) -> Callable
Purpose¶
Update the default values of a function’s parameters without changing its external behavior for explicit arguments. This is handy when a top-level API calls deeply nested functions but you want to tweak defaults of those inner functions without touching every call site. When placing the desired function you can create a new one (wrapper) or keep the existing one with the new default parameters.
⚠️ Unlike
functools.partial
, this does not bind/force arguments at call time; it only changes what the function uses when an argument is omitted by the caller.
Parameters, returns and Raises¶
Parameters - func: the function (or unbound method) whose defaults will be updated.
- inplace:
-
True — mutate func in place by editing defaults (positional/positional-or-keyword) and kwdefaults (keyword-only).
-
False — return a wrapper that applies the new defaults but leaves the original func untouched; the wrapper’s signature is updated to show the new defaults.
-
**kwargs: mapping of parameter_name=new_default_value.
Returns - If inplace=True: the same (possibly bound) object you passed (so existing references still work).
- If inplace=False: a new callable wrapper with updated signature.
Raises¶
-
TypeError if func is not callable.
-
ValueError if a passed name does not exist in func’s signature.
-
ValueError if a passed name exists but does not have a default (you cannot create a default where none exists).
-
ValueError if you try to target args or *kwargs (variadics have no defaults).
When to use and Examples¶
Use when you control a pipeline with deep calls and want different defaults globally for a nested function. Or when you want to expose a variant of a function with new defaults while keeping the original intact (use inplace=False).
see tests/helper_functions/Miscellaneouys for more Examples
# 1) Positional + keyword-only defaults (in-place)
def f(a, b=2, c=3, *, d=4):
return a, b, c, d
print(f(10)) # -> (10, 2, 3, 4)
# Mutate the original defaults
set_default_args(f, b=20, d=40)
print(f(10)) # -> (10, 20, 3, 40)
#2) Non in-place: return a wrapper with update defaults
def g(a, b=2, c=3, *, d=4):
return a, b, c, d
g2 = set_default_args(g, inplace=False, b=99, d=111)
print(g(1)) # -> (1, 2, 3, 4) (original unchanged)
print(g2(1)) # -> (1, 99, 3, 111) (wrapper uses new defaults)
#3) Exapected errors (bad names / no default present)
def h(a, b, c=3):
return a, b, c
try:
set_default_args(h, x=1) # No parameter `x` in `h`
except ValueError as e:
print("Error:", e)
try:
set_default_args(h, b=10) # `b` exists but has NO default in the signature
except ValueError as e:
print("Error:", e)
####################################
monotonic_indices
¶
Signature¶
monotonic_indices(x: array_like) -> np.ndarray
Purpose¶
Return the indices of a strictly increasing subsequence of x, always including the first and last elements. This is useful to "repair" a nearly-monotonic grid(e.g., a few spurious downward spikes) without sorting or regridding—a common need before calling routines that require strictly increasing coordinates (e.g., numerical differentiation or interpolation). This function removes non-increasing points between the start and end of the array
Parameters, returns and Raises¶
Parameters
- x
: (array_like): Input 1D sequence (Numpy array).
Returns
- np.ndaary
of shape (m,): Indices I such that x[I]
is strictly increasing and I[0]==0
, I[-1]==len(x)-1
if x is increasing and the opposite if x is decreasing
Raises / Assumptions¶
- If the overall trend is decreasing (
x[0] > x[-1]
), the function works by reversing internally and then mapping the indices back - assumes
len(x)>=1
and that x is a ndarray.
When to use and Examples¶
- Before calling finite difference derivatives which require strictly monotonic coordinate arrays.
- Before interpolation on grids that should be increasing but contain small gliches
- To quickly visualize or compute on a clean, mnotone subset of a noise 1D grid withoud sorting or re-sampling
see the full test script in tests/helper_functions/Miscellaneouys for more
Examples
# 1) Clean a mostly-increasing sequence with one bad spike
x = [1, 2, 3, -1, 20, 19, 50] # overall increasing, but has local decreases
idx = monotonic_indices(x)
x_clean = [x[i] for i in idx]
print(idx) # e.g., [0, 1, 2, 4, 6]
print(x_clean) # -> [1, 2, 3, 20, 50] (strictly increasing, kept endpoints)
# 3) Pre-conditioning before derivatives (deriv14, deriv23, deriv1n)
# Suppose x is supposed to be increasing, but isn’t strictly so due to noise.
x = np.array([0.0, 0.1, 0.21, 0.20, 0.4, 0.5]) # small non-monotonic blip at 0.21 -> 0.20
y = np.sin(x)
idx = monotonic_indices(x)
x_mono = x[idx]
y_mono = y[idx]
# Now x_mono is strictly increasing and safe for derivative/interpolation routines.
####################################
clamp_val
¶
Signature¶
clamp_val(x: np.array, a: int, b: int) -> np.ndarray
Purpose¶
Force (or "clip") all values of x
to lie inside the closed interval [min(a,b), max[a,b]]
This is useful to eliminate non-physical or unstable values (e.g., negative densities, probabilites outside [0,1]
, or arguments to log/sqrt
that must stay positive)
Parameters, returns and Raises¶
Parameters
- x
(array_like): Values to clamp. Any shape; will be returned as a NumPy array
- a,b
(int or array): Lower and upper bounds. Can be a scalar or a array. Bounds can be given in any order
Returns
- np.ndaary
: Array with the same shape as x
, where every entry is clipped to [a,b]
interval.
Raises¶
- No custom exceptions.
When to use and Examples¶
- When you want to enforce physical constraints, like negative densities, probabilites outside
[0,1]
, or arguments tolog/sqrt
that must stay positive - To avoid numerical pathologies in interative algorithms
- Sanitizing imputs for pltting or interpolation routines
see the full test script in tests/helper_functions/Miscellaneouys for more
Examples
# 1) Simple clamping with scalar bounds
x = [1, 2, 3, -1, 20, 19, 50]
y = clamp_val(x, a=1, b=20)
print(y) # -> [ 1 2 3 1 20 19 20]
# 3) Array bounds with broadcasting (per-column limits)
X = np.array([[ -5.0, 0.2, 9.0],
[ 0.5, 2.5, 11.0]])
low = np.array([0.0, 0.0, 1.0]) # shape (3,)
high = np.array([1.0, 2.0, 10.0]) # shape (3,)
print(clamp_val(X, low, high))
# -> [[0. 0.2 9. ]
# [0.5 2. 10.]]
# 4) Prevent non-physical negatives before a sqrt
data = np.array([1e-6, -1e-8, 4.0])
safe = clamp_val(data, 0.0, np.inf)
print(np.sqrt(safe)) # well-defined