Skip to content

Instantly share code, notes, and snippets.

@fcostin
Created June 25, 2015 04:11
Show Gist options
  • Save fcostin/2225b2a51ad8859b5d1b to your computer and use it in GitHub Desktop.
Save fcostin/2225b2a51ad8859b5d1b to your computer and use it in GitHub Desktop.
a simple yet perhaps informative population model.
"""
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)
print
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
print 'over the %f year period, a total of %f person-years of consumption occurred.' % (t_duration, acc_event_logger._total_consumption)
print
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment