1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /*
4  Copyright (C) 2014 Jose Aparicio
5 
6  This file is part of QuantLib, a free-software/open-source library
7  for financial quantitative analysts and developers - http://quantlib.org/
8 
9  QuantLib is free software: you can redistribute it and/or modify it
10  under the terms of the QuantLib license.  You should have received a
11  copy of the license along with this program; if not, please email
12  <quantlib-dev@lists.sf.net>. The license is also available online at
13  <http://quantlib.org/license.shtml>.
14 
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE.  See the license for more details.
18 */
19 
20 #ifndef quantlib_tcopula_policy_hpp
21 #define quantlib_tcopula_policy_hpp
22 
23 #include <ql/errors.hpp>
24 #include <ql/utilities/disposable.hpp>
25 #include <ql/experimental/math/convolvedstudentt.hpp>
26 #include <ql/functional.hpp>
27 #include <boost/math/distributions/students_t.hpp>
28 #include <vector>
29 
30 namespace QuantLib {
31 
32     /*! \brief Student-T Latent Model's copula policy.
33 
34     Describes the copula of a set of normalized Student-T independent random
35     factors to be fed into the latent variable model.
36     The latent model requires the independent variables to be of unit variance
37     so the policy expects the factors coefficients to be as usual and the T
38     variables to be normalized, the normalization is performed by the policy.
39     To normalize the random variables they are divided by the square root of
40     the variance of each T (\f$ \frac{\nu}{\nu-2}\f$)
41     */
42     class TCopulaPolicy {
43     public:
44         /*! Stores the parameters defining the factors random variable
45         T-distributions. As it is now the latent models are restricted to
46         having the same distribution for all idiosyncratic factors, so only
47         one parameter is needed for them.
48         */
49         typedef
50             struct {
51                 std::vector<Integer> tOrders;
52             } initTraits;
53 
54         /*! Delayed initialization of the distribution parameters and caches.
55         To be called by the latent model. */
56         /* \todo
57         Explore other constructors, with different vector dimensions, defining
58         simpler combinations (only one correlation, only one variable) might
59         simplify memory.
60         */
61         explicit TCopulaPolicy(
62             const std::vector<std::vector<Real> >& factorWeights =
63                 std::vector<std::vector<Real> >(),
64             const initTraits& vals = initTraits());
65 
66         //! Number of independent random factors.
numFactors() const67         Size numFactors() const {
68             return latentVarsInverters_.size() + varianceFactors_.size() - 1;
69         }
70 
71         //! returns a copy of the initialization arguments
72         //... better to have a cache?
getInitTraits() const73         initTraits getInitTraits() const {
74             initTraits data;
75             data.tOrders.resize(distributions_.size());
76             for (Size i=0; i<distributions_.size(); ++i) {
77                 data.tOrders[i] = static_cast<Integer>(
78                     distributions_[i].degrees_of_freedom());
79             }
80             return data;
81         }
varianceFactors() const82         const std::vector<Real>& varianceFactors() const {
83             return varianceFactors_;
84         }
85         /*! Cumulative probability of a given latent variable.
86             The iVariable parameter is the index of the requested variable.
87         */
cumulativeY(Real val,Size iVariable) const88         Probability cumulativeY(Real val, Size iVariable) const {
89     #if defined(QL_EXTRA_SAFETY_CHECKS)
90             QL_REQUIRE(iVariable < latentVarsCumul_.size(),
91                 "Latent variable index out of bounds.");
92     #endif
93             return latentVarsCumul_[iVariable](val);
94         }
95         //! Cumulative probability of the idiosyncratic factors (all the same)
cumulativeZ(Real z) const96         Probability cumulativeZ(Real z) const {
97             return boost::math::cdf(distributions_.back(), z /
98                 varianceFactors_.back());
99         }
100         /*! Probability density of a given realization of values of the systemic
101           factors (remember they are independent).
102           Intended to be used in numerical integration of an arbitrary function
103           depending on those values.
104         */
density(const std::vector<Real> & m) const105         Probability density(const std::vector<Real>& m) const {
106     #if defined(QL_EXTRA_SAFETY_CHECKS)
107             QL_REQUIRE(m.size() == distributions_.size()-1,
108                 "Incompatible sample and latent model sizes");
109     #endif
110             Real prodDensities = 1.;
111             for(Size i=0; i<m.size(); i++)
112                 prodDensities *= boost::math::pdf(distributions_[i],
113                     m[i] /varianceFactors_[i]) /varianceFactors_[i];
114                  // accumulate lambda
115             return prodDensities;
116         }
117         /*! Returns the inverse of the cumulative distribution of the (modelled)
118           latent variable (as indexed by iVariable). Involves the convolution
119           of the factors' distributions.
120         */
inverseCumulativeY(Probability p,Size iVariable) const121         Real inverseCumulativeY(Probability p, Size iVariable) const {
122     #if defined(QL_EXTRA_SAFETY_CHECKS)
123             QL_REQUIRE(iVariable < latentVarsCumul_.size(),
124                 "Latent variable index out of bounds.");
125     #endif
126             return latentVarsInverters_[iVariable](p);
127         }
128         /*! Returns the inverse of the cumulative distribution of the
129         idiosincratic factor. The LM here is limited to all idiosincratic
130         factors following the same distribution.
131         */
inverseCumulativeZ(Probability p) const132         Real inverseCumulativeZ(Probability p) const {
133             return boost::math::quantile(distributions_.back(), p)
134                 * varianceFactors_.back();
135         }
136         /*! Returns the inverse of the cumulative distribution of the
137           systemic factor iFactor.
138         */
inverseCumulativeDensity(Probability p,Size iFactor) const139         Real inverseCumulativeDensity(Probability p, Size iFactor) const {
140     #if defined(QL_EXTRA_SAFETY_CHECKS)
141             QL_REQUIRE(iFactor < distributions_.size()-1,
142                 "Random factor variable index out of bounds.");
143     #endif
144             return boost::math::quantile(distributions_[iFactor], p)
145                 * varianceFactors_[iFactor];
146         }
147         //to use this (by default) version, the generator must be a uniform one.
148         Disposable<std::vector<Real> >
149             allFactorCumulInverter(const std::vector<Real>& probs) const;
150     private:
151         mutable std::vector<boost::math::students_t_distribution<> >
152             distributions_;
153         mutable std::vector<Real> varianceFactors_;
154         mutable std::vector<CumulativeBehrensFisher> latentVarsCumul_;
155         mutable std::vector<InverseCumulativeBehrensFisher>
156             latentVarsInverters_;
157     };
158 
159 }
160 
161 #endif
162