<jupyter_start><jupyter_text>Numba Example Use CaseSimulation of Casino visits playing roulette. A stop-loss amount (money carried with you), a stop-win amount (when you take the won money and leave) and a maximum number of games per visit (when you stop playing even if neither stop-loss nor stop-win is reached) can be configured.Main assumption:Constant bet of 1 unit per game (no increase/ decrease of bets during casino visit)The calculation of the casino visit profit/ loss cannot be vectorized efficiently to use numpy due to the stop conditions.The alternative would be to simulate max_games for each series, which is inefficient because a stop condition could be reached much earlier. Used Functionalities* Numpy* Numba just-in-time compiler* matplotlib for plotting results Code<jupyter_code>import numpy as np
from matplotlib import pyplot
from numba import jit
@jit
def game(game_type):
    """
    A single roulette game.
    Returns profit or loss of this game.
    """
    nr = np.random.randint(0,36+1)
    if game_type == 1: #black/white, etc.
        if 0 < nr <= 18:
            return 2 - 1
    elif game_type == 2: #3 choices
        if 0 < nr <= 12:
            return 3 - 1
    elif game_type == 3: #single number
        if nr == 1:
            return 36 - 1
    return 0 - 1

@jit
def game_series(game_type, stop_loss, stop_win, max_games=100):
    """
    A series of roulette games, corresponding to a casino visit.
    The series ends if either stop-loss, stop-win or max games is reached.
    Returns profit or loss of the casino visit.
    """
    assert stop_loss < 0
    assert stop_win > 0
    assert max_games > 0
    assert game_type in (1, 2, 3)
    
    # this calculation cannot be vectorized efficiently to use numpy due to the stop conditions
    # the alternative would be to simulate max_games for each series, which is inefficient
    # because a stop condition could be reached much earlier
    pocket = 0
    for i in range(max_games):
        pocket += game(game_type)
        if pocket <= stop_loss or pocket >= stop_win:
            break
    return pocket

def series_stats(game_type, stop_loss, stop_win, max_games, simulations=1000000):
    """
    Simulates a large number (up to millions) of casino visits and calculates statistical 
    properties of the p&l.
    """
    runs = np.array([game_series(game_type, stop_loss, stop_win, max_games) 
                     for i in range(simulations)])
    bins, counts = np.unique(runs, return_counts=True)
    return {
        'mean': np.mean(runs),
        'stddev': np.std(runs),
        'bins': bins,
        'counts': counts/simulations,
        'gain': (runs > 0).sum()/simulations,
        }
stats = series_stats(game_type=1, stop_loss=-10, stop_win=10, max_games=50)
stats
pyplot.plot(stats['bins'], stats['counts'])<jupyter_output><empty_output><jupyter_text>Calculation Time<jupyter_code>%timeit series_stats(game_type=1, stop_loss=-10, stop_win=10, max_games=50, simulations=10000)
def game_no_jit(game_type):
    """
    A single roulette game.
    Returns profit or loss of this game.
    """
    nr = np.random.randint(0,36+1)
    if game_type == 1: #black/white, etc.
        if 0 < nr <= 18:
            return 2 - 1
    elif game_type == 2: #3 choices
        if 0 < nr <= 12:
            return 3 - 1
    elif game_type == 3: #single number
        if nr == 1:
            return 36 - 1
    return 0 - 1

def game_series_no_jit(game_type, stop_loss, stop_win, max_games=100):
    """
    A series of roulette games, corresponding to a casino visit.
    The series ends if either stop-loss, stop-win or max games is reached.
    Returns profit or loss of the casino visit.
    """
    assert stop_loss < 0
    assert stop_win > 0
    assert max_games > 0
    assert game_type in (1, 2, 3)
    
    # this calculation cannot be vectorized efficiently to use numpy due to the stop conditions
    # the alternative would be to simulate max_games for each series, which is inefficient
    # because a stop condition could be reached much earlier
    pocket = 0
    for i in range(max_games):
        pocket += game_no_jit(game_type)
        if pocket <= stop_loss or pocket >= stop_win:
            break
    return pocket

def series_stats_no_jit(game_type, stop_loss, stop_win, max_games, simulations=1000000):
    """
    Simulates a large number (up to millions) of casino visits and calculates statistical 
    properties of the p&l.
    """
    runs = np.array([game_series_no_jit(game_type, stop_loss, stop_win, max_games) 
                     for i in range(simulations)])
    bins, counts = np.unique(runs, return_counts=True)
    return {
        'mean': np.mean(runs),
        'stddev': np.std(runs),
        'bins': bins,
        'counts': counts/simulations,
        'gain': (runs > 0).sum()/simulations,
        }
%timeit series_stats_no_jit(game_type=1, stop_loss=-10, stop_win=10, max_games=50, simulations=10000)<jupyter_output>2.85 s ± 10.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
{'fraction_non_alphanumeric': 0.08638535031847133, 'fraction_numerical': 0.024681528662420384, 'mean_word_length': 2.9013975155279503, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 16, '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': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '478433', 'license': 'MIT', 'n_tokens_mistral': 1639, 'n_tokens_neox': 1507, 'n_words': 576, 'path': 'notebooks/numba_jit.ipynb', 'repo_name': 'lungben/python_tutorial', 'size': '5024'}