Skip to content

Instantly share code, notes, and snippets.

@John-Colvin
Last active April 19, 2016 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save John-Colvin/10578e0a096b2ac9fe994cac08c8243a to your computer and use it in GitHub Desktop.
Save John-Colvin/10578e0a096b2ac9fe994cac08c8243a to your computer and use it in GitHub Desktop.
contrast function based on the error function
def sigmoidContrastGetRawSlope(bias, slope):
'''Get a value for rawSlope (as used in sigmoidContrast)'''
import scipy.optimize
import scipy.special
assert slope >= 1, "slope must be in [1, oo]"
if slope == 1:
return 0
q = slope * np.sqrt(np.pi) * 0.5
f_0 = (0.5 * np.sqrt(np.pi) - q)**2
res = scipy.optimize.minimize_scalar(
lambda s: f_0 if s == 0 else (s / (scipy.special.erf(bias * s) - scipy.special.erf(s * (bias - 1))) - q)**2,
bracket = (0, 2*slope))
assert res.success, "could not find a value for rawSlope from specified slope and bias"
return abs(res.x) # can end up negative as objective function is even
def sigmoidContrast(x, bias, slope=None, rawSlope=None, inPlace=False):
'''The error function scaled to the range 0..1 over domain 0..1
parameters:
- bias: adjusts the position of the maximum gradient of the contrast
function w.r.t. the input domain.
- slope: adjusts the maximum gradient of the contrast function
- rawSlope: raw adjustment of the maximum gradient, saves some setup time
but is less intuitive. The value of slope will be inferred from this, but
will also depend on the bias.
I recomend trying out some parameters to get a feel of this
function, it's very intuitive once you see it.
Using rawSlope is cheaper than using slope, as an iterative procedure is
necessary to infer it from slope and bias. It is unlikely to be a significant
cost except when x is scalars or a small array. You can pay the cost up-front
by using sigmoidContrastGetRawSlope to generate your desired rawSlope.
The actual function evaluated is equivalent to
(np.erf(bias * rawSlope) - np.erf(rawSlope * (bias - x))) /
(np.erf(bias * rawSlope) - np.erf(rawSlope * (bias - 1)))
'''
import scipy.special
if rawSlope == None:
assert slope, "must specify one of slope or rawSlope"
rawSlope = sigmoidContrastGetRawSlope(bias, slope)
else:
assert slope == None, "please specify one of slope and rawSlope, not both"
assert rawSlope >= 0, "rawSlope must be in [0, oo]"
if rawSlope == 0:
return x if inPlace else x.copy()
c = scipy.special.erf(bias * rawSlope)
d = scipy.special.erf(rawSlope * (bias - 1))
if inPlace and isinstance(x, np.ndarray):
x -= bias
xC = x
else:
xC = x - bias
xC *= rawSlope
scipy.special.erf.at(xC, tuple(map(lambda s: slice(0, s), xC.shape)))
xC += c
xC *= 1.0/(c - d)
return xC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment