Example #1
0
    def __init__(self, tax_rate, welfare, tax_rate_increment,
                 welfare_increment, starting_welfare_req):
        self._state = {'cash': 0}
        self.tax_rate = tax_rate
        self.tax_rate_increment = tax_rate_increment
        self.welfare = welfare
        self.welfare_increment = welfare_increment
        self.welfare_req = starting_welfare_req
        self.subsidies = {
            ConsumerGoodFirm: 0,
            CapitalEquipmentFirm: 0,
            RawMaterialFirm: 0,
            Hospital: 0
        }
        self.altruism = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(3)}
        self.learner = QLearner(states_actions,
                                self.reward,
                                discount=0.5,
                                explore=0.01,
                                learning_rate=0.5)

        # keep track of previous step's quality of life for comparison
        self.prev_qol = 0
Example #2
0
    def __init__(self, labor_cost_per_good, material_cost_per_good, labor_per_equipment, labor_per_worker, supply_increment, profit_increment, wage_increment):
        self._super(Firm, self).__init__(state={
            'desired_supply': 1,
            'desired_equipment': 0,
            'worker_change': 0,
            'workers': [],
            'cash': 50000,
            'revenue': 0,
            'costs': 0,
            'price': 0,
            'profit': 0,
            'prev_profit': 0,
            'leftover': 0,
            'supply': 0,
            'n_sold': 0,
            'profit_margin': 1,
            'equipment': 0,
            'materials': 0,
        })

        self.material_cost_per_good = material_cost_per_good
        self.labor_cost_per_good = labor_cost_per_good
        self.labor_per_equipment = labor_per_equipment
        self.labor_per_worker = labor_per_worker
        self.supply_increment = supply_increment
        self.profit_increment = profit_increment
        self.wage_increment = wage_increment

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions, self.reward, discount=0.5, explore=0.1, learning_rate=0.8)
Example #3
0
    def __init__(self, labor_cost_per_good, material_cost_per_good,
                 labor_per_equipment, labor_per_worker, supply_increment,
                 profit_increment, wage_increment):
        self._super(Firm, self).__init__(
            state={
                'desired_supply': 1,
                'desired_equipment': 0,
                'worker_change': 0,
                'workers': [],
                'cash': 50000,
                'revenue': 0,
                'costs': 0,
                'price': 0,
                'profit': 0,
                'prev_profit': 0,
                'leftover': 0,
                'supply': 0,
                'n_sold': 0,
                'profit_margin': 1,
                'equipment': 0,
                'materials': 0,
            })

        self.material_cost_per_good = material_cost_per_good
        self.labor_cost_per_good = labor_cost_per_good
        self.labor_per_equipment = labor_per_equipment
        self.labor_per_worker = labor_per_worker
        self.supply_increment = supply_increment
        self.profit_increment = profit_increment
        self.wage_increment = wage_increment

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions,
                                self.reward,
                                discount=0.5,
                                explore=0.1,
                                learning_rate=0.8)
Example #4
0
    def __init__(self, owner):
        self.owner = owner
        self.owner._state['firm_owner'] = True
        self.owner.firm = self
        self.desired_supply = 1

        # initialize
        self.workers = []
        self.revenue = 0
        self.costs = 0
        self.supply = 0
        self.n_sold = 0
        self.profit_margin = 1
        self.equipment = 0
        self.materials = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions,
                                self.reward,
                                discount=0.5,
                                explore=0.01,
                                learning_rate=0.8)
Example #5
0
    def __init__(self, owner):
        self.owner = owner
        self.owner._state['firm_owner'] = True
        self.owner.firm = self
        self.desired_supply = 1

        # initialize
        self.workers = []
        self.revenue = 0
        self.costs = 0
        self.supply = 0
        self.n_sold = 0
        self.profit_margin = 1
        self.equipment = 0
        self.materials = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions, self.reward, discount=0.5, explore=0.01, learning_rate=0.8)
Example #6
0
    def __init__(self, tax_rate, welfare, tax_rate_increment, welfare_increment, starting_welfare_req):
        self._state = {'cash': 0}
        self.tax_rate = tax_rate
        self.tax_rate_increment = tax_rate_increment
        self.welfare = welfare
        self.welfare_increment = welfare_increment
        self.welfare_req = starting_welfare_req
        self.subsidies = {
            ConsumerGoodFirm: 0,
            CapitalEquipmentFirm: 0,
            RawMaterialFirm: 0,
            Hospital: 0
        }
        self.altruism = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(3)}
        self.learner = QLearner(states_actions, self.reward, discount=0.5, explore=0.01, learning_rate=0.5)

        # keep track of previous step's quality of life for comparison
        self.prev_qol = 0
Example #7
0
class Firm(Agent):
    config = {}

    def __init__(self, owner):
        self.owner = owner
        self.owner._state['firm_owner'] = True
        self.owner.firm = self
        self.desired_supply = 1

        # initialize
        self.workers = []
        self.revenue = 0
        self.costs = 0
        self.supply = 0
        self.n_sold = 0
        self.profit_margin = 1
        self.equipment = 0
        self.materials = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions, self.reward, discount=0.5, explore=0.01, learning_rate=0.8)

    @property
    def id(self):
        return self.owner.id

    @property
    def public(self):
        return type(self.owner).__name__ == 'Government'

    def change_owner(self, owner):
        self.owner._state['firm_owner'] = False
        self.owner.firm = None

        owner._state['firm_owner'] = True
        owner.firm = self
        self.owner = owner

    def pay(self, cost):
        self.cash -= cost
        self.costs += cost

    @property
    def cash(self):
        return self.owner._state['cash']

    @cash.setter
    def cash(self, value):
        self.owner._state['cash'] = value

    @property
    def profit(self):
        return self.revenue - self.costs

    def __repr__(self):
        return '{}\'s {}'.format(self.owner.name, type(self).__name__)

    @property
    def production_capacity(self):
        return math.floor(self.labor/self.config['labor_cost_per_good'])

    @property
    def worker_labor(self):
        return self.config['labor_per_worker'] * len(self.workers)

    @property
    def equipment_labor(self):
        return min(len(self.workers), self.equipment) * self.config['labor_per_equipment']

    @property
    def labor(self):
        return self.worker_labor + self.equipment_labor

    def _labor(self, equipment):
        return self.worker_labor + min(len(self.workers), equipment) * self.config['labor_per_equipment']

    def fire(self, worker):
        worker.employer = None
        worker.wage = 0
        self.workers.remove(worker)
        logger.info('person:{}'.format(json.dumps({
            'event': 'fired',
            'id': worker.id
        })))

    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 close(self):
        self.owner._state['firm_owner'] = False
        self.owner.firm = None
        for worker in self.workers:
            self.fire(worker)

    def produce(self, world):
        """produce the firm's product. the firm will produce the desired supply if possible,
        otherwise, they will produce as much as they can."""

        # limit desired supply to what can be produced given current capacity
        self.supply = max(1, min(self.desired_supply, self.production_capacity))

        # set desired price
        wages = sum(w.wage for w in self.workers)
        self.costs += wages
        self.costs += self.building.rent/30 # approximately spread out rent cost
        self.cash -= wages
        cost_per_unit = self.costs/self.supply
        self.price = max(0, cost_per_unit + self.profit_margin)

        return self.supply, self.price

    def sell(self, quantity):
        n_sold = min(self.supply, quantity)
        self.supply -= n_sold
        self.n_sold += n_sold
        self.revenue = self.price * n_sold
        self.cash += self.revenue

    @property
    def current_state(self):
        """represent as a discrete state"""
        if self.n_sold == 0:
            return 0
        elif self.n_sold > 0 and self.leftover > 0:
            return 1
        elif self.n_sold > 0 and self.leftover == 0 and self.profit <= 0:
            return 2
        elif self.n_sold > 0 and self.leftover == 0 and self.profit > 0 and self.profit - self.prev_profit < 0:
            return 3
        elif self.n_sold > 0 and self.leftover == 0 and self.profit > 0 and self.profit - self.prev_profit >= 0:
            return 4

    def reward(self, state):
        """the discrete states we map to are the reward values, so just return that"""
        return state

    @property
    def actions(self):
        """these actions are possible from any state"""
        return [
            {'supply': self.config['supply_increment']},
            {'supply': -self.config['supply_increment']},
            {'supply': self.config['supply_increment'], 'profit_margin': self.config['profit_increment']},
            {'supply': self.config['supply_increment'], 'profit_margin': -self.config['profit_increment']},
            {'supply': -self.config['supply_increment'], 'profit_margin': self.config['profit_increment']},
            {'supply': -self.config['supply_increment'], 'profit_margin': -self.config['profit_increment']}
        ]

    def assess_assets(self, required_labor, mean_wage, mean_equip_price):
        """identify desired mixture of productive assets, i.e. workers, equipment, and wage"""
        down_wage_pressure = (-self.owner.altruism+1.1) * -self.config['wage_increment']

        def objective(x):
            n_workers, wage, n_equipment = x
            return n_workers * wage + n_equipment * mean_equip_price

        def constraint(x):
            n_workers, wage, n_equipment = x
            equip_labor = min(n_workers * self.config['labor_per_equipment'], n_equipment * self.config['labor_per_equipment'])
            return n_workers * self.config['labor_per_worker'] + equip_labor - required_labor

        results = optimize.minimize(objective, (1,0,0), constraints=[
            {'type': 'ineq', 'fun': constraint},
            {'type': 'ineq', 'fun': lambda x: x[0]},
            {'type': 'ineq', 'fun': lambda x: x[1] - (mean_wage - down_wage_pressure)},
            {'type': 'ineq', 'fun': lambda x: x[2]}
        ], options={'maxiter':20})
        n_workers, wage, n_equipment = np.ceil(results.x).astype(np.int)
        return n_workers, wage, n_equipment

    def purchase_equipment(self, supplier):
        total_equipment_cost = (self.desired_equipment - self.equipment) * supplier.price

        if not total_equipment_cost:
            n_equipment = max(0, self.desired_equipment - self.equipment)
        else:
            equipment_budget = max(0, min(self.cash, total_equipment_cost))

            # how much equipment can be purchased
            n_equipment = math.floor(equipment_budget/supplier.price)

        to_purchase = min(supplier.supply, n_equipment)
        supplier.sell(to_purchase)
        self.equipment += to_purchase
        cost = to_purchase * supplier.price
        self.cash -= cost
        self.costs += cost
        return self.desired_equipment - self.equipment, to_purchase

    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
Example #8
0
class Firm(Agent):
    def __init__(self, labor_cost_per_good, material_cost_per_good, labor_per_equipment, labor_per_worker, supply_increment, profit_increment, wage_increment):
        self._super(Firm, self).__init__(state={
            'desired_supply': 1,
            'desired_equipment': 0,
            'worker_change': 0,
            'workers': [],
            'cash': 50000,
            'revenue': 0,
            'costs': 0,
            'price': 0,
            'profit': 0,
            'prev_profit': 0,
            'leftover': 0,
            'supply': 0,
            'n_sold': 0,
            'profit_margin': 1,
            'equipment': 0,
            'materials': 0,
        })

        self.material_cost_per_good = material_cost_per_good
        self.labor_cost_per_good = labor_cost_per_good
        self.labor_per_equipment = labor_per_equipment
        self.labor_per_worker = labor_per_worker
        self.supply_increment = supply_increment
        self.profit_increment = profit_increment
        self.wage_increment = wage_increment

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions, self.reward, discount=0.5, explore=0.1, learning_rate=0.8)

    def pay(self, cost):
        self['cash'] -= cost
        self['costs'] += cost

    @property
    def _production_capacity(self):
        """how many goods can be produced given current labor power"""
        return math.floor(self._labor/self.labor_cost_per_good)

    @property
    def _worker_labor(self):
        """labor from workers, not counting equipment"""
        return self.labor_per_worker * len(self['workers'])

    @property
    def _equipment_labor(self):
        """how much labor can be generated by owned equipment, limited by number of workers
        (one worker is required to operate one piece of equipment)"""
        return min(len(self['workers']), self['equipment']) * self.labor_per_equipment

    @property
    def _labor(self):
        """total productive labor"""
        return self._worker_labor + self._equipment_labor

    def _labor_for_equipment(self, equipment):
        """hypothetical labor that could be produced by some amount of equipment,
        limited by number of workers"""
        return self._worker_labor + min(len(self['workers']), equipment) * self.labor_per_equipment

    @asyncio.coroutine
    def fire(self, worker):
        self['workers'].remove(worker)
        yield from worker.call('quit')

    @asyncio.coroutine
    def hire(self, applicants, wage):
        hired = []
        while self['worker_change'] > 0 and applicants:
            worker = random.choice(applicants)
            employer = yield from worker.get('employer')
            if employer is not None:
                yield from employer.call('fire', worker)
            yield from worker.call('hire', AgentProxy(self), wage)
            applicants.remove(worker)
            self['workers'].append(worker)
            hired.append(worker)
            self['worker_change'] -= 1

        # increase wage to attract more employees
        if self['worker_change'] > 0:
            wage += self.wage_increment
        return hired, self['worker_change'], wage

    @asyncio.coroutine
    def shutdown(self):
        for worker in self['workers']:
            yield from self.fire(worker)

    def produce(self, world):
        """produce the firm's product. the firm will produce the desired supply if possible,
        otherwise, they will produce as much as they can."""

        # limit desired supply to what can be produced given current capacity
        self['supply'] = max(1, min(self['desired_supply'], self._production_capacity))

        # set desired price
        wages = 0
        for w in self['workers']:
            wages += (yield from w.get('wage'))
        self['costs'] += wages
        self['cash'] -= wages
        cost_per_unit = self['costs']/self['supply']
        self['price'] = max(0, cost_per_unit + self['profit_margin'])

        return self['supply'], self['price']

    @asyncio.coroutine
    def sell(self, quantity):
        n_sold = min(self['supply'], quantity)
        self['supply'] -= n_sold
        self['n_sold'] += n_sold
        self['revenue'] = self['price'] * n_sold
        self['cash'] += self['revenue']
        return n_sold

    @property
    def curren(self):
        """represent as a discrete state"""
        if self['n_sold'] == 0:
            return 0
        elif self['n_sold'] > 0 and self['leftover'] > 0:
            return 1
        elif self['n_sold'] > 0 and self['leftover'] == 0 and self['profit'] <= 0:
            return 2
        elif self['n_sold'] > 0 and self['leftover'] == 0 and self['profit'] > 0 and self['profit'] - self['prev_profit'] < 0:
            return 3
        elif self['n_sold'] > 0 and self['leftover'] == 0 and self['profit'] > 0 and self['profit'] - self['prev_profit'] >= 0:
            return 4

    def reward(self, state):
        """the discrete states we map to are the reward values, so just return that"""
        return state

    @property
    def actions(self):
        """these actions are possible from any state"""
        return [
            {'supply': self.supply_increment},
            {'supply': -self.supply_increment},
            {'supply': self.supply_increment, 'profit_margin': self.profit_increment},
            {'supply': self.supply_increment, 'profit_margin': -self.profit_increment},
            {'supply': -self.supply_increment, 'profit_margin': self.profit_increment},
            {'supply': -self.supply_increment, 'profit_margin': -self.profit_increment}
        ]

    def assess_assets(self, required_labor, mean_wage, mean_equip_price):
        """identify desired mixture of productive assets, i.e. workers, equipment, and wage"""
        down_wage_pressure = self.wage_increment

        def objective(x):
            n_workers, wage, n_equipment = x
            return n_workers * wage + n_equipment * mean_equip_price

        def constraint(x):
            n_workers, wage, n_equipment = x
            equip_labor = min(n_workers * self.labor_per_equipment, n_equipment * self.labor_per_equipment)
            return n_workers * self.labor_per_worker + equip_labor - required_labor

        results = optimize.minimize(objective, (1,0,0), constraints=[
            {'type': 'ineq', 'fun': constraint},
            {'type': 'ineq', 'fun': lambda x: x[0]},
            {'type': 'ineq', 'fun': lambda x: x[1] - (mean_wage - down_wage_pressure)},
            {'type': 'ineq', 'fun': lambda x: x[2]}
        ], options={'maxiter':20})
        n_workers, wage, n_equipment = np.ceil(results.x).astype(np.int)
        return n_workers, wage, n_equipment

    @asyncio.coroutine
    def purchase_equipment(self, supplier):
        price, supply = yield from supplier.get('price', 'supply')
        total_equipment_cost = (self['desired_equipment'] - self['equipment']) * price

        if not total_equipment_cost:
            n_equipment = max(0, self['desired_equipment'] - self['equipment'])
        else:
            equipment_budget = max(0, min(self['cash'], total_equipment_cost))

            # how much equipment can be purchased
            n_equipment = math.floor(equipment_budget/price)

        to_purchase = min(supply, n_equipment)
        yield from supplier.call('sell', to_purchase)
        self['equipment'] += to_purchase
        cost = to_purchase * price
        self.pay(cost)
        return self['desired_equipment'] - self['equipment'], to_purchase

    @asyncio.coroutine
    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.curren)
        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

        # figure out labor goal
        required_labor = self['desired_supply'] * self.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
        n_workers = max(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:
            worker = random.choice(self['workers'])
            yield from self.fire(worker)
            self['worker_change'] += 1

        # job vacancies
        return self['worker_change'], wage
Example #9
0
class Firm(Agent):
    config = {}

    def __init__(self, owner):
        self.owner = owner
        self.owner._state['firm_owner'] = True
        self.owner.firm = self
        self.desired_supply = 1

        # initialize
        self.workers = []
        self.revenue = 0
        self.costs = 0
        self.supply = 0
        self.n_sold = 0
        self.profit_margin = 1
        self.equipment = 0
        self.materials = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions,
                                self.reward,
                                discount=0.5,
                                explore=0.01,
                                learning_rate=0.8)

    @property
    def id(self):
        return self.owner.id

    @property
    def public(self):
        return type(self.owner).__name__ == 'Government'

    def change_owner(self, owner):
        self.owner._state['firm_owner'] = False
        self.owner.firm = None

        owner._state['firm_owner'] = True
        owner.firm = self
        self.owner = owner

    def pay(self, cost):
        self.cash -= cost
        self.costs += cost

    @property
    def cash(self):
        return self.owner._state['cash']

    @cash.setter
    def cash(self, value):
        self.owner._state['cash'] = value

    @property
    def profit(self):
        return self.revenue - self.costs

    def __repr__(self):
        return '{}\'s {}'.format(self.owner.name, type(self).__name__)

    @property
    def production_capacity(self):
        return math.floor(self.labor / self.config['labor_cost_per_good'])

    @property
    def worker_labor(self):
        return self.config['labor_per_worker'] * len(self.workers)

    @property
    def equipment_labor(self):
        return min(len(self.workers),
                   self.equipment) * self.config['labor_per_equipment']

    @property
    def labor(self):
        return self.worker_labor + self.equipment_labor

    def _labor(self, equipment):
        return self.worker_labor + min(len(
            self.workers), equipment) * self.config['labor_per_equipment']

    def fire(self, worker):
        worker.employer = None
        worker.wage = 0
        self.workers.remove(worker)
        logger.info('person:{}'.format(
            json.dumps({
                'event': 'fired',
                'id': worker.id
            })))

    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 close(self):
        self.owner._state['firm_owner'] = False
        self.owner.firm = None
        for worker in self.workers:
            self.fire(worker)

    def produce(self, world):
        """produce the firm's product. the firm will produce the desired supply if possible,
        otherwise, they will produce as much as they can."""

        # limit desired supply to what can be produced given current capacity
        self.supply = max(1, min(self.desired_supply,
                                 self.production_capacity))

        # set desired price
        wages = sum(w.wage for w in self.workers)
        self.costs += wages
        self.costs += self.building.rent / 30  # approximately spread out rent cost
        self.cash -= wages
        cost_per_unit = self.costs / self.supply
        self.price = max(0, cost_per_unit + self.profit_margin)

        return self.supply, self.price

    def sell(self, quantity):
        n_sold = min(self.supply, quantity)
        self.supply -= n_sold
        self.n_sold += n_sold
        self.revenue = self.price * n_sold
        self.cash += self.revenue

    @property
    def current_state(self):
        """represent as a discrete state"""
        if self.n_sold == 0:
            return 0
        elif self.n_sold > 0 and self.leftover > 0:
            return 1
        elif self.n_sold > 0 and self.leftover == 0 and self.profit <= 0:
            return 2
        elif self.n_sold > 0 and self.leftover == 0 and self.profit > 0 and self.profit - self.prev_profit < 0:
            return 3
        elif self.n_sold > 0 and self.leftover == 0 and self.profit > 0 and self.profit - self.prev_profit >= 0:
            return 4

    def reward(self, state):
        """the discrete states we map to are the reward values, so just return that"""
        return state

    @property
    def actions(self):
        """these actions are possible from any state"""
        return [{
            'supply': self.config['supply_increment']
        }, {
            'supply': -self.config['supply_increment']
        }, {
            'supply': self.config['supply_increment'],
            'profit_margin': self.config['profit_increment']
        }, {
            'supply': self.config['supply_increment'],
            'profit_margin': -self.config['profit_increment']
        }, {
            'supply': -self.config['supply_increment'],
            'profit_margin': self.config['profit_increment']
        }, {
            'supply': -self.config['supply_increment'],
            'profit_margin': -self.config['profit_increment']
        }]

    def assess_assets(self, required_labor, mean_wage, mean_equip_price):
        """identify desired mixture of productive assets, i.e. workers, equipment, and wage"""
        down_wage_pressure = (-self.owner.altruism +
                              1.1) * -self.config['wage_increment']

        def objective(x):
            n_workers, wage, n_equipment = x
            return n_workers * wage + n_equipment * mean_equip_price

        def constraint(x):
            n_workers, wage, n_equipment = x
            equip_labor = min(n_workers * self.config['labor_per_equipment'],
                              n_equipment * self.config['labor_per_equipment'])
            return n_workers * self.config[
                'labor_per_worker'] + equip_labor - required_labor

        results = optimize.minimize(objective, (1, 0, 0),
                                    constraints=[{
                                        'type': 'ineq',
                                        'fun': constraint
                                    }, {
                                        'type': 'ineq',
                                        'fun': lambda x: x[0]
                                    }, {
                                        'type':
                                        'ineq',
                                        'fun':
                                        lambda x: x[1] -
                                        (mean_wage - down_wage_pressure)
                                    }, {
                                        'type': 'ineq',
                                        'fun': lambda x: x[2]
                                    }],
                                    options={'maxiter': 20})
        n_workers, wage, n_equipment = np.ceil(results.x).astype(np.int)
        return n_workers, wage, n_equipment

    def purchase_equipment(self, supplier):
        total_equipment_cost = (self.desired_equipment -
                                self.equipment) * supplier.price

        if not total_equipment_cost:
            n_equipment = max(0, self.desired_equipment - self.equipment)
        else:
            equipment_budget = max(0, min(self.cash, total_equipment_cost))

            # how much equipment can be purchased
            n_equipment = math.floor(equipment_budget / supplier.price)

        to_purchase = min(supplier.supply, n_equipment)
        supplier.sell(to_purchase)
        self.equipment += to_purchase
        cost = to_purchase * supplier.price
        self.cash -= cost
        self.costs += cost
        return self.desired_equipment - self.equipment, to_purchase

    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
Example #10
0
class Firm(Agent):
    def __init__(self, labor_cost_per_good, material_cost_per_good,
                 labor_per_equipment, labor_per_worker, supply_increment,
                 profit_increment, wage_increment):
        self._super(Firm, self).__init__(
            state={
                'desired_supply': 1,
                'desired_equipment': 0,
                'worker_change': 0,
                'workers': [],
                'cash': 50000,
                'revenue': 0,
                'costs': 0,
                'price': 0,
                'profit': 0,
                'prev_profit': 0,
                'leftover': 0,
                'supply': 0,
                'n_sold': 0,
                'profit_margin': 1,
                'equipment': 0,
                'materials': 0,
            })

        self.material_cost_per_good = material_cost_per_good
        self.labor_cost_per_good = labor_cost_per_good
        self.labor_per_equipment = labor_per_equipment
        self.labor_per_worker = labor_per_worker
        self.supply_increment = supply_increment
        self.profit_increment = profit_increment
        self.wage_increment = wage_increment

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(5)}
        self.learner = QLearner(states_actions,
                                self.reward,
                                discount=0.5,
                                explore=0.1,
                                learning_rate=0.8)

    def pay(self, cost):
        self['cash'] -= cost
        self['costs'] += cost

    @property
    def _production_capacity(self):
        """how many goods can be produced given current labor power"""
        return math.floor(self._labor / self.labor_cost_per_good)

    @property
    def _worker_labor(self):
        """labor from workers, not counting equipment"""
        return self.labor_per_worker * len(self['workers'])

    @property
    def _equipment_labor(self):
        """how much labor can be generated by owned equipment, limited by number of workers
        (one worker is required to operate one piece of equipment)"""
        return min(len(self['workers']),
                   self['equipment']) * self.labor_per_equipment

    @property
    def _labor(self):
        """total productive labor"""
        return self._worker_labor + self._equipment_labor

    def _labor_for_equipment(self, equipment):
        """hypothetical labor that could be produced by some amount of equipment,
        limited by number of workers"""
        return self._worker_labor + min(len(self['workers']),
                                        equipment) * self.labor_per_equipment

    @asyncio.coroutine
    def fire(self, worker):
        self['workers'].remove(worker)
        yield from worker.call('quit')

    @asyncio.coroutine
    def hire(self, applicants, wage):
        hired = []
        while self['worker_change'] > 0 and applicants:
            worker = random.choice(applicants)
            employer = yield from worker.get('employer')
            if employer is not None:
                yield from employer.call('fire', worker)
            yield from worker.call('hire', AgentProxy(self), wage)
            applicants.remove(worker)
            self['workers'].append(worker)
            hired.append(worker)
            self['worker_change'] -= 1

        # increase wage to attract more employees
        if self['worker_change'] > 0:
            wage += self.wage_increment
        return hired, self['worker_change'], wage

    @asyncio.coroutine
    def shutdown(self):
        for worker in self['workers']:
            yield from self.fire(worker)

    def produce(self, world):
        """produce the firm's product. the firm will produce the desired supply if possible,
        otherwise, they will produce as much as they can."""

        # limit desired supply to what can be produced given current capacity
        self['supply'] = max(
            1, min(self['desired_supply'], self._production_capacity))

        # set desired price
        wages = 0
        for w in self['workers']:
            wages += (yield from w.get('wage'))
        self['costs'] += wages
        self['cash'] -= wages
        cost_per_unit = self['costs'] / self['supply']
        self['price'] = max(0, cost_per_unit + self['profit_margin'])

        return self['supply'], self['price']

    @asyncio.coroutine
    def sell(self, quantity):
        n_sold = min(self['supply'], quantity)
        self['supply'] -= n_sold
        self['n_sold'] += n_sold
        self['revenue'] = self['price'] * n_sold
        self['cash'] += self['revenue']
        return n_sold

    @property
    def curren(self):
        """represent as a discrete state"""
        if self['n_sold'] == 0:
            return 0
        elif self['n_sold'] > 0 and self['leftover'] > 0:
            return 1
        elif self['n_sold'] > 0 and self[
                'leftover'] == 0 and self['profit'] <= 0:
            return 2
        elif self['n_sold'] > 0 and self['leftover'] == 0 and self[
                'profit'] > 0 and self['profit'] - self['prev_profit'] < 0:
            return 3
        elif self['n_sold'] > 0 and self['leftover'] == 0 and self[
                'profit'] > 0 and self['profit'] - self['prev_profit'] >= 0:
            return 4

    def reward(self, state):
        """the discrete states we map to are the reward values, so just return that"""
        return state

    @property
    def actions(self):
        """these actions are possible from any state"""
        return [{
            'supply': self.supply_increment
        }, {
            'supply': -self.supply_increment
        }, {
            'supply': self.supply_increment,
            'profit_margin': self.profit_increment
        }, {
            'supply': self.supply_increment,
            'profit_margin': -self.profit_increment
        }, {
            'supply': -self.supply_increment,
            'profit_margin': self.profit_increment
        }, {
            'supply': -self.supply_increment,
            'profit_margin': -self.profit_increment
        }]

    def assess_assets(self, required_labor, mean_wage, mean_equip_price):
        """identify desired mixture of productive assets, i.e. workers, equipment, and wage"""
        down_wage_pressure = self.wage_increment

        def objective(x):
            n_workers, wage, n_equipment = x
            return n_workers * wage + n_equipment * mean_equip_price

        def constraint(x):
            n_workers, wage, n_equipment = x
            equip_labor = min(n_workers * self.labor_per_equipment,
                              n_equipment * self.labor_per_equipment)
            return n_workers * self.labor_per_worker + equip_labor - required_labor

        results = optimize.minimize(objective, (1, 0, 0),
                                    constraints=[{
                                        'type': 'ineq',
                                        'fun': constraint
                                    }, {
                                        'type': 'ineq',
                                        'fun': lambda x: x[0]
                                    }, {
                                        'type':
                                        'ineq',
                                        'fun':
                                        lambda x: x[1] -
                                        (mean_wage - down_wage_pressure)
                                    }, {
                                        'type': 'ineq',
                                        'fun': lambda x: x[2]
                                    }],
                                    options={'maxiter': 20})
        n_workers, wage, n_equipment = np.ceil(results.x).astype(np.int)
        return n_workers, wage, n_equipment

    @asyncio.coroutine
    def purchase_equipment(self, supplier):
        price, supply = yield from supplier.get('price', 'supply')
        total_equipment_cost = (self['desired_equipment'] -
                                self['equipment']) * price

        if not total_equipment_cost:
            n_equipment = max(0, self['desired_equipment'] - self['equipment'])
        else:
            equipment_budget = max(0, min(self['cash'], total_equipment_cost))

            # how much equipment can be purchased
            n_equipment = math.floor(equipment_budget / price)

        to_purchase = min(supply, n_equipment)
        yield from supplier.call('sell', to_purchase)
        self['equipment'] += to_purchase
        cost = to_purchase * price
        self.pay(cost)
        return self['desired_equipment'] - self['equipment'], to_purchase

    @asyncio.coroutine
    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.curren)
        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

        # figure out labor goal
        required_labor = self['desired_supply'] * self.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
        n_workers = max(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:
            worker = random.choice(self['workers'])
            yield from self.fire(worker)
            self['worker_change'] += 1

        # job vacancies
        return self['worker_change'], wage
Example #11
0
class Government(Agent):
    def __init__(self, tax_rate, welfare, tax_rate_increment, welfare_increment, starting_welfare_req):
        self._state = {'cash': 0}
        self.tax_rate = tax_rate
        self.tax_rate_increment = tax_rate_increment
        self.welfare = welfare
        self.welfare_increment = welfare_increment
        self.welfare_req = starting_welfare_req
        self.subsidies = {
            ConsumerGoodFirm: 0,
            CapitalEquipmentFirm: 0,
            RawMaterialFirm: 0,
            Hospital: 0
        }
        self.altruism = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(3)}
        self.learner = QLearner(states_actions, self.reward, discount=0.5, explore=0.01, learning_rate=0.5)

        # keep track of previous step's quality of life for comparison
        self.prev_qol = 0

    @property
    def cash(self):
        return self._state['cash']

    @cash.setter
    def cash(self, value):
        self._state['cash'] = value

    @property
    def actions(self):
        """these actions are possible from any state"""
        return [
            {'tax_rate': self.tax_rate_increment},
            {'tax_rate': -self.tax_rate_increment},
            {'tax_rate': self.tax_rate_increment, 'welfare': self.welfare_increment},
            {'tax_rate': self.tax_rate_increment, 'welfare': -self.welfare_increment},
            {'tax_rate': -self.tax_rate_increment, 'welfare': self.welfare_increment},
            {'tax_rate': -self.tax_rate_increment, 'welfare': -self.welfare_increment}
        ]

    def current_state(self, households):
        """represent as a discrete state"""
        qol = sum(h.quality_of_life for h in households)/len(households) if households else 0

        if qol <= 0:
            return 0
        elif qol > 0 and qol - self.prev_qol <= 0:
            return 1
        elif qol > 0 and qol - self.prev_qol > 0:
            return 2

    def reward(self, state):
        """the discrete states we map to are the reward values, so just return that"""
        return state

    def adjust(self, households):
        action = self.learner.choose_action(self.current_state(households))
        action = self.actions[action]
        self.tax_rate = min(1, max(0, self.tax_rate + action.get('tax_rate', 0)))
        max_per_person = self.cash/sum(len(h.people) for h in households if h.income <= self.welfare_req) if households else 0
        self.welfare = min(max(0, self.welfare + action.get('welfare', 0)), max_per_person)
        self.prev_qol = sum(h.quality_of_life for h in households)/len(households) if households else 0

    def apply_proposal(self, proposal, world):
        t = proposal['type']
        v = float(proposal['value']) if proposal.get('value') is not None else None
        if t == ProposalType.nationalize.name:
            industry = proposal['target']
            firm = random.choice(world.firms_of_type(industries[industry]))
            firm.change_owner(self)
        elif t == ProposalType.privatize.name:
            industry = proposal['target']
            firm = random.choice(world.firms_of_type(industries[industry]))

            # randomly pick new owner
            # we pick the person with the most money who does not already have a firm
            candidates = sorted([p for p in world.people if not p._state['firm_owner']], key=lambda p: p._state['cash'], reverse=True)
            new_owner = candidates[0]
            firm.change_owner(new_owner)
        elif t == ProposalType.tax_rate.name:
            self.tax_rate = v
        elif t == ProposalType.welfare.name:
            self.welfare = v
        elif t == ProposalType.welfare_req.name:
            self.welfare_req = v
        elif t == ProposalType.subsidy.name:
            self.subsidies[industries[proposal['target']]] = v

    def proposal_options(self, world):
        options = [{
            'type': ProposalType.tax_rate.name,
            'name': 'adjust tax rate',
            'description': 'Adjust the tax rate for all income and corporate profits',
            'values': [0, 1],
            'targets': None,
            'value': self.tax_rate
        }, {
            'type': ProposalType.welfare.name,
            'name': 'adjust welfare',
            'description': 'Set the amount of cash distributed to every citizen who makes less than the welfare requirement',
            'values': [0, None],
            'targets': None,
            'value': self.welfare
        }, {
            'type': ProposalType.welfare_req.name,
            'name': 'adjust welfare income threshold',
            'description': 'Citizens who make less than this income will receive welfare',
            'values': [0, None],
            'targets': None,
            'value': self.welfare_req
        }, {
            'type': ProposalType.subsidy.name,
            'name': 'adjust industry subsidy',
            'description': 'Set the amount of cash government gives a particular industry',
            'values': [0, None],
            'targets': list(industries.keys()),
            'value': None
        }]

        private_industries = self.filter_industries(world, public=False)
        public_industries = self.filter_industries(world, public=True)
        if public_industries:
            options.append({
                'type': ProposalType.privatize.name,
                'name': 'privatize a public firm',
                'description': 'Release a national firm into private management',
                'values': None,
                'targets': public_industries,
                'value': None
            })
        if private_industries:
            options.append({
                'type': ProposalType.nationalize.name,
                'name': 'nationalize a private firm',
                'description': 'Put a private firm into the control of the people',
                'values': None,
                'targets': private_industries,
                'value': None
            })

        return options

    def filter_industries(self, world, public=False):
        """return industries that have firms in them"""
        return [ind for ind, typ in industries.items() if [f for f in world.firms_of_type(typ) if f.public == public]]

    def as_json(self):
        return {
            'tax_rate': self.tax_rate,
            'welfare': self.welfare,
            'welfare_req': self.welfare_req,
            'subsidies': {k.__name__: v for k, v in self.subsidies.items()}
        }
Example #12
0
class Government(Agent):
    def __init__(self, tax_rate, welfare, tax_rate_increment,
                 welfare_increment, starting_welfare_req):
        self._state = {'cash': 0}
        self.tax_rate = tax_rate
        self.tax_rate_increment = tax_rate_increment
        self.welfare = welfare
        self.welfare_increment = welfare_increment
        self.welfare_req = starting_welfare_req
        self.subsidies = {
            ConsumerGoodFirm: 0,
            CapitalEquipmentFirm: 0,
            RawMaterialFirm: 0,
            Hospital: 0
        }
        self.altruism = 0

        # all states map to the same actions
        action_ids = [i for i in range(len(self.actions))]
        states_actions = {s: action_ids for s in range(3)}
        self.learner = QLearner(states_actions,
                                self.reward,
                                discount=0.5,
                                explore=0.01,
                                learning_rate=0.5)

        # keep track of previous step's quality of life for comparison
        self.prev_qol = 0

    @property
    def cash(self):
        return self._state['cash']

    @cash.setter
    def cash(self, value):
        self._state['cash'] = value

    @property
    def actions(self):
        """these actions are possible from any state"""
        return [{
            'tax_rate': self.tax_rate_increment
        }, {
            'tax_rate': -self.tax_rate_increment
        }, {
            'tax_rate': self.tax_rate_increment,
            'welfare': self.welfare_increment
        }, {
            'tax_rate': self.tax_rate_increment,
            'welfare': -self.welfare_increment
        }, {
            'tax_rate': -self.tax_rate_increment,
            'welfare': self.welfare_increment
        }, {
            'tax_rate': -self.tax_rate_increment,
            'welfare': -self.welfare_increment
        }]

    def current_state(self, households):
        """represent as a discrete state"""
        qol = sum(h.quality_of_life
                  for h in households) / len(households) if households else 0

        if qol <= 0:
            return 0
        elif qol > 0 and qol - self.prev_qol <= 0:
            return 1
        elif qol > 0 and qol - self.prev_qol > 0:
            return 2

    def reward(self, state):
        """the discrete states we map to are the reward values, so just return that"""
        return state

    def adjust(self, households):
        action = self.learner.choose_action(self.current_state(households))
        action = self.actions[action]
        self.tax_rate = min(1, max(0,
                                   self.tax_rate + action.get('tax_rate', 0)))
        max_per_person = self.cash / sum(
            len(h.people) for h in households
            if h.income <= self.welfare_req) if households else 0
        self.welfare = min(max(0, self.welfare + action.get('welfare', 0)),
                           max_per_person)
        self.prev_qol = sum(
            h.quality_of_life
            for h in households) / len(households) if households else 0

    def apply_proposal(self, proposal, world):
        t = proposal['type']
        v = float(
            proposal['value']) if proposal.get('value') is not None else None
        if t == ProposalType.nationalize.name:
            industry = proposal['target']
            firm = random.choice(world.firms_of_type(industries[industry]))
            firm.change_owner(self)
        elif t == ProposalType.privatize.name:
            industry = proposal['target']
            firm = random.choice(world.firms_of_type(industries[industry]))

            # randomly pick new owner
            # we pick the person with the most money who does not already have a firm
            candidates = sorted(
                [p for p in world.people if not p._state['firm_owner']],
                key=lambda p: p._state['cash'],
                reverse=True)
            new_owner = candidates[0]
            firm.change_owner(new_owner)
        elif t == ProposalType.tax_rate.name:
            self.tax_rate = v
        elif t == ProposalType.welfare.name:
            self.welfare = v
        elif t == ProposalType.welfare_req.name:
            self.welfare_req = v
        elif t == ProposalType.subsidy.name:
            self.subsidies[industries[proposal['target']]] = v

    def proposal_options(self, world):
        options = [{
            'type': ProposalType.tax_rate.name,
            'name': 'adjust tax rate',
            'description':
            'Adjust the tax rate for all income and corporate profits',
            'values': [0, 1],
            'targets': None,
            'value': self.tax_rate
        }, {
            'type': ProposalType.welfare.name,
            'name': 'adjust welfare',
            'description':
            'Set the amount of cash distributed to every citizen who makes less than the welfare requirement',
            'values': [0, None],
            'targets': None,
            'value': self.welfare
        }, {
            'type': ProposalType.welfare_req.name,
            'name': 'adjust welfare income threshold',
            'description':
            'Citizens who make less than this income will receive welfare',
            'values': [0, None],
            'targets': None,
            'value': self.welfare_req
        }, {
            'type': ProposalType.subsidy.name,
            'name': 'adjust industry subsidy',
            'description':
            'Set the amount of cash government gives a particular industry',
            'values': [0, None],
            'targets': list(industries.keys()),
            'value': None
        }]

        private_industries = self.filter_industries(world, public=False)
        public_industries = self.filter_industries(world, public=True)
        if public_industries:
            options.append({
                'type': ProposalType.privatize.name,
                'name': 'privatize a public firm',
                'description':
                'Release a national firm into private management',
                'values': None,
                'targets': public_industries,
                'value': None
            })
        if private_industries:
            options.append({
                'type': ProposalType.nationalize.name,
                'name': 'nationalize a private firm',
                'description':
                'Put a private firm into the control of the people',
                'values': None,
                'targets': private_industries,
                'value': None
            })

        return options

    def filter_industries(self, world, public=False):
        """return industries that have firms in them"""
        return [
            ind for ind, typ in industries.items()
            if [f for f in world.firms_of_type(typ) if f.public == public]
        ]

    def as_json(self):
        return {
            'tax_rate': self.tax_rate,
            'welfare': self.welfare,
            'welfare_req': self.welfare_req,
            'subsidies': {k.__name__: v
                          for k, v in self.subsidies.items()}
        }