def labor_market(self, jobs): tasks = [p.get('employer') for p in self.people] applicants = {f: [] for (_, __), f in jobs} employers = yield from asyncio.gather(*tasks) job_seekers = [p for p, e in zip(self.people, employers) if e is None] while job_seekers and jobs: job_dist = self.job_distribution(jobs) for p in shuffle(job_seekers): (n_vacancies, wage), firm = random_choice(job_dist) applicants[firm].append(p) # firms select from their applicants _jobs = [] for job in shuffle(jobs): # filter down to valid applicants (n_vacancies, wage), firm = job apps = [a for a in applicants[firm] if a in job_seekers] hired, n_vacancies, wage = yield from firm.call('hire', apps, wage) # remove hired people from the job seeker pool for p in hired: job_seekers.remove(p) if not job_seekers: break # if vacancies remain, post the new jobs with the new wage if n_vacancies: _jobs.append(((n_vacancies, wage), firm)) jobs = _jobs
def healthcare_market(self): sold = [] hospitals = [h for h in self.hospitals] for person in shuffle(self.people): if person._state['health'] < 1.: affordable = [ h for h in hospitals if h.price <= person._state['cash'] ] if not affordable: continue utilities = [ person.health_change_utility(1 - person._state['health']) + person.cash_change_utility(-h.price) for h in affordable ] u_t = sum(utilities) choices = [(h, u / u_t) for h, u in zip(hospitals, utilities)] hospital = random_choice(choices) hospital.sell(1) person._state['cash'] -= hospital.price person._state['health'] = 1 person._state['sick'] = False if random.random( ) < self.state['recovery_prob'] else True sold.append(hospital.price) if hospital.supply == 0: hospitals.remove(hospital) if not hospitals: break profits = [f.revenue - f.costs for f in self.hospitals] return sold, profits
def step(self): tasks = [firm.call('set_production_target', self.state) for firm in shuffle(self.firms)] jobs = yield from asyncio.gather(*tasks) jobs = list(zip(jobs, self.firms)) yield from self.labor_market(jobs) yield from asyncio.gather(*[firm.call('produce', self.state) for firm in self.raw_material_firms]) # raw material market sold = yield from self.market( self.raw_material_firms, self.consumer_good_firms + self.capital_equipment_firms, 'purchase_materials') print('materials: ${:.2f}'.format(self.mean_sales_price(sold))) yield from asyncio.gather(*[firm.call('produce', self.state) for firm in self.capital_equipment_firms]) # capital equipment market sold = yield from self.market( self.capital_equipment_firms, self.consumer_good_firms + self.raw_material_firms, 'purchase_equipment') print('equipment: ${:.2f}'.format(self.mean_sales_price(sold))) yield from asyncio.gather(*[firm.call('produce', self.state) for firm in self.consumer_good_firms]) # consumer good market sold = yield from self.market( self.consumer_good_firms, self.people, 'purchase_goods') print('consumer goods: ${:.2f}'.format(self.mean_sales_price(sold))) # people consume (reset) their goods yield from asyncio.gather(*[p.call('consume') for p in self.people])
def consumer_good_market(self): firm_dist = self.firm_distribution(self.consumer_good_firms) sold = [] households = [h for h in self.households] rounds = 0 while households and firm_dist and rounds < MAX_ROUNDS: for household in shuffle(households): supplier = random_choice(firm_dist) desired, purchased = household.purchase_goods(supplier) sold.append((purchased, supplier.price)) if desired == 0: households.remove(household) # if supplier sold out, update firm distribution if supplier.supply == 0: firm_dist = self.firm_distribution(self.consumer_good_firms) if not firm_dist: break rounds += 1 profits = [f.revenue - f.costs for f in self.consumer_good_firms] return sold, profits
def labor_market(self, jobs): job_seekers = [p for p in self.people if p.seeking_job(self.state)] applicants = {f: [] for _, __, f in jobs} # iterate until there are no more job seekers or no more jobs while job_seekers and jobs: # job seekers apply to jobs which satisfy their wage criteria # TODO should they apply to anything if nothing satifies their # criteria? for p in shuffle(job_seekers): for job in jobs: n_vacancies, wage, firm = job if wage >= p.wage_minimum: applicants[firm].append(p) # firms select from their applicants _jobs = [] for job in jobs: # filter down to valid applicants n_vacancies, wage, firm = job apps = [a for a in applicants[firm] if a in job_seekers] hired, n_vacancies, wage = firm.hire(apps, wage, self.state) # remove hired people from the job seeker pool for p in hired: job_seekers.remove(p) if not job_seekers: break # if vacancies remain, post the new jobs with the new wage if n_vacancies: _jobs.append((n_vacancies, wage, firm)) jobs = _jobs
def consumer_good_market(self): firm_dist = self.firm_distribution(self.consumer_good_firms) sold = [] households = [h for h in self.households] rounds = 0 while households and firm_dist and rounds < MAX_ROUNDS: for household in shuffle(households): supplier = random_choice(firm_dist) desired, purchased = household.purchase_goods(supplier) sold.append((purchased, supplier.price)) if desired == 0: households.remove(household) # if supplier sold out, update firm distribution if supplier.supply == 0: firm_dist = self.firm_distribution( self.consumer_good_firms) if not firm_dist: break rounds += 1 profits = [f.revenue - f.costs for f in self.consumer_good_firms] return sold, profits
def market(self, sellers, buyers, purchase_func): sold = [] seller_dist = yield from self.firm_distribution(sellers) while buyers and seller_dist: for buyer in shuffle(buyers): supplier = random_choice(seller_dist) required, purchased = yield from buyer.call(purchase_func, supplier) supply, price = yield from supplier.get('supply', 'price') sold.append((purchased, price)) if required == 0: buyers.remove(buyer) # if supplier sold out, update firm distribution if supply == 0: seller_dist = yield from self.firm_distribution(sellers) if not seller_dist: break return sold
def capital_equipment_market(self): sold = [] firm_dist = self.firm_distribution(self.capital_equipment_firms) firms = self.consumer_good_firms + self.raw_material_firms rounds = 0 while firms and firm_dist and rounds < MAX_ROUNDS: for firm in shuffle(firms): supplier = random_choice(firm_dist) required, purchased = firm.purchase_equipment(supplier) sold.append((purchased, supplier.price)) if required == 0: firms.remove(firm) # if supplier sold out, update firm distribution if supplier.supply == 0: firm_dist = self.firm_distribution(self.capital_equipment_firms) if not firm_dist: break rounds += 1 profits = [f.revenue - f.costs for f in self.capital_equipment_firms] return sold, profits
def raw_material_market(self): sold = [] firm_dist = self.firm_distribution(self.raw_material_firms) firms = self.consumer_good_firms + self.capital_equipment_firms rounds = 0 while firms and firm_dist and rounds < MAX_ROUNDS: for firm in shuffle(firms): supplier = random_choice(firm_dist) required, purchased = firm.purchase_materials(supplier) sold.append((purchased, supplier.price)) if required == 0: firms.remove(firm) # if supplier sold out, update firm distribution if supplier.supply == 0: firm_dist = self.firm_distribution(self.raw_material_firms) if not firm_dist: break rounds += 1 profits = [f.revenue - f.costs for f in self.raw_material_firms] return sold, profits
def healthcare_market(self): sold = [] hospitals = [h for h in self.hospitals] for person in shuffle(self.people): if person._state['health'] < 1.: affordable = [h for h in hospitals if h.price <= person._state['cash']] if not affordable: continue utilities = [person.health_change_utility(1 - person._state['health']) + person.cash_change_utility(-h.price) for h in affordable] u_t = sum(utilities) choices = [(h, u/u_t) for h, u in zip(hospitals, utilities)] hospital = random_choice(choices) hospital.sell(1) person._state['cash'] -= hospital.price person._state['health'] = 1 person._state['sick'] = False if random.random() < self.state['recovery_prob'] else True sold.append(hospital.price) if hospital.supply == 0: hospitals.remove(hospital) if not hospitals: break profits = [f.revenue - f.costs for f in self.hospitals] return sold, profits
def step(self): """one time step in the model (a day)""" super().step() prev_month = self.date.month self.date += relativedelta(days=1) self.state['month'] = self.date.month self.state['year'] = self.date.year self._log('step', { 'people': [p.as_json() for p in self.people] }) self._log('datetime', {'month': self.date.month, 'day': self.date.day, 'year': self.date.year}) if not self.initialized: # create initial firms for person in self.people: if person._state['firm_owner']: industry = random.choice(['equip', 'material', 'consumer_good', 'healthcare']) building = random.choice(self.buildings) self.start_firm(person, industry, building) self.initialized = True # month change if prev_month != self.date.month: # pay rent for building in self.buildings: for tenant in building.tenants: tenant.pay(building.rent) for household in self.households: household.step() n_deaths = self.contagion_model() self.stat('n_sick', len([p for p in self.people if p._state['sick']])) # self.real_estate_market() # see if anyone want to start a business # only possible if there is space available to rent self.state['available_space'] = sum(b.available_space for b in self.buildings) n_tenants = sum(len(b.tenants) for b in self.buildings) mean_rent = sum(b.rent * len(b.tenants) for b in self.buildings)/n_tenants if n_tenants else 0 self.ewma_stat('mean_rent', mean_rent, graph=True) if self.state['available_space']: for person in shuffle(self.people): yes, industry, building = person.start_business(self.state, self.buildings) if yes: self.start_firm(person, industry, building) jobs = [] for firm in shuffle(self.firms): n_vacancies, wage = firm.set_production_target(self.state) jobs.append((n_vacancies, wage, firm)) self.labor_market(jobs) labor_force = [p for p in self.people if p.wage != 0] mean_wage = sum(p.wage for p in labor_force)/len(labor_force) if labor_force else 0 self.ewma_stat('mean_wage', mean_wage, graph=True) for firm in self.raw_material_firms: firm.produce(self.state) sold, profits = self.raw_material_market() mean = sum(profits)/len(profits) if profits else 0 self.ewma_stat('mean_material_profit', mean, graph=True) sell_prices = [] for amt, price in sold: sell_prices += [price for _ in range(amt)] mean = sum(sell_prices)/len(sell_prices) if sell_prices else 0 self.ewma_stat('mean_material_price', mean, graph=True) for firm in self.capital_equipment_firms: firm.produce(self.state) sold, profits = self.capital_equipment_market() mean = sum(profits)/len(profits) if profits else 0 self.ewma_stat('mean_equip_profit', mean, graph=True) sell_prices = [] for amt, price in sold: sell_prices += [price for _ in range(amt)] mean = sum(sell_prices)/len(sell_prices) if sell_prices else 0 self.ewma_stat('mean_equip_price', mean, graph=True) for firm in self.consumer_good_firms: firm.produce(self.state) sold, profits = self.consumer_good_market() mean = sum(profits)/len(profits) if profits else 0 self.ewma_stat('mean_consumer_good_profit', mean, graph=True) sell_prices = [] for amt, price in sold: sell_prices += [price for _ in range(amt)] mean = sum(sell_prices)/len(sell_prices) if sell_prices else 0 self.ewma_stat('mean_consumer_good_price', mean, graph=True) for household in self.households: if not household.check_goods(): for p in household.people: self.dies(p) n_deaths += 1 self.stat('n_deaths', n_deaths) self.stat('n_population', len(self.people)) # taxes and wages for person in self.people: taxes = 0 if person._state['firm_owner']: profit = max(person.firm.profit, 0) taxes = profit * self.government.tax_rate person.firm.cash -= taxes elif person.employer is not None: wage = min(person.wage, person.employer.cash) taxes = wage * self.government.tax_rate person._state['cash'] += (wage - taxes) # TODO should people keep track of how much they are _actually_ # paid vs their stated wage? self.government.cash += taxes for firm in self.hospitals: firm.produce(self.state) sold, profits = self.healthcare_market() mean = sum(profits)/len(profits) if profits else 0 self.ewma_stat('mean_healthcare_profit', mean, graph=True) mean = sum(sold)/len(sold) if sold else 0 self.ewma_stat('mean_healthcare_price', mean, graph=True) # TODO this should be limited by the amount of cash the gov't actual # has, or should deficit spending be ok? # gov't subsidies subs = self.government.subsidies subsidy = subs[CapitalEquipmentFirm]/len(self.capital_equipment_firms) if self.capital_equipment_firms else 0 for firm in self.capital_equipment_firms: firm.cash += subsidy subsidy = subs[CapitalEquipmentFirm]/len(self.capital_equipment_firms) if self.capital_equipment_firms else 0 for firm in self.consumer_good_firms: firm.cash += subsidy subsidy = subs[RawMaterialFirm]/len(self.raw_material_firms) if self.raw_material_firms else 0 for firm in self.raw_material_firms: firm.cash += subsidy n_bankruptcies = 0 for firm in self.firms: # bankrupt if firm.cash < 0: n_bankruptcies += 1 self.close_firm(firm) self.stat('n_bankruptcies', n_bankruptcies) self.stat('n_firms', len(self.firms)) mean_quality_of_life = sum(h.quality_of_life for h in self.households)/len(self.households) if self.households else 0 self.ewma_stat('mean_quality_of_life', mean_quality_of_life, graph=True) mean_cash = sum(h.cash for h in self.households)/len(self.households) if self.households else 0 self.ewma_stat('mean_cash', mean_cash, graph=True) self.government.adjust(self.households) self.stat('welfare', self.government.welfare) self.stat('tax_rate', self.government.tax_rate) for person in self.people: person._state['cash'] += self.government.welfare self.government.cash -= self.government.welfare
def step(self): """one time step in the model (a day)""" super().step() prev_month = self.date.month self.date += relativedelta(days=1) self.state['month'] = self.date.month self.state['year'] = self.date.year self._log('step', {'people': [p.as_json() for p in self.people]}) self._log('datetime', { 'month': self.date.month, 'day': self.date.day, 'year': self.date.year }) if not self.initialized: # create initial firms for person in self.people: if person._state['firm_owner']: industry = random.choice( ['equip', 'material', 'consumer_good', 'healthcare']) building = random.choice(self.buildings) self.start_firm(person, industry, building) self.initialized = True # month change if prev_month != self.date.month: # pay rent for building in self.buildings: for tenant in building.tenants: tenant.pay(building.rent) for household in self.households: household.step() n_deaths = self.contagion_model() self.stat('n_sick', len([p for p in self.people if p._state['sick']])) # self.real_estate_market() # see if anyone want to start a business # only possible if there is space available to rent self.state['available_space'] = sum(b.available_space for b in self.buildings) n_tenants = sum(len(b.tenants) for b in self.buildings) mean_rent = sum( b.rent * len(b.tenants) for b in self.buildings) / n_tenants if n_tenants else 0 self.ewma_stat('mean_rent', mean_rent, graph=True) if self.state['available_space']: for person in shuffle(self.people): yes, industry, building = person.start_business( self.state, self.buildings) if yes: self.start_firm(person, industry, building) jobs = [] for firm in shuffle(self.firms): n_vacancies, wage = firm.set_production_target(self.state) jobs.append((n_vacancies, wage, firm)) self.labor_market(jobs) labor_force = [p for p in self.people if p.wage != 0] mean_wage = sum( p.wage for p in labor_force) / len(labor_force) if labor_force else 0 self.ewma_stat('mean_wage', mean_wage, graph=True) for firm in self.raw_material_firms: firm.produce(self.state) sold, profits = self.raw_material_market() mean = sum(profits) / len(profits) if profits else 0 self.ewma_stat('mean_material_profit', mean, graph=True) sell_prices = [] for amt, price in sold: sell_prices += [price for _ in range(amt)] mean = sum(sell_prices) / len(sell_prices) if sell_prices else 0 self.ewma_stat('mean_material_price', mean, graph=True) for firm in self.capital_equipment_firms: firm.produce(self.state) sold, profits = self.capital_equipment_market() mean = sum(profits) / len(profits) if profits else 0 self.ewma_stat('mean_equip_profit', mean, graph=True) sell_prices = [] for amt, price in sold: sell_prices += [price for _ in range(amt)] mean = sum(sell_prices) / len(sell_prices) if sell_prices else 0 self.ewma_stat('mean_equip_price', mean, graph=True) for firm in self.consumer_good_firms: firm.produce(self.state) sold, profits = self.consumer_good_market() mean = sum(profits) / len(profits) if profits else 0 self.ewma_stat('mean_consumer_good_profit', mean, graph=True) sell_prices = [] for amt, price in sold: sell_prices += [price for _ in range(amt)] mean = sum(sell_prices) / len(sell_prices) if sell_prices else 0 self.ewma_stat('mean_consumer_good_price', mean, graph=True) for household in self.households: if not household.check_goods(): for p in household.people: self.dies(p) n_deaths += 1 self.stat('n_deaths', n_deaths) self.stat('n_population', len(self.people)) # taxes and wages for person in self.people: taxes = 0 if person._state['firm_owner']: profit = max(person.firm.profit, 0) taxes = profit * self.government.tax_rate person.firm.cash -= taxes elif person.employer is not None: wage = min(person.wage, person.employer.cash) taxes = wage * self.government.tax_rate person._state['cash'] += (wage - taxes) # TODO should people keep track of how much they are _actually_ # paid vs their stated wage? self.government.cash += taxes for firm in self.hospitals: firm.produce(self.state) sold, profits = self.healthcare_market() mean = sum(profits) / len(profits) if profits else 0 self.ewma_stat('mean_healthcare_profit', mean, graph=True) mean = sum(sold) / len(sold) if sold else 0 self.ewma_stat('mean_healthcare_price', mean, graph=True) # TODO this should be limited by the amount of cash the gov't actual # has, or should deficit spending be ok? # gov't subsidies subs = self.government.subsidies subsidy = subs[CapitalEquipmentFirm] / len( self.capital_equipment_firms ) if self.capital_equipment_firms else 0 for firm in self.capital_equipment_firms: firm.cash += subsidy subsidy = subs[CapitalEquipmentFirm] / len( self.capital_equipment_firms ) if self.capital_equipment_firms else 0 for firm in self.consumer_good_firms: firm.cash += subsidy subsidy = subs[RawMaterialFirm] / len( self.raw_material_firms) if self.raw_material_firms else 0 for firm in self.raw_material_firms: firm.cash += subsidy n_bankruptcies = 0 for firm in self.firms: # bankrupt if firm.cash < 0: n_bankruptcies += 1 self.close_firm(firm) self.stat('n_bankruptcies', n_bankruptcies) self.stat('n_firms', len(self.firms)) mean_quality_of_life = sum( h.quality_of_life for h in self.households) / len( self.households) if self.households else 0 self.ewma_stat('mean_quality_of_life', mean_quality_of_life, graph=True) mean_cash = sum(h.cash for h in self.households) / len( self.households) if self.households else 0 self.ewma_stat('mean_cash', mean_cash, graph=True) self.government.adjust(self.households) self.stat('welfare', self.government.welfare) self.stat('tax_rate', self.government.tax_rate) for person in self.people: person._state['cash'] += self.government.welfare self.government.cash -= self.government.welfare