HyperbolicSineGrid.java

package net.finmath.finitedifference.grids;

/**
 * Non-uniform grid based on a hyperbolic sine transformation between
 * {@code minimumValue} and {@code maximumValue}.
 *
 * <p>
 * This grid clusters points around a specified {@code centering} value.
 * The parameter {@code alpha} controls the degree of clustering, with larger
 * values leading to a tighter concentration near the {@code centering} point.
 * </p>
 *
 * <p>
 * The grid points are generated by applying a hyperbolic sine mapping to a
 * uniform parameterization of the interval.
 * </p>
 *
 * @author Michela Birtele
 */
public class HyperbolicSineGrid extends AbstractGrid {

	/**
	 * The number of steps.
	 */
	private final int numberOfSteps;
	/**
	 * The minimum value.
	 */
	private final double minimumValue;
	/**
	 * The maximum value.
	 */
	private final double maximumValue;

	/**
	 * The centering.
	 */
	private final double centering;
	/**
	 * The alpha.
	 */
	private final double alpha;

	/**
	 * The grid.
	 */
	private double[] grid;
	/**
	 * The delta.
	 */
	private double[] delta;

	/**
	 * Constructs a hyperbolic sine grid with the specified parameters.
	 *
	 * @param numberOfSteps The number of intervals in the grid.
	 * @param minimumValue  The lower bound of the grid interval.
	 * @param maximumValue  The upper bound of the grid interval.
	 * @param centering     The point around which the grid points cluster.
	 * @param alpha         Parameter controlling the clustering strength
	 *                      around {@code centering}.
	 */
	public HyperbolicSineGrid(
			final int numberOfSteps,
			final double minimumValue,
			final double maximumValue,
			final double centering,
			final double alpha) {

		this.numberOfSteps = numberOfSteps;
		this.minimumValue = minimumValue;
		this.maximumValue = maximumValue;
		this.centering = centering;
		this.alpha = alpha;
	}

	/**
	 * Generates the grid points using the hyperbolic sine transformation.
	 */
	private void generateGrid() {

		grid = new double[numberOfSteps + 1];

		final double y1 = (minimumValue - centering) / alpha;
		final double y2 = (maximumValue - centering) / alpha;

		final double c1 = Math.log(y1 + Math.sqrt(y1 * y1 + 1.0));
		final double c2 = Math.log(y2 + Math.sqrt(y2 * y2 + 1.0));

		grid[0] = minimumValue;

		for (int i = 1; i <= numberOfSteps; i++) {

			final double argument =
					(c2 * i / numberOfSteps)
					+ c1 * (1.0 - i / (double) numberOfSteps);

			grid[i] =
					centering
					+ alpha
					* (Math.exp(argument) - Math.exp(-argument)) / 2.0;
		}
	}

	/**
	 * Generates the spacing (delta) array as differences between consecutive
	 * grid points.
	 *
	 * @param vector Array of values at grid points (usually the grid itself).
	 */
	private void generateDelta(final double[] vector) {

		if (grid == null) {
			generateGrid();
		}

		delta = new double[numberOfSteps];

		for (int i = 0; i < vector.length - 1; i++) {
			delta[i] = vector[i + 1] - vector[i];
		}
	}

	/**
	 * Returns the grid points, generating them if necessary.
	 *
	 * @return The grid points distributed via the hyperbolic sine
	 *     transformation.
	 */
	@Override
	public double[] getGrid() {

		if (grid == null) {
			generateGrid();
		}

		return grid;
	}

	/**
	 * Returns the spacing between grid points, generating it if necessary.
	 *
	 * @param vector Array of values at grid points (usually the grid itself).
	 * @return The spacing between consecutive grid points.
	 */
	@Override
	public double[] getDelta(final double[] vector) {

		if (delta == null) {
			generateDelta(vector);
		}

		return delta;
	}
}