/// <summary>
        /// Simulate state variables needed for the pricing step of the exposure calculation. State variables
        /// are not independent variables of the adjoint algorithmic differentation, and therefore can be simulated in advance.
        /// </summary>
        /// <param name="model"></param>
        /// <param name="allZeroRatesT0"></param>
        /// <param name="timePoints"></param>
        /// <param name="graph"></param>
        /// <param name="pricers"></param>
        public void SimulateModel(out LinearGaussianModel model, out IEnumerable<NArray> allZeroRatesT0,
            out TimePoint[] timePoints, out SimulationGraph graph, out List<IPricer> pricers)
        {
            var testDate = new DateTime(2015, 12, 1);

            graph = new SimulationGraph(StorageLocation.Host, testDate);
            var context = graph.Context;
            context.Settings.SimulationCount = 5000;

            var fixedLeg = Enumerable.Range(0, 40).Select(i => new FixedCashflowDeal()
            {
                Notional = -1e6,
                Rate = 0.002,
                Currency = Currency.EUR,
                StartDate = testDate.AddMonths(3 * i),
                EndDate = testDate.AddMonths(3 * (i + 1))
            });

            var floatingLeg = Enumerable.Range(0, 40).Select(i => new FloatingCashflowDeal()
            {
                Notional = 1e6,
                Currency = Currency.EUR,
                StartDate = testDate.AddMonths(3 * i),
                EndDate = testDate.AddMonths(3 * (i + 1))
            });

            var correlationMatrix = context.Factory.CreateNArray(new double[,] {
                { 1.0,      -0.92,   0.5},
                { -0.92,     1.0,    -0.8},
                { 0.5,    -0.8,   1.0}
            });

            var identifiers = new string[] { "IR_DiscountFactor_EUR_Factor0", "IR_DiscountFactor_EUR_Factor1",
                "IR_DiscountFactor_EUR_Factor2"};
            CorrelationHelper.AddMultivariateModelWeightsProvider(context, identifiers, correlationMatrix);

            pricers = fixedLeg.Select(d => new FixedCashflowPricer(d) as IPricer)
                .Concat(floatingLeg.Select(d => new FloatingCashflowPricer(d) as IPricer)).ToList();
            foreach (var pricer in pricers) pricer.Register(graph);

            var testVariates0 = graph.RegisterFactor<NormalVariates>("IR_DiscountFactor_EUR_Factor0");
            var testVariates1 = graph.RegisterFactor<NormalVariates>("IR_DiscountFactor_EUR_Factor1");
            var testVariates2 = graph.RegisterFactor<NormalVariates>("IR_DiscountFactor_EUR_Factor2");
            var testFactor = graph.RegisterModel<MeanRevertingNormalPathModel>("IR_DiscountFactor_EUR_Factor0");
            model = graph.RegisterModel<LinearGaussianModel>("EUR");
            var numeraire = graph.RegisterModel<NumeraireModel>("EUR");

            model.Factors[0].Sigma = 0.02; model.Factors[0].Lambda = 0.05;
            model.Factors[1].Sigma = 0.03; model.Factors[1].Lambda = 0.2;
            model.Factors[2].Sigma = 0.01; model.Factors[2].Lambda = 1.0;

            var years = new double[] { 0, 1/365.35, 7/365.25, 14/365.25, 1/12, 2/12, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 };
            var zeroRatesInPercent = new double[] { -0.3, -0.3, -0.29, -0.29, -0.3, -0.31, -0.31, -0.32, -0.32, -0.33, -0.34, -0.35, -0.35, -0.33, -0.28, -0.2, -0.11, -0.01, 0.1, 0.2, 0.37, 0.56, 0.71, 0.75, 0.76, 0.75, 0.75, 0.72, 0.7, 0.67, 0.64 };

            model.ZeroRatesT0 = new Curve(years.Zip(zeroRatesInPercent,
                (y, r) => new DataPoint(
                    testDate.AddDays(y * 365.25),
                    NArray.CreateScalar(r / 100)
                    )).ToArray());

            var runner = graph.ToSimulationRunner();
            runner.Prepare();
            runner.Simulate();

            timePoints = graph.Context.Settings.SimulationTimePoints;
            allZeroRatesT0 = model.ZeroRatesT0.Data.Select(d => d.Value);
        }
        /// <summary>
        /// Test building blocks of interest rate swap exposure calculation.
        /// </summary>
        public void TestBasics(LinearGaussianModel model, IEnumerable<NArray> allZeroRatesT0,
            TimePoint[] timePoints, SimulationGraph graph)
        {
            bool testModel = true;
            if (testModel)
            {
                var check1 = model[1, timePoints[1].DateTime.AddDays(182)].First();
                Assert.IsTrue(TestHelpers.AgreesAbsolute(check1, 1.0015314301020275));

                var check2 = model[1, timePoints[1].DateTime.AddDays(91)].First();
                Assert.IsTrue(TestHelpers.AgreesAbsolute(check2, 1.0007451895710209));

                var check3 = model.ForwardRate(1,
                    timePoints[1].DateTime.AddDays(91), timePoints[1].DateTime.AddDays(182)).First();
                Assert.IsTrue(TestHelpers.AgreesAbsolute(check3, -0.0031509366920208916));
            }

            bool testProfile = true;
            if (testProfile)
            {
                var testVariates0 = graph.RegisterFactor<NormalVariates>("IR_DiscountFactor_EUR_Factor0");
                var testVariates1 = graph.RegisterFactor<NormalVariates>("IR_DiscountFactor_EUR_Factor1");
                var testVariates2 = graph.RegisterFactor<NormalVariates>("IR_DiscountFactor_EUR_Factor2");

                var check = Descriptive.Correlation(testVariates1.Value, testVariates2.Value);

                // check 3M rolling tenor
                var percentiles = new double[] { 1, 10, 50, 90, 99 };
                var measures = new List<IList<double>>();
                for (int i = 0; i < timePoints.Length; ++i)
                {
                    var tenorMonths = 3;
                    var tenorYears = tenorMonths / 12.0;
                    var df = model[i, timePoints[i].DateTime.AddMonths(3)];
                    var zeroRate = -NMath.Log(df) / tenorYears;
                    var values = NMath.Percentiles(zeroRate, percentiles).ToList();
                    measures.Add(values);
                }

                var times = timePoints.Select(p => p.YearsFromBaseDate).ToArray();
                var profile10 = measures.Select(p => p[1]).ToArray();
                var profile90 = measures.Select(p => p[3]).ToArray();
            }

            bool testForwardRate = true;
            if (testForwardRate)
            {
                // check 3M rolling tenor
                var percentiles = new double[] { 1, 10, 50, 90, 99 };
                var measures = new List<IList<double>>();
                for (int i = 0; i < timePoints.Length; ++i)
                {
                    var tenorMonths = 3;
                    var tenorYears = tenorMonths / 12.0;
                    var forwardRate = model.ForwardRate(i, timePoints[i].DateTime.AddMonths(3), timePoints[i].DateTime.AddMonths(6));
                    var values = NMath.Percentiles(forwardRate, percentiles).ToList();
                    measures.Add(values);
                }

                var times = timePoints.Select(p => p.YearsFromBaseDate).ToArray();
                var profile10 = measures.Select(p => p[1]).ToArray();
                var profile90 = measures.Select(p => p[3]).ToArray();
            }

            bool testAAD = true;
            if (testAAD)
            {
                // just get the result
                var result0 = NArray.Evaluate(() =>
                {
                    var df = model[10, timePoints[10].DateTime.AddMonths(3)];
                    return df;
                });

                // get the result and single derivative
                var result1 = NArray.Evaluate(() =>
                {
                    var df = model[10, timePoints[10].DateTime.AddMonths(3)];
                    return df;
                }, model.ZeroRatesT0[6].Value);

                var log = new StringBuilder();

                // get the result and all derivatives (and log output)
                var result2 = NArray.Evaluate(() =>
                {
                    var df = model[10, timePoints[10].DateTime.AddMonths(3)];
                    return df;
                }, log, allZeroRatesT0.ToArray());

                // now forward rate and all derivatives
                var result3 = NArray.Evaluate(() =>
                {
                    var df = model.ForwardRate(10, timePoints[10].DateTime.AddMonths(3), timePoints[10].DateTime.AddMonths(6));
                    return df;
                }, allZeroRatesT0.ToArray());

                var unbumped0 = model[10, timePoints[10].DateTime.AddMonths(3)];

                var expected0 = unbumped0.DebugDataView.ToArray();
                var unbumped3 = model.ForwardRate(10, timePoints[10].DateTime.AddMonths(3), timePoints[10].DateTime.AddMonths(6));
                var expected3 = unbumped3.DebugDataView.ToArray();

                var obtained0 = result1[0].DebugDataView.ToArray();
                var obtained1 = result1[0].DebugDataView.ToArray();
                var obtained2 = result2[0].DebugDataView.ToArray();
                var obtained3 = result3[0].DebugDataView.ToArray();

                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected0, obtained0));
                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected0, obtained1));
                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected0, obtained2));
                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected3, obtained3));

                var logCheck = log.ToString();

                var obtained_deriv1 = result1[1].DebugDataView.ToArray();
                var obtained_deriv2 = result2[7].DebugDataView.ToArray();
                var obtained_deriv3 = result3[7].DebugDataView.ToArray();

                model.ZeroRatesT0.Data[6] = new DataPoint(model.ZeroRatesT0.Data[6].Time, model.ZeroRatesT0.Data[6].Value + 1e-6);

                var bumped0 = model[10, timePoints[10].DateTime.AddMonths(3)];
                var bumped3 = model.ForwardRate(10, timePoints[10].DateTime.AddMonths(3), timePoints[10].DateTime.AddMonths(6));

                var expected_deriv1 = ((bumped0 - unbumped0) / 1e-6)
                    .DebugDataView.ToArray();

                var expected_deriv3 = ((bumped3 - unbumped3) / 1e-6)
                    .DebugDataView.ToArray();

                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected_deriv1, obtained_deriv1, 1e-5));
                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected_deriv1, obtained_deriv2, 1e-5));
                Assert.IsTrue(TestHelpers.AgreesAbsolute(expected_deriv3, obtained_deriv3, 1e-5));
            }

            bool checkTimings = true;
            if (checkTimings)
            {
                // check timings
                Console.WriteLine("Immediate execution");
                VectorAccelerator.Tests.TestHelpers.Timeit(() =>
                {
                    var df = model[10, timePoints[10].DateTime.AddMonths(3)];
                }, 10, 10);

                Console.WriteLine(); Console.WriteLine("Deferred execution no derivatives");
                VectorAccelerator.Tests.TestHelpers.Timeit(() =>
                {
                    NArray.Evaluate(() =>
                    {
                        var df = model[10, timePoints[10].DateTime.AddMonths(3)];
                        return df;
                    });
                }, 10, 10);

                Console.WriteLine(); Console.WriteLine("Deferred execution all derivatives");
                VectorAccelerator.Tests.TestHelpers.Timeit(() =>
                    {
                        NArray.Evaluate(() =>
                        {
                            var df = model[10, timePoints[10].DateTime.AddMonths(3)];
                            return df;
                        }, allZeroRatesT0.ToArray());
                    }, 10, 10);
            }
        }