Created
June 25, 2015 04:11
-
-
Save fcostin/2225b2a51ad8859b5d1b to your computer and use it in GitHub Desktop.
a simple yet perhaps informative population model.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
a stupid population model | |
Simplifying assumptions: | |
assume everyone in each country behaves the same | |
everyone lives until their birth life expectancy | |
everyone has same number of kids [1] | |
uniform life expectancy (take geom average men and women) [2m 2f] | |
everyone pair-bonds into a couple and has kids | |
same age to have kids [3] | |
all these parameters are stationary wrt time | |
Parameters: | |
Bangladesh United States | |
------------------------------------------------------------------------------------- | |
[1] Fertility rate, births per woman 2.2 (y2013) 1.9 (y2013) | |
[2f] life expectancy at birth, female 71 (y2013) 81 (y2013) | |
[2m] life expectancy at birth, male 70 (y2013) 77 (y2013) | |
[2] life expectancy mf geom average 70.5 79 | |
[3] average age of first child birth 18 (y2011) 25.4 (y2010) | |
### Sources | |
http://data.worldbank.org/indicator/SP.DYN.LE00.FE.IN/countries | |
https://en.wikipedia.org/wiki/Advanced_maternal_age | |
http://timesofindia.indiatimes.com/india/Fewer-women-having-kids-in-30s-Study/articleshow/10898196.cms | |
### Very shoddy estimates | |
bangladesh: over a period of 100 years, 1 person produces a tail of ~7.7 people, including themselves | |
note: this is NOT in person years. | |
united states: over a period of 100 years, 1 person produces a tail of ~4.5 people, including themselves | |
so, there's about a factor of ~2 difference in terms of person-years, say (guessing crudely!) | |
there's about a factor of 60 difference in terms of resource usage | |
### baseline resource footprints: | |
US person has about 60x the CO2 pollution resource footprint of a Bangladeshi person. | |
United States person who has average number of children: a * 1.0 * 60 = 60a | |
Bangladeshi person who has average number of children: a * 2.0 * 1 = 2a | |
United States person who does not have children: a * 0.34 * 60 = 20a | |
where a := arbitrary constant > 0. | |
### now | |
let us do this numerically... | |
""" | |
""" | |
### python/haskell/pseudo/bogo encoding | |
can_advance : t -> t | |
advance : t -> [event] | |
consume for r years | spawn | consume for s years | die | |
A : the type of person. eg A := united states | |
EVENT_CONSUME(r, A) | |
duration := r | |
EVENT_SPAWN(A) | |
duration := 0 | |
EVENT_DIE(A) | |
duration := 0 | |
can_advance : t * [EVENT] -> t | |
can_advance(t, []) := inf | |
can_advance(t, [e:ee]) := min(t, e.duration) | |
advance : t * [EVENT] -> maybe EVENT * [EVENT] | |
advance(t, []) := nothing * [] | |
advance(t, [e:ee]) := | |
where t > e.duration | |
error | |
else where t == e.duration | |
just e * [ee] | |
else where 0 < t < e.duration | |
just e._replace(duration=t) * [e._replace(duration=e.duration-t):ee] | |
else where t = 0 | |
nothing * [e:ee] | |
else | |
error | |
jargon: State := [EVENT] | |
can_advance_pop : t * [STATE] -> t | |
can_advance_pop(t, [STATE]) := max(can_advance(t, state) for state in [STATE]) | |
advance_pop : t * [STATE] -> [EVENT] * [STATE] | |
<gather the just events while advancing all the states using `advance`> | |
error on any error. | |
""" | |
from collections import namedtuple | |
INF = float('inf') | |
TEMPLATE = { | |
'bangladesh' : { | |
'fertility_rate': 2.2, | |
'life_expectancy': 70.5, | |
'age_of_first_birth': 18.0, | |
}, | |
'united_states' : { | |
'fertility_rate': 1.9, | |
'life_expectancy': 79.0, | |
'age_of_first_birth': 25.4, | |
}, | |
'united_states_vhemt' : { | |
'fertility_rate': 0.0, # wherein everyone in the US decides to have 0 children. | |
'life_expectancy': 79.0, | |
'age_of_first_birth': 25.4, | |
}, | |
} | |
Event = namedtuple('Event', ['duration', 'weight', 'what', 'template']) | |
State = namedtuple('State', ['events']) | |
def make_state(template, weight): | |
return State(events=[ | |
Event(duration=template['age_of_first_birth'], weight=weight, what='consume', template=template), # before becoming a parent | |
Event(duration=0, weight=weight, what='birth', template=template), | |
Event(duration=(template['life_expectancy'] - template['age_of_first_birth']), weight=weight, what='consume', template=template), # after becoming a parent | |
Event(duration=0, weight=weight, what='death', template=template), | |
]) | |
def can_advance(t, state): | |
if not state.events: | |
return min(t, INF) | |
else: | |
return min(t, state.events[0].duration) | |
def advance(t, state): | |
if not state.events: | |
return (None, state) | |
else: | |
e = state.events[0] | |
ee = state.events[1:] | |
if e.duration < t: | |
raise ValueError(t) | |
elif e.duration == t: # note this also includes case where t and e.duration == 0 | |
state_prime = state._replace(events=ee) | |
return (e, state_prime) | |
elif 0 < t < e.duration: | |
e_done = e._replace(duration=t) | |
e_remain = e._replace(duration=(e.duration-t)) | |
state_prime = state._replace(events=[e_remain]+ee) | |
return (e_done, state_prime) | |
elif t == 0: | |
assert e.duration > 0 | |
return (None, state) | |
else: | |
raise ValueError(t) | |
def can_advance_pop(t, states): | |
if not states: | |
return min(t, INF) | |
else: | |
return min(can_advance(t, state) for state in states) | |
def advance_pop(t, states): | |
maybe_events, states_prime = zip(*[advance(t, state) for state in states]) | |
return filter(None, maybe_events), states_prime | |
def handle_events(states, events, event_logger): | |
states_prime = list(states) | |
for e in events: | |
if e.what == 'death': | |
pass | |
elif e.what == 'consume': | |
pass | |
elif e.what == 'birth': | |
ww = e.weight * e.template['fertility_rate'] * 0.5 # assume half of people are female, half are male, etc. | |
if ww > 0.0: | |
states_prime.append(make_state(e.template, weight=ww)) | |
if ww < 0.0: | |
raise ValueError(ww) # huh? | |
else: | |
raise ValueError(e) | |
event_logger.log(e) | |
return states_prime | |
class SpammingEventLogger: | |
def log(self, e): | |
if e.what == 'death': | |
print 'DEATH' | |
elif e.what == 'consume': | |
print 'CONSUME duration=%r weight=%r' % (e.duration, e.weight) | |
elif e.what == 'birth': | |
print 'BIRTH' | |
else: | |
raise ValueError(e) | |
class AccumulatingEventLogger: | |
def __init__(self, total_consumption=0.0): | |
self._total_consumption=total_consumption | |
def log(self, e): | |
if e.what == 'death': | |
pass | |
elif e.what == 'consume': | |
self._total_consumption += e.duration * e.weight | |
elif e.what == 'birth': | |
pass | |
else: | |
raise ValueError(e) | |
class EnsembleEventLogger: | |
def __init__(self, loggers): | |
self._loggers = loggers | |
def log(self, e): | |
for logger in self._loggers: | |
logger.log(e) | |
def main(): | |
scenarios = { | |
'bangladesh': [make_state(TEMPLATE['bangladesh'], weight=1.0)], | |
'united_states': [make_state(TEMPLATE['united_states'], weight=1.0)], | |
'united_states_vhemt': [make_state(TEMPLATE['united_states_vhemt'], weight=1.0)], | |
} | |
verbose = False | |
for scenario_name in sorted(scenarios): | |
scenario_states = scenarios[scenario_name] | |
print 'SCENARIO: %r' % (scenario_name) | |
states = list(scenario_states) | |
acc_event_logger = AccumulatingEventLogger() | |
sub_loggers = [] | |
if verbose: | |
sub_loggers.append(SpammingEventLogger()) | |
sub_loggers.append(acc_event_logger) | |
event_logger = EnsembleEventLogger(sub_loggers) | |
t_duration = 100.0 # years | |
t_remaining = t_duration | |
prev_events = None | |
while (t_remaining > 0.0) or (prev_events): | |
if verbose: | |
print 't_remaining = %f' % t_remaining | |
tau = can_advance_pop(t_remaining, states) | |
if verbose: | |
print '\ttau = %f' % tau | |
events, states_prime = advance_pop(tau, states) | |
states_prime_prime = handle_events(states_prime, events, event_logger) | |
states = states_prime_prime | |
prev_events = events | |
t_remaining -= tau | |
print 'over the %f year period, a total of %f person-years of consumption occurred.' % (t_duration, acc_event_logger._total_consumption) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment