def generate_name(sex, race): name = random_choice(name_given_sex[sex.name].items()) race = race_map[race.name] if isinstance(race, list): race = random.choice(race) surname = random_choice(surname_given_race[race].items()) return '{} {}'.format(name, surname).title()
def start_business(self, world, buildings): # can only have one business if self._state['firm_owner']: return False, None, None # must be able to find a place with affordable rent buildings = [b for b in buildings if b.available_space] if not buildings: return False, None, None denom = sum(1/b.rent for b in buildings) building = random_choice([(b, 1/(b.rent*denom)) for b in buildings]) # must be able to hire at least one employee min_cost = self.min_business_capital + building.rent + world['mean_wage'] if self._state['cash'] < min_cost: return False, None, None industries = ['equip', 'material', 'consumer_good', 'healthcare'] total_mean_profit = sum(world['mean_{}_profit'.format(name)] for name in industries) industry_dist = [(name, world['mean_{}_profit'.format(name)]/total_mean_profit) for name in industries] industry = random_choice(industry_dist) # choose an industry (based on highest EWMA profit) self.twoot('i\'m starting a BUSINESS in {}!'.format(industry), world) logger.info('person:{}'.format(json.dumps({ 'event': 'started_firm', 'id': self.id }))) return True, industry, building
def hire(self, applicants, wage, world): hired = [] while self.worker_change > 0 and applicants: # based on employment prob apps = [] for a in applicants: ref = 'friend' if set(a.friends).intersection(self.workers) else 'ad_or_cold_call' p = offer_prob(world['year'], world['month'], a.sex, a.race, ref, precomputed_emp_dist=emp_dist) apps.append((a, p)) apps_mass = sum(p for a, p in apps) apps = [(a, pr/apps_mass) for a, pr in apps] worker = random_choice(apps) if worker.employer is not None: worker.employer.fire(worker) worker.wage = wage worker.employer = self applicants.remove(worker) self.workers.append(worker) hired.append(worker) logger.info('person:{}'.format(json.dumps({ 'event': 'hired', 'id': worker.id }))) self.worker_change -= 1 # increase wage to attract more employees if self.worker_change > 0: wage += self.config['wage_increment'] * (1.1+self.owner.altruism) return hired, self.worker_change, wage
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 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 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 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 set_production_target(self, world): """firm decides on how much supply they want to produce this step, and what they need to do to accomplish that""" # assess previous day's results self.prev_profit = self.profit self.leftover = self.supply # adjust production action = self.learner.choose_action(self.current_state) action = self.actions[action] self.desired_supply = max(1, self.desired_supply + action.get('supply', 0)) self.profit_margin += action.get('profit_margin', 0) # supply expires every day self.supply = 0 # unused materials expire every day self.materials = 0 # resets every day self.n_sold = 0 self.revenue = 0 self.costs = 0 # fire workers that are being paid too much for worker in self.workers: if worker.wage >= world['mean_wage'] + (self.config['extravagant_wage_range'] * (1.1+self.owner.altruism)): self.fire(worker) # figure out labor goal required_labor = self.desired_supply * self.config['labor_cost_per_good'] n_workers, wage, n_equip = self.assess_assets(required_labor, world['mean_wage'], world['mean_equip_price']) # sometimes optimization function returns a huge negative value for # workers, need to look into that further if n_workers < 0: n_workers = 0 self.worker_change = n_workers - len(self.workers) self.desired_equipment = self.equipment + max(0, n_equip - self.equipment) # fire workers if necessary while self.worker_change < 0: # weighted random choice by unemployment prob ws = [] for w in self.workers: pu = emp_dist[world['year']][world['month'] - 1][w.race.name][w.sex.name] ws.append((w, pu['unemployed'])) ws_mass = sum(p for w, p in ws) ws = [(w, p/ws_mass) for w, p in ws] worker = random_choice(ws) self.fire(worker) self.worker_change += 1 # job vacancies return self.worker_change, wage
def sample_rent(year, puma): """generates a plausible rent amount (here, rent can mean a monthly mortgage payment) given a year and a PUMA""" rent = 0 rent_info = rent_dists[year][puma] if random.random() < rent_info['p_owned']: ownership = random_choice(rent_info['p_ownership'].items()) if ownership == 'free': rent = 0 elif ownership == 'mortgage1': rent = rent_info['mortgage1_dist'].resample(1)[0][0] elif ownership == 'mortgage2': try: rent += rent_info['mortgage2_dist'].resample(1)[0][0] except AttributeError: rent += rent_info['mortgage2_dist'] else: rent = rent_info['rent_dist'].resample(1)[0][0] return rent
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 sample_node(self, n, sampled): """sample an individual node, samples from parents as necessary""" parents = self.g.predecessors(n) if not parents: # if no parents, use p(n) try: prob_dist = self._cache[n.name]['_'] except KeyError: prob_dist = self.p_n(n) else: # if parents, use prod(p(n|x_i) for x_i in parents) for parent in parents: if parent not in sampled: sampled = self.sample_node(parent, sampled) given = {k: v for k, v in sampled.items() if k in parents} prob_dist = self.probs_given(n, given=given) sampled[n] = random_choice(prob_dist.items()) return sampled
def sample_node(self, n, sampled): """sample an individual node, samples from parents as necessary""" parents = self.g.predecessors(n) if not parents: # if no parents, use p(n) try: prob_dist = self._cache[n.name]['_'] except KeyError: prob_dist = self.p_n(n) else: # if parents, use prod(p(n|x_i) for x_i in parents) for parent in parents: if parent not in sampled: sampled = self.sample_node(parent, sampled) given = {k:v for k,v in sampled.items() if k in parents} prob_dist = self.probs_given(n, given=given) sampled[n] = random_choice(prob_dist.items()) return sampled
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 hire(self, applicants, wage, world): hired = [] while self.worker_change > 0 and applicants: # based on employment prob apps = [] for a in applicants: ref = 'friend' if set(a.friends).intersection( self.workers) else 'ad_or_cold_call' p = offer_prob(world['year'], world['month'], a.sex, a.race, ref, precomputed_emp_dist=emp_dist) apps.append((a, p)) apps_mass = sum(p for a, p in apps) apps = [(a, pr / apps_mass) for a, pr in apps] worker = random_choice(apps) if worker.employer is not None: worker.employer.fire(worker) worker.wage = wage worker.employer = self applicants.remove(worker) self.workers.append(worker) hired.append(worker) logger.info('person:{}'.format( json.dumps({ 'event': 'hired', 'id': worker.id }))) self.worker_change -= 1 # increase wage to attract more employees if self.worker_change > 0: wage += self.config['wage_increment'] * (1.1 + self.owner.altruism) return hired, self.worker_change, wage
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 set_production_target(self, world): """firm decides on how much supply they want to produce this step, and what they need to do to accomplish that""" # assess previous day's results self.prev_profit = self.profit self.leftover = self.supply # adjust production action = self.learner.choose_action(self.current_state) action = self.actions[action] self.desired_supply = max( 1, self.desired_supply + action.get('supply', 0)) self.profit_margin += action.get('profit_margin', 0) # supply expires every day self.supply = 0 # unused materials expire every day self.materials = 0 # resets every day self.n_sold = 0 self.revenue = 0 self.costs = 0 # fire workers that are being paid too much for worker in self.workers: if worker.wage >= world['mean_wage'] + ( self.config['extravagant_wage_range'] * (1.1 + self.owner.altruism)): self.fire(worker) # figure out labor goal required_labor = self.desired_supply * self.config[ 'labor_cost_per_good'] n_workers, wage, n_equip = self.assess_assets( required_labor, world['mean_wage'], world['mean_equip_price']) # sometimes optimization function returns a huge negative value for # workers, need to look into that further if n_workers < 0: n_workers = 0 self.worker_change = n_workers - len(self.workers) self.desired_equipment = self.equipment + max(0, n_equip - self.equipment) # fire workers if necessary while self.worker_change < 0: # weighted random choice by unemployment prob ws = [] for w in self.workers: pu = emp_dist[world['year']][world['month'] - 1][w.race.name][w.sex.name] ws.append((w, pu['unemployed'])) ws_mass = sum(p for w, p in ws) ws = [(w, p / ws_mass) for w, p in ws] worker = random_choice(ws) self.fire(worker) self.worker_change += 1 # job vacancies return self.worker_change, wage