Coverage Report

Created: 2025-12-08 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/models/marketmodels/pathwiseaccountingengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
Copyright (C) 2008 Mark Joshi
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
<https://www.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
#include <ql/models/marketmodels/curvestate.hpp>
21
#include <ql/models/marketmodels/discounter.hpp>
22
#include <ql/models/marketmodels/evolutiondescription.hpp>
23
#include <ql/models/marketmodels/evolvers/lognormalfwdrateeuler.hpp>
24
#include <ql/models/marketmodels/marketmodel.hpp>
25
#include <ql/models/marketmodels/pathwiseaccountingengine.hpp>
26
#include <algorithm>
27
#include <utility>
28
29
namespace QuantLib {
30
31
    PathwiseAccountingEngine::PathwiseAccountingEngine(
32
        ext::shared_ptr<LogNormalFwdRateEuler> evolver, // method relies heavily on LMM Euler
33
        const Clone<MarketModelPathwiseMultiProduct>& product,
34
        ext::shared_ptr<MarketModel> pseudoRootStructure, // we need pseudo-roots and displacements
35
        Real initialNumeraireValue)
36
0
    : evolver_(std::move(evolver)), product_(product),
37
0
      pseudoRootStructure_(std::move(pseudoRootStructure)),
38
0
      initialNumeraireValue_(initialNumeraireValue), numberProducts_(product->numberOfProducts()),
39
0
      doDeflation_(!product->alreadyDeflated()), numerairesHeld_(product->numberOfProducts()),
40
0
      numberCashFlowsThisStep_(product->numberOfProducts()),
41
0
      cashFlowsGenerated_(product->numberOfProducts()),
42
0
      deflatorAndDerivatives_(pseudoRootStructure_->numberOfRates() + 1) {
43
44
0
        numberRates_ = pseudoRootStructure_->numberOfRates();
45
0
        numberSteps_ = pseudoRootStructure_->numberOfSteps();
46
47
0
        Matrix VModel(numberSteps_+1,numberRates_);
48
49
50
51
0
        Discounts_ = Matrix(numberSteps_+1,numberRates_+1);
52
53
0
        for (Size i=0; i <= numberSteps_; ++i)
54
0
            Discounts_[i][0] = 1.0;
55
56
57
0
        V_.reserve(numberProducts_);
58
59
0
        Matrix  modelCashFlowIndex(product_->possibleCashFlowTimes().size(), numberRates_+1);
60
61
62
0
        numberCashFlowsThisIndex_.resize(numberProducts_);
63
64
0
        for (Size i=0; i<numberProducts_; ++i)
65
0
        {
66
0
            cashFlowsGenerated_[i].resize(
67
0
                product_->maxNumberOfCashFlowsPerProductPerStep());
68
69
0
            for (auto& j : cashFlowsGenerated_[i])
70
0
                j.amount.resize(numberRates_ + 1);
71
72
0
            numberCashFlowsThisIndex_[i].resize(product_->possibleCashFlowTimes().size());
73
74
0
            V_.push_back(VModel);
75
76
77
0
            totalCashFlowsThisIndex_.push_back(modelCashFlowIndex);
78
0
        }
79
80
0
        LIBORRatios_ = VModel;
81
0
        StepsDiscountsSquared_ = VModel;
82
0
        LIBORRates_ =VModel;
83
84
85
86
87
0
        const std::vector<Time>& cashFlowTimes =
88
0
            product_->possibleCashFlowTimes();
89
0
        numberCashFlowTimes_ = cashFlowTimes.size();
90
91
0
        const std::vector<Time>& rateTimes = product_->evolution().rateTimes();
92
0
        const std::vector<Time>& evolutionTimes = product_->evolution().evolutionTimes();
93
0
        discounters_.reserve(cashFlowTimes.size());
94
95
0
        for (Real cashFlowTime : cashFlowTimes)
96
0
            discounters_.emplace_back(cashFlowTime, rateTimes);
97
98
99
        // need to check that we are in money market measure
100
101
102
        // we need to allocate cash-flow times to steps, i.e. what is the last step completed before a flow occurs
103
        // what we really need is for each step, what cash flow time indices to look at
104
105
0
        cashFlowIndicesThisStep_.resize(numberSteps_);
106
107
0
        for (Size i=0; i < numberCashFlowTimes_; ++i)
108
0
        {
109
0
            auto it =
110
0
                std::upper_bound(evolutionTimes.begin(), evolutionTimes.end(), cashFlowTimes[i]);
111
0
            if (it != evolutionTimes.begin())
112
0
                --it;
113
0
            Size index = it - evolutionTimes.begin();
114
0
            cashFlowIndicesThisStep_[index].push_back(i);
115
0
        }
116
117
0
        partials_ = Matrix(pseudoRootStructure_->numberOfFactors(),numberRates_);
118
0
    }
119
120
    Real PathwiseAccountingEngine::singlePathValues(std::vector<Real>& values)
121
0
    {
122
123
0
        const std::vector<Real> initialForwards_(pseudoRootStructure_->initialRates());
124
0
        currentForwards_ = initialForwards_;
125
        // clear accumulation variables
126
0
        for (Size i=0; i < numberProducts_; ++i)
127
0
        {
128
0
            numerairesHeld_[i]=0.0;
129
130
0
            for (Size j=0; j < numberCashFlowTimes_; ++j)
131
0
            {
132
0
                numberCashFlowsThisIndex_[i][j] =0;
133
134
0
                for (Size k=0; k <= numberRates_; ++k)
135
0
                    totalCashFlowsThisIndex_[i][j][k] =0.0;
136
0
            }
137
138
0
            for (Size l=0;  l< numberRates_; ++l)
139
0
                for (Size m=0; m <= numberSteps_; ++m)
140
0
                    V_[i][m][l] =0.0;
141
142
0
        }
143
144
145
146
0
        Real weight = evolver_->startNewPath();
147
0
        product_->reset();
148
149
0
        Size thisStep;
150
151
0
        bool done = false;
152
0
        do {
153
0
            thisStep = evolver_->currentStep();
154
0
            Size storeStep = thisStep+1;
155
0
            weight *= evolver_->advanceStep();
156
157
0
            done = product_->nextTimeStep(evolver_->currentState(),
158
0
                numberCashFlowsThisStep_,
159
0
                cashFlowsGenerated_);
160
161
0
            lastForwards_ = currentForwards_;
162
0
            currentForwards_ =  evolver_->currentState().forwardRates();
163
164
0
            for (unsigned long i=0; i < numberRates_; ++i)
165
0
            {
166
0
                Real x=  evolver_->currentState().discountRatio(i+1,i);
167
0
                StepsDiscountsSquared_[storeStep][i] = x*x;
168
169
0
                LIBORRatios_[storeStep][i] = currentForwards_[i]/lastForwards_[i];
170
0
                LIBORRates_[storeStep][i] = currentForwards_[i];
171
0
                Discounts_[storeStep][i+1] = evolver_->currentState().discountRatio(i+1,0);
172
0
            }
173
174
            // for each product...
175
0
            for (Size i=0; i<numberProducts_; ++i)
176
0
            {
177
                // ...and each cash flow...
178
0
                for (Size j=0; j<numberCashFlowsThisStep_[i]; ++j)
179
0
                {
180
0
                    Size k = cashFlowsGenerated_[i][j].timeIndex;
181
0
                    ++numberCashFlowsThisIndex_[i][ k];
182
183
0
                    for (Size l=0; l <= numberRates_; ++l)
184
0
                        totalCashFlowsThisIndex_[i][k][l] += cashFlowsGenerated_[i][j].amount[l]*weight;
185
186
0
                }
187
0
            }
188
189
190
0
        } while (!done);
191
192
        // ok we've gathered cash-flows, still have to backwards computation
193
194
0
        Size factors = pseudoRootStructure_->numberOfFactors();
195
0
        const std::vector<Time>& taus= pseudoRootStructure_->evolution(). rateTaus();
196
197
0
        bool flowsFound = false;
198
199
0
        Integer finalStepDone = thisStep;
200
201
0
        for (Integer currentStep =  numberSteps_-1; currentStep >=0 ; --currentStep) // must be a signed type as we go negative
202
0
        {
203
0
            Integer stepToUse = std::min<Integer>(currentStep, finalStepDone)+1;
204
205
0
            for (Size k=0; k < cashFlowIndicesThisStep_[currentStep].size(); ++k)
206
0
            {
207
0
                Size cashFlowIndex =cashFlowIndicesThisStep_[currentStep][k];
208
209
                // first check to see if anything actually happened before spending time on computing stuff
210
0
                bool noFlows = true;
211
0
                for (Size l=0; l < numberProducts_ && noFlows; ++l)
212
0
                    noFlows = noFlows && (numberCashFlowsThisIndex_[l][cashFlowIndex] ==0);
213
214
0
                flowsFound = flowsFound || !noFlows;
215
216
0
                if (!noFlows)
217
0
                {
218
0
                    if (doDeflation_)
219
0
                        discounters_[cashFlowIndex].getFactors(LIBORRates_, Discounts_,stepToUse, deflatorAndDerivatives_); // get amount to discount cash flow by and amount to multiply its derivatives by
220
221
0
                    for (Size j=0; j < numberProducts_; ++j)
222
0
                    {
223
0
                        if (numberCashFlowsThisIndex_[j][cashFlowIndex] > 0)
224
0
                        {
225
0
                            Real deflatedCashFlow = totalCashFlowsThisIndex_[j][cashFlowIndex][0];
226
0
                            if (doDeflation_)
227
0
                                deflatedCashFlow *= deflatorAndDerivatives_[0];
228
                            //cashFlowsGenerated_[j][cashFlowIndex].amount[0]*deflatorAndDerivatives_[0];
229
0
                            numerairesHeld_[j] += deflatedCashFlow;
230
231
0
                            for (Size i=1; i <= numberRates_; ++i)
232
0
                            {
233
0
                                Real thisDerivative =  totalCashFlowsThisIndex_[j][cashFlowIndex][i];
234
0
                                if (doDeflation_)
235
0
                                {
236
0
                                    thisDerivative *= deflatorAndDerivatives_[0];
237
0
                                    thisDerivative +=  totalCashFlowsThisIndex_[j][cashFlowIndex][0]*deflatorAndDerivatives_[i];
238
0
                                }
239
240
0
                                V_[j][stepToUse][i-1] += thisDerivative; // zeroth row of V is t =0 not t_0
241
0
                            }
242
0
                        }
243
0
                    }
244
0
                }
245
0
            }
246
247
            // need to do backwards updating
248
0
            if (flowsFound)
249
0
            {
250
0
                Integer nextStepToUse  = std::min<Integer>(currentStep-1, finalStepDone);
251
0
                Integer nextStepIndex = nextStepToUse+1;
252
0
                if (nextStepIndex != stepToUse) // then we need to update V
253
0
                {
254
255
0
                    const Matrix& thisPseudoRoot_= pseudoRootStructure_->pseudoRoot(currentStep);
256
257
0
                    for (Size i=0; i < numberProducts_; ++i)
258
0
                    {
259
                        // compute partials
260
0
                        for (Size f=0; f < factors; ++f)
261
0
                        {
262
0
                            Real libor = LIBORRates_[stepToUse][numberRates_-1];
263
0
                            Real V = V_[i][stepToUse][numberRates_-1];
264
0
                            Real pseudo = thisPseudoRoot_[numberRates_-1][f];
265
0
                            Real thisPartialTerm = libor*V*pseudo;
266
0
                            partials_[f][numberRates_-1] = thisPartialTerm;
267
268
0
                            for (Integer r = numberRates_-2; r >=0 ; --r)
269
0
                            {
270
0
                                Real thisPartialTermr = LIBORRates_[stepToUse][r]*V_[i][stepToUse][r]*thisPseudoRoot_[r][f];
271
272
0
                                partials_[f][r] = partials_[f][r+1] + thisPartialTermr;
273
274
0
                            }
275
0
                        }
276
0
                        for (Size j=0; j < numberRates_; ++j)
277
0
                        {
278
0
                            Real nextV = V_[i][stepToUse][j] * LIBORRatios_[stepToUse][j];
279
0
                            V_[i][nextStepIndex][j] = nextV;
280
281
0
                            Real summandTerm = 0.0;
282
0
                            for (Size f=0; f < factors; ++f)
283
0
                                summandTerm += thisPseudoRoot_[j][f]*partials_[f][j];
284
285
0
                            summandTerm *= taus[j]*StepsDiscountsSquared_[stepToUse][j];
286
287
0
                            V_[i][nextStepIndex][j] += summandTerm;
288
289
0
                        }
290
0
                    }
291
292
0
                }
293
0
            }
294
295
296
297
298
0
        }
299
300
        // write answer into values
301
302
0
        for (Size i=0; i < numberProducts_; ++i)
303
0
        {
304
0
            values[i] = numerairesHeld_[i]*initialNumeraireValue_;
305
0
            for (Size j=0; j < numberRates_; ++j)
306
0
                values[(i+1)*numberProducts_+j] = V_[i][0][j]*initialNumeraireValue_;
307
0
        }
308
309
0
        return 1.0; // we have put the weight in already, this results in lower variance since weight changes along the path
310
0
    }
311
312
    void PathwiseAccountingEngine::multiplePathValues(SequenceStatisticsInc& stats,
313
        Size numberOfPaths)
314
0
    {
315
0
        std::vector<Real> values(product_->numberOfProducts()*(numberRates_+1));
316
0
        for (Size i=0; i<numberOfPaths; ++i)
317
0
        {
318
0
            Real weight = singlePathValues(values);
319
0
            stats.add(values,weight);
320
0
        }
321
0
    }
322
323
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
324
 
325
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
326
327
    PathwiseVegasAccountingEngine::PathwiseVegasAccountingEngine(
328
        ext::shared_ptr<LogNormalFwdRateEuler> evolver, // method relies heavily on LMM Euler
329
        const Clone<MarketModelPathwiseMultiProduct>& product,
330
        ext::shared_ptr<MarketModel> pseudoRootStructure, // we need pseudo-roots and displacements
331
        const std::vector<std::vector<Matrix> >& vegaBumps,
332
        Real initialNumeraireValue)
333
0
    : evolver_(std::move(evolver)), product_(product),
334
0
      pseudoRootStructure_(std::move(pseudoRootStructure)),
335
0
      initialNumeraireValue_(initialNumeraireValue), numberProducts_(product->numberOfProducts()),
336
0
      doDeflation_(!product->alreadyDeflated()), numerairesHeld_(product->numberOfProducts()),
337
0
      numberCashFlowsThisStep_(product->numberOfProducts()),
338
0
      cashFlowsGenerated_(product->numberOfProducts()),
339
0
      stepsDiscounts_(pseudoRootStructure_->numberOfRates() + 1),
340
0
      vegasThisPath_(product->numberOfProducts(), vegaBumps[0].size()),
341
0
      deflatorAndDerivatives_(pseudoRootStructure_->numberOfRates() + 1) {
342
343
0
        stepsDiscounts_[0]=1.0;
344
345
0
        numberRates_ = pseudoRootStructure_->numberOfRates();
346
0
        numberSteps_ = pseudoRootStructure_->numberOfSteps();
347
0
        fullDerivatives_.resize(numberRates_);
348
349
350
0
        const EvolutionDescription& evolution = pseudoRootStructure_->evolution();
351
0
        numeraires_ =  moneyMarketMeasure(evolution);
352
353
354
0
        QL_REQUIRE(vegaBumps.size() == numberSteps_, "we need one vector of vega bumps for each step.");
355
356
0
        numberBumps_ = vegaBumps[0].size();
357
358
0
        for (Size i =0; i < numberSteps_; ++i)
359
0
        {
360
0
            Size thisSize = vegaBumps[i].size();
361
0
            QL_REQUIRE(thisSize == numberBumps_,"We must have precisely the same number of bumps for each step.");
362
0
            jacobianComputers_.emplace_back(
363
0
                pseudoRootStructure_->pseudoRoot(i), evolution.firstAliveRate()[i], numeraires_[i],
364
0
                evolution.rateTaus(), vegaBumps[i], pseudoRootStructure_->displacements());
365
366
0
            jacobiansThisPaths_.emplace_back(numberBumps_, pseudoRootStructure_->numberOfRates());
367
0
        }
368
369
370
371
0
        Matrix VModel(numberSteps_+1,numberRates_);
372
373
374
375
0
        Discounts_ = Matrix(numberSteps_+1,numberRates_+1);
376
377
0
        for (Size i=0; i <= numberSteps_; ++i)
378
0
            Discounts_[i][0] = 1.0;
379
380
381
0
        V_.reserve(numberProducts_);
382
383
0
        Matrix  modelCashFlowIndex(product_->possibleCashFlowTimes().size(), numberRates_+1);
384
385
386
0
        numberCashFlowsThisIndex_.resize(numberProducts_);
387
388
0
        for (Size i=0; i<numberProducts_; ++i)
389
0
        {
390
0
            cashFlowsGenerated_[i].resize(
391
0
                product_->maxNumberOfCashFlowsPerProductPerStep());
392
393
0
            for (auto& j : cashFlowsGenerated_[i])
394
0
                j.amount.resize(numberRates_ + 1);
395
396
0
            numberCashFlowsThisIndex_[i].resize(product_->possibleCashFlowTimes().size());
397
398
0
            V_.push_back(VModel);
399
400
401
0
            totalCashFlowsThisIndex_.push_back(modelCashFlowIndex);
402
0
        }
403
404
0
        LIBORRatios_ = VModel;
405
0
        StepsDiscountsSquared_ = VModel;
406
0
        LIBORRates_ =VModel;
407
408
409
410
411
0
        const std::vector<Time>& cashFlowTimes =
412
0
            product_->possibleCashFlowTimes();
413
0
        numberCashFlowTimes_ = cashFlowTimes.size();
414
415
0
        const std::vector<Time>& rateTimes = product_->evolution().rateTimes();
416
0
        const std::vector<Time>& evolutionTimes = product_->evolution().evolutionTimes();
417
0
        discounters_.reserve(cashFlowTimes.size());
418
419
0
        for (Real cashFlowTime : cashFlowTimes)
420
0
            discounters_.emplace_back(cashFlowTime, rateTimes);
421
422
423
        // need to check that we are in money market measure
424
425
426
        // we need to allocate cash-flow times to steps, i.e. what is the last step completed before a flow occurs
427
        // what we really need is for each step, what cash flow time indices to look at
428
429
0
        cashFlowIndicesThisStep_.resize(numberSteps_);
430
431
0
        for (Size i=0; i < numberCashFlowTimes_; ++i)
432
0
        {
433
0
            auto it =
434
0
                std::upper_bound(evolutionTimes.begin(), evolutionTimes.end(), cashFlowTimes[i]);
435
0
            if (it != evolutionTimes.begin())
436
0
                --it;
437
0
            Size index = it - evolutionTimes.begin();
438
0
            cashFlowIndicesThisStep_[index].push_back(i);
439
0
        }
440
441
0
        partials_ = Matrix(pseudoRootStructure_->numberOfFactors(),numberRates_);
442
0
    }
443
444
    Real PathwiseVegasAccountingEngine::singlePathValues(std::vector<Real>& values)
445
0
    {
446
447
0
        const std::vector<Real>& initialForwards_(pseudoRootStructure_->initialRates());
448
0
        currentForwards_ = initialForwards_;
449
        // clear accumulation variables
450
0
        for (Size i=0; i < numberProducts_; ++i)
451
0
        {
452
0
            numerairesHeld_[i]=0.0;
453
454
0
            for (Size j=0; j < numberCashFlowTimes_; ++j)
455
0
            {
456
0
                numberCashFlowsThisIndex_[i][j] =0;
457
458
0
                for (Size k=0; k <= numberRates_; ++k)
459
0
                    totalCashFlowsThisIndex_[i][j][k] =0.0;
460
0
            }
461
462
0
            for (Size l=0;  l< numberRates_; ++l)
463
0
                for (Size m=0; m <= numberSteps_; ++m)
464
0
                    V_[i][m][l] =0.0;
465
466
0
            for (Size p=0; p < numberBumps_; ++p)
467
0
                vegasThisPath_[i][p] =0.0;
468
469
0
        }
470
471
472
473
0
        Real weight = evolver_->startNewPath();
474
0
        product_->reset();
475
476
0
        Size thisStep;
477
478
0
        bool done = false;
479
0
        do {
480
0
            thisStep = evolver_->currentStep();
481
0
            Size storeStep = thisStep+1;
482
0
            weight *= evolver_->advanceStep();
483
484
0
            done = product_->nextTimeStep(evolver_->currentState(),
485
0
                numberCashFlowsThisStep_,
486
0
                cashFlowsGenerated_);
487
488
0
            lastForwards_ = currentForwards_;
489
0
            currentForwards_ =  evolver_->currentState().forwardRates();
490
491
0
            for (unsigned long i=0; i < numberRates_; ++i)
492
0
            {
493
0
                Real x=  evolver_->currentState().discountRatio(i+1,i);
494
0
                stepsDiscounts_[i+1] = x;
495
0
                StepsDiscountsSquared_[storeStep][i] = x*x;
496
497
0
                LIBORRatios_[storeStep][i] = currentForwards_[i]/lastForwards_[i];
498
0
                LIBORRates_[storeStep][i] = currentForwards_[i];
499
0
                Discounts_[storeStep][i+1] = evolver_->currentState().discountRatio(i+1,0);
500
0
            }
501
502
0
            jacobianComputers_[thisStep].getBumps(lastForwards_,
503
0
                                         stepsDiscounts_,
504
0
                                         currentForwards_,
505
0
                                         evolver_->browniansThisStep(),
506
0
                                         jacobiansThisPaths_[thisStep]);
507
508
509
510
            // for each product...
511
0
            for (Size i=0; i<numberProducts_; ++i)
512
0
            {
513
                // ...and each cash flow...
514
0
                for (Size j=0; j<numberCashFlowsThisStep_[i]; ++j)
515
0
                {
516
0
                    Size k = cashFlowsGenerated_[i][j].timeIndex;
517
0
                    ++numberCashFlowsThisIndex_[i][ k];
518
519
0
                    for (Size l=0; l <= numberRates_; ++l)
520
0
                        totalCashFlowsThisIndex_[i][k][l] += cashFlowsGenerated_[i][j].amount[l]*weight;
521
522
0
                }
523
0
            }
524
525
526
0
        } while (!done);
527
528
        // ok we've gathered cash-flows, still have to backwards computation
529
530
0
        Size factors = pseudoRootStructure_->numberOfFactors();
531
0
        const std::vector<Time>& taus= pseudoRootStructure_->evolution(). rateTaus();
532
533
0
        bool flowsFound = false;
534
535
0
        Integer finalStepDone = thisStep;
536
537
0
        for (Integer currentStep =  numberSteps_-1; currentStep >=0 ; --currentStep) // must be a signed type as we go negative
538
0
        {
539
0
            Integer stepToUse = std::min<Integer>(currentStep, finalStepDone)+1;
540
541
0
            for (Size k=0; k < cashFlowIndicesThisStep_[currentStep].size(); ++k)
542
0
            {
543
0
                Size cashFlowIndex =cashFlowIndicesThisStep_[currentStep][k];
544
545
                // first check to see if anything actually happened before spending time on computing stuff
546
0
                bool noFlows = true;
547
0
                for (Size l=0; l < numberProducts_ && noFlows; ++l)
548
0
                    noFlows = noFlows && (numberCashFlowsThisIndex_[l][cashFlowIndex] ==0);
549
550
0
                flowsFound = flowsFound || !noFlows;
551
552
0
                if (!noFlows)
553
0
                {
554
0
                    if (doDeflation_)
555
0
                        discounters_[cashFlowIndex].getFactors(LIBORRates_, Discounts_,stepToUse, deflatorAndDerivatives_); // get amount to discount cash flow by and amount to multiply its derivatives by
556
557
0
                    for (Size j=0; j < numberProducts_; ++j)
558
0
                    {
559
0
                        if (numberCashFlowsThisIndex_[j][cashFlowIndex] > 0)
560
0
                        {
561
0
                            Real deflatedCashFlow = totalCashFlowsThisIndex_[j][cashFlowIndex][0];
562
0
                            if (doDeflation_)
563
0
                                deflatedCashFlow *= deflatorAndDerivatives_[0];
564
                            //cashFlowsGenerated_[j][cashFlowIndex].amount[0]*deflatorAndDerivatives_[0];
565
0
                            numerairesHeld_[j] += deflatedCashFlow;
566
567
0
                            for (Size i=1; i <= numberRates_; ++i)
568
0
                            {
569
0
                                Real thisDerivative =  totalCashFlowsThisIndex_[j][cashFlowIndex][i];
570
0
                                if (doDeflation_)
571
0
                                {
572
0
                                    thisDerivative *= deflatorAndDerivatives_[0];
573
0
                                    thisDerivative +=  totalCashFlowsThisIndex_[j][cashFlowIndex][0]*deflatorAndDerivatives_[i];
574
0
                                    fullDerivatives_[i-1] = thisDerivative;
575
0
                                }
576
0
                                else
577
0
                                    fullDerivatives_[i-1] = thisDerivative;
578
579
0
                                V_[j][stepToUse][i-1] += thisDerivative; // zeroth row of V is t =0 not t_0
580
0
                            }
581
582
                            // ok we've got the derivatives and stored them, now add them to vegas
583
                            // this corresponds to the \frac{\partial F_n}[\partial theta} term
584
                            // we add the indirect terms later
585
586
0
                            for (Size k=0; k < numberBumps_; ++k)
587
0
                                for (Size i=0; i < numberRates_; ++i)
588
0
                                {
589
0
                                    vegasThisPath_[j][k] +=  fullDerivatives_[i]*jacobiansThisPaths_[stepToUse-1][k][i];
590
0
                                }
591
592
593
0
                        } // end of (numberCashFlowsThisIndex_[j][cashFlowIndex] > 0)
594
0
                    } // end of (Size j=0; j < numberProducts_; ++j)
595
0
                } // end of  if (!noFlows)
596
0
            }
597
598
            // need to do backwards updating
599
0
            if (flowsFound)
600
0
            {
601
0
                Integer nextStepToUse  = std::min<Integer>(currentStep-1, finalStepDone);
602
0
                Integer nextStepIndex = nextStepToUse+1;
603
0
                if (nextStepIndex != stepToUse) // then we need to update V
604
0
                {
605
606
0
                    const Matrix& thisPseudoRoot_= pseudoRootStructure_->pseudoRoot(currentStep);
607
608
0
                    for (Size i=0; i < numberProducts_; ++i)
609
0
                    {
610
                        // compute partials
611
0
                        for (Size f=0; f < factors; ++f)
612
0
                        {
613
0
                            Real libor = LIBORRates_[stepToUse][numberRates_-1];
614
0
                            Real V = V_[i][stepToUse][numberRates_-1];
615
0
                            Real pseudo = thisPseudoRoot_[numberRates_-1][f];
616
0
                            Real thisPartialTerm = libor*V*pseudo;
617
0
                            partials_[f][numberRates_-1] = thisPartialTerm;
618
619
0
                            for (Integer r = numberRates_-2; r >=0 ; --r)
620
0
                            {
621
0
                                Real thisPartialTermr = LIBORRates_[stepToUse][r]*V_[i][stepToUse][r]*thisPseudoRoot_[r][f];
622
623
0
                                partials_[f][r] = partials_[f][r+1] + thisPartialTermr;
624
625
0
                            }
626
0
                        } // end of (Size f=0; f < factors; ++f)
627
628
0
                        for (Size j=0; j < numberRates_; ++j)
629
0
                        {
630
0
                            Real nextV = V_[i][stepToUse][j] * LIBORRatios_[stepToUse][j];
631
0
                            V_[i][nextStepIndex][j] = nextV;
632
633
0
                            Real summandTerm = 0.0;
634
0
                            for (Size f=0; f < factors; ++f)
635
0
                                summandTerm += thisPseudoRoot_[j][f]*partials_[f][j];
636
637
0
                            summandTerm *= taus[j]*StepsDiscountsSquared_[stepToUse][j];
638
639
0
                            V_[i][nextStepIndex][j] += summandTerm;
640
641
0
                        } //end of  for (Size j=0; j < numberRates_; ++j)
642
643
                    // we've done the Vs now the vegas
644
645
0
                        if (nextStepIndex >0)
646
647
0
                            for (Size l=0; l < numberBumps_; ++l)
648
0
                                for (Size j=0; j < numberRates_; ++j)
649
0
                                    vegasThisPath_[i][l] +=  V_[i][nextStepIndex][j] * jacobiansThisPaths_[nextStepIndex-1][l][j];
650
651
652
0
                    } // end of (Size i=0; i < numberProducts_; ++i)
653
654
655
656
0
                } //  end of   if (nextStepIndex != stepToUse)
657
0
            } // end of  if (flowsFound)
658
659
0
        } // end of  for (Integer currentStep =  numberSteps_-1; currentStep >=0 ; --currentStep)
660
661
        // write answer into values
662
663
0
        Size entriesPerProduct = 1+numberRates_+numberBumps_;
664
665
0
        for (Size i=0; i < numberProducts_; ++i)
666
0
        {
667
0
            values[i*entriesPerProduct] = numerairesHeld_[i]*initialNumeraireValue_;
668
0
            for (Size j=0; j < numberRates_; ++j)
669
0
                values[i*entriesPerProduct+1+j] = V_[i][0][j]*initialNumeraireValue_;
670
0
            for (Size k=0; k < numberBumps_; ++k)
671
0
                values[i*entriesPerProduct + numberRates_ +k +1 ] = vegasThisPath_[i][k]*initialNumeraireValue_;
672
0
        }
673
674
0
        return 1.0; // we have put the weight in already, this results in lower variance since weight changes along the path
675
0
    }
676
677
    void PathwiseVegasAccountingEngine::multiplePathValues(std::vector<Real>& means, std::vector<Real>& errors,
678
        Size numberOfPaths)
679
0
    {
680
0
        std::vector<Real> values(product_->numberOfProducts()*(1+numberRates_+numberBumps_));
681
0
        means.resize(values.size());
682
0
        errors.resize(values.size());
683
0
        std::vector<Real> sums(values.size(),0.0);
684
0
        std::vector<Real> sumsqs(values.size(),0.0);
685
686
687
688
0
        for (Size i=0; i<numberOfPaths; ++i)
689
0
        {
690
0
            /* Real weight = */ singlePathValues(values);
691
            // stats.add(values,weight);
692
0
            for (Size j=0; j < values.size(); ++j)
693
0
            {
694
0
                sums[j] += values[j];
695
0
                sumsqs[j] += values[j]*values[j];
696
697
0
            }
698
0
        }
699
700
0
        for (Size j=0; j < values.size(); ++j)
701
0
            {
702
0
                means[j] = sums[j]/numberOfPaths;
703
0
                Real meanSq = sumsqs[j]/numberOfPaths;
704
0
                Real variance = meanSq - means[j]*means[j];
705
0
                errors[j] = std::sqrt(variance/numberOfPaths);
706
707
0
            }
708
0
    }
709
710
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
711
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
712
713
    PathwiseVegasOuterAccountingEngine::PathwiseVegasOuterAccountingEngine(
714
        ext::shared_ptr<LogNormalFwdRateEuler> evolver, // method relies heavily on LMM Euler
715
        const Clone<MarketModelPathwiseMultiProduct>& product,
716
        ext::shared_ptr<MarketModel> pseudoRootStructure, // we need pseudo-roots and displacements
717
        const std::vector<std::vector<Matrix> >& vegaBumps,
718
        Real initialNumeraireValue)
719
0
    : evolver_(std::move(evolver)), product_(product),
720
0
      pseudoRootStructure_(std::move(pseudoRootStructure)), vegaBumps_(vegaBumps),
721
0
      initialNumeraireValue_(initialNumeraireValue), numberProducts_(product->numberOfProducts()),
722
0
      doDeflation_(!product->alreadyDeflated()), numerairesHeld_(product->numberOfProducts()),
723
0
      numberCashFlowsThisStep_(product->numberOfProducts()),
724
0
      cashFlowsGenerated_(product->numberOfProducts()),
725
0
      stepsDiscounts_(pseudoRootStructure_->numberOfRates() + 1),
726
0
      elementary_vegas_ThisPath_(product->numberOfProducts()),
727
0
      deflatorAndDerivatives_(pseudoRootStructure_->numberOfRates() + 1) {
728
729
0
        stepsDiscounts_[0]=1.0;
730
731
0
        numberRates_ = pseudoRootStructure_->numberOfRates();
732
0
        numberSteps_ = pseudoRootStructure_->numberOfSteps();
733
0
        factors_ = pseudoRootStructure_->numberOfFactors();
734
0
        fullDerivatives_.resize(numberRates_);
735
736
737
0
        const EvolutionDescription& evolution = pseudoRootStructure_->evolution();
738
0
        numeraires_ =  moneyMarketMeasure(evolution);
739
740
741
0
        QL_REQUIRE(vegaBumps.size() == numberSteps_, "we need precisely one vector of vega bumps for each step.");
742
743
0
        numberBumps_ = vegaBumps[0].size();
744
745
0
       std::vector<Matrix> jacobiansThisPathsModel;
746
0
       jacobiansThisPathsModel.reserve(numberRates_);
747
0
       for (Size i =0; i < numberRates_; ++i)
748
0
           jacobiansThisPathsModel.emplace_back(numberRates_, factors_);
749
750
751
0
        for (Size i =0; i < numberSteps_; ++i)
752
0
        {
753
0
            jacobianComputers_.emplace_back(
754
0
                pseudoRootStructure_->pseudoRoot(i), evolution.firstAliveRate()[i], numeraires_[i],
755
0
                evolution.rateTaus(), pseudoRootStructure_->displacements());
756
757
            // vector of vector of matrices to store jacobians of rates with respect to pseudo-root
758
            // elements
759
0
            jacobiansThisPaths_.push_back(jacobiansThisPathsModel);
760
0
        }
761
762
763
764
0
        Matrix VModel(numberSteps_+1,numberRates_);
765
766
767
768
0
        Discounts_ = Matrix(numberSteps_+1,numberRates_+1);
769
770
0
        for (Size i=0; i <= numberSteps_; ++i)
771
0
            Discounts_[i][0] = 1.0;
772
773
774
0
        V_.reserve(numberProducts_);
775
776
0
        Matrix  modelCashFlowIndex(product_->possibleCashFlowTimes().size(), numberRates_+1);
777
778
779
0
        numberCashFlowsThisIndex_.resize(numberProducts_);
780
781
0
        for (Size i=0; i<numberProducts_; ++i)
782
0
        {
783
0
            cashFlowsGenerated_[i].resize(
784
0
                product_->maxNumberOfCashFlowsPerProductPerStep());
785
786
0
            for (auto& j : cashFlowsGenerated_[i])
787
0
                j.amount.resize(numberRates_ + 1);
788
789
0
            numberCashFlowsThisIndex_[i].resize(product_->possibleCashFlowTimes().size());
790
791
0
            V_.push_back(VModel);
792
793
794
0
            totalCashFlowsThisIndex_.push_back(modelCashFlowIndex);
795
0
        }
796
797
0
        LIBORRatios_ = VModel;
798
0
        StepsDiscountsSquared_ = VModel;
799
0
        LIBORRates_ =VModel;
800
801
802
803
804
0
        const std::vector<Time>& cashFlowTimes =
805
0
            product_->possibleCashFlowTimes();
806
0
        numberCashFlowTimes_ = cashFlowTimes.size();
807
808
0
        const std::vector<Time>& rateTimes = product_->evolution().rateTimes();
809
0
        const std::vector<Time>& evolutionTimes = product_->evolution().evolutionTimes();
810
0
        discounters_.reserve(cashFlowTimes.size());
811
812
0
        for (Real cashFlowTime : cashFlowTimes)
813
0
            discounters_.emplace_back(cashFlowTime, rateTimes);
814
815
816
        // need to check that we are in money market measure
817
818
819
        // we need to allocate cash-flow times to steps, i.e. what is the last step completed before a flow occurs
820
        // what we really need is for each step, what cash flow time indices to look at
821
822
0
        cashFlowIndicesThisStep_.resize(numberSteps_);
823
824
0
        for (Size i=0; i < numberCashFlowTimes_; ++i)
825
0
        {
826
0
            auto it =
827
0
                std::upper_bound(evolutionTimes.begin(), evolutionTimes.end(), cashFlowTimes[i]);
828
0
            if (it != evolutionTimes.begin())
829
0
                --it;
830
0
            Size index = it - evolutionTimes.begin();
831
0
            cashFlowIndicesThisStep_[index].push_back(i);
832
0
        }
833
834
0
        partials_ = Matrix(pseudoRootStructure_->numberOfFactors(),numberRates_);
835
836
//      set up this container object        
837
//        std::vector<std::vector<std::vector<Matrix> >  > elementary_vegas_ThisPath_;  // dimensions are product, step, rate, rate and factor
838
839
0
        { // force destruction of modelVegaMatrix as soon as no longer needed 
840
0
            Matrix modelVegaMatrix(numberRates_, factors_,0.0);
841
842
0
            for (Size i=0; i < numberProducts_; ++i)
843
0
            {  
844
0
                elementary_vegas_ThisPath_[i].resize(numberSteps_);
845
0
                for (Size j=0; j < numberSteps_; ++j)
846
0
                {
847
                   
848
0
                        elementary_vegas_ThisPath_[i][j]= modelVegaMatrix;
849
0
                }
850
0
            }
851
0
        } // modelVegaMatrix destroyed here 
852
853
0
        numberElementaryVegas_ = numberSteps_*numberRates_*factors_;
854
/*
855
        gaussians_.resize(numberSteps_);
856
        distinguishedFactor_=0;
857
        distinguishedRate_=0;
858
        distinguishedStep_=0;
859
*/
860
0
    }
861
862
    Real PathwiseVegasOuterAccountingEngine::singlePathValues(std::vector<Real>& values)
863
0
    {
864
865
0
        const std::vector<Real>& initialForwards_(pseudoRootStructure_->initialRates());
866
0
        currentForwards_ = initialForwards_;
867
        // clear accumulation variables
868
0
        for (Size i=0; i < numberProducts_; ++i)
869
0
        {
870
0
            numerairesHeld_[i]=0.0;
871
872
0
            for (Size j=0; j < numberCashFlowTimes_; ++j)
873
0
            {
874
0
                numberCashFlowsThisIndex_[i][j] =0;
875
876
0
                for (Size k=0; k <= numberRates_; ++k)
877
0
                    totalCashFlowsThisIndex_[i][j][k] =0.0;
878
0
            }
879
880
0
            for (Size l=0;  l< numberRates_; ++l)
881
0
                for (Size m=0; m <= numberSteps_; ++m)
882
0
                    V_[i][m][l] =0.0;
883
884
0
        }
885
886
887
888
0
        Real weight = evolver_->startNewPath();
889
0
        product_->reset();
890
891
0
        Size thisStep;
892
893
0
        bool done = false;
894
0
        do 
895
0
        {
896
0
            thisStep = evolver_->currentStep();
897
0
            Size storeStep = thisStep+1;
898
0
            weight *= evolver_->advanceStep();
899
900
0
            done = product_->nextTimeStep(evolver_->currentState(),
901
0
                numberCashFlowsThisStep_,
902
0
                cashFlowsGenerated_);
903
904
0
            lastForwards_ = currentForwards_;
905
0
            currentForwards_ =  evolver_->currentState().forwardRates();
906
907
0
            for (unsigned long i=0; i < numberRates_; ++i)
908
0
            {
909
0
                Real x=  evolver_->currentState().discountRatio(i+1,i);
910
0
                stepsDiscounts_[i+1] = x;
911
0
                StepsDiscountsSquared_[storeStep][i] = x*x;
912
913
0
                LIBORRatios_[storeStep][i] = currentForwards_[i]/lastForwards_[i];
914
0
                LIBORRates_[storeStep][i] = currentForwards_[i];
915
0
                Discounts_[storeStep][i+1] = evolver_->currentState().discountRatio(i+1,0);
916
0
            }
917
918
0
            jacobianComputers_[thisStep].getBumps(lastForwards_,
919
0
                                         stepsDiscounts_,
920
0
                                         currentForwards_,
921
0
                                         evolver_->browniansThisStep(),
922
0
                                         jacobiansThisPaths_[thisStep]);
923
924
//            gaussians_[thisStep] = evolver_->browniansThisStep();
925
926
927
928
            // for each product...
929
0
            for (Size i=0; i<numberProducts_; ++i)
930
0
            {
931
                // ...and each cash flow...
932
0
                for (Size j=0; j<numberCashFlowsThisStep_[i]; ++j)
933
0
                {
934
0
                    Size k = cashFlowsGenerated_[i][j].timeIndex;
935
0
                    ++numberCashFlowsThisIndex_[i][ k];
936
937
0
                    for (Size l=0; l <= numberRates_; ++l)
938
0
                        totalCashFlowsThisIndex_[i][k][l] += cashFlowsGenerated_[i][j].amount[l]*weight;
939
940
0
                }
941
0
            }
942
943
944
0
        } while (!done);
945
946
        // ok we've gathered cash-flows, still have to backwards computation
947
948
0
        Size factors = pseudoRootStructure_->numberOfFactors();
949
0
        const std::vector<Time>& taus= pseudoRootStructure_->evolution(). rateTaus();
950
951
0
        bool flowsFound = false;
952
953
0
        Integer finalStepDone = thisStep;
954
955
0
        for (Integer currentStep =  numberSteps_-1; currentStep >=0 ; --currentStep) // must be a signed type as we go negative
956
0
        {
957
0
            Integer stepToUse = std::min<Integer>(currentStep, finalStepDone)+1;
958
959
0
            for (Size k=0; k < cashFlowIndicesThisStep_[currentStep].size(); ++k)
960
0
            {
961
0
                Size cashFlowIndex =cashFlowIndicesThisStep_[currentStep][k];
962
963
                // first check to see if anything actually happened before spending time on computing stuff
964
0
                bool noFlows = true;
965
0
                for (Size l=0; l < numberProducts_ && noFlows; ++l)
966
0
                    noFlows = noFlows && (numberCashFlowsThisIndex_[l][cashFlowIndex] ==0);
967
968
0
                flowsFound = flowsFound || !noFlows;
969
970
0
                if (!noFlows)
971
0
                {
972
0
                    if (doDeflation_)
973
0
                        discounters_[cashFlowIndex].getFactors(LIBORRates_, Discounts_,stepToUse, deflatorAndDerivatives_); // get amount to discount cash flow by and amount to multiply its derivatives by
974
975
0
                    for (Size j=0; j < numberProducts_; ++j)
976
0
                    {
977
0
                        if (numberCashFlowsThisIndex_[j][cashFlowIndex] > 0)
978
0
                        {
979
0
                            Real deflatedCashFlow = totalCashFlowsThisIndex_[j][cashFlowIndex][0];
980
0
                            if (doDeflation_)
981
0
                                deflatedCashFlow *= deflatorAndDerivatives_[0];
982
                            //cashFlowsGenerated_[j][cashFlowIndex].amount[0]*deflatorAndDerivatives_[0];
983
0
                            numerairesHeld_[j] += deflatedCashFlow;
984
985
0
                            for (Size i=1; i <= numberRates_; ++i)
986
0
                            {
987
0
                                Real thisDerivative =  totalCashFlowsThisIndex_[j][cashFlowIndex][i];
988
0
                                if (doDeflation_)
989
0
                                {
990
0
                                    thisDerivative *= deflatorAndDerivatives_[0];
991
0
                                    thisDerivative +=  totalCashFlowsThisIndex_[j][cashFlowIndex][0]*deflatorAndDerivatives_[i];
992
0
                                    fullDerivatives_[i-1] = thisDerivative;
993
0
                                }
994
0
                                else
995
0
                                    fullDerivatives_[i-1] = thisDerivative;
996
997
0
                                V_[j][stepToUse][i-1] += thisDerivative; // zeroth row of V is t =0 not t_0
998
0
                            } // end of  for (Size i=1; i <= numberRates_; ++i)
999
0
                        } // end of (numberCashFlowsThisIndex_[j][cashFlowIndex] > 0)
1000
0
                    } // end of (Size j=0; j < numberProducts_; ++j)
1001
0
                } // end of  if (!noFlows)
1002
0
            }
1003
1004
            // need to do backwards updating
1005
0
            if (flowsFound)
1006
0
            {
1007
0
                Integer nextStepToUse  = std::min<Integer>(currentStep-1, finalStepDone);
1008
0
                Integer nextStepIndex = nextStepToUse+1;
1009
0
                if (nextStepIndex != stepToUse) // then we need to update V
1010
0
                {
1011
1012
0
                    const Matrix& thisPseudoRoot_= pseudoRootStructure_->pseudoRoot(currentStep);
1013
1014
0
                    for (Size i=0; i < numberProducts_; ++i)
1015
0
                    {
1016
                        // compute partials
1017
0
                        for (Size f=0; f < factors; ++f)
1018
0
                        {
1019
0
                            Real libor = LIBORRates_[stepToUse][numberRates_-1];
1020
0
                            Real V = V_[i][stepToUse][numberRates_-1];
1021
0
                            Real pseudo = thisPseudoRoot_[numberRates_-1][f];
1022
0
                            Real thisPartialTerm = libor*V*pseudo;
1023
0
                            partials_[f][numberRates_-1] = thisPartialTerm;
1024
1025
0
                            for (Integer r = numberRates_-2; r >=0 ; --r)
1026
0
                            {
1027
0
                                Real thisPartialTermr = LIBORRates_[stepToUse][r]*V_[i][stepToUse][r]*thisPseudoRoot_[r][f];
1028
1029
0
                                partials_[f][r] = partials_[f][r+1] + thisPartialTermr;
1030
1031
0
                            }
1032
0
                        } // end of (Size f=0; f < factors; ++f)
1033
1034
0
                        for (Size j=0; j < numberRates_; ++j)
1035
0
                        {
1036
0
                            Real nextV = V_[i][stepToUse][j] * LIBORRatios_[stepToUse][j];
1037
0
                            V_[i][nextStepIndex][j] = nextV;
1038
1039
0
                            Real summandTerm = 0.0;
1040
0
                            for (Size f=0; f < factors; ++f)
1041
0
                                summandTerm += thisPseudoRoot_[j][f]*partials_[f][j];
1042
1043
0
                            summandTerm *= taus[j]*StepsDiscountsSquared_[stepToUse][j];
1044
1045
0
                            V_[i][nextStepIndex][j] += summandTerm;
1046
1047
0
                        } //end of  for (Size j=0; j < numberRates_; ++j)
1048
1049
0
                    } // end of (Size i=0; i < numberProducts_; ++i)
1050
1051
0
                } //  end of   if (nextStepIndex != stepToUse)
1052
1053
0
            } // end of  if (flowsFound)
1054
1055
0
        } // end of  for (Integer currentStep =  numberSteps_-1; currentStep >=0 ; --currentStep)
1056
1057
1058
        // all V matrices computed we now compute the elementary vegas for this path 
1059
1060
0
        for (Size i=0; i < numberProducts_; ++i)
1061
0
        {
1062
0
                for (Size j=0; j < numberSteps_; ++j)
1063
0
                {
1064
0
                    Size nextIndex = j+1;
1065
1066
                    // we know V, we need to pair against the senstivity of the rate to the elementary vega
1067
                    // note the simplification here arising from the fact that the elementary vega affects the evolution on precisely one step
1068
1069
0
                    for (Size k=0; k < numberRates_; ++k)
1070
0
                        for (Size f=0; f < factors_; ++f)
1071
0
                        {
1072
0
                                Real sensitivity =0.0;
1073
1074
0
                                for (Size r=0; r < numberRates_; ++r)
1075
0
                                {
1076
0
                                        sensitivity += V_[i][nextIndex][r]*jacobiansThisPaths_[j][r][k][f];
1077
1078
0
                                  }
1079
/*
1080
                                  if (j ==distinguishedStep_ && k ==distinguishedRate_ &&f== distinguishedFactor_)
1081
                                      std::cout << sensitivity << "," <<  jacobiansThisPaths_[j][j][k][f] << "," << gaussians_[j][f] << "," << V_[i][nextIndex][j] << "," << LIBORRates_[nextIndex][j] << "\n";
1082
  */                
1083
1084
1085
0
                                elementary_vegas_ThisPath_[i][j][k][f] = sensitivity;
1086
0
                        }
1087
0
                }
1088
0
        }
1089
1090
1091
        // write answer into values
1092
1093
0
        Size entriesPerProduct = 1+numberRates_+numberElementaryVegas_;
1094
1095
0
        for (Size i=0; i < numberProducts_; ++i)
1096
0
        {
1097
0
            values[i*entriesPerProduct] = numerairesHeld_[i]*initialNumeraireValue_;
1098
0
            for (Size j=0; j < numberRates_; ++j)
1099
0
                values[i*entriesPerProduct+1+j] = V_[i][0][j]*initialNumeraireValue_;
1100
1101
0
            for (Size k=0; k < numberSteps_; ++k)
1102
0
                for (Size l=0; l < numberRates_; ++l)
1103
0
                    for (Size m=0; m < factors_; ++m)
1104
0
                        values[i*entriesPerProduct + numberRates_ +1 + m+ l*factors_ + k*numberRates_*factors_] = elementary_vegas_ThisPath_[i][k][l][m]*initialNumeraireValue_;
1105
1106
0
        }
1107
1108
0
        return 1.0; // we have put the weight in already, this results in lower variance since weight changes along the path
1109
    
1110
0
}
1111
1112
    void PathwiseVegasOuterAccountingEngine::multiplePathValuesElementary(std::vector<Real>& means, std::vector<Real>& errors,
1113
        Size numberOfPaths)
1114
0
    {
1115
0
        Size numberOfElementaryVegas = numberRates_*numberSteps_*factors_;
1116
1117
0
        std::vector<Real> values(product_->numberOfProducts()*(1+numberRates_+numberOfElementaryVegas));
1118
0
        means.resize(values.size());
1119
0
        errors.resize(values.size());
1120
0
        std::vector<Real> sums(values.size(),0.0);
1121
0
        std::vector<Real> sumsqs(values.size(),0.0);
1122
1123
1124
1125
0
        for (Size i=0; i<numberOfPaths; ++i)
1126
0
        {
1127
0
          singlePathValues(values);
1128
          
1129
0
          for (Size j=0; j < values.size(); ++j)
1130
0
            {
1131
0
                sums[j] += values[j];
1132
0
                sumsqs[j] += values[j]*values[j];
1133
1134
0
            }
1135
0
        }
1136
1137
0
        for (Size j=0; j < values.size(); ++j)
1138
0
            {
1139
0
                means[j] = sums[j]/numberOfPaths;
1140
0
                Real meanSq = sumsqs[j]/numberOfPaths;
1141
0
                Real variance = meanSq - means[j]*means[j];
1142
0
                errors[j] = std::sqrt(variance/numberOfPaths);
1143
1144
0
            }
1145
0
    }
1146
1147
        void PathwiseVegasOuterAccountingEngine::multiplePathValues(std::vector<Real>& means, std::vector<Real>& errors,Size numberOfPaths)
1148
0
        {
1149
0
            std::vector<Real> allMeans;
1150
0
            std::vector<Real> allErrors;
1151
1152
0
            multiplePathValuesElementary(allMeans,allErrors,numberOfPaths);
1153
1154
0
            Size outDataPerProduct = 1+numberRates_+numberBumps_;
1155
0
            Size inDataPerProduct = 1+numberRates_+numberElementaryVegas_;
1156
1157
0
            means.resize((1+numberRates_+numberBumps_)*numberProducts_);
1158
0
            errors.resize((1+numberRates_+numberBumps_)*numberProducts_); // post linear combinations, errors are not meaningful so don't attempt to compute s.e.s for vegas
1159
1160
0
            for (Size p=0; p < numberProducts_; ++p)
1161
0
            {
1162
0
                for (Size i=0; i < 1 + numberRates_; ++i)
1163
0
                {
1164
0
                      means[i+p*outDataPerProduct] = allMeans[i+p*inDataPerProduct];
1165
0
                      errors[i+p*outDataPerProduct] = allErrors[i+p*inDataPerProduct];
1166
0
                }
1167
1168
0
               for (Size bump=0; bump<numberBumps_; ++bump)
1169
0
                {
1170
0
                    Real thisVega=0.0;
1171
1172
1173
0
                    for (Size t=0; t < numberSteps_; ++t)
1174
0
                        for (Size r=0; r < numberRates_; ++r)
1175
0
                            for (Size f=0; f < factors_; ++f)
1176
0
                                thisVega+= vegaBumps_[t][bump][r][f]*allMeans[p*inDataPerProduct+1+numberRates_+t*numberRates_*factors_+r*factors_+f];
1177
1178
1179
0
                    means[p*outDataPerProduct+1+numberRates_+bump] = thisVega;
1180
0
               }
1181
                    
1182
0
            }
1183
1184
0
        } // end of method
1185
1186
} // end of namespace
1187
1188
1189
1190
1191
1192
1193
1194
1195