def produce(self, production_function, input_goods): """ Produces output goods given the specified amount of inputs. Transforms the Agent's goods specified in input goods according to a given production_function to output goods. Automatically changes the agent's belonging. Raises an exception, when the agent does not have sufficient resources. Args: production_function: A production_function produced with py:meth:`~abceagent.FirmMultiTechnologies..create_production_function`, py:meth:`~abceagent.FirmMultiTechnologies..create_cobb_douglas` or py:meth:`~abceagent.FirmMultiTechnologies..create_leontief` input goods {dictionary}: dictionary containing the amount of input good used for the production. Raises: NotEnoughGoods: This is raised when the goods are insufficient. Example:: car = {'tire': 4, 'metal': 2000, 'plastic': 40} bike = {'tire': 2, 'metal': 400, 'plastic': 20} try: self.produce(car_production_function, car) except NotEnoughGoods: A.produce(bike_production_function, bike) """ if production_function.use == 'all': for good in list(input_goods.keys()): if self._inventory[good] < input_goods[good] - epsilon: raise NotEnoughGoods( self.name, good, (input_goods[good] - self._inventory[good])) for good in input_goods: self._inventory.haves[good] -= input_goods[good] else: for good in list(production_function.use.keys()): if self._inventory[good] < input_goods[good] - epsilon: raise NotEnoughGoods( self.name, good, (input_goods[good] - self._inventory[good])) for good, use in production_function.use.items(): self._inventory.haves[good] -= input_goods[good] * use output_dict = production_function.production(input_goods) for good in list(output_dict.keys()): self._inventory.haves[good] += output_dict[good] return output_dict
def reserve(self, good, quantity): self.reserved[good] += quantity if self.reserved[good] > self.haves[good]: self.reserved[good] += quantity raise NotEnoughGoods( self.name, good, quantity - (self.haves[good] - self.reserved[good]))
def reserve(self, good, quantity): self._reserved[good] += quantity if self._reserved[good] > self.haves[good]: if isclose(self._reserved[good], self.haves[good]): self._reserved[good] = self.haves[good] else: self._reserved[good] -= quantity raise NotEnoughGoods( self.name, good, quantity - (self.haves[good] - self._reserved[good]))
def pay_contract(self, contract): """ delivers on a contract """ assert contract.pay_group == self.group and contract.pay_id == self.id money = contract.quantity * contract.price available = self._haves['money'] if money > available + epsilon + epsilon * max(money, available): raise NotEnoughGoods(self.name, 'money', money - available) if money > available: money = available self._haves['money'] -= money self._send(contract.deliver_good_group, contract.deliver_good_id, '_dp', contract)
def deliver_contract(self, contract): """ delivers on a contract """ assert contract.deliver_good_group == self.group and contract.deliver_good_id == self.id quantity = contract.quantity available = self._haves[contract.good] if quantity > available + epsilon + epsilon * max(quantity, available): raise NotEnoughGoods(self.name, contract.good, quantity - available) if quantity > available: quantity = available self._haves[contract.good] -= quantity self._send(contract.pay_group, contract.pay_id, '_dp', contract)
def give(self, receiver, good, quantity, epsilon=epsilon): """ gives a good to another agent Args: receiver: The name of the receiving agent a tuple (group, id). e.G. ('firm', 15) good: the good to be transfered quantity: amount to be transfered epsilon (optional): if you have floating point errors, a quantity or prices is a fraction of number to high or low. You can increase the floating point tolerance. See troubleshooting -- floating point problems Raises: AssertionError, when good smaller than 0. Return: Dictionary, with the transfer, which can be used by self.log(...). Example:: self.log('taxes', self.give('money': 0.05 * self['money']) """ assert quantity > -epsilon, 'quantity %.30f is smaller than 0 - epsilon (%.30f)' % ( quantity, -epsilon) if quantity < 0: quantity = 0 available = self._inventory[good] if quantity > available + epsilon + epsilon * max(quantity, available): raise NotEnoughGoods(self.name, good, quantity - available) if quantity > available: quantity = available self._inventory.haves[good] -= quantity self.send(receiver, 'abce_receive_good', [good, quantity]) return {good: quantity}
def destroy(self, good, quantity=None): """ destroys quantity of the good. If quantity is omitted destroys all Args:: 'good': is the name of the good quantity (optional): number Raises:: NotEnoughGoods: when goods are insufficient """ if quantity is None: self._haves[good] = 0 else: self._haves[good] -= quantity if self._haves[good] < 0: self._haves[good] = 0 raise NotEnoughGoods(self.name, good, quantity - self._haves[good])
def destroy(self, good, quantity=None): """ destroys quantity of the good. If quantity is omitted destroys all Use with care. Args:: 'good': is the name of the good quantity (optional): number Raises:: NotEnoughGoods: when goods are insufficient """ if quantity is None: self[good] = 0 else: assert quantity >= 0.0 available = self[good] if available < quantity - epsilon: raise NotEnoughGoods(self.name, good, quantity - available) self[good] -= quantity
def accept(self, offer, quantity=-999, epsilon=epsilon): """ The buy or sell offer is accepted and cleared. If no quantity is given the offer is fully accepted; If a quantity is given the offer is partial accepted. Args: offer: the offer the other party made quantity: quantity to accept. If not given all is accepted epsilon (optional): if you have floating point errors, a quantity or prices is a fraction of number to high or low. You can increase the floating point tolerance. See troubleshooting -- floating point problems Return: Returns a dictionary with the good's quantity and the amount paid. """ offer_quantity = offer.quantity if quantity == -999: quantity = offer_quantity assert quantity > - epsilon, 'quantity %.30f is smaller than 0 - epsilon (%.30f)' % (quantity, - epsilon) if quantity < 0: quantity = 0 if quantity > offer_quantity + epsilon * max(quantity, offer_quantity): raise AssertionError('accepted more than offered %s: %.100f >= %.100f' % (offer.good, quantity, offer_quantity)) if quantity > offer_quantity: quantity = offer_quantity if quantity == 0: self.reject(offer) return {offer.good: 0, offer.currency: 0} money_amount = quantity * offer.price if offer.sell: # ord('s') assert money_amount > - epsilon, 'money = quantity * offer.price %.30f is smaller than 0 - epsilon (%.30f)' % (money_amount, - epsilon) if money_amount < 0: money_amount = 0 available = self._haves[offer.currency] if money_amount > available + epsilon + epsilon * max(money_amount, available): raise NotEnoughGoods(self.name, offer.currency, money_amount - available) if money_amount > available: money_amount = available self._haves[offer.good] += quantity self._haves[offer.currency] -= quantity * offer.price else: assert quantity > - epsilon, 'quantity %.30f is smaller than 0 - epsilon (%.30f)' % (quantity, - epsilon) if quantity < 0: quantity = 0 available = self._haves[offer.good] if quantity > available + epsilon + epsilon * max(quantity, available): raise NotEnoughGoods(self.name, offer.good, quantity - available) if quantity > available: quantity = available self._haves[offer.good] -= quantity self._haves[offer.currency] += quantity * offer.price offer.final_quantity = quantity self._send(offer.sender_group, offer.sender_id, '_p', (offer.id, quantity)) del self._polled_offers[offer.id] if offer.sell: # ord('s') return {offer.good: - quantity, offer.currency: money_amount} else: return {offer.good: quantity, offer.currency: - money_amount}
def buy(self, receiver, good, quantity, price, currency='money', epsilon=epsilon): """ commits to sell the quantity of good at price The goods are not in haves or self.count(). When the offer is rejected it is automatically re-credited. When the offer is accepted the money amount is credited. (partial acceptance accordingly) Args: receiver: The name of the receiving agent a tuple (group, id). e.G. ('firm', 15) 'good': name of the good quantity: maximum units disposed to buy at this price price: price per unit currency: is the currency of this transaction (defaults to 'money') epsilon (optional): if you have floating point errors, a quantity or prices is a fraction of number to high or low. You can increase the floating point tolerance. See troubleshooting -- floating point problems """ assert price > - epsilon, 'price %.30f is smaller than 0 - epsilon (%.30f)' % (price, - epsilon) if price < 0: price = 0 money_amount = quantity * price # makes sure the money_amount is between zero and maximum available, but # if its only a little bit above or below its set to the bounds available = self._haves[currency] assert money_amount > - epsilon, '%s (price * quantity) %.30f is smaller than 0 - epsilon (%.30f)' % (currency, money_amount, - epsilon) if money_amount < 0: money_amount = 0 if money_amount > available + epsilon + epsilon * max(money_amount, available): raise NotEnoughGoods(self.name, currency, money_amount - available) if money_amount > available: money_amount = available offer_id = self._offer_counter() self._haves[currency] -= money_amount offer = Offer(self.group, self.id, receiver[0], receiver[1], good, quantity, price, currency, False, 'new', -1, offer_id, self.round, -1) self._send(receiver[0], receiver[1], '!b', offer) self.given_offers[offer_id] = offer return offer
def sell(self, receiver, good, quantity, price, currency='money', epsilon=epsilon): """ commits to sell the quantity of good at price The good is not available for the agent. When the offer is rejected it is automatically re-credited. When the offer is accepted the money amount is credited. (partial acceptance accordingly) Args: receiver_group: group of the receiving agent receiver_id: number of the receiving agent 'good': name of the good quantity: maximum units disposed to buy at this price price: price per unit currency: is the currency of this transaction (defaults to 'money') epsilon (optional): if you have floating point errors, a quantity or prices is a fraction of number to high or low. You can increase the floating point tolerance. See troubleshooting -- floating point problems Returns: A reference to the offer. The offer and the offer status can be accessed with `self.info(offer_reference)`. Example:: def subround_1(self): self.offer = self.sell('household', 1, 'cookies', quantity=5, price=0.1) def subround_2(self): offer = self.info(self.offer) if offer.status == 'accepted': print(offer.final_quantity , 'cookies have be bougth') else: offer.status == 'rejected': print('On diet') """ assert price > - epsilon, 'price %.30f is smaller than 0 - epsilon (%.30f)' % (price, - epsilon) if price < 0: price = 0 # makes sure the quantity is between zero and maximum available, but # if its only a little bit above or below its set to the bounds available = self._haves[good] assert quantity > - epsilon, 'quantity %.30f is smaller than 0 - epsilon (%.30f)' % (quantity, - epsilon) if quantity < 0: quantity = 0 if quantity > available + epsilon + epsilon * max(quantity, available): raise NotEnoughGoods(self.name, good, quantity - available) if quantity > available: quantity = available offer_id = self._offer_counter() self._haves[good] -= quantity offer = Offer(self.group, self.id, receiver[0], receiver[1], good, quantity, price, currency, True, 'new', -2, offer_id, self.round, -2) self.given_offers[offer_id] = offer self._send(receiver[0], receiver[1], '!s', offer) return offer
def sufficient_goods(self, input_goods): """ checks whether the agent has all the goods in the vector input """ for good in input_goods: if self._haves[good] < input_goods[good] - epsilon: raise NotEnoughGoods( self.name, good, input_goods[good] - self._haves[good])