""" File: 03antithetic.py

 Author: Michel Bierlaire, EPFL
 Date: Wed Dec 11 17:00:05 2019

Calculation of a simple integral using Monte-Carlo integration with
Halton draws base 13. It illustrates how to use draws that
are not directly available in Biogeme.


"""

import numpy as np
import pandas as pd
import biogeme.database as db
import biogeme.biogeme as bio
import biogeme.draws as draws
from biogeme.expressions import exp, bioDraws, MonteCarlo


# We create a fake database with one entry, as it is required to store the draws
pandas = pd.DataFrame()
pandas['FakeColumn'] = [1.0]
database = db.Database("fakeDatabase",pandas)

# The user can define new draws. For example, Halton draws with base 13, skipping the first 10 draws.

def halton13_anti(sampleSize,numberOfDraws):
    R = int(numberOfDraws / 2)
    d = draws.getHaltonDraws(sampleSize,
                                 R,
                                 base=13,
                                 skip=10)
    return np.concatenate((d,1-d),axis=1)

mydraws = {'HALTON13_ANTI':(halton13_anti,'Antithetic Halton draws, base 13, skipping 10')}
database.setRandomNumberGenerators(mydraws)


integrand = exp(bioDraws('U','UNIFORM_ANTI'))
simulatedI = MonteCarlo(integrand)

integrand_halton13 = exp(bioDraws('U_halton13','HALTON13_ANTI'))
simulatedI_halton13 = MonteCarlo(integrand_halton13)

integrand_mlhs = exp(bioDraws('U_mlhs','UNIFORM_MLHS_ANTI'))
simulatedI_mlhs = MonteCarlo(integrand_mlhs)

trueI = exp(1.0) - 1.0 

R = 20000

error = simulatedI - trueI

error_halton13 = simulatedI_halton13 - trueI

error_mlhs = simulatedI_mlhs - trueI


simulate = {'Analytical Integral': trueI,
            'Simulated Integral': simulatedI,
            'Error             ': error,
            'Simulated Integral (Halton13)': simulatedI_halton13,
            'Error (Halton13)             ': error_halton13,
            'Simulated Integral (MLHS)': simulatedI_mlhs,
            'Error (MLHS)             ': error_mlhs}



biogeme  = bio.BIOGEME(database,simulate,numberOfDraws=R)
biogeme.modelName = "03antithetic"
results = biogeme.simulate()
print(f"Analytical integral:{results.iloc[0]['Analytical Integral']:.6f}")
print(f"\t\tUniform (Anti)\t\tHalton13 (Anti)\t\tMLHS (Anti)")
print(f"Simulated\t{results.iloc[0]['Simulated Integral']:.6g}\t{results.iloc[0]['Simulated Integral (Halton13)']:.6g}\t{results.iloc[0]['Simulated Integral (MLHS)']:.6g}")
print(f"Error\t\t{results.iloc[0]['Error             ']:.6g}\t{results.iloc[0]['Error (Halton13)             ']:.6g}\t{results.iloc[0]['Error (MLHS)             ']:.6g}")


