<jupyter_start><jupyter_text>Vectorized Functions in Python<jupyter_code>import numpy as np
from platform import python_version
python_version()
def lorenz(y, sigma, beta, rho):
    """The Lorenz ordinary differential equations.
    
    Arguments:
        y (array): Array of y values shape (..., 3).
        sigma (float): Lorenz parameter.
        beta (float): Lorenz parameter.
        rho (float): Lorenz parameter.
    
    Returns:
        dydt (tuple): Derivatives of y (dy/dt).
    """
    return (
        sigma * (y[1] - y[0]), 
        y[0] * (rho - y[2]) - y[1], 
        y[0] * y[1] - beta * y[2]
    )

# Lorenz system parameters
beta = 8 / 3
sigma = 10
rho = 28
# Test1 - Single function call
y0 = (-8, 8, 27)
assert lorenz(y0, sigma, beta, rho) == (160, -16, -136)
# Test 2 - Single function calls with for loop
def make_function_calls(f, y, n, *args, **kwargs):
    dy = np.empty_like(y)
    for i in range(n):
        dy[:, i] = f(y[:, i], *args, **kwargs)
    return dy

y = np.repeat(np.array((-8, 8, 27)).reshape(-1,1), 10, axis=1)
dy = make_function_calls(lorenz, y, 10, sigma=sigma, beta=beta, rho=rho)
assert dy.shape == y.shape
assert np.array_equiv(dy.T, (160, -16, -136))
# Test 3 - vectorized function call
y = np.repeat(np.array((-8, 8, 27)).reshape(-1,1), 10, axis=1)
dy = lorenz(y, sigma, beta, rho)
dy = np.vstack(dy)
assert dy.shape == y.shape
assert np.array_equiv(dy.T, (160, -16, -136))<jupyter_output><empty_output><jupyter_text>Speed tests<jupyter_code>n = 10000
# Use n random y vectors
y = np.random.randn(3, n)
# Test 1 - Single function call
y0 = y[:, 0]
%timeit dy = lorenz(y0, sigma, beta, rho)
# Test 2 - Single function calls with for loop
%timeit dy = make_function_calls(lorenz, y, n, sigma=sigma, beta=beta, rho=rho)
# Test 3- Vectorized function calls
%timeit dy = lorenz(y, sigma, beta, rho)<jupyter_output>58.2 µs ± 381 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)<jupyter_text>Recursive Calculation using a Generator<jupyter_code>def lorenz_generator_discrete(y0, stepsize, sigma, beta, rho):
    y = np.array(y0)
    dy = np.empty(3)
    while True:
        dy[:] = lorenz(y, sigma, beta, rho)
        y = y + stepsize * dy
        yield y

def lorenz_generator_tuples(y0, stepsize, sigma, beta, rho):
    y = np.array(y0)
    dy = np.empty(3)
    while True:
        dy[:] = lorenz(y, sigma, beta, rho)
        y = y + stepsize * dy
        yield y[0], y[1], y[2]



# Lorenz system parameters
beta = 8 / 3
sigma = 10
rho = 28
y0 = (-8, 8, 27)
stepsize = 0.0001
gen = lorenz_generator_discrete(y0, stepsize, sigma, beta, rho)
y_data = np.zeros((100, 3))
for i in range(y_data.shape[0]):
    y_data[i] = next(gen)
gen = lorenz_generator_tuples(y0, stepsize, sigma, beta, rho)
next(gen)
# Unfortunately, np.fromiter only works for 1D arrays
#np.fromiter(gen, float, count=10)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14961725817675714, 'fraction_numerical': 0.0441892832289492, 'mean_word_length': 3.3233082706766917, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '574774', 'license': 'OLDAP-2.3', 'n_tokens_mistral': 1208, 'n_tokens_neox': 1108, 'n_words': 329, 'path': 'Vectorized-Functions-in-Python.ipynb', 'repo_name': 'billtubbs/lang-comp', 'size': '2874'}