com.opengamma.strata.math.impl.minimization.DoubleRangeLimitTransform Maven / Gradle / Ivy
Show all versions of strata-math Show documentation
/*
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.math.impl.minimization;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.math.impl.TrigonometricFunctionUtils;
/**
* Limit transform.
*
* If a model parameter $x$ is constrained to be between two values $a \geq x
* \geq b$, the function to transform it to an unconstrained variable is $y$ is
* given by
* $$
* \begin{align*}
* y &= \tanh^{-1}\left(\frac{x - m}{s}\right)\\
* m &= \frac{a + b}{2}\\
* s &= \frac{b - a}{2}
* \end{align*}
* $$
* with the inverse transform
* $$
* \begin{align*}
* x &= s\tanh(y) + m\\
* \end{align*}
* $$
*/
public class DoubleRangeLimitTransform implements ParameterLimitsTransform {
private static final double TANH_MAX = 25.0;
private final double _lower;
private final double _upper;
private final double _scale;
private final double _mid;
/**
* Creates an instance.
* @param lower Lower limit
* @param upper Upper limit
* @throws IllegalArgumentException If the upper limit is not greater than the lower limit
*/
public DoubleRangeLimitTransform(double lower, double upper) {
ArgChecker.isTrue(upper > lower, "upper limit must be greater than lower");
_lower = lower;
_upper = upper;
_mid = (lower + upper) / 2;
_scale = (upper - lower) / 2;
}
/**
* If $y > 25$, this returns $b$. If $y < -25$ returns $a$.
* {@inheritDoc}
*/
@Override
public double inverseTransform(double y) {
if (y > TANH_MAX) {
return _upper;
} else if (y < -TANH_MAX) {
return _lower;
}
return _mid + _scale * TrigonometricFunctionUtils.tanh(y);
}
/**
* {@inheritDoc}
* @throws IllegalArgumentException If $x > b$ or $x < a$
*/
@Override
public double transform(double x) {
ArgChecker.isTrue(x <= _upper && x >= _lower, "parameter out of range");
if (x == _upper) {
return TANH_MAX;
} else if (x == _lower) {
return -TANH_MAX;
}
return TrigonometricFunctionUtils.atanh((x - _mid) / _scale);
}
/**
* If $|y| > 25$, this returns 0.
* {@inheritDoc}
*/
@Override
public double inverseTransformGradient(double y) {
if (y > TANH_MAX || y < -TANH_MAX) {
return 0.0;
}
double p = 2 * y;
double ep = Math.exp(p);
double epp1 = ep + 1;
return _scale * 4 * ep / (epp1 * epp1);
}
/**
* {@inheritDoc}
* @throws IllegalArgumentException If $x > b$ or $x < a$
*/
@Override
public double transformGradient(double x) {
ArgChecker.isTrue(x <= _upper && x >= _lower, "parameter out of range");
double t = (x - _mid) / _scale;
return 1 / (_scale * (1 - t * t));
}
@Override
public int hashCode() {
int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(_lower);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(_upper);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
DoubleRangeLimitTransform other = (DoubleRangeLimitTransform) obj;
if (Double.doubleToLongBits(_lower) != Double.doubleToLongBits(other._lower)) {
return false;
}
return Double.doubleToLongBits(_upper) == Double.doubleToLongBits(other._upper);
}
}