MinimalQuadraticHaganWest.java

  1. package org.drip.spline.pchip;

  2. /*
  3.  * -*- mode: java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  4.  */

  5. /*!
  6.  * Copyright (C) 2020 Lakshmi Krishnamurthy
  7.  * Copyright (C) 2019 Lakshmi Krishnamurthy
  8.  * Copyright (C) 2018 Lakshmi Krishnamurthy
  9.  * Copyright (C) 2017 Lakshmi Krishnamurthy
  10.  * Copyright (C) 2016 Lakshmi Krishnamurthy
  11.  * Copyright (C) 2015 Lakshmi Krishnamurthy
  12.  * Copyright (C) 2014 Lakshmi Krishnamurthy
  13.  * Copyright (C) 2013 Lakshmi Krishnamurthy
  14.  *
  15.  *  This file is part of DROP, an open-source library targeting analytics/risk, transaction cost analytics,
  16.  *      asset liability management analytics, capital, exposure, and margin analytics, valuation adjustment
  17.  *      analytics, and portfolio construction analytics within and across fixed income, credit, commodity,
  18.  *      equity, FX, and structured products. It also includes auxiliary libraries for algorithm support,
  19.  *      numerical analysis, numerical optimization, spline builder, model validation, statistical learning,
  20.  *      and computational support.
  21.  *  
  22.  *      https://lakshmidrip.github.io/DROP/
  23.  *  
  24.  *  DROP is composed of three modules:
  25.  *  
  26.  *  - DROP Product Core - https://lakshmidrip.github.io/DROP-Product-Core/
  27.  *  - DROP Portfolio Core - https://lakshmidrip.github.io/DROP-Portfolio-Core/
  28.  *  - DROP Computational Core - https://lakshmidrip.github.io/DROP-Computational-Core/
  29.  *
  30.  *  DROP Product Core implements libraries for the following:
  31.  *  - Fixed Income Analytics
  32.  *  - Loan Analytics
  33.  *  - Transaction Cost Analytics
  34.  *
  35.  *  DROP Portfolio Core implements libraries for the following:
  36.  *  - Asset Allocation Analytics
  37.  *  - Asset Liability Management Analytics
  38.  *  - Capital Estimation Analytics
  39.  *  - Exposure Analytics
  40.  *  - Margin Analytics
  41.  *  - XVA Analytics
  42.  *
  43.  *  DROP Computational Core implements libraries for the following:
  44.  *  - Algorithm Support
  45.  *  - Computation Support
  46.  *  - Function Analysis
  47.  *  - Model Validation
  48.  *  - Numerical Analysis
  49.  *  - Numerical Optimizer
  50.  *  - Spline Builder
  51.  *  - Statistical Learning
  52.  *
  53.  *  Documentation for DROP is Spread Over:
  54.  *
  55.  *  - Main                     => https://lakshmidrip.github.io/DROP/
  56.  *  - Wiki                     => https://github.com/lakshmiDRIP/DROP/wiki
  57.  *  - GitHub                   => https://github.com/lakshmiDRIP/DROP
  58.  *  - Repo Layout Taxonomy     => https://github.com/lakshmiDRIP/DROP/blob/master/Taxonomy.md
  59.  *  - Javadoc                  => https://lakshmidrip.github.io/DROP/Javadoc/index.html
  60.  *  - Technical Specifications => https://github.com/lakshmiDRIP/DROP/tree/master/Docs/Internal
  61.  *  - Release Versions         => https://lakshmidrip.github.io/DROP/version.html
  62.  *  - Community Credits        => https://lakshmidrip.github.io/DROP/credits.html
  63.  *  - Issues Catalog           => https://github.com/lakshmiDRIP/DROP/issues
  64.  *  - JUnit                    => https://lakshmidrip.github.io/DROP/junit/index.html
  65.  *  - Jacoco                   => https://lakshmidrip.github.io/DROP/jacoco/index.html
  66.  *
  67.  *  Licensed under the Apache License, Version 2.0 (the "License");
  68.  *      you may not use this file except in compliance with the License.
  69.  *  
  70.  *  You may obtain a copy of the License at
  71.  *      http://www.apache.org/licenses/LICENSE-2.0
  72.  *  
  73.  *  Unless required by applicable law or agreed to in writing, software
  74.  *      distributed under the License is distributed on an "AS IS" BASIS,
  75.  *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  76.  *  
  77.  *  See the License for the specific language governing permissions and
  78.  *      limitations under the License.
  79.  */

  80. /**
  81.  * <i>MinimalQuadraticHaganWest</i> implements the regime using the Hagan and West (2006) Minimal Quadratic
  82.  * Estimator.
  83.  *
  84.  * <br><br>
  85.  *  <ul>
  86.  *      <li><b>Module </b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/ComputationalCore.md">Computational Core Module</a></li>
  87.  *      <li><b>Library</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/SplineBuilderLibrary.md">Spline Builder Library</a></li>
  88.  *      <li><b>Project</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/spline/README.md">Basis Splines and Linear Compounders across a Broad Family of Spline Basis Functions</a></li>
  89.  *      <li><b>Package</b> = <a href = "https://github.com/lakshmiDRIP/DROP/tree/master/src/main/java/org/drip/spline/pchip/README.md">Monotone Convex Themed PCHIP Splines</a></li>
  90.  *  </ul>
  91.  * <br><br>
  92.  *
  93.  * @author Lakshmi Krishnamurthy
  94.  */

  95. public class MinimalQuadraticHaganWest {
  96.     private double[] _adblA = null;
  97.     private double[] _adblB = null;
  98.     private double[] _adblC = null;
  99.     private double[] _adblObservation = null;
  100.     private double[] _adblPredictorOrdinate = null;
  101.     private double _dblWeight = java.lang.Double.NaN;

  102.     /**
  103.      * Create an instance of MinimalQuadraticHaganWest
  104.      *
  105.      * @param adblPredictorOrdinate Array of Predictor Ordinates
  106.      * @param adblObservation Array of Observations
  107.      * @param dblWeight Relative Weights applied across the first and the second derivatives
  108.      *
  109.      * @return Instance of MinimalQuadraticHaganWest
  110.      */

  111.     public static final MinimalQuadraticHaganWest Create (
  112.         final double[] adblPredictorOrdinate,
  113.         final double[] adblObservation,
  114.         final double dblWeight)
  115.     {
  116.         MinimalQuadraticHaganWest mchw = null;

  117.         try {
  118.             mchw = new MinimalQuadraticHaganWest (adblPredictorOrdinate, adblObservation, dblWeight);
  119.         } catch (java.lang.Exception e) {
  120.             e.printStackTrace();

  121.             return null;
  122.         }

  123.         return mchw.setupCoefficients() ? mchw : null;
  124.     }

  125.     private MinimalQuadraticHaganWest (
  126.         final double[] adblPredictorOrdinate,
  127.         final double[] adblObservation,
  128.         final double dblWeight)
  129.         throws java.lang.Exception
  130.     {
  131.         if (null == (_adblObservation = adblObservation) || null == (_adblPredictorOrdinate =
  132.             adblPredictorOrdinate) || !org.drip.numerical.common.NumberUtil.IsValid (_dblWeight = dblWeight))
  133.             throw new java.lang.Exception ("MinimalQuadraticHaganWest ctr: Invalid Inputs!");

  134.         int iNumObservation = _adblObservation.length;

  135.         if (1 >= iNumObservation || iNumObservation + 1 != _adblPredictorOrdinate.length)
  136.             throw new java.lang.Exception ("MinimalQuadraticHaganWest ctr: Invalid Inputs!");
  137.     }

  138.     private boolean setupCoefficients()
  139.     {
  140.         int iNumObservation = _adblObservation.length;
  141.         _adblA = new double[iNumObservation];
  142.         _adblB = new double[iNumObservation];
  143.         _adblC = new double[iNumObservation];
  144.         double[] adblH = new double[iNumObservation];
  145.         double[] adblRHS = new double[3 * iNumObservation];
  146.         double[][] aadblCoeffMatrix = new double[3 * iNumObservation][3 * iNumObservation];

  147.         for (int i = 0; i < 3 * iNumObservation; ++i) {
  148.             adblRHS[i] = 0.;

  149.             for (int j = 0; j < 3 * iNumObservation; ++j)
  150.                 aadblCoeffMatrix[i][j] = 0.;
  151.         }

  152.         for (int i = 0; i < iNumObservation; ++i)
  153.             adblH[i] = _adblPredictorOrdinate[i + 1] - _adblPredictorOrdinate[i];

  154.         /*
  155.          * Setting up the coefficient linear constraint equation set
  156.          *
  157.          *  - Left index => Equation Index
  158.          *  - Right Index => Coefficient Index
  159.          */

  160.         /*
  161.          * Set up the conserved quantities; Laid out as:
  162.          *      A_i + (H_i / 2.) * B_i + (H_i * H_i / 3.) * C_i = Observation_i
  163.          */

  164.         for (int iEq = 0; iEq < iNumObservation; ++iEq) {
  165.             int iSegmentIndex = iEq;
  166.             adblRHS[iEq] = _adblObservation[iEq]; // Z_i
  167.             aadblCoeffMatrix[iEq][3 * iSegmentIndex] = 1.; // A_i
  168.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 1] = 0.5 * adblH[iSegmentIndex]; // B_i
  169.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 2] = adblH[iSegmentIndex] * adblH[iSegmentIndex] / 3.; // C_i
  170.         }

  171.         /*
  172.          * Set up the continuity constraints; Laid out as:
  173.          *      A_i + H_i * B_i + (H_i * H_i) * C_i - A_i+1 = 0.
  174.          */

  175.         for (int iEq = iNumObservation; iEq < 2 * iNumObservation - 1; ++iEq) {
  176.             adblRHS[iEq] = 0.;
  177.             int iSegmentIndex = iEq - iNumObservation;
  178.             aadblCoeffMatrix[iEq][3 * iSegmentIndex] = 1.; // A_i
  179.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 1] = adblH[iSegmentIndex]; // B_i
  180.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 2] = adblH[iSegmentIndex] * adblH[iSegmentIndex]; // C_i
  181.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 3] = -1.; // A_i+1
  182.         }

  183.         /*
  184.          * Set up the derivative penalty minimizer; Laid out as:
  185.          *      w * B_i + (2. * H_i) * C_i - w * B_i+1 = 0.
  186.          */

  187.         for (int iEq = 2 * iNumObservation - 1; iEq < 3 * iNumObservation - 2; ++iEq) {
  188.             adblRHS[iEq] = 0.;
  189.             int iSegmentIndex = iEq - 2 * iNumObservation + 1;
  190.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 1] = _dblWeight; // B_i
  191.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 2] = 2. * adblH[iSegmentIndex]; // C_i
  192.             aadblCoeffMatrix[iEq][3 * iSegmentIndex + 4] = -1. * _dblWeight; // B_i+1
  193.         }

  194.         /*
  195.          * Left Boundary Condition: Starting Left Slope is zero, i.e., B_0 = 0.
  196.          */

  197.         adblRHS[3 * iNumObservation - 2] = 0.;
  198.         aadblCoeffMatrix[3 * iNumObservation - 2][1] = 1.;

  199.         /*
  200.          * Right Boundary Condition: Final First Derivative is zero, i.e., B_n-1 = 0.
  201.          */

  202.         adblRHS[3 * iNumObservation - 1] = 0.;
  203.         aadblCoeffMatrix[3 * iNumObservation - 1][3 * iNumObservation - 2] = 1.;

  204.         org.drip.numerical.linearalgebra.LinearizationOutput lssGaussianElimination =
  205.             org.drip.numerical.linearalgebra.LinearSystemSolver.SolveUsingGaussianElimination (aadblCoeffMatrix,
  206.                 adblRHS);

  207.         if (null == lssGaussianElimination) return false;

  208.         double[] adblCoeff = lssGaussianElimination.getTransformedRHS();

  209.         if (null == adblCoeff || 3 * iNumObservation != adblCoeff.length) return false;

  210.         int iSegment = 0;

  211.         for (int i = 0; i < 3 * iNumObservation; ++i) {
  212.             if (0 == i % 3)
  213.                 _adblA[iSegment] = adblCoeff[i];
  214.             else if (1 == i % 3)
  215.                 _adblB[iSegment] = adblCoeff[i];
  216.             else if (2 == i % 3) {
  217.                 _adblC[iSegment] = adblCoeff[i];
  218.                 ++iSegment;
  219.             }
  220.         }

  221.         return true;
  222.     }

  223.     private int containingIndex (
  224.         final double dblPredictorOrdinate,
  225.         final boolean bIncludeLeft,
  226.         final boolean bIncludeRight)
  227.         throws java.lang.Exception
  228.     {
  229.         int iNumSegment = _adblA.length;

  230.         for (int i = 0 ; i < iNumSegment; ++i) {
  231.             boolean bLeftValid = bIncludeLeft ? _adblPredictorOrdinate[i] <= dblPredictorOrdinate :
  232.                 _adblPredictorOrdinate[i] < dblPredictorOrdinate;

  233.             boolean bRightValid = bIncludeRight ? _adblPredictorOrdinate[i + 1] >= dblPredictorOrdinate :
  234.                 _adblPredictorOrdinate[i + 1] > dblPredictorOrdinate;

  235.             if (bLeftValid && bRightValid) return i;
  236.         }

  237.         throw new java.lang.Exception
  238.             ("MinimalQuadraticHaganWest::containingIndex => Cannot locate Containing Index");
  239.     }

  240.     /**
  241.      * Calculate the Response Value given the Predictor Ordinate
  242.      *
  243.      * @param dblPredictorOrdinate The Predictor Ordinate
  244.      *
  245.      * @return The Response Value
  246.      *
  247.      * @throws java.lang.Exception Thrown if the input is invalid
  248.      */

  249.     public double responseValue (
  250.         final double dblPredictorOrdinate)
  251.         throws java.lang.Exception
  252.     {
  253.         int i = containingIndex (dblPredictorOrdinate, true, true);

  254.         return _adblA[i] + _adblB[i] * (dblPredictorOrdinate - _adblPredictorOrdinate[i]) + _adblC[i] *
  255.             (dblPredictorOrdinate - _adblPredictorOrdinate[i]) * (dblPredictorOrdinate -
  256.                 _adblPredictorOrdinate[i]);
  257.     }

  258.     public double[] calcConservedConstraint()
  259.     {
  260.         int iNumObservation = _adblObservation.length;
  261.         double[] adblConservedConstraint = new double[iNumObservation];

  262.         for (int i = 0; i < iNumObservation; ++i)
  263.             adblConservedConstraint[i] = _adblA[i] + _adblB[i] * 0.5 + _adblC[i] / 3.;

  264.         return adblConservedConstraint;
  265.     }

  266.     public static final void main (
  267.         final String[] astrArgs)
  268.         throws Exception
  269.     {
  270.         double[] adblTime = new double[] {0., 1.0, 2.0};
  271.         double[] adblForwardRate = new double[] {0.02, 0.026};

  272.         MinimalQuadraticHaganWest mqhw = MinimalQuadraticHaganWest.Create (adblTime, adblForwardRate, 0.5);

  273.         double[] adblConservedConstraint = mqhw.calcConservedConstraint();

  274.         for (int i = 0; i < adblConservedConstraint.length; ++i)
  275.             System.out.println ("Conserved Constraint[" + i + "] => " +
  276.                 org.drip.numerical.common.FormatUtil.FormatDouble (adblConservedConstraint[i], 1, 6, 1.));

  277.         for (double dblTime = adblTime[0]; dblTime <= adblTime[adblTime.length - 1]; dblTime += 0.25)
  278.             System.out.println ("Response[" + org.drip.numerical.common.FormatUtil.FormatDouble (dblTime, 2, 2,
  279.                 1.) + "] = " + org.drip.numerical.common.FormatUtil.FormatDouble (mqhw.responseValue (dblTime), 1,
  280.                     6, 1.));
  281.     }
  282. }