<jupyter_start><jupyter_text><NAME>  Instalaciones necesarias - Para que el código a continuación funcione, es necesario tener instalado `dask`.<jupyter_code>#%pip install --user dask   
#%pip install --user dask distributed<jupyter_output><empty_output><jupyter_text>- Para habilitar el dashboard, debemos installar `bokeh` :<jupyter_code>#%pip install --user bokeh
#%%bash 
#sudo apt-get install -y graphviz
#%pip install --user -q graphviz
#%pip install --user numpy 
#import IPython
#app=IPython.Application.instance()
#app.kernel.do_shutdown(True) #restarting kernell
#from IPython.display import Image<jupyter_output><empty_output><jupyter_text>Montecarlo 1D Empezamos con el algoritmo de monte carlo en 1 dimensiónIntegramos la función:$$f(x)=\int_0^1 \frac{4}{1+x^2}=\pi$$   Método secuencial:<jupyter_code>import numpy as np
import math
import time
# definimos la funcion del error relativo
err_rel=lambda ap,ob: math.fabs(ap-ob)/math.fabs(ob)
# número de puntos a utilizar
density_p=10**7

# función a integrar
f=lambda x: 4/(1+x**2)

# resultado conocido de la integral (valor objetivo)
obj=math.pi

# intervalo
a=0
b=1
# dimensiones
dims=1
%%time
#approximación mediante método montecarlo
x_p = np.random.uniform(0,1,density_p) #obtiene tantos números aleatorios entre 0 y 1 como density_p.
vol = b-a # esta es la longitud del intervalo de integración.
approx = vol*np.mean(f(x_p))

print("---------------------")
print("El valor calculado de la integral es: ",approx)
print("El error relativo: {:0.4e}".format(err_rel(approx,obj)))
print("---------------------")<jupyter_output>---------------------
El valor calculado de la integral es:  3.141549100747702
El error relativo: 1.3863e-05
---------------------
CPU times: user 286 ms, sys: 91.2 ms, total: 377 ms
Wall time: 199 ms<jupyter_text>A continuación, simplemente se vuelve función el proceso anterior para poder evaluar de manera eficiente varias veces el computo de dicha función.<jupyter_code>def montecarlo_1D(density_p, f, a=0, b=1):
    '''
    Esta función aproxima la integral en el intervalo [a,b] por el método de montecarlo en 1 dimensión para la función
    f(x).
    _______
    Argumentos:
        - density_p (float): Densisdad para determinar cuantos puntos aleatorios se generan en cada dimensión a partir de una
        distribución uniforme.
        - f (lambda): función sobre la cual se va a integrar
        - a: punto inferior del intervalo sobre el que se va a integrar
        - b: punto inferisuperior del intervalo sobre el que se va a integrar
    Salidas:
        - aprox: aproximación de la integral por Montecarlo
    '''
    x_p = np.random.uniform(0,1,density_p) 
    vol = b-a
    aprox = vol*np.mean(f(x_p))
    
    return aprox
start_time = time.time()
aprox = montecarlo_1D(density_p=10**7, f=lambda x: 4/(1+x**2), a=0, b=1)
end_time = time.time()
secs = end_time-start_time

# lambda statement para calcualr el error relativo
err_rel=lambda ap,ob: math.fabs(ap-ob)/math.fabs(ob)
# valor de la integral
obj=math.pi

print("Evalución de integral de montecarlo de forma secuencial:")
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print(f"El tiempo de ejecución: {secs} segundos")
print("---------------------")<jupyter_output>Evalución de integral de montecarlo de forma secuencial:
---------------------
El valor calculado de la integral es:  3.1415762575615984
El error relativo: 5.2190e-06
El tiempo de ejecución: 0.17838501930236816 segundos
---------------------<jupyter_text>Utilizando `time`, el tiempo de ejecución gue de 3.14 segundos. Ahora se evaluará de nuevo el algoritmo, utilizando el `magic command`, `timeit`, el cual permite calcular el tiempo de ejecución de un programa en python tras ser ejecutado en un ciclo por `-n` veces, repitiendo este procedimiento `-r` veces. El flag `-o` se utiliza para tener una salida de tipo timeit que pueda ser almacenada en una variable, en este caso, "montecarlo_secuencial_timeit".<jupyter_code>montecarlo_secuencial_timeit = %timeit -n 5 -r 10 -o montecarlo_1D(density_p=10**7, f=lambda x: 4/(1+x**2), a=0, b=1)<jupyter_output>146 ms ± 12.9 ms per loop (mean ± std. dev. of 10 runs, 5 loops each)<jupyter_text>Los anteriores resultados en términos de tiempo son consistentes a los ejercicios realizados incialmente para le evaluación de desempeño del programa generado secuencialmnte.  Paralelización de MC en Dask Para paralelizar estos resultados lo que pienso que tenemos que hacer es dividir las tareas en n subintervalos. La porpuesta es la siguiente:  1. Dividir el dominio de la función en $p$ partes iguales. $p$ será el número de cpus disponibles en la máquina en que se corre la tarea.2. Dividir el total de puntos a utilizar equitativamente entre $p$.3. Resolver el método para cada intervalo.4. Sumar los resultados de cada intervalo. **Nota:** Para el caso de más dimensiones solamente se paraleliza una dimensión pues al hacerlo con todas se estaría dividiendo el espacio en $p_{dim}$ subespacios.<jupyter_code>import multiprocessing
import time
from dask.distributed import Client
client = Client()
client<jupyter_output><empty_output><jupyter_text>Si se quiere ser más explícito con el uso de los recursos computacionales:<jupyter_code>from dask.distributed import Client, progress
client = Client(processes=False, threads_per_worker=4,
                n_workers=1, memory_limit='6GB')
client
# paso 1. Dividir el dominio en partes iguales
p = multiprocessing.cpu_count() # cpus disponibles
n_subint = int(density_p/p) # número de puntos o nodos en cada core o cpu
def construye_subintervalos1D(ids,a,b,p,dims):
    """
    Función que construye "p" subintervalos, delimitados entre a y b. 
    El número de subintervalos dependerá del número de cores empleados. 
    Este ejercicio fue ejecutado en una computadoracon 16 cores, por lo 
    tanto, se crean 16 subintervalos.
    
    Argumentos:
    ----------
    * ids: Identificador del core dónde se está corriendo el task. 
    Este ejercicio fue ejecutado en una computadora con 16 cores, por 
    ello los posibles valores de "ids" son números enteros entre 0 y 15.
    * a, b (float): Intervalo de integración [a, b]
    * p (int) : Número de cores o cpus disponibles
    * dims: Dimensión de la integral.
    
    Salidas:
    -------
    * (begin, end): Subintervalos de integración.
    
    Ejemplo:
    -------
    construye_subintervalos(ids=0,a,b,p,dims)
    return:
    (0.0, 0.0625)

    """
    tamano_int = (b-a)/p #tamaño de cada sub intervalo.
    begin = a + ids*tamano_int #construyen los subintervalo
    end = begin+tamano_int
    return (begin,end)

    
def evalua_subintervalos1D(intervalo,f,n_subint,dims):
    """
    Función que evalúa la integral en cada uno los p subintervalos \\
    previamente construidos con la función "construye_subintervalos"
    
    Argumentos:
    ----------
    * intervalo: Intervalo de la integral sobre la cual se va aproximar \\
                 la integral
    * f: Función que se está aproximando
    * n_subint: Número de puntos o nodos en cada core o cpu
    * dims: Dimensiones de la integral
    
    Salidas:
    --------
    + vol*np.mean(f(x_p)): Approximación a la integral
    
    """   
    x_p=np.random.uniform(intervalo[0],intervalo[1],n_subint) #draw samples from a uniform distribution
    vol=intervalo[1]-intervalo[0]
    return vol*np.mean(f(x_p)) #calcula la integral para una cada regió: subintervalo.
%%time

# submitting p function calls
futures_intervalos = client.map(construye_subintervalos1D,range(p),
                                                **{'a':a,
                                                   'b':b,
                                                   'p':p,
                                                   'dims':dims})

futures_evaluando=client.map(evalua_subintervalos1D, futures_intervalos,
                                             **{'f':f,
                                                'n_subint':n_subint,
                                                'dims':dims})

results=client.gather(futures_evaluando) # los resultados viven en varios workers 
aprox=sum(results) #sumamos los resultados de todos los workers

print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print("---------------------")<jupyter_output>---------------------
El valor calculado de la integral es:  3.14160007267131
El error relativo: 2.3616e-06
---------------------
CPU times: user 192 ms, sys: 25.8 ms, total: 218 ms
Wall time: 139 ms<jupyter_text>- Utilizando una computadora de 16 núcleos (y por lo tanto 16 intervalos)con 8 GB de ram:Para el método en secuencial obtenemos un error relativo de $3.6251x10^{-6}$ y un tiempo de ejecución de $2.38$ seg y para el método en paralelo obtenemos un error relativo de $6.0184x10^{-7}$ en $110$ ms.- Utilizando una computadora de 8 núcleos (y por lo tanto 8 intervalos)con 4 GB de ram:se obtiene un error relativo de $1.8433x10^{-7}$ y se tarda un tiempo de ejecución de 428 ms. Con el fin de visualizar el tiempo de ejecución de las tareas paralelizadas, se presenta a continuación la gráfica generada en `dask` por medio de `bokeh`:![dask_tasKstream](imgs/mc_1dim_dask_taskStream.png)A pesar de que los colores sean casi indistinguibles entre las dos tareas, es posible diferenciarlas por medio de la magnitud de sus barras. El bloque "construye_subintervalos" es la tarea más rápida,dado que solo implica la construcción de subintervalos en función del número de cores empledos en la tarea. La tarea "evalua_subintervalos" es la que mayor tiempo de computo lleva, en la medida que implica el cálculo de un área para cada subintervalor, lo que implica mayor complejidad.  **Comparación de algunos resultados de computaciones**<jupyter_code>import dask
%time dask.compute(*results)
futures = dask.persist(*results)
futures
client.cluster.scale(10)
client.close()<jupyter_output><empty_output><jupyter_text>Monte carlo en n-dimensionesSe hace la generalización en que solamente se divide la primera dimensión y se paraleliza sobre esa dimensión usando las otras diemnsiones completas.  Los intervalos sobre los que se trabaja son intervalos divididos en $p$ partes iguales para la primera dimensión y el intervalo de intergración completo para cada una de las siguientes dimensiones. **Definición de funciones** > Para ejecución de paralelización de Monte Carlo<jupyter_code>#funciones para multiples dimensiones

def construye_subintervalos(ids,a,b,p,dims):
    """
    Función que construye "p" subintervalos, delimitados entre a y b para la primera dimensión.    
    En las demás dimensiones integra sobre el intervalo completo.
    
    Argumentos:
    ----------
    * ids: Identificador del core dónde se está corriendo el task. 
    * a (float): Vector con el límite inferior de cada intervalo de integración [ax1,ax2,...,axn] para cada diemnsión. Tiene dims entradas.
    * b (float): Vector con el límite superior de cada intervalo de integración [bx1,bx2,...,bxn] para cada diemnsión. Tiene dims entradas.
    * p (int) : Número de cores o cpus disponibles
    * dims: Dimensiónes de la integral.
    
    Salidas:
    -------
    * intervalos : lista de pares de intervalos de integración ordenados por dimensión
    
    """
    if(dims==1):
        tamano_int=(b-a)/p
        begin= a + ids*tamano_int
        end=begin+tamano_int
        return (begin,end)

    else:
        tamano_int=(b-a)/p

        #aux solamente cambia la primera dimension
        aux=np.identity(dims)
        aux=aux[0,:]
        aux=aux*ids*tamano_int
        begin= a + aux
        
        aux=np.identity(dims)
        aux=aux[0,:]
        aux=aux*tamano_int
        end=begin+aux
        
        intervalos=[]
        for i in range(dims):
            if(i==0):
                intervalos=np.append(intervalos,[begin[i],end[i]])
            else:
                intervalos=np.append(intervalos,[a[i],b[i]])
                
        return intervalos
def evalua_subintervalos(intervalo,f,n_subint,dims):
    """
    Función que evalúa la integral en cada uno los p subintervalos \\
    previamente construidos con la función "construye_subintervalos"
    
    Argumentos:
    ----------
    * intervalo: Intervalo de la integral sobre la cual se va aproximar \\
                 la integral
    * f: Función que se está aproximando
    * n_subint: Número de puntos o nodos en cada core o cpu
    * dims: Dimensiones de la integral
    
    Salidas:
    --------
    + vol*np.mean(f(x)): Approximación a la integral
    
    """   

    x=[]
    vol=1
    for i in range(dims):
        x=np.append(x,np.random.uniform(intervalo[2*i],intervalo[2*i+1],n_subint))
        vol=vol*(intervalo[2*i+1]-intervalo[2*i])
    
    x=np.reshape(x,(n_subint,dims), order='F')
    return np.mean(f(x))*vol<jupyter_output><empty_output><jupyter_text>> Para aproximación de Montecarlo Secuencial<jupyter_code>def aprox_montecarlo(density_p, f, dims, x0, x1, y0, y1, z0=1, z1=1):
    '''
    Función que computa aproximación de montecarlo hasta en 3 dimensiones para la función n-variada en los intervalos
    definidos.
    _______
    Argumentos:
        - density_p (float): Densisdad para determinar cuantos puntos aleatorios se generan en cada dimensión a partir de una
        distribución uniforme.
        - f (lambda): función sobre la cual se va a integrar
        - dims (int): número de dimensiones sobre las que se va a integrar
    Salidas:
        - aprox: aproximación de la integral por Montecarlo
    '''
    if dims == 3:
        x_p=np.random.uniform(x0,x1,density_p)
        y_p=np.random.uniform(y0,y1,density_p)
        z_p=np.random.uniform(z0,z1,density_p)
        vol=(x1-x0)*(y1-y0)*(z1-z0)
        approx=vol*np.mean(f(x_p,y_p,z_p))
        
    elif dims == 2:
        x_p=np.random.uniform(x0,x1,density_p)
        y_p=np.random.uniform(y0,y1,density_p)
        
        vol=(x1-x0)*(y1-y0)
        approx=vol*np.mean(f(x_p,y_p)) 
        
    return approx<jupyter_output><empty_output><jupyter_text>Ejemplo 1$$\int_0^2 \int_{-1}^1 \int_0^1 (2x+3y+z)dzdydx = 10$$ Primero se realiza una aplicación del método sin utilizar computo en paralelo, para efectos de comparación. **Secuencial** Ahora evaluamos el rendimiento de esta función en términos de tiempo.<jupyter_code>start_time = time.time()
aprox = aprox_montecarlo(density_p=10**7, f=lambda x,y,z: 2*x+3*y+z, dims=3, x0=0, x1=2, y0=-1, y1=1, z0=0, z1=1)
end_time = time.time()
secs = end_time-start_time

# valor de la función objetivo
obj=10
# lambda statement para calcualr el error relativo
err_rel=lambda ap,ob: math.fabs(ap-ob)/math.fabs(ob)

print("Evaluación de integral de montecarlo de forma secuencial:")
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,10)))
print(f"El tiempo de ejecución: {secs} segundos")
print("---------------------")<jupyter_output>Evaluación de integral de montecarlo de forma secuencial:
---------------------
El valor calculado de la integral es:  9.995002888188685
El error relativo: 4.9971e-04
El tiempo de ejecución: 0.34130215644836426 segundos
---------------------<jupyter_text>evaluación utilizando `%%time`<jupyter_code>#%%time
#error_est = math.sqrt(sum((f(x_p,y_p,z_p)-aprox)**2)/density_p)
#print("Evaluación de integral de montacarlo de forma secuencial:")
#print("---------------------")
#print("El valor calculado de la integral es: ",aprox)
#print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
#print(f"El tiempo de ejecución: {secs} segundos")
#print("* Intervalo par el área: ")
#print((approx-vol*error_est, approx+vol*error_est))
#print("---------------------")<jupyter_output><empty_output><jupyter_text>Como se observa anteriormente, el tiempo de usuario de CPU, _CPU times_, (el tiempo que el procesador trabajó en el programa ejectuado) fue de 455 ms, mientras que el tiempo de _sys_ (lo que el procesador tarda en conectarse a funciones necesarias para que el programa se ejecute) fue de 57.8 ms. Por último, el _wall time_ calculado (tiempo que tarda el programa en ejecutarse de inicio a fin), fue de 513 ms. __Pregunta para Erick:__ ¿por qué es posible que wall time sea menor a total time (sys + CPU?Ahora se evaluará de nuevo el algoritmo, utilizando el `magic command`, `timeit`, el cual permite calcular el tiempo de ejecución de un programa en `python` tras ser ejecutado en un ciclo por `-n` veces, repitiendo este procedimiento `-r` veces. El _flag_ `-o` se utiliza para tener una salida de tipo `timeit` que pueda ser almacenada en una variable, en este caso, "montecarlo_secuencial_timeit".<jupyter_code>montecarlo_secuencial_timeit = %timeit -n 5 -r 10 -o aprox<jupyter_output>43.9 ns ± 12.6 ns per loop (mean ± std. dev. of 10 runs, 5 loops each)<jupyter_text>Luego de realizar el ejercicio anterior para obtener el rendimiento promedio del computo de nuestro programa, se obtiene que este se tarda aproximadamente 382 ms por loop, con una desviación de 23 ms. El resultado de realizar el ejercicio varias veces es consistente con el reportado anteriormente. Ahora, se ejectua el ejercicio utilizando su **implementación en paralelo**:<jupyter_code>from dask.distributed import Client, progress
client = Client()
client
# numero de procesadores
p = multiprocessing.cpu_count()
print(f"Número de procesadores: {p}")
# numero de puntos a utlizar
density_p = 10**7
n_subint = int(density_p/p)


# funcion a integrar
# cada variable se toma como la columna de una matriz
f = lambda x: 2*x[:,0]+3*x[:,1]+x[:,2]

#reultado conocido de la integral (valor objetivo)
obj=10

# intervalo
# se debe de poner (x1_a,x2_a,...,xn_a) para los mínimos
a = np.array([0,-1,0])
# se debe de poner (x1_b,x2_b,...,xn_b) para los máxios
b = np.array([2,1,1])

# volumen
vol = np.prod(b-a)

#dimensiones
dims=len(a)
%%time
#llamado para multiples dimensiones
futures_intervalos = client.map(construye_subintervalos, range(p),
                                            **{'a':a,
                                               'b':b,
                                               'p':p,
                                               'dims':dims})

futures_evaluando=client.map(evalua_subintervalos, futures_intervalos,
                                            **{'f':f,
                                               'n_subint':n_subint,
                                               'dims':dims})

results=client.gather(futures_evaluando)
aprox=sum(results)

print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print("---------------------")<jupyter_output>---------------------
El valor calculado de la integral es:  10.00337685448729
El error relativo: 3.3769e-04
---------------------
CPU times: user 128 ms, sys: 12.7 ms, total: 141 ms
Wall time: 285 ms<jupyter_text>Como se puede observar, en este primer ejercicio de medición, el tiempo tanto en _CPU times_, como en _sys_ y _total_es menor en la implementación en paralelo. El resultado también es consistente con las mediciones del tiempo de inicia fin de ejecución del programa entero, correspondiente a _walltime_.Con el fin de visualizar el tiempo de ejecución de las tareas paralelizadas, se presenta a continuación la gráfica generada en `dask` por medio de `bokeh`:![dask_tasKstream](imgs/mc_ndim_dask_taskStream.png)Como se puede observar, al igual que en ejercicio de montecarlo para una dimensión, el bloque `construye_subintervalos` es la tarea que menos tiempo tarda en ejecutarse, lo cual podría ser anticipable, en la medida que solo implica la construcción de subintervalos en función del número de _cores_ empledos en la tarea. Por otro lado, la tarea `evalua_subintervalos` es la que mayor tiempo de computo lleva, en la medida que implica el cálculo de un área para cada subintervalor, lo que implica mayor complejidad. Este comentario aplica para los ejemplos subsecuentes.  Ejemplo 2 $$\int_D \int \exp{(x^2+y^2)}dydx = \pi(e^9-1)$$ donde: $$D=\{(x,y) \in \mathbb{R}^2 | x^2+y^2 \leq 9\}$$ **Secuencial**<jupyter_code>def f(x_p,y_p): 
    r2=y_p**2+x_p**2
    res=np.exp(r2)
    res[r2>9]=0
    return res
x0=-3
x1=3
y0=-3
y1=3
x_p=np.random.uniform(x0,x1,density_p)
y_p=np.random.uniform(y0,y1,density_p)
vol=(x1-x0)*(y1-y0)
start_time = time.time()
aprox=vol*np.mean(f(x_p,y_p))
end_time = time.time()
secs = end_time-start_time
# lambda statement para calcular el error relativo
obj=np.pi*(np.exp(9)-1) #resultado conocido de la integral (valor objetivo)
print("Evaluación de integral de montecarlo de forma secuencial:")
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print(f"El tiempo de ejecución: {secs} segundos")
print("---------------------")
%%time
error_est = math.sqrt(sum((f(x_p, y_p)-aprox)**2)/density_p)
print("Evaluación de integral de montacarlo de forma secuencial:")
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print(f"El tiempo de ejecución: {secs} segundos")
print("* Intervalo par el área: ")
print((approx-vol*error_est, approx+vol*error_est))
print("---------------------")<jupyter_output>Evaluación de integral de montacarlo de forma secuencial:
---------------------
El valor calculado de la integral es:  25439.637837248316
El error relativo: 5.4254e-04
El tiempo de ejecución: 0.21666693687438965 segundos
* Intervalo par el área: 
(-892101.5393984494, 892107.8224966509)
---------------------
CPU times: user 1.8 s, sys: 40.2 ms, total: 1.84 s
Wall time: 1.82 s<jupyter_text>**En paralelo**<jupyter_code>def f(x): 

    r2=x[:,1]**2+x[:,0]**2

    res=np.exp(r2)

    res[r2>9]=0

    return res

#intervalo
a=np.array([-3,-3]) # se debe de poner (x1_a,x2_a,...,xn_a) para los mínimos

b=np.array([3,3]) # se debe de poner (x1_b,x2_b,...,xn_b) para los máxios

#volumen
vol=np.prod(b-a)

#dimensiones
dims=len(a)
%%time
futures_intervalos = client.map(construye_subintervalos, range(p),
                                            **{'a':a,
                                               'b':b,
                                               'p':p,
                                               'dims':dims})

futures_evaluando=client.map(evalua_subintervalos, futures_intervalos,
                                            **{'f':f,
                                               'n_subint':n_subint,
                                               'dims':dims})

results=client.gather(futures_evaluando)
aprox=(vol/p)*sum(results)
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El valor objetivo de la integral es: ",obj)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print("---------------------")<jupyter_output>---------------------
El valor calculado de la integral es:  57291.43518222632
El valor objetivo de la integral es:  25453.447345638764
El error relativo: 1.2508e+00
---------------------
CPU times: user 125 ms, sys: 10.5 ms, total: 136 ms
Wall time: 233 ms<jupyter_text>Ejemplo 3 $$\int_{-1}^1 \int_0^1 (x^2+y^2)dydx = \frac{4}{3}$$<jupyter_code>start_time = time.time()
aprox = aprox_montecarlo(density_p=10**7, f=lambda x,y:x**2+y**2, dims=2, x0=-1, x1=1, y0=0, y1=1)
end_time = time.time()
secs = end_time-start_time
ob=4/3
# lambda statement para calcualr el error relativo
err_rel=lambda ap,ob: math.fabs(ap-ob)/math.fabs(ob)

print("Evaluación de integral de montecarlo de forma secuencial:")
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print(f"El tiempo de ejecución: {secs} segundos")
print("---------------------")
#%%time
#error_est = math.sqrt(sum((f(x_p,y_p)-aprox)**2)/density_p)
#print("Evaluación de integral de montacarlo de forma secuencial:")
#print("---------------------")
#print("El valor calculado de la integral es: ",aprox)
#print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
#print(f"El tiempo de ejecución: {secs} segundos")
#print("* Intervalo para el área: ")
#print((approx-vol*error_est, approx+vol*error_est))
#print("---------------------")
montecarlo_secuencial_timeit = %timeit -n 5 -r 10 -o aprox<jupyter_output>63 ns ± 16.6 ns per loop (mean ± std. dev. of 10 runs, 5 loops each)<jupyter_text>**En paralelo**<jupyter_code>#funcion a integrar
def f(x): 
    return x[:,0]**2+ x[:,1]**2

#resultado conocido de la integral (valor objetivo)
obj=4/3
print("El valor objetivo de la integral es: ",obj)
# se debe de poner (x1_a,x2_a,...,xn_a) para los mínimos
a=np.array([-1,0])
# se debe de poner (x1_b,x2_b,...,xn_b) para los máxios
b=np.array([1,1])

#volumen
vol=np.prod(b-a)

#dimensiones
dims=len(a)
%%time
futures_intervalos = client.map(construye_subintervalos, range(p),
                                            **{'a':a,
                                               'b':b,
                                               'p':p,
                                               'dims':dims})

futures_evaluando=client.map(evalua_subintervalos, futures_intervalos,
                                            **{'f':f,
                                               'n_subint':n_subint,
                                               'dims':dims})

results=client.gather(futures_evaluando)
aprox=(vol/p)*sum(results)
print("---------------------")
print("El valor calculado de la integral es: ",aprox)
print("El valor objetivo de la integral es: ",obj)
print("El error relativo: {:0.4e}".format(err_rel(aprox,obj)))
print("---------------------")
client.close()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13408381202050965, 'fraction_numerical': 0.02748756698407803, 'mean_word_length': 3.0055589870290302, 'pattern_counts': {'":': 0, '<': 64, '<?xml version=': 0, '>': 68, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 57, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '252085', 'license': 'MIT', 'n_tokens_mistral': 9132, 'n_tokens_neox': 8771, 'n_words': 2664, 'path': '.ipynb_checkpoints/mc_paralelo-checkpoint.ipynb', 'repo_name': 'mno-2020-gh-classroom/ejercicios-de-computo-en-paralelo-con-cpus-dapivei', 'size': '25956'}