//! floating reference date, fixed market data public SwaptionVolatilityMatrix( Calendar calendar, BusinessDayConvention bdc, List <Period> optionTenors, List <Period> swapTenors, Matrix vols, DayCounter dayCounter) : base(optionTenors, swapTenors, 0, calendar, bdc, dayCounter) { volHandles_ = new InitializedList <List <Handle <Quote> > >(vols.rows()); volatilities_ = new Matrix(vols.rows(), vols.columns()); checkInputs(vols.rows(), vols.columns()); // fill dummy handles to allow generic handle-based // computations later on for (int i = 0; i < vols.rows(); ++i) { volHandles_[i] = new InitializedList <Handle <Quote> >(vols.columns()); for (int j = 0; j < vols.columns(); ++j) { volHandles_[i][j] = new Handle <Quote>((new SimpleQuote(vols[i, j]))); } } interpolation_ = new BilinearInterpolation(swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_); }
//! fixed reference date, floating market data public SwaptionVolatilityMatrix( Date referenceDate, Calendar calendar, BusinessDayConvention bdc, List <Period> optionTenors, List <Period> swapTenors, List <List <Handle <Quote> > > vols, DayCounter dayCounter, bool flatExtrapolation = false, VolatilityType type = VolatilityType.ShiftedLognormal, List <List <double> > shifts = null) : base(optionTenors, swapTenors, referenceDate, calendar, bdc, dayCounter) { volHandles_ = vols; shiftValues_ = shifts; volatilities_ = new Matrix(vols.Count, vols.First().Count); shifts_ = new Matrix(vols.Count, vols.First().Count, 0.0); volatilityType_ = type; checkInputs(volatilities_.rows(), volatilities_.columns(), shifts_.rows(), shifts_.columns()); registerWithMarketData(); // fill dummy handles to allow generic handle-based if (shiftValues_ == null) { shiftValues_ = new InitializedList <List <double> >(volatilities_.rows()); for (int i = 0; i < volatilities_.rows(); ++i) { shiftValues_[i] = new InitializedList <double>(volatilities_.columns()); for (int j = 0; j < volatilities_.columns(); ++j) { shiftValues_[i][j] = shifts_.rows() > 0 ? shifts_[i, j] : 0.0; } } } if (flatExtrapolation) { interpolation_ = new FlatExtrapolator2D(new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_)); interpolationShifts_ = new FlatExtrapolator2D(new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, shifts_)); } else { interpolation_ = new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_); interpolationShifts_ = new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, shifts_); } }
//! floating reference date, fixed market data public SwaptionVolatilityMatrix( Calendar calendar, BusinessDayConvention bdc, List <Period> optionTenors, List <Period> swapTenors, Matrix vols, DayCounter dayCounter, bool flatExtrapolation = false, VolatilityType type = VolatilityType.ShiftedLognormal, Matrix shifts = null) : base(optionTenors, swapTenors, 0, calendar, bdc, dayCounter) { volHandles_ = new InitializedList <List <Handle <Quote> > >(vols.rows()); shiftValues_ = new InitializedList <List <double> >(vols.rows()); volatilities_ = new Matrix(vols.rows(), vols.columns()); shifts_ = shifts ?? new Matrix(vols.rows(), vols.columns(), 0.0); volatilityType_ = type; checkInputs(volatilities_.rows(), volatilities_.columns(), shifts_.rows(), shifts_.columns()); // fill dummy handles to allow generic handle-based // computations later on for (int i = 0; i < vols.rows(); ++i) { volHandles_[i] = new InitializedList <Handle <Quote> >(vols.columns()); shiftValues_[i] = new InitializedList <double>(vols.columns()); for (int j = 0; j < vols.columns(); ++j) { volHandles_[i][j] = new Handle <Quote>((new SimpleQuote(vols[i, j]))); } } if (flatExtrapolation) { interpolation_ = new FlatExtrapolator2D(new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_)); interpolationShifts_ = new FlatExtrapolator2D(new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, shifts_)); } else { interpolation_ = new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_); interpolationShifts_ = new BilinearInterpolation( swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, shifts_); } }
/// Build matrices /// /// Forward moneyness /// public void matricesBuildingForwardMoneyness() { double discount_ = 0.0; for (int j = 0; j < fwMoneynessGrid_.columns(); j++) { for (int i = 0; i < fwMoneynessGrid_.rows(); i++) { discount_ = dividendTS_.link.discount(times_[i], true) / riskFreeTS_.link.discount(times_[i], true); fwMoneynessGrid_[i, j] = strikes_[j] / (spot_ * discount_); } } }
public static Matrix transpose(Matrix m) { Matrix result = new Matrix(m.columns(), m.rows()); for (int i = 0; i < m.rows(); i++) { for (int j = 0; j < m.columns(); j++) { result[j, i] = m[i, j]; } } return(result); }
public static Matrix CholeskyDecomposition(Matrix S, bool flexible) { int i, j, size = S.rows(); if (size != S.columns()) { throw new ApplicationException("input matrix is not a square matrix"); } #if QL_EXTRA_SAFETY_CHECKS for (i = 0; i < S.rows(); i++) { for (j = 0; j < i; j++) { QL_REQUIRE(S[i][j] == S[j][i], () => "input matrix is not symmetric"); } } #endif Matrix result = new Matrix(size, size, 0.0); double sum; for (i = 0; i < size; i++) { for (j = i; j < size; j++) { sum = S[i, j]; for (int k = 0; k <= i - 1; k++) { sum -= result[i, k] * result[j, k]; } if (i == j) { if (!(flexible || sum > 0.0)) { throw new ApplicationException("input matrix is not positive definite"); } // To handle positive semi-definite matrices take the // square root of sum if positive, else zero. result[i, i] = Math.Sqrt(Math.Max(sum, 0.0)); } else { // With positive semi-definite matrices is possible // to have result[i][i]==0.0 // In this case sum happens to be zero as well result[j, i] = (sum == 0.0 ? 0.0 : sum / result[i, i]); } } } return(result); }
public override Vector evolve(double t0, Vector x0, double dt, Vector dw) { /* predictor-corrector step to reduce discretization errors. * * Short - but slow - solution would be * * Array rnd_0 = stdDeviation(t0, x0, dt)*dw; * Array drift_0 = discretization_->drift(*this, t0, x0, dt); * * return apply(x0, ( drift_0 + discretization_ * ->drift(*this,t0,apply(x0, drift_0 + rnd_0),dt) )*0.5 + rnd_0); * * The following implementation does the same but is faster. */ int m = nextIndexReset(t0); double sdt = Math.Sqrt(dt); Vector f = new Vector(x0); Matrix diff = lfmParam_.diffusion(t0, x0); Matrix covariance = lfmParam_.covariance(t0, x0); for (int k = m; k < size_; ++k) { double y = accrualPeriod_[k] * x0[k]; m1[k] = y / (1 + y); double d = 0; m1.GetRange(m, k + 1 - m).ForEach( (ii, vv) => d += vv * covariance.column(k).GetRange(m, covariance.rows() - m)[ii]); d = (d - 0.5 * covariance[k, k]) * dt; double r = 0; diff.row(k).ForEach((kk, vv) => r += vv * dw[kk]); r *= sdt; double x = y * Math.Exp(d + r); m2[k] = x / (1 + x); double inner_product = 0; m2.GetRange(m, k + 1 - m).ForEach( (ii, vv) => inner_product += vv * covariance.column(k).GetRange(m, covariance.rows() - m)[ii]); f[k] = x0[k] * Math.Exp(0.5 * (d + (inner_product - 0.5 * covariance[k, k]) * dt) + r); } return(f); }
public BlackVarianceSurface(Date referenceDate, Calendar calendar, List <Date> dates, List <double> strikes, Matrix blackVolMatrix, DayCounter dayCounter, Extrapolation lowerExtrapolation = Extrapolation.InterpolatorDefaultExtrapolation, Extrapolation upperExtrapolation = Extrapolation.InterpolatorDefaultExtrapolation) : base(referenceDate, calendar) { dayCounter_ = dayCounter; maxDate_ = dates.Last(); strikes_ = strikes; lowerExtrapolation_ = lowerExtrapolation; upperExtrapolation_ = upperExtrapolation; dates_ = dates; volatilities_ = blackVolMatrix; Utils.QL_REQUIRE(dates.Count == blackVolMatrix.columns(), () => "mismatch between date vector and vol matrix colums"); Utils.QL_REQUIRE(strikes_.Count == blackVolMatrix.rows(), () => "mismatch between money-strike vector and vol matrix rows"); Utils.QL_REQUIRE(dates[0] >= referenceDate, () => "cannot have dates[0] < referenceDate"); int i, j; times_ = new InitializedList <double>(dates.Count + 1); times_[0] = 0.0; variances_ = new Matrix(strikes_.Count, dates.Count + 1); for (i = 0; i < blackVolMatrix.rows(); i++) { variances_[i, 0] = 0.0; } for (j = 1; j <= blackVolMatrix.columns(); j++) { times_[j] = timeFromReference(dates[j - 1]); Utils.QL_REQUIRE(times_[j] > times_[j - 1], () => "dates must be sorted unique!"); for (i = 0; i < blackVolMatrix.rows(); i++) { variances_[i, j] = times_[j] * blackVolMatrix[i, j - 1] * blackVolMatrix[i, j - 1]; } } // default: bilinear interpolation setInterpolation <Bilinear>(); }
private void checkSurface() { Utils.QL_REQUIRE(times_.Count == localVolMatrix_.columns(), () => "mismatch between date vector and vol matrix colums"); for (int i = 0; i < strikes_.Count; ++i) { Utils.QL_REQUIRE(strikes_[i].Count == localVolMatrix_.rows(), () => "mismatch between money-strike vector and " + "vol matrix rows"); } for (int j = 1; j < times_.Count; j++) { Utils.QL_REQUIRE(times_[j] > times_[j - 1], () => "dates must be sorted unique!"); } for (int i = 0; i < strikes_.Count; ++i) { for (int j = 1; j < strikes_[i].Count; j++) { Utils.QL_REQUIRE(strikes_[i][j] >= strikes_[i][j - 1], () => "strikes must be sorted"); } } }
public static void normalizePseudoRoot(Matrix matrix, Matrix pseudo) { int size = matrix.rows(); if (size != pseudo.rows()) { throw new Exception("matrix/pseudo mismatch: matrix rows are " + size + " while pseudo rows are " + pseudo.columns()); } int pseudoCols = pseudo.columns(); // row normalization for (int i = 0; i < size; ++i) { double norm = 0.0; for (int j = 0; j < pseudoCols; ++j) { norm += pseudo[i, j] * pseudo[i, j]; } if (norm > 0.0) { double normAdj = Math.Sqrt(matrix[i, i] / norm); for (int j = 0; j < pseudoCols; ++j) { pseudo[i, j] *= normAdj; } } } }
public void matricesBuildingB() { int k = 0; for (int j = 0; j < B_.columns(); j++) { for (int i = 0; i < B_.rows(); i++) { if ((i == j) && (i < strikes_.Count + 2)) { B_[i, i] = 1.0; } if ((j >= strikes_.Count - 1) && (i >= strikes_.Count + 2)) { if (i == j) { k = i - strikesSpline_[0].Count; B_[i, i] = lambda_ * (1.0 / 3) * (strikesSpline_[0][k + 2] - strikesSpline_[0][k]); } if ((i == j + 1) || (i + 1 == j)) { k = i - strikesSpline_[0].Count; B_[i, j] = lambda_ * (1.0 / 6) * (strikesSpline_[0][k + 1] - strikesSpline_[0][k]); } } } } }
//! floating reference date, fixed market data public CapFloorTermVolSurface(int settlementDays, Calendar calendar, BusinessDayConvention bdc, List <Period> optionTenors, List <double> strikes, Matrix vols, DayCounter dc = null) : base(settlementDays, calendar, bdc, dc ?? new Actual365Fixed()) { nOptionTenors_ = optionTenors.Count; optionTenors_ = optionTenors; optionDates_ = new InitializedList <Date>(nOptionTenors_); optionTimes_ = new InitializedList <double>(nOptionTenors_); nStrikes_ = strikes.Count; strikes_ = strikes; volHandles_ = new InitializedList <List <Handle <Quote> > >(vols.rows()); vols_ = vols; checkInputs(); initializeOptionDatesAndTimes(); // fill dummy handles to allow generic handle-based computations later for (int i = 0; i < nOptionTenors_; ++i) { volHandles_[i] = new InitializedList <Handle <Quote> >(nStrikes_); for (int j = 0; j < nStrikes_; ++j) { volHandles_[i][j] = new Handle <Quote>(new SimpleQuote(vols_[i, j])); } } interpolate(); }
//! fixed reference date, floating market data public CapFloorTermVolSurface(Date settlementDate, Calendar calendar, BusinessDayConvention bdc, List <Period> optionTenors, List <double> strikes, List <List <Handle <Quote> > > vols, DayCounter dc = null) : base(settlementDate, calendar, bdc, dc ?? new Actual365Fixed()) { nOptionTenors_ = optionTenors.Count; optionTenors_ = optionTenors; optionDates_ = new InitializedList <Date>(nOptionTenors_); optionTimes_ = new InitializedList <double>(nOptionTenors_); nStrikes_ = strikes.Count; strikes_ = strikes; volHandles_ = vols; vols_ = new Matrix(vols.Count, vols[0].Count); checkInputs(); initializeOptionDatesAndTimes(); for (int i = 0; i < nOptionTenors_; ++i) { Utils.QL_REQUIRE(volHandles_[i].Count == nStrikes_, () => (i + 1) + " row of vol handles has size " + volHandles_[i].Count + " instead of " + nStrikes_); } registerWithMarketData(); for (int i = 0; i < vols_.rows(); ++i) { for (int j = 0; j < vols_.columns(); ++j) { vols_[i, j] = volHandles_[i][j].link.value(); } } interpolate(); }
public void setLayer(int i, Matrix x) { Utils.QL_REQUIRE(i < nLayers_, () => "Cube::setLayer: incompatible number of layer "); Utils.QL_REQUIRE(x.rows() == optionTimes_.Count, () => "Cube::setLayer: incompatible size 1"); Utils.QL_REQUIRE(x.columns() == swapLengths_.Count, () => "Cube::setLayer: incompatible size 2"); points_[i] = x; }
public HypersphereCostFunction(Matrix targetMatrix, Vector targetVariance, bool lowerDiagonal) { size_ = targetMatrix.rows(); lowerDiagonal_ = lowerDiagonal; targetMatrix_ = targetMatrix; targetVariance_ = targetVariance; currentRoot_ = new Matrix(size_, size_); tempMatrix_ = new Matrix(size_, size_); currentMatrix_ = new Matrix(size_, size_); }
public static Matrix inverse(Matrix m) { Utils.QL_REQUIRE(m.rows() == m.columns(), () => "matrix is not square"); int n = m.rows(); Matrix result = new Matrix(n, n); for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { result[i, j] = m[i, j]; } } Matrix lum; int[] perm; decompose(m, out lum, out perm); double[] b = new double[n]; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (i == perm[j]) { b[j] = 1.0; } else { b[j] = 0.0; } } double[] x = Helper(lum, b); // for (int j = 0; j < n; ++j) { result[j, i] = x[j]; } } return(result); }
public StochasticProcessArray(List<StochasticProcess1D> processes, Matrix correlation) { processes_ = processes; sqrtCorrelation_ = MatrixUtilitites.pseudoSqrt(correlation, MatrixUtilitites.SalvagingAlgorithm.Spectral); if (processes.Count == 0) throw new ApplicationException("no processes given"); if(correlation.rows() != processes.Count) throw new ApplicationException("mismatch between number of processes and size of correlation matrix"); for (int i=0; i<processes_.Count; i++) processes_[i].registerWith(update); }
public StochasticProcessArray(List <StochasticProcess1D> processes, Matrix correlation) { processes_ = processes; sqrtCorrelation_ = MatrixUtilitites.pseudoSqrt(correlation, MatrixUtilitites.SalvagingAlgorithm.Spectral); Utils.QL_REQUIRE(processes.Count != 0, () => "no processes given"); Utils.QL_REQUIRE(correlation.rows() == processes.Count, () => "mismatch between number of processes and size of correlation matrix"); for (int i = 0; i < processes_.Count; i++) { processes_[i].registerWith(update); } }
public override Vector evolve(double t0, Vector x0, double dt, Vector dw) { // predictor-corrector step to reduce discretization errors. int m = nextIndexReset(t0); double sdt = Math.Sqrt(dt); Vector f = new Vector(x0); Matrix diff = lfmParam_.diffusion(t0, x0); Matrix covariance = lfmParam_.covariance(t0, x0); for (int k = m; k < size_; ++k) { double y = accrualPeriod_[k] * x0[k]; m1[k] = y / (1 + y); double d = 0; m1.GetRange(m, k + 1 - m).ForEach( (ii, vv) => d += vv * covariance.column(k).GetRange(m, covariance.rows() - m)[ii]); d = (d - 0.5 * covariance[k, k]) * dt; double r = 0; diff.row(k).ForEach((kk, vv) => r += vv * dw[kk]); r *= sdt; double x = y * Math.Exp(d + r); m2[k] = x / (1 + x); double inner_product = 0; m2.GetRange(m, k + 1 - m).ForEach( (ii, vv) => inner_product += vv * covariance.column(k).GetRange(m, covariance.rows() - m)[ii]); f[k] = x0[k] * Math.Exp(0.5 * (d + (inner_product - 0.5 * covariance[k, k]) * dt) + r); } return(f); }
public static Matrix CholeskyDecomposition(Matrix S, bool flexible) { int i, j, size = S.rows(); if(size != S.columns()) throw new ApplicationException("input matrix is not a square matrix"); #if QL_EXTRA_SAFETY_CHECKS for (i=0; i<S.rows(); i++) for (j=0; j<i; j++) QL_REQUIRE(S[i][j] == S[j][i], "input matrix is not symmetric"); #endif Matrix result = new Matrix(size, size, 0.0); double sum; for (i=0; i<size; i++) { for (j=i; j<size; j++) { sum = S[i,j]; for (int k=0; k<=i-1; k++) { sum -= result[i,k]*result[j,k]; } if (i == j) { if (!(flexible || sum > 0.0)) throw new ApplicationException("input matrix is not positive definite"); // To handle positive semi-definite matrices take the // square root of sum if positive, else zero. result[i,i] = Math.Sqrt(Math.Max(sum, 0.0)); } else { // With positive semi-definite matrices is possible // to have result[i][i]==0.0 // In this case sum happens to be zero as well result[j,i] = (sum==0.0 ? 0.0 : sum/result[i,i]); } } } return result; }
// Take a matrix and make all the diagonal entries 1. private static Matrix projectToUnitDiagonalMatrix(Matrix M) { int size = M.rows(); Utils.QL_REQUIRE(size == M.columns(), () => "matrix not square"); Matrix result = new Matrix(M); for (int i = 0; i < size; ++i) { result[i, i] = 1.0; } return(result); }
/*! \relates Matrix */ public static Matrix operator *(Matrix m1, Matrix m2) { Utils.QL_REQUIRE(m1.columns() == m2.rows(), () => "matrices with different sizes (" + m1.rows() + "x" + m1.columns() + ", " + m2.rows() + "x" + m2.columns() + ") cannot be multiplied"); Matrix result = new Matrix(m1.rows(), m2.columns()); for (int i = 0; i < result.rows(); i++) { for (int j = 0; j < result.columns(); j++) { result[i, j] = m1.row(i) * m2.column(j); } } return(result); }
// Matrix infinity norm. See Golub and van Loan (2.3.10) or // <http://en.wikipedia.org/wiki/Matrix_norm> private static double normInf(Matrix M) { int rows = M.rows(); int cols = M.columns(); double norm = 0.0; for (int i = 0; i < rows; ++i) { double colSum = 0.0; for (int j = 0; j < cols; ++j) { colSum += Math.Abs(M[i, j]); } norm = Math.Max(norm, colSum); } return(norm); }
public KernelInterpolation2DImpl(List <double> xBegin, int size, List <double> yBegin, int ySize, Matrix zData, Kernel kernel) : base(xBegin, size, yBegin, ySize, zData) { xSize_ = size; ySize_ = yBegin.Count; xySize_ = xSize_ * ySize_; invPrec_ = 1.0e-10; alphaVec_ = new Vector(xySize_); yVec_ = new Vector(xySize_); M_ = new Matrix(xySize_, xySize_); kernel_ = kernel; Utils.QL_REQUIRE(zData.rows() == xSize_, () => "Z value matrix has wrong number of rows"); Utils.QL_REQUIRE(zData.columns() == ySize_, () => "Z value matrix has wrong number of columns"); }
public StochasticProcessArray(List <StochasticProcess1D> processes, Matrix correlation) { processes_ = processes; sqrtCorrelation_ = MatrixUtilitites.pseudoSqrt(correlation, MatrixUtilitites.SalvagingAlgorithm.Spectral); if (processes.Count == 0) { throw new ApplicationException("no processes given"); } if (correlation.rows() != processes.Count) { throw new ApplicationException("mismatch between number of processes and size of correlation matrix"); } for (int i = 0; i < processes_.Count; i++) { processes_[i].registerWith(update); } }
//! fixed reference date, floating market data public SwaptionVolatilityMatrix( Date referenceDate, Calendar calendar, BusinessDayConvention bdc, List <Period> optionTenors, List <Period> swapTenors, List <List <Handle <Quote> > > vols, DayCounter dayCounter) : base(optionTenors, swapTenors, referenceDate, calendar, bdc, dayCounter) { volHandles_ = vols; volatilities_ = new Matrix(vols.Count, vols.First().Count); checkInputs(volatilities_.rows(), volatilities_.columns()); registerWithMarketData(); interpolation_ = new BilinearInterpolation(swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_); }
// Take a matrix and make all the diagonal entries 1. private static Matrix projectToUnitDiagonalMatrix(Matrix M) { int size = M.rows(); if (size != M.columns()) { throw new Exception("matrix not square"); } Matrix result = new Matrix(M); for (int i = 0; i < size; ++i) { result[i, i] = 1.0; } return(result); }
public override Vector drift(double t, Vector x) { Vector f = new Vector(size_, 0.0); Matrix covariance = lfmParam_.covariance(t, x); int m = nextIndexReset(t); for (int k = m; k < size_; ++k) { m1[k] = accrualPeriod_[k] * x[k] / (1 + accrualPeriod_[k] * x[k]); double inner_product = 0; m1.GetRange(m, k + 1 - m).ForEach( (ii, vv) => inner_product += vv * covariance.column(k).GetRange(m, covariance.rows() - m)[ii]); f[k] = inner_product - 0.5 * covariance[k, k]; } return(f); }
//! floating reference date, floating market data public SwaptionVolatilityMatrix( Calendar calendar, BusinessDayConvention bdc, List<Period> optionTenors, List<Period> swapTenors, List<List<Handle<Quote> > > vols, DayCounter dayCounter) : base(optionTenors, swapTenors, 0, calendar, bdc, dayCounter) { volHandles_=vols; volatilities_ = new Matrix(vols.Count, vols.First().Count ); checkInputs(volatilities_.rows(), volatilities_.columns()); registerWithMarketData(); interpolation_ = new BilinearInterpolation(swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_); }
// Take a matrix and make all the eigenvalues non-negative private static Matrix projectToPositiveSemidefiniteMatrix(Matrix M) { int size = M.rows(); Utils.QL_REQUIRE(size == M.columns(), () => "matrix not square"); Matrix diagonal = new Matrix(size, size); SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(M); for (int i = 0; i < size; ++i) { diagonal[i, i] = Math.Max(jd.eigenvalues()[i], 0.0); } Matrix result = jd.eigenvectors() * diagonal * Matrix.transpose(jd.eigenvectors()); return(result); }
public static void normalizePseudoRoot(Matrix matrix, Matrix pseudo) { int size = matrix.rows(); if (size != pseudo.rows()) throw new ApplicationException("matrix/pseudo mismatch: matrix rows are " + size + " while pseudo rows are " + pseudo.columns()); int pseudoCols = pseudo.columns(); // row normalization for (int i=0; i<size; ++i) { double norm = 0.0; for (int j=0; j<pseudoCols; ++j) norm += pseudo[i,j]*pseudo[i,j]; if (norm>0.0) { double normAdj = Math.Sqrt(matrix[i,i]/norm); for (int j=0; j<pseudoCols; ++j) pseudo[i,j] *= normAdj; } } }
/*! \relates Matrix */ public static Matrix operator *(Matrix m1, Matrix m2) { if (!(m1.columns() == m2.rows())) { throw new Exception("matrices with different sizes (" + m1.rows() + "x" + m1.columns() + ", " + m2.rows() + "x" + m2.columns() + ") cannot be multiplied"); } Matrix result = new Matrix(m1.rows(), m2.columns()); for (int i = 0; i < result.rows(); i++) { for (int j = 0; j < result.columns(); j++) { result[i, j] = m1.row(i) * m2.column(j); } } return(result); }
//! QR Solve /*! This implementation is based on MINPACK * (<http://www.netlib.org/minpack>, * <http://www.netlib.org/cephes/linalg.tgz>) * * Given an m by n matrix A, an n by n diagonal matrix d, * and an m-vector b, the problem is to determine an x which * solves the system * * A*x = b , d*x = 0 , * * in the least squares sense. * * d is an input array of length n which must contain the * diagonal elements of the matrix d. * * See lmdiff.cpp for further details. */ public static Vector qrSolve(Matrix a, Vector b, bool pivot = true, Vector d = null) { int m = a.rows(); int n = a.columns(); if (d == null) { d = new Vector(); } Utils.QL_REQUIRE(b.Count == m, () => "dimensions of A and b don't match"); Utils.QL_REQUIRE(d.Count == n || d.empty(), () => "dimensions of A and d don't match"); Matrix q = new Matrix(m, n), r = new Matrix(n, n); List <int> lipvt = MatrixUtilities.qrDecomposition(a, ref q, ref r, pivot); List <int> ipvt = new List <int>(n); ipvt = lipvt; //std::copy(lipvt.begin(), lipvt.end(), ipvt.get()); Matrix aT = Matrix.transpose(a); Matrix rT = Matrix.transpose(r); Vector sdiag = new Vector(n); Vector wa = new Vector(n); Vector ld = new Vector(n, 0.0); if (!d.empty()) { ld = d; //std::copy(d.begin(), d.end(), ld.begin()); } Vector x = new Vector(n); Vector qtb = Matrix.transpose(q) * b; MINPACK.qrsolv(n, rT, n, ipvt, ld, qtb, x, sdiag, wa); return(x); }
// implementation of the Higham algorithm to find the nearest correlation matrix. private static Matrix highamImplementation(Matrix A, int maxIterations, double tolerance) { int size = A.rows(); Matrix R, Y = new Matrix(A), X = new Matrix(A), deltaS = new Matrix(size, size, 0.0); Matrix lastX = new Matrix(X); Matrix lastY = new Matrix(Y); for (int i = 0; i < maxIterations; ++i) { R = Y - deltaS; X = projectToPositiveSemidefiniteMatrix(R); deltaS = X - R; Y = projectToUnitDiagonalMatrix(X); // convergence test if (Math.Max(normInf(X - lastX) / normInf(X), Math.Max(normInf(Y - lastY) / normInf(Y), normInf(Y - X) / normInf(Y))) <= tolerance) { break; } lastX = X; lastY = Y; } // ensure we return a symmetric matrix for (int i = 0; i < size; ++i) { for (int j = 0; j < i; ++j) { Y[i, j] = Y[j, i]; } } return(Y); }
public void testQRSolve() { // Testing QR solve... setup(); double tol = 1.0e-12; MersenneTwisterUniformRng rng = new MersenneTwisterUniformRng( 1234 ); Matrix bigM = new Matrix( 50, 100, 0.0 ); for ( int i = 0; i < Math.Min( bigM.rows(), bigM.columns() ); ++i ) { bigM[i, i] = i + 1.0; } Matrix[] testMatrices = { M1, M2, M3, Matrix.transpose(M3), M4, Matrix.transpose(M4), M5, I, M7, bigM, Matrix.transpose(bigM) }; for ( int j = 0; j < testMatrices.Length; j++ ) { Matrix A = testMatrices[j]; Vector b = new Vector( A.rows() ); for ( int k = 0; k < 10; ++k ) { for ( int i = 0; i < b.Count; ++i ) { b[i] = rng.next().value; } Vector x = MatrixUtilities.qrSolve( A, b, true ); if ( A.columns() >= A.rows() ) { if ( norm( A * x - b ) > tol ) Assert.Fail( "A*x does not match vector b (norm = " + norm( A * x - b ) + ")" ); } else { // use the SVD to calculate the reference values int n = A.columns(); Vector xr = new Vector( n, 0.0 ); SVD svd = new SVD( A ); Matrix V = svd.V(); Matrix U = svd.U(); Vector w = svd.singularValues(); double threshold = n * Const.QL_EPSILON; for ( int i = 0; i < n; ++i ) { if ( w[i] > threshold ) { double u = 0; int zero = 0; for ( int kk = 0; kk < U.rows(); kk++ ) u += ( U[kk, i] * b[zero++] ) / w[i]; for ( int jj = 0; jj < n; ++jj ) { xr[jj] += u * V[jj, i]; } } } if ( norm( xr - x ) > tol ) { Assert.Fail( "least square solution does not match (norm = " + norm( x - xr ) + ")" ); } } } } }
public LfmHullWhiteParameterization( LiborForwardModelProcess process, OptionletVolatilityStructure capletVol, Matrix correlation, int factors) : base(process.size(), factors) { diffusion_ = new Matrix(size_-1, factors_); fixingTimes_= process.fixingTimes(); Matrix sqrtCorr = new Matrix(size_ - 1, factors_, 1.0); if (correlation.empty()) { if(!(factors_ == 1)) throw new ApplicationException("correlation matrix must be given for "+ "multi factor models"); } else { if(!(correlation.rows() == size_-1 && correlation.rows() == correlation.columns())) throw new ApplicationException("wrong dimesion of the correlation matrix"); if(!(factors_ <= size_-1)) throw new ApplicationException("too many factors for given LFM process"); Matrix tmpSqrtCorr =MatrixUtilitites.pseudoSqrt(correlation, MatrixUtilitites.SalvagingAlgorithm.Spectral); // reduce to n factor model // "Reconstructing a valid correlation matrix from invalid data" // (<http://www.quarchome.org/correlationmatrix.pdf>) for (int i=0; i < size_-1; ++i) { double d = 0; tmpSqrtCorr.row(i).GetRange(0, factors_).ForEach((ii, vv) => d += vv*tmpSqrtCorr.row(i)[ii]); //sqrtCorr.row(i).GetRange(0, factors_).ForEach((ii, vv) => sqrtCorr.row(i)[ii] = tmpSqrtCorr.row(i).GetRange(0, factors_)[ii] / Math.Sqrt(d)); for (int k = 0; k < factors_; ++k){ sqrtCorr[i, k] = tmpSqrtCorr.row(i).GetRange(0, factors_)[k] / Math.Sqrt(d); } } } List<double> lambda=new List<double>(); DayCounter dayCounter = process.index().dayCounter(); List<double> fixingTimes = process.fixingTimes(); List<Date> fixingDates = process.fixingDates(); for (int i = 1; i < size_; ++i) { double cumVar = 0.0; for (int j = 1; j < i; ++j) { cumVar += lambda[i-j-1] * lambda[i-j-1] * (fixingTimes[j+1] - fixingTimes[j]); } double vol = capletVol.volatility(fixingDates[i], 0.0,false); double var = vol * vol * capletVol.dayCounter().yearFraction(fixingDates[0], fixingDates[i]); lambda.Add(Math.Sqrt( (var - cumVar) / (fixingTimes[1] - fixingTimes[0])) ); for (int q=0; q<factors_; ++q) { diffusion_[i - 1, q]=sqrtCorr[i - 1, q] * lambda.Last() ; } } covariance_ = diffusion_ * Matrix.transpose(diffusion_); }
public TqrEigenDecomposition(Vector diag, Vector sub, EigenVectorCalculation calc = EigenVectorCalculation.WithEigenVector, ShiftStrategy strategy = ShiftStrategy.CloseEigenValue) { iter_ = 0; d_ = new Vector(diag); int row = calc == EigenVectorCalculation.WithEigenVector ? d_.size() : calc == EigenVectorCalculation.WithoutEigenVector ? 0 : 1; ev_ = new Matrix(row, d_.size(), 0.0); int n = diag.size(); Utils.QL_REQUIRE( n == sub.size() + 1, () => "Wrong dimensions" ); Vector e = new Vector(sub); int i; for (i=0; i < ev_.rows(); ++i) { ev_[i,i] = 1.0; } for (int k=n-1; k >=1; --k) { while (!offDiagIsZero(k, e)) { int l = k; while (--l > 0 && !offDiagIsZero(l,e)); iter_++; double q = d_[l]; if (strategy != ShiftStrategy.NoShift) { // calculated eigenvalue of 2x2 sub matrix of // [ d_[k-1] e_[k] ] // [ e_[k] d_[k] ] // which is closer to d_[k+1]. // FLOATING_POINT_EXCEPTION double t1 = Math.Sqrt( 0.25*(d_[k]*d_[k] + d_[k-1]*d_[k-1]) - 0.5*d_[k-1]*d_[k] + e[k]*e[k]); double t2 = 0.5*(d_[k]+d_[k-1]); double lambda = (Math.Abs(t2+t1 - d_[k]) < Math.Abs(t2-t1 - d_[k]))? t2+t1 : t2-t1; if (strategy == ShiftStrategy.CloseEigenValue) { q-=lambda; } else { q-=((k==n-1)? 1.25 : 1.0)*lambda; } } // the QR transformation double sine = 1.0; double cosine = 1.0; double u = 0.0; bool recoverUnderflow = false; for (i=l+1; i <= k && !recoverUnderflow; ++i) { double h = cosine*e[i]; double p = sine*e[i]; e[i-1] = Math.Sqrt(p*p+q*q); if (e[i-1] != 0.0) { sine = p/e[i-1]; cosine = q/e[i-1]; double g = d_[i-1]-u; double t = (d_[i]-g)*sine+2*cosine*h; u = sine*t; d_[i-1] = g + u; q = cosine*t - h; for (int j=0; j < ev_.rows(); ++j) { double tmp = ev_[j,i-1]; ev_[j,i-1] = sine*ev_[j,i] + cosine*tmp; ev_[j,i] = cosine*ev_[j,i] - sine*tmp; } } else { // recover from underflow d_[i-1] -= u; e[l] = 0.0; recoverUnderflow = true; } } if (!recoverUnderflow) { d_[k] -= u; e[k] = q; e[l] = 0.0; } } } // sort (eigenvalues, eigenvectors), // code taken from symmetricSchureDecomposition.cpp List<KeyValuePair<double, List<double> > > temp = new List<KeyValuePair<double,List<double>>>(n); List<double> eigenVector = new List<double>(ev_.rows()); for (i=0; i<n; i++) { if (ev_.rows() > 0) //std::copy(ev_.column_begin(i),ev_.column_end(i), eigenVector.begin()); eigenVector = ev_.column(i) ; temp[i] = new KeyValuePair<double,List<double>>(d_[i], eigenVector); } //std::sort(temp.begin(), temp.end(), std::greater<std::pair<Real, std::vector<Real> > >()); temp.Sort(); // first element is positive for (i=0; i<n; i++) { d_[i] = temp[i].Key; double sign = 1.0; if (ev_.rows() > 0 && temp[i].Value[0]<0.0) sign = -1.0; for (int j=0; j<ev_.rows(); ++j) { ev_[j,i] = sign * temp[i].Value[j]; } } }
/*! \relates Matrix */ public static Matrix operator *(Matrix m1, Matrix m2) { if (!(m1.columns() == m2.rows())) throw new ApplicationException("matrices with different sizes (" + m1.rows() + "x" + m1.columns() + ", " + m2.rows() + "x" + m2.columns() + ") cannot be multiplied"); Matrix result = new Matrix(m1.rows(), m2.columns()); for (int i = 0; i < result.rows(); i++) for (int j = 0; j < result.columns(); j++) result[i, j] = m1.row(i) * m2.column(j); return result; }
//! QR decompoisition /*! This implementation is based on MINPACK (<http://www.netlib.org/minpack>, <http://www.netlib.org/cephes/linalg.tgz>) This subroutine uses householder transformations with column pivoting (optional) to compute a qr factorization of the m by n matrix A. That is, qrfac determines an orthogonal matrix q, a permutation matrix p, and an upper trapezoidal matrix r with diagonal elements of nonincreasing magnitude, such that A*p = q*r. Return value ipvt is an integer array of length n, which defines the permutation matrix p such that A*p = q*r. Column j of p is column ipvt(j) of the identity matrix. See lmdiff.cpp for further details. */ //public static List<int> qrDecomposition(Matrix A, Matrix q, Matrix r, bool pivot = true) { public static List<int> qrDecomposition(Matrix M, Matrix q, Matrix r, bool pivot) { Matrix mT = Matrix.transpose(M); int m = M.rows(); int n = M.columns(); List<int> lipvt = new InitializedList<int>(n); Vector rdiag = new Vector(n); Vector wa = new Vector(n); MINPACK.qrfac(m, n, mT, 0, (pivot)?1:0, ref lipvt, n, ref rdiag, ref rdiag, wa); if (r.columns() != n || r.rows() !=n) r = new Matrix(n, n); for (int i=0; i < n; ++i) { // std::fill(r.row_begin(i), r.row_begin(i)+i, 0.0); r[i, i] = rdiag[i]; if (i < m) { // std::copy(mT.column_begin(i)+i+1, mT.column_end(i), // r.row_begin(i)+i+1); } else { // std::fill(r.row_begin(i)+i+1, r.row_end(i), 0.0); } } if (q.rows() != m || q.columns() != n) q = new Matrix(m, n); Vector w = new Vector(m); //for (int k=0; k < m; ++k) { // std::fill(w.begin(), w.end(), 0.0); // w[k] = 1.0; // for (int j=0; j < Math.Min(n, m); ++j) { // double t3 = mT[j,j]; // if (t3 != 0.0) { // double t // = std::inner_product(mT.row_begin(j)+j, mT.row_end(j), // w.begin()+j, 0.0)/t3; // for (int i=j; i<m; ++i) { // w[i]-=mT[j,i]*t; // } // } // q[k,j] = w[j]; // } // std::fill(q.row_begin(k) + Math.Min(n, m), q.row_end(k), 0.0); //} List<int> ipvt = new InitializedList<int>(n); //if (pivot) { // std::copy(lipvt.get(), lipvt.get()+n, ipvt.begin()); //} //else { // for (int i=0; i < n; ++i) // ipvt[i] = i; //} return ipvt; }
// Optimization function for hypersphere and lower-diagonal algorithm private static Matrix hypersphereOptimize(Matrix targetMatrix, Matrix currentRoot, bool lowerDiagonal) { int i,j,k,size = targetMatrix.rows(); Matrix result = new Matrix(currentRoot); Vector variance = new Vector(size); for (i=0; i<size; i++){ variance[i]=Math.Sqrt(targetMatrix[i,i]); } if (lowerDiagonal) { Matrix approxMatrix = result*Matrix.transpose(result); result = MatrixUtilities.CholeskyDecomposition(approxMatrix, true); for (i=0; i<size; i++) { for (j=0; j<size; j++) { result[i,j]/=Math.Sqrt(approxMatrix[i,i]); } } } else { for (i=0; i<size; i++) { for (j=0; j<size; j++) { result[i,j]/=variance[i]; } } } ConjugateGradient optimize = new ConjugateGradient(); EndCriteria endCriteria = new EndCriteria(100, 10, 1e-8, 1e-8, 1e-8); HypersphereCostFunction costFunction = new HypersphereCostFunction(targetMatrix, variance, lowerDiagonal); NoConstraint constraint = new NoConstraint(); // hypersphere vector optimization if (lowerDiagonal) { Vector theta = new Vector(size * (size-1)/2); const double eps=1e-16; for (i=1; i<size; i++) { for (j=0; j<i; j++) { theta[i*(i-1)/2+j]=result[i,j]; if (theta[i*(i-1)/2+j]>1-eps) theta[i*(i-1)/2+j]=1-eps; if (theta[i*(i-1)/2+j]<-1+eps) theta[i*(i-1)/2+j]=-1+eps; for (k=0; k<j; k++) { theta[i*(i-1)/2+j] /= Math.Sin(theta[i*(i-1)/2+k]); if (theta[i*(i-1)/2+j]>1-eps) theta[i*(i-1)/2+j]=1-eps; if (theta[i*(i-1)/2+j]<-1+eps) theta[i*(i-1)/2+j]=-1+eps; } theta[i*(i-1)/2+j] = Math.Acos(theta[i*(i-1)/2+j]); if (j==i-1) { if (result[i,i]<0) theta[i*(i-1)/2+j]=-theta[i*(i-1)/2+j]; } } } Problem p = new Problem(costFunction, constraint, theta); optimize.minimize(p, endCriteria); theta = p.currentValue(); result.fill(1); for (i=0; i<size; i++) { for (k=0; k<size; k++) { if (k>i) { result[i,k]=0; } else { for (j=0; j<=k; j++) { if (j == k && k!=i) result[i,k] *= Math.Cos(theta[i*(i-1)/2+j]); else if (j!=i) result[i,k] *= Math.Sin(theta[i*(i-1)/2+j]); } } } } } else { Vector theta = new Vector(size * (size-1)); const double eps=1e-16; for (i=0; i<size; i++) { for (j=0; j<size-1; j++) { theta[j*size+i]=result[i,j]; if (theta[j*size+i]>1-eps) theta[j*size+i]=1-eps; if (theta[j*size+i]<-1+eps) theta[j*size+i]=-1+eps; for (k=0;k<j;k++) { theta[j*size+i] /= Math.Sin(theta[k*size+i]); if (theta[j*size+i]>1-eps) theta[j*size+i]=1-eps; if (theta[j*size+i]<-1+eps) theta[j*size+i]=-1+eps; } theta[j*size+i] = Math.Acos(theta[j*size+i]); if (j==size-2) { if (result[i,j+1]<0) theta[j*size+i]=-theta[j*size+i]; } } } Problem p = new Problem(costFunction, constraint, theta); optimize.minimize(p, endCriteria); theta=p.currentValue(); result.fill(1); for (i = 0; i < size; i++) { for (k=0; k<size; k++) { for (j=0; j<=k; j++) { if (j == k && k!=size-1) result[i,k] *= Math.Cos(theta[j*size+i]); else if (j!=size-1) result[i,k] *= Math.Sin(theta[j*size+i]); } } } } for (i=0; i<size; i++) { for (j=0; j<size; j++) { result[i,j]*=variance[i]; } } return result; }
// Matrix infinity norm. See Golub and van Loan (2.3.10) or // <http://en.wikipedia.org/wiki/Matrix_norm> private static double normInf(Matrix M) { int rows = M.rows(); int cols = M.columns(); double norm = 0.0; for (int i=0; i<rows; ++i) { double colSum = 0.0; for (int j=0; j<cols; ++j) colSum += Math.Abs(M[i,j]); norm = Math.Max(norm, colSum); } return norm; }
// Take a matrix and make all the eigenvalues non-negative private static Matrix projectToPositiveSemidefiniteMatrix(Matrix M) { int size = M.rows(); if (size != M.columns()) throw new ApplicationException("matrix not square"); Matrix diagonal = new Matrix(size, size); SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(M); for (int i=0; i<size; ++i) diagonal[i,i] = Math.Max(jd.eigenvalues()[i], 0.0); Matrix result = jd.eigenvectors()*diagonal*Matrix.transpose(jd.eigenvectors()); return result; }
// Take a matrix and make all the diagonal entries 1. private static Matrix projectToUnitDiagonalMatrix(Matrix M) { int size = M.rows(); if (size != M.columns()) throw new ApplicationException("matrix not square"); Matrix result = new Matrix(M); for (int i=0; i<size; ++i) result[i,i] = 1.0; return result; }
//! Returns the pseudo square root of a real symmetric matrix /*! Given a matrix \f$ M \f$, the result \f$ S \f$ is defined as the matrix such that \f$ S S^T = M. \f$ If the matrix is not positive semi definite, it can return an approximation of the pseudo square root using a (user selected) salvaging algorithm. For more information see: "The most general methodology to create a valid correlation matrix for risk management and option pricing purposes", by R. Rebonato and P. Jдckel. The Journal of Risk, 2(2), Winter 1999/2000 http://www.rebonato.com/correlationmatrix.pdf Revised and extended in "Monte Carlo Methods in Finance", by Peter Jдckel, Chapter 6. \pre the given matrix must be symmetric. \relates Matrix \warning Higham algorithm only works for correlation matrices. \test - the correctness of the results is tested by reproducing known good data. - the correctness of the results is tested by checking returned values against numerical calculations. */ public static Matrix pseudoSqrt(Matrix matrix, SalvagingAlgorithm sa) { int size = matrix.rows(); #if QL_EXTRA_SAFETY_CHECKS checkSymmetry(matrix); #else if (size != matrix.columns()) throw new ApplicationException("non square matrix: " + size + " rows, " + matrix.columns() + " columns"); #endif // spectral (a.k.a Principal Component) analysis SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(matrix); Matrix diagonal = new Matrix(size, size, 0.0); // salvaging algorithm Matrix result = new Matrix(size, size); bool negative; switch (sa) { case SalvagingAlgorithm.None: // eigenvalues are sorted in decreasing order if (!(jd.eigenvalues()[size-1]>=-1e-16)) throw new ApplicationException("negative eigenvalue(s) (" + jd.eigenvalues()[size-1] + ")"); result = MatrixUtilities.CholeskyDecomposition(matrix, true); break; case SalvagingAlgorithm.Spectral: // negative eigenvalues set to zero for (int i=0; i<size; i++) diagonal[i,i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); break; case SalvagingAlgorithm.Hypersphere: // negative eigenvalues set to zero negative=false; for (int i=0; i<size; ++i){ diagonal[i,i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); if (jd.eigenvalues()[i]<0.0) negative=true; } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); if (negative) result = hypersphereOptimize(matrix, result, false); break; case SalvagingAlgorithm.LowerDiagonal: // negative eigenvalues set to zero negative=false; for (int i=0; i<size; ++i){ diagonal[i,i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); if (jd.eigenvalues()[i]<0.0) negative=true; } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); if (negative) result = hypersphereOptimize(matrix, result, true); break; case SalvagingAlgorithm.Higham: int maxIterations = 40; double tol = 1e-6; result = highamImplementation(matrix, maxIterations, tol); result = MatrixUtilities.CholeskyDecomposition(result, true); break; default: throw new ApplicationException("unknown salvaging algorithm"); } return result; }
public SVD(Matrix M) { Matrix A; /* The implementation requires that rows > columns. If this is not the case, we decompose M^T instead. Swapping the resulting U and V gives the desired result for M as M^T = U S V^T (decomposition of M^T) M = (U S V^T)^T (transpose) M = (V^T^T S^T U^T) ((AB)^T = B^T A^T) M = V S U^T (idempotence of transposition, symmetry of diagonal matrix S) */ if (M.rows() >= M.columns()) { A = new Matrix(M); transpose_ = false; } else { A = Matrix.transpose(M); transpose_ = true; } m_ = A.rows(); n_ = A.columns(); // we're sure that m_ >= n_ s_ = new Vector(n_); U_ = new Matrix(m_, n_); V_ = new Matrix(n_, n_); Vector e = new Vector(n_); Vector work = new Vector(m_); int i, j, k; // Reduce A to bidiagonal form, storing the diagonal elements // in s and the super-diagonal elements in e. int nct = Math.Min(m_ - 1, n_); int nrt = Math.Max(0, n_ - 2); for (k = 0; k < Math.Max(nct, nrt); k++) { if (k < nct) { // Compute the transformation for the k-th column and // place the k-th diagonal in s[k]. // Compute 2-norm of k-th column without under/overflow. s_[k] = 0; for (i = k; i < m_; i++) { s_[k] = hypot(s_[k], A[i, k]); } if (s_[k] != 0.0) { if (A[k, k] < 0.0) { s_[k] = -s_[k]; } for (i = k; i < m_; i++) { A[i, k] /= s_[k]; } A[k, k] += 1.0; } s_[k] = -s_[k]; } for (j = k + 1; j < n_; j++) { if ((k < nct) && (s_[k] != 0.0)) { // Apply the transformation. double t = 0; for (i = k; i < m_; i++) { t += A[i, k] * A[i, j]; } t = -t / A[k, k]; for (i = k; i < m_; i++) { A[i, j] += t * A[i, k]; } } // Place the k-th row of A into e for the // subsequent calculation of the row transformation. e[j] = A[k, j]; } if (k < nct) { // Place the transformation in U for subsequent back multiplication. for (i = k; i < m_; i++) { U_[i, k] = A[i, k]; } } if (k < nrt) { // Compute the k-th row transformation and place the k-th super-diagonal in e[k]. // Compute 2-norm without under/overflow. e[k] = 0; for (i = k + 1; i < n_; i++) { e[k] = hypot(e[k], e[i]); } if (e[k] != 0.0) { if (e[k + 1] < 0.0) { e[k] = -e[k]; } for (i = k + 1; i < n_; i++) { e[i] /= e[k]; } e[k + 1] += 1.0; } e[k] = -e[k]; if ((k + 1 < m_) & (e[k] != 0.0)) { // Apply the transformation. for (i = k + 1; i < m_; i++) { work[i] = 0.0; } for (j = k + 1; j < n_; j++) { for (i = k + 1; i < m_; i++) { work[i] += e[j] * A[i, j]; } } for (j = k + 1; j < n_; j++) { double t = -e[j] / e[k + 1]; for (i = k + 1; i < m_; i++) { A[i, j] += t * work[i]; } } } // Place the transformation in V for subsequent back multiplication. for (i = k + 1; i < n_; i++) { V_[i, k] = e[i]; } } } // Set up the final bidiagonal matrix or order n. if (nct < n_) { s_[nct] = A[nct, nct]; } if (nrt + 1 < n_) { e[nrt] = A[nrt, n_ - 1]; } e[n_ - 1] = 0.0; // generate U for (j = nct; j < n_; j++) { for (i = 0; i < m_; i++) { U_[i, j] = 0.0; } U_[j, j] = 1.0; } for (k = nct - 1; k >= 0; --k) { if (s_[k] != 0.0) { for (j = k + 1; j < n_; ++j) { double t = 0; for (i = k; i < m_; i++) { t += U_[i, k] * U_[i, j]; } t = -t / U_[k, k]; for (i = k; i < m_; i++) { U_[i, j] += t * U_[i, k]; } } for (i = k; i < m_; i++) { U_[i, k] = -U_[i, k]; } U_[k, k] = 1.0 + U_[k, k]; for (i = 0; i < k - 1; i++) { U_[i, k] = 0.0; } } else { for (i = 0; i < m_; i++) { U_[i, k] = 0.0; } U_[k, k] = 1.0; } } // generate V for (k = n_ - 1; k >= 0; --k) { if ((k < nrt) & (e[k] != 0.0)) { for (j = k + 1; j < n_; ++j) { double t = 0; for (i = k + 1; i < n_; i++) { t += V_[i, k] * V_[i, j]; } t = -t / V_[k + 1, k]; for (i = k + 1; i < n_; i++) { V_[i, j] += t * V_[i, k]; } } } for (i = 0; i < n_; i++) { V_[i, k] = 0.0; } V_[k, k] = 1.0; } // Main iteration loop for the singular values. int p = n_, pp = p - 1; int iter = 0; double eps = Math.Pow(2.0, -52.0); while (p > 0) { int kase; // Here is where a test for too many iterations would go. // This section of the program inspects for // negligible elements in the s and e arrays. On // completion the variables kase and k are set as follows. // kase = 1 if s(p) and e[k-1] are negligible and k<p // kase = 2 if s(k) is negligible and k<p // kase = 3 if e[k-1] is negligible, k<p, and // s(k), ..., s(p) are not negligible (qr step). // kase = 4 if e(p-1) is negligible (convergence). for (k = p - 2; k >= -1; --k) { if (k == -1) { break; } if (Math.Abs(e[k]) <= eps * (Math.Abs(s_[k]) + Math.Abs(s_[k + 1]))) { e[k] = 0.0; break; } } if (k == p - 2) { kase = 4; } else { int ks; for (ks = p - 1; ks >= k; --ks) { if (ks == k) { break; } double t = (ks != p ? Math.Abs(e[ks]) : 0) + (ks != k + 1 ? Math.Abs(e[ks - 1]) : 0); if (Math.Abs(s_[ks]) <= eps * t) { s_[ks] = 0.0; break; } } if (ks == k) { kase = 3; } else if (ks == p - 1) { kase = 1; } else { kase = 2; k = ks; } } k++; // Perform the task indicated by kase. switch (kase) { // Deflate negligible s(p). case 1: { double f = e[p - 2]; e[p - 2] = 0.0; for (j = p - 2; j >= k; --j) { double t = hypot(s_[j], f); double cs = s_[j] / t; double sn = f / t; s_[j] = t; if (j != k) { f = -sn * e[j - 1]; e[j - 1] = cs * e[j - 1]; } for (i = 0; i < n_; i++) { t = cs * V_[i, j] + sn * V_[i, p - 1]; V_[i, p - 1] = -sn * V_[i, j] + cs * V_[i, p - 1]; V_[i, j] = t; } } } break; // Split at negligible s(k). case 2: { double f = e[k - 1]; e[k - 1] = 0.0; for (j = k; j < p; j++) { double t = hypot(s_[j], f); double cs = s_[j] / t; double sn = f / t; s_[j] = t; f = -sn * e[j]; e[j] = cs * e[j]; for (i = 0; i < m_; i++) { t = cs * U_[i, j] + sn * U_[i, k - 1]; U_[i, k - 1] = -sn * U_[i, j] + cs * U_[i, k - 1]; U_[i, j] = t; } } } break; // Perform one qr step. case 3: { // Calculate the shift. double scale = Math.Max( Math.Max( Math.Max( Math.Max(Math.Abs(s_[p - 1]), Math.Abs(s_[p - 2])), Math.Abs(e[p - 2])), Math.Abs(s_[k])), Math.Abs(e[k])); double sp = s_[p - 1] / scale; double spm1 = s_[p - 2] / scale; double epm1 = e[p - 2] / scale; double sk = s_[k] / scale; double ek = e[k] / scale; double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0; double c = (sp * epm1) * (sp * epm1); double shift = 0.0; if ((b != 0.0) | (c != 0.0)) { shift = Math.Sqrt(b * b + c); if (b < 0.0) { shift = -shift; } shift = c / (b + shift); } double f = (sk + sp) * (sk - sp) + shift; double g = sk * ek; // Chase zeros. for (j = k; j < p - 1; j++) { double t = hypot(f, g); double cs = f / t; double sn = g / t; if (j != k) { e[j - 1] = t; } f = cs * s_[j] + sn * e[j]; e[j] = cs * e[j] - sn * s_[j]; g = sn * s_[j + 1]; s_[j + 1] = cs * s_[j + 1]; for (i = 0; i < n_; i++) { t = cs * V_[i, j] + sn * V_[i, j + 1]; V_[i, j + 1] = -sn * V_[i, j] + cs * V_[i, j + 1]; V_[i, j] = t; } t = hypot(f, g); cs = f / t; sn = g / t; s_[j] = t; f = cs * e[j] + sn * s_[j + 1]; s_[j + 1] = -sn * e[j] + cs * s_[j + 1]; g = sn * e[j + 1]; e[j + 1] = cs * e[j + 1]; if (j < m_ - 1) { for (i = 0; i < m_; i++) { t = cs * U_[i, j] + sn * U_[i, j + 1]; U_[i, j + 1] = -sn * U_[i, j] + cs * U_[i, j + 1]; U_[i, j] = t; } } } e[p - 2] = f; iter = iter + 1; } break; // Convergence. case 4: { // Make the singular values positive. if (s_[k] <= 0.0) { s_[k] = (s_[k] < 0.0 ? -s_[k] : 0.0); for (i = 0; i <= pp; i++) { V_[i, k] = -V_[i, k]; } } // Order the singular values. while (k < pp) { if (s_[k] >= s_[k + 1]) { break; } s_.swap(k, k + 1); if (k < n_ - 1) { for (i = 0; i < n_; i++) { V_.swap(i, k, i, k + 1); } } if (k < m_ - 1) { for (i = 0; i < m_; i++) { U_.swap(i, k, i, k + 1); } } k++; } iter = 0; --p; } break; } } }
public static Matrix transpose(Matrix m) { Matrix result = new Matrix(m.columns(),m.rows()); for (int i=0; i<m.rows(); i++) for (int j=0; j<m.columns();j++) result[j,i] = m[i,j]; return result; }
//! QR decompoisition /*! This implementation is based on MINPACK (<http://www.netlib.org/minpack>, <http://www.netlib.org/cephes/linalg.tgz>) This subroutine uses householder transformations with column pivoting (optional) to compute a qr factorization of the m by n matrix A. That is, qrfac determines an orthogonal matrix q, a permutation matrix p, and an upper trapezoidal matrix r with diagonal elements of nonincreasing magnitude, such that A*p = q*r. Return value ipvt is an integer array of length n, which defines the permutation matrix p such that A*p = q*r. Column j of p is column ipvt(j) of the identity matrix. See lmdiff.cpp for further details. */ //public static List<int> qrDecomposition(Matrix A, Matrix q, Matrix r, bool pivot = true) { public static List<int> qrDecomposition(Matrix M, ref Matrix q, ref Matrix r, bool pivot) { Matrix mT = Matrix.transpose(M); int m = M.rows(); int n = M.columns(); List<int> lipvt = new InitializedList<int>(n); Vector rdiag = new Vector(n); Vector wa = new Vector(n); MINPACK.qrfac(m, n, mT, 0, (pivot)?1:0, ref lipvt, n, ref rdiag, ref rdiag, wa); if (r.columns() != n || r.rows() !=n) r = new Matrix(n, n); for (int i=0; i < n; ++i) { r[i, i] = rdiag[i]; if (i < m) { for ( int j = i; j < mT.rows()-1; j++ ) r[i, j + 1] = mT[j+1, i]; } } if (q.rows() != m || q.columns() != n) q = new Matrix(m, n); Vector w = new Vector(m); for (int k=0; k < m; ++k) { w.Erase(); w[k] = 1.0; for (int j=0; j < Math.Min(n, m); ++j) { double t3 = mT[j,j]; if (t3 != 0.0) { double t = 0; for ( int kk = j ; kk < mT.columns(); kk++ ) t += ( mT[j,kk] * w[kk] ) / t3 ; for (int i=j; i<m; ++i) { w[i]-=mT[j,i]*t; } } q[k,j] = w[j]; } } List<int> ipvt = new InitializedList<int>(n); if (pivot) { for ( int i = 0; i < n; ++i ) ipvt[i] = lipvt[i]; } else { for (int i=0; i < n; ++i) ipvt[i] = i; } return ipvt; }
double norm(Matrix m) { double sum = 0.0; for (int i=0; i<m.rows(); i++) for (int j=0; j<m.columns(); j++) sum += m[i,j]*m[i,j]; return Math.Sqrt(sum); }
// implementation of the Higham algorithm to find the nearest correlation matrix. private static Matrix highamImplementation(Matrix A, int maxIterations, double tolerance) { int size = A.rows(); Matrix R, Y = new Matrix(A), X = new Matrix(A), deltaS = new Matrix(size, size, 0.0); Matrix lastX = new Matrix(X); Matrix lastY = new Matrix(Y); for (int i=0; i<maxIterations; ++i) { R = Y - deltaS; X = projectToPositiveSemidefiniteMatrix(R); deltaS = X - R; Y = projectToUnitDiagonalMatrix(X); // convergence test if (Math.Max(normInf(X-lastX)/normInf(X), Math.Max(normInf(Y-lastY)/normInf(Y), normInf(Y-X)/normInf(Y))) <= tolerance) { break; } lastX = X; lastY = Y; } // ensure we return a symmetric matrix for (int i=0; i<size; ++i) for (int j=0; j<i; ++j) Y[i,j] = Y[j,i]; return Y; }
//! QR Solve /*! This implementation is based on MINPACK (<http://www.netlib.org/minpack>, <http://www.netlib.org/cephes/linalg.tgz>) Given an m by n matrix A, an n by n diagonal matrix d, and an m-vector b, the problem is to determine an x which solves the system A*x = b , d*x = 0 , in the least squares sense. d is an input array of length n which must contain the diagonal elements of the matrix d. See lmdiff.cpp for further details. */ public static Vector qrSolve( Matrix a, Vector b, bool pivot = true, Vector d = null ) { int m = a.rows(); int n = a.columns(); if ( d == null ) d = new Vector(); Utils.QL_REQUIRE( b.Count == m, () => "dimensions of A and b don't match" ); Utils.QL_REQUIRE( d.Count == n || d.empty(), () => "dimensions of A and d don't match" ); Matrix q = new Matrix( m, n ), r = new Matrix( n, n ); List<int> lipvt = MatrixUtilities.qrDecomposition( a, ref q, ref r, pivot ); List<int> ipvt = new List<int>( n ); ipvt = lipvt; //std::copy(lipvt.begin(), lipvt.end(), ipvt.get()); Matrix aT = Matrix.transpose( a ); Matrix rT = Matrix.transpose( r ); Vector sdiag = new Vector( n ); Vector wa = new Vector( n ); Vector ld = new Vector( n, 0.0 ); if ( !d.empty() ) { ld = d; //std::copy(d.begin(), d.end(), ld.begin()); } Vector x = new Vector( n ); Vector qtb = Matrix.transpose( q ) * b; MINPACK.qrsolv( n, rT, n, ipvt, ld, qtb, x, sdiag, wa ); return x; }
public static Matrix rankReducedSqrt(Matrix matrix, int maxRank, double componentRetainedPercentage, SalvagingAlgorithm sa) { int size = matrix.rows(); #if QL_EXTRA_SAFETY_CHECKS checkSymmetry(matrix); #else if (size != matrix.columns()) throw new ApplicationException("non square matrix: " + size + " rows, " + matrix.columns() + " columns"); #endif if (!(componentRetainedPercentage > 0.0)) throw new ApplicationException("no eigenvalues retained"); if (!(componentRetainedPercentage <= 1.0)) throw new ApplicationException("percentage to be retained > 100%"); if (!(maxRank >= 1)) throw new ApplicationException("max rank required < 1"); // spectral (a.k.a Principal Component) analysis SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(matrix); Vector eigenValues = jd.eigenvalues(); // salvaging algorithm switch (sa) { case SalvagingAlgorithm.None: // eigenvalues are sorted in decreasing order if (!(eigenValues[size - 1] >= -1e-16)) throw new ApplicationException("negative eigenvalue(s) (" + eigenValues[size - 1] + ")"); break; case SalvagingAlgorithm.Spectral: // negative eigenvalues set to zero for (int i = 0; i < size; ++i) eigenValues[i] = Math.Max(eigenValues[i], 0.0); break; case SalvagingAlgorithm.Higham: { int maxIterations = 40; double tolerance = 1e-6; Matrix adjustedMatrix = highamImplementation(matrix, maxIterations, tolerance); jd = new SymmetricSchurDecomposition(adjustedMatrix); eigenValues = jd.eigenvalues(); } break; default: throw new ApplicationException("unknown or invalid salvaging algorithm"); } // factor reduction /*std::accumulate(eigenValues.begin(), eigenValues.end(), 0.0);*/ double accumulate = 0; eigenValues.ForEach((ii, vv) => accumulate += eigenValues[ii]); double enough = componentRetainedPercentage * accumulate; if (componentRetainedPercentage == 1.0) { // numerical glitches might cause some factors to be discarded enough *= 1.1; } // retain at least one factor double components = eigenValues[0]; int retainedFactors = 1; for (int i = 1; components < enough && i < size; ++i) { components += eigenValues[i]; retainedFactors++; } // output is granted to have a rank<=maxRank retainedFactors = Math.Min(retainedFactors, maxRank); Matrix diagonal = new Matrix(size, retainedFactors, 0.0); for (int i = 0; i < retainedFactors; ++i) diagonal[i, i] = Math.Sqrt(eigenValues[i]); Matrix result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); return result; }
/*! \pre s must be symmetric */ public SymmetricSchurDecomposition(Matrix s) { diagonal_ = new Vector(s.rows()); eigenVectors_ = new Matrix(s.rows(), s.columns(), 0.0); if (!(s.rows() > 0 && s.columns() > 0)) throw new ApplicationException( "null matrix given"); if (s.rows()!=s.columns()) throw new ApplicationException( "input matrix must be square"); int size = s.rows(); for (int q=0; q<size; q++) { diagonal_[q] = s[q,q]; eigenVectors_[q,q] = 1.0; } Matrix ss = new Matrix(s); Vector tmpDiag = new Vector(diagonal_); Vector tmpAccumulate = new Vector(size, 0.0); double threshold, epsPrec = 1e-15; bool keeplooping = true; int maxIterations = 100, ite = 1; do { //main loop double sum = 0; for (int a=0; a<size-1; a++) { for (int b=a+1; b<size; b++) { sum += Math.Abs(ss[a,b]); } } if (sum==0) { keeplooping = false; } else { /* To speed up computation a threshold is introduced to make sure it is worthy to perform the Jacobi rotation */ if (ite<5) threshold = 0.2*sum/(size*size); else threshold = 0.0; int j, k, l; for (j=0; j<size-1; j++) { for (k=j+1; k<size; k++) { double sine, rho, cosin, heig, tang, beta; double smll = Math.Abs(ss[j,k]); if(ite> 5 && smll<epsPrec*Math.Abs(diagonal_[j]) && smll<epsPrec*Math.Abs(diagonal_[k])) { ss[j,k] = 0; } else if (Math.Abs(ss[j,k])>threshold) { heig = diagonal_[k]-diagonal_[j]; if (smll<epsPrec*Math.Abs(heig)) { tang = ss[j,k]/heig; } else { beta = 0.5*heig/ss[j,k]; tang = 1.0/(Math.Abs(beta)+ Math.Sqrt(1+beta*beta)); if (beta<0) tang = -tang; } cosin = 1/Math.Sqrt(1+tang*tang); sine = tang*cosin; rho = sine/(1+cosin); heig = tang*ss[j,k]; tmpAccumulate[j] -= heig; tmpAccumulate[k] += heig; diagonal_[j] -= heig; diagonal_[k] += heig; ss[j,k] = 0.0; for (l=0; l+1<=j; l++) jacobiRotate_(ss, rho, sine, l, j, l, k); for (l=j+1; l<=k-1; l++) jacobiRotate_(ss, rho, sine, j, l, l, k); for (l=k+1; l<size; l++) jacobiRotate_(ss, rho, sine, j, l, k, l); for (l=0; l<size; l++) jacobiRotate_(eigenVectors_, rho, sine, l, j, l, k); } } } for (k=0; k<size; k++) { tmpDiag[k] += tmpAccumulate[k]; diagonal_[k] = tmpDiag[k]; tmpAccumulate[k] = 0.0; } } } while (++ite<=maxIterations && keeplooping); if(!(ite<=maxIterations)) throw new ApplicationException("Too many iterations (" + maxIterations + ") reached"); // sort (eigenvalues, eigenvectors) List<KeyValuePair<double, Vector>> temp = new InitializedList<KeyValuePair<double, Vector>>(size); int row, col; for (col=0; col<size; col++) { Vector eigenVector = new Vector(size); eigenVectors_.column(col).ForEach((ii, xx) => eigenVector[ii] = xx); temp[col] = new KeyValuePair<double,Vector>(diagonal_[col], eigenVector); } // sort descending: std::greater temp.Sort((x, y) => y.Key.CompareTo(x.Key)); double maxEv = temp[0].Key; for (col=0; col<size; col++) { // check for round-off errors diagonal_[col] = (Math.Abs(temp[col].Key/maxEv)<1e-16 ? 0.0 : temp[col].Key); double sign = 1.0; if (temp[col].Value[0]<0.0) sign = -1.0; for (row=0; row<size; row++) { eigenVectors_[row,col] = sign * temp[col].Value[row]; } } }
//! floating reference date, fixed market data public SwaptionVolatilityMatrix( Calendar calendar, BusinessDayConvention bdc, List<Period> optionTenors, List<Period> swapTenors, Matrix vols, DayCounter dayCounter) : base(optionTenors, swapTenors, 0, calendar, bdc, dayCounter) { volHandles_ = new InitializedList<List<Handle<Quote>>>(vols.rows()); volatilities_ = new Matrix(vols.rows(), vols.columns()); checkInputs(vols.rows(), vols.columns()); // fill dummy handles to allow generic handle-based // computations later on for (int i=0; i<vols.rows(); ++i) { volHandles_[i] = new InitializedList<Handle<Quote>>(vols.columns()); for (int j=0; j<vols.columns(); ++j) volHandles_[i][j] = new Handle<Quote>((new SimpleQuote(vols[i,j]))); } interpolation_ = new BilinearInterpolation(swapLengths_, swapLengths_.Count, optionTimes_, optionTimes_.Count, volatilities_); }