Exercise: now in parallel


In [30]:
!python2.7 optimize.py


optimizing W,L for symmetric=True
we define optimality with (4*profited + survived)/5

best: 12.3333333333 @ (5.2954808091448538, 5.2954808091448538)
best: 21.2666666667 @ (3.8977832401318593, 3.8977832401318593)
best: 33.1333333333 @ (2.897523790268695, 2.897523790268695)
best: 37.5333333333 @ (2.9963608076334802, 2.9963608076334802)
best: 75.5333333333 @ (1.7737868730422826, 1.7737868730422826)
10.00% done
best: 76.7333333333 @ (1.7992143869754553, 1.7992143869754553)
best: 77.4666666667 @ (1.7283323434489248, 1.7283323434489248)
20.00% done
30.00% done
40.00% done
50.00% done
60.00% done
70.00% done
80.00% done
90.00% done
100.00% done

time: 12.2723178864

In [4]:
%%file roll.py
"""
rolls a slightly biased (100-sided) die
"""

import random
import numpy as np
import numpy.random as npr
import state

def win(x):
    '''
    a simple win/loss: where win if (100 > x > 50)
    '''
    return int(bool(100 > x > 50))


def die():
    '''     
    a simple win/loss: where win if (100 > random(1,100) > 50)
    '''         
    rng = state.random_state(module=random)
    return win(rng.randint(1,100))


_win = np.vectorize(win)


def dice(N=1):
    '''
    a simple win/loss: where win if (100 > random(1,100,N) > 50)
    '''
    rng = state.random_state(module=npr)
    return _win(rng.randint(1,100,N))


def many_die(N=1):
    '''roll simple (biased) win/loss N times'''
    rolls = []
    for i in range(N):
        rolls.append(die())
    return rolls


# EOF


Overwriting roll.py

In [40]:
# %load use_roll.py
"""
usage testing of roll
"""

import roll

def multi_roll(N, pool):
    _die = lambda i:roll.die()
    rolls = np.asarray(pool.map(_die, xrange(N)), int)
    return rolls


if __name__ == '__main__':
    N = 25
    
    import time
    import pathos
    pool = pathos.pools.ThreadPool()

    start = time.time()
    print(roll.many_die(N))
    print "%s: loop" % (time.time() - start)
    
    start = time.time()
    print(roll.dice(N))
    print "%s: vector" % (time.time() - start)
    
    start = time.time()
    print(multi_roll(N, pool))
    print "%s: pool" % (time.time() - start)
    pool.close()
    pool.join()

    
# EOF


[0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1]
0.000175952911377: loop
[1 1 1 1 1 0 1 0 0 1 1 1 0 0 1 0 0 0 1 0 0 0 1 0 1]
0.00055193901062: vector
[1 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0]
0.00357294082642: pool

In [5]:
%%file trials.py
"""
monte carlo trials for betting strategies, and measures of success
"""

import numpy as np


def monte(bettor, initial_funds, initial_bet, number_bets, number_players, W,L, pool=None):
    "monte carlo run for a betting strategy"
    if pool:
        _bettor = lambda i:bettor(initial_funds, initial_bet, number_bets, W,L)
        return np.array(pool.map(_bettor, xrange(number_players)))

    history = []
    while len(history) < number_players:
        history.append(bettor(initial_funds, initial_bet, number_bets, W,L))
    return np.array(history)


def alive(history, number_players):
    "find the percentage of players that are not broke"
    return 100. * sum(np.asarray(history, bool).T[-1])/number_players


def gains(history, number_players, initial_funds):
    "find the percentage of players that have profited"
    return 100. * sum(history.T[-1] > initial_funds)/number_players


def profit(history, number_players, initial_funds):
    "find the total profit"
    return np.max(history.T[-1]) - initial_funds


def margin(history, number_players, initial_funds):
    "find the percentage the return on investment is over the initial funds"
    initial = number_players * initial_funds
    return 100.* (sum(history.T[-1]) - initial)/initial


# EOF


Overwriting trials.py

In [59]:
%%file optimize.py
'''
monte carlo optimization of betting strategies for a given quantitative measure
'''

import numpy as np
import strategy


def monte_stats(bettor, initial_funds, initial_bet, number_bets, number_players, W,L, pool=None):
    "get the desired stats from a monte carlo run"
    import numpy as np
    from trials import monte, alive, gains, profit, margin
    settings = np.seterr(over='ignore', invalid='ignore')
    history = monte(bettor, initial_funds, initial_bet, number_bets, number_players, W,L, pool)
    survived = alive(history, number_players)
    profited = gains(history, number_players, initial_funds)
    max_profit = profit(history, number_players, initial_funds)
    ave_profit = margin(history, number_players, initial_funds)
    np.seterr(**settings)
    return survived, profited, max_profit, ave_profit


def safety_metric(initial_funds, survived, profited, max_profit, ave_profit):
    "we define optimality with (4*profited + survived)/5"
    return (4*profited + survived)/5


def profit_metric(initial_funds, survived, profited, max_profit, ave_profit):
    "we define optimality with (max_profit - ave_profit)"
    return max_profit - (np.mean(ave_profit) * initial_funds)


def optimize_WL(metric, players=100, funds=None, bet=None, number_bets=None, symmetric=False, 
                                                           outerPool=None, innerPool=None):
    "soewhat hacky Monte Carlo optimization of betting parameters for martingale strategy"
    samples = int(np.random.uniform(100., 1000.)) if players is None else int(players)
    if funds is None: funds = np.random.uniform(1000. ,1000000.)
    if bet is None: bet = np.random.uniform(1. ,1000.)
    number_bets = int(np.random.uniform(10.,10000.)) if number_bets is None else int(number_bets)
    bettor = strategy.martingale
    
    W = np.random.uniform(0.1, 10., samples)
    L = W if symmetric else np.random.uniform(0.1, 10., samples)

    if outerPool is None:
        import pathos
        outerPool = pathos.pools.SerialPool
    if innerPool:
        innerPool = innerPool()
    
    def measure(W,L, pool):
        "let's hide this, because it's ugly (*groan*)" # good indicator a class should be built
        return safety_metric(funds, *monte_stats(bettor, funds, bet, number_bets, samples, W,L, pool))
    
    # use imap to run the monte carlo, because it's cool
    p = outerPool()
    results = p.imap(measure, W,L, [innerPool]*samples)

    i = 0
    best_result = 0.0
    best_value = W[0],L[0]
    for result in results:
        if result > best_result:
            best_result = result
            best_value = W[i],L[i]
            print "best: %s @ %s" % (best_result, best_value)
        i += 1
        if not i%np.floor_divide(samples, 10):
            print "{:.2f}% done".format(100. * i / samples)
            
    p.close()
    p.join()
    if innerPool:
        innerPool.close()
        innerPool.join()
    return best_value


if __name__ == '__main__':
    import pathos
    #oPool = pathos.pools.ParallelPool
    oPool = pathos.pools.ProcessPool
    iPool = None
    
    initial_funds = 10000
    initial_bet = 100
    number_bets = 100
    number_players = 300    # XXX: THIS SHOULD BE MUCH LARGER
    metric = safety_metric
    symmetric = True

    import time
    print "optimizing W,L for symmetric=%s\n%s\n" % (symmetric, metric.__doc__)
    start = time.time()
    optimize_WL(metric, number_players, initial_funds, initial_bet, number_bets, symmetric, 
                                                                                 oPool, iPool)
    print "\ntime: %s" % (time.time() - start)


# EOF


Overwriting optimize.py

In [2]:
!python2.7 optimize.py


optimizing W,L for symmetric=True
we define optimality with (4*profited + survived)/5

best: 17.0666666667 @ (4.3813960767315105, 4.3813960767315105)
best: 73.5333333333 @ (1.5801859624522592, 1.5801859624522592)
10.00% done
20.00% done
30.00% done
40.00% done
50.00% done
60.00% done
best: 75.6 @ (1.7481916493807834, 1.7481916493807834)
70.00% done
80.00% done
best: 75.7333333333 @ (1.715356516351312, 1.715356516351312)
90.00% done
100.00% done

time: 4.05974698067

In [ ]: