i think i fixed things

This commit is contained in:
2023-01-14 14:13:10 +01:00
parent 8801ebaf58
commit 166c96e66d
8 changed files with 255 additions and 22 deletions

View File

@@ -11,7 +11,3 @@ class TradingAgent(BaseAgent):
name = "TradingAgent"
class TradingAgentLogic():
"""Logic for trading agent"""
def __init__(self,) -> None:
pass

View File

@@ -76,8 +76,7 @@ class Registry:
See Registry class docstring for example.
"""
if cls_name.lower() not in self._lookup:
raise KeyError('"{}" is not a name of a registered class'.format(cls_name))
if cls_name.lower() not in self._lookup: raise KeyError('"{}" is not a name of a registered class'.format(cls_name))
return self._lookup[cls_name.lower()]
def has(self, cls_name):

View File

@@ -37,7 +37,7 @@ class ContinuousDoubleAuction(BaseComponent):
name = "ContinuousDoubleAuction"
component_type = "Trade"
required_entities = ["Coin", "Labor"]
agent_subclasses = ["BasicMobileAgent"]
agent_subclasses = ["BasicMobileAgent","TradingAgent"]
def __init__(
self,
@@ -417,7 +417,7 @@ class ContinuousDoubleAuction(BaseComponent):
"""
# This component adds 2*(1+max_bid_ask)*n_resources possible actions:
# buy/sell x each-price x each-resource
if agent_cls_name == "BasicMobileAgent":
if agent_cls_name in self.agent_subclasses:
trades = []
for c in self.commodities:
trades.append(

View File

@@ -79,6 +79,6 @@ class Gem(Resource):
name = "Gem"
color = np.array([241, 233, 219]) / 255.0
collectible = False
collectible = True
craft_recp= {"Gem_Raw": 1}
craft_labour_base= 1

View File

@@ -1,4 +1,6 @@
from . import(
simple_gather,
simple_build
simple_build,
crafting,
external_market
)

View File

@@ -47,7 +47,9 @@ class Craft(BaseComponent):
skill_dist="none",
**base_component_kwargs
):
assert len(commodities)>0
#setup commodities
self.recip_map={}
for v in commodities:
res_class=resource_registry.get(v)
res=res_class()
@@ -56,6 +58,7 @@ class Craft(BaseComponent):
assert res.craft_recp!={}
assert res.craft_labour_base >= 0
self.required_entities.append(v)
self.recip_map[res.name]=res.craft_recp
self.commodities.append(res)
@@ -67,16 +70,17 @@ class Craft(BaseComponent):
assert self.max_skill_labour_benefit <= 1
self.skill_dist = skill_dist.lower()
assert self.skill_dist in ["none", "pareto", "lognormal"]
assert self.skill_dist in ["none", "pareto"]
self.sampled_skills = {}
self.builds = []
super().__init__(*base_component_args, **base_component_kwargs)
def agent_can_build(self, agent, recipe):
def agent_can_build(self, agent, res):
"""Return True if agent can actually build in its current location."""
# See if the agent has the resources necessary to complete the action
recipe= self.recip_map[res]
for resource, cost in recipe.items():
if agent.state["inventory"][resource] < cost:
return False
@@ -171,9 +175,10 @@ class Craft(BaseComponent):
obs_dict = dict()
for agent in self.world.agents:
if agent.name in self.agent_subclasses:
obs_dict[agent.idx]["craft_skill"]={}
obs_dict[agent.idx]={}
for k in self.commodities:
obs_dict[agent.idx]["craft_skill"][k.name] = agent.state["craft_skill"][k.name]
obs_dict[agent.idx]["craft_skill_{}".format(k.name)] = agent.state["craft_skill"][k.name]
return obs_dict
@@ -238,7 +243,7 @@ class Craft(BaseComponent):
for agent in world.agents:
if agent.name not in self.agent_subclasses | agent.is_setup:
if (agent.name not in self.agent_subclasses) | agent.is_setup:
continue
agent.state["craft_skill"]={}
agent.state["craft_labour"]={}

View File

@@ -0,0 +1,220 @@
# Copyright (c) 2020, salesforce.com, inc.
# All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
# For full license text, see the LICENSE file in the repo root
# or https://opensource.org/licenses/BSD-3-Clause
import numpy as np
from ai_economist.foundation.base.base_component import (
BaseComponent,
component_registry,
)
@component_registry.add
class ExternalMarket(BaseComponent):
"""
Allows mobile agents to build house landmarks in the world using stone and wood,
earning income.
Can be configured to include heterogeneous building skill where agents earn
different levels of income when building.
Args:
payment (int): Default amount of coin agents earn from building.
Must be >= 0. Default is 10.
market_demand (dict): Resource name -> amout of money
skill_dist (str): Distribution type for sampling skills. Default ("none")
gives all agents identical skill equal to a multiplier of 1. "pareto" and
"lognormal" sample skills from the associated distributions.
build_labor (float): Labor cost associated with building a house.
Must be >= 0. Default is 10.
"""
name = "ExternalMarket"
component_type = "Trade"
required_entities = ["Coin", "Labor"]
agent_subclasses = ["TradingAgent"]
def __init__(
self,
*base_component_args,
market_demand={},
trade_labor=1.0,
**base_component_kwargs
):
super().__init__(*base_component_args, **base_component_kwargs)
self.market_demand = market_demand
self.action_res_map={}
for k in market_demand.keys():
self.action_res_map[len(self.action_res_map)+1]=k
self.trade_labor = float(trade_labor)
assert self.trade_labor >= 0
self.builds = []
def agent_can_sell(self, agent,res):
"""Return True if agent can sell a res."""
# See if the agent has the resources necessary to complete the action
if agent.state["inventory"][res]>= 1:
return True
return False
# Required methods for implementing components
# --------------------------------------------
def get_n_actions(self, agent_cls_name):
"""
See base_component.py for detailed description.
Add a single action (build) for mobile agents.
"""
# This component adds 1 action that mobile agents can take: build a house
if agent_cls_name in self.agent_subclasses:
return len(self.action_res_map)
return None
def get_additional_state_fields(self, agent_cls_name):
"""
See base_component.py for detailed description.
For mobile agents, add state fields for building skill.
"""
return {}
def component_step(self):
"""
See base_component.py for detailed description.
Convert stone+wood to house+coin for agents that choose to build and can.
"""
world = self.world
build = []
# Apply any building actions taken by the mobile agents
for agent in world.get_random_order_agents():
action = agent.get_component_action(self.name)
# This component doesn't apply to this agent!
if action is None:
continue
# NO-OP!
if action == 0:
pass
action-=1
res_name=self.action_res_map[action]
# Build! (If you can.)
if self.agent_can_sell(agent,res_name):
# Remove the resources
agent.state["inventory"][res_name] -= 1
# Receive payment for the house
agent.state["inventory"]["Coin"] += self.market_demand[res_name]
# Incur the labor cost for building
agent.state["endogenous"]["Labor"] += self.trade_labor
build.append(
{
"seller": agent.idx,
"commodity": res_name,
"income": self.market_demand[res_name],
}
)
else:
raise ValueError
self.builds.append(build)
def generate_observations(self):
"""
See base_component.py for detailed description.
Here, agents observe their build skill. The planner does not observe anything
from this component.
"""
obs_dict = dict()
for agent in self.world.agents:
if agent.name in self.agent_subclasses:
obs_dict[agent.idx] = {}
for res_name,coin in self.market_demand.items():
obs_dict[agent.idx]["external_{}_price".format(res_name)]: self.inv_scale*coin
return obs_dict
def generate_masks(self, completions=0):
"""
See base_component.py for detailed description.
Prevent building only if a landmark already occupies the agent's location.
"""
masks = {}
# Mobile agents' build action is masked if they cannot build with their
# current location and/or endowment
for agent in self.world.agents:
mask=[]
for res in self.market_demand:
mask.append(self.agent_can_sell(agent,res))
masks[agent.idx] = mask
return masks
# For non-required customization
# ------------------------------
def get_metrics(self):
"""
Metrics that capture what happened through this component.
Returns:
metrics (dict): A dictionary of {"metric_name": metric_value},
where metric_value is a scalar.
"""
world = self.world
build_stats = {a.idx: {"n_builds": 0} for a in world.agents}
for builds in self.builds:
for build in builds:
idx = build["builder"]
build_stats[idx]["n_builds"] += 1
out_dict = {}
for a in world.agents:
for k, v in build_stats[a.idx].items():
out_dict["{}/{}".format(a.idx, k)] = v
num_houses = np.sum(world.maps.get("House") > 0)
out_dict["total_builds"] = num_houses
return out_dict
def additional_reset_steps(self):
"""
See base_component.py for detailed description.
Re-sample agents' building skills.
"""
self.builds = []
def get_dense_log(self):
"""
Log builds.
Returns:
builds (list): A list of build events. Each entry corresponds to a single
timestep and contains a description of any builds that occurred on
that timestep.
"""
return self.builds

25
main.py
View File

@@ -8,6 +8,7 @@ from sb3_contrib.ppo_mask import MaskablePPO
import envs
import wrapper
import resources
from agents import trading_agent
from wrapper.base_econ_wrapper import BaseEconWrapper
from wrapper.reciever_econ_wrapper import RecieverEconWrapper
from wrapper.sb3_econ_converter import SB3EconConverter
@@ -36,11 +37,14 @@ env_config = {
# The order in which components reset, step, and generate obs follows their listed order below.
'components': [
# (1) Building houses
('SimpleCraft', {'skill_dist': "none", 'payment_max_skill_multiplier': 3}),
('Craft', {'skill_dist': "pareto", 'commodities': ["Gem"]}),
# (2) Trading collectible resources
#('ContinuousDoubleAuction', {'max_num_orders': 10}),
('ContinuousDoubleAuction', {'max_num_orders': 10}),
# (3) Movement and resource collection
('SimpleGather', {}),
('ExternalMarket',{'market_demand':{
'Gem': 10
}}),
],
# ===== SCENARIO CLASS ARGUMENTS =====
@@ -51,7 +55,7 @@ env_config = {
# ===== STANDARD ARGUMENTS ======
# kwargs that are used by every Scenario class (i.e. defined in BaseEnvironment)
'agent_composition': {"BasicMobileAgent": 20}, # Number of non-planner agents (must be > 1)
'agent_composition': {"BasicMobileAgent": 20,"TradingAgent":5}, # Number of non-planner agents (must be > 1)
'world_size': [5, 5], # [Height, Width] of the env world
'episode_length': 256, # Number of timesteps per episode
'allow_observation_scaling': True,
@@ -88,11 +92,14 @@ eval_env_config = {
# The order in which components reset, step, and generate obs follows their listed order below.
'components': [
# (1) Building houses
('SimpleCraft', {'skill_dist': "none", 'payment_max_skill_multiplier': 3}),
('Craft', {'skill_dist': "pareto", 'commodities': ["Gem"]}),
# (2) Trading collectible resources
#('ContinuousDoubleAuction', {'max_num_orders': 10}),
('ContinuousDoubleAuction', {'max_num_orders': 10}),
# (3) Movement and resource collection
('SimpleGather', {}),
('ExternalMarket',{'market_demand':{
'Gem': 10
}}),
],
# ===== SCENARIO CLASS ARGUMENTS =====
@@ -103,7 +110,7 @@ eval_env_config = {
# ===== STANDARD ARGUMENTS ======
# kwargs that are used by every Scenario class (i.e. defined in BaseEnvironment)
'agent_composition': {"BasicMobileAgent": 20}, # Number of non-planner agents (must be > 1)
'agent_composition': {"BasicMobileAgent": 20,"TradingAgent":5}, # Number of non-planner agents (must be > 1)
'world_size': [1, 1], # [Height, Width] of the env world
'episode_length': 256, # Number of timesteps per episode
'allow_observation_scaling': True,
@@ -209,9 +216,13 @@ def printReplay(econ,agentid):
#Setup Env Objects
econ=foundation.make_env_instance(**env_config)
market=econ.get_component("ContinuousDoubleAuction")
action=market.get_n_actions("TradingAgent")
baseEconWrapper=BaseEconWrapper(econ)
baseEconWrapper.run()
mobileRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="BasicMobileAgent")
tradeRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="TradingAgent")
sb3Converter=SB3EconConverter(mobileRecieverEconWrapper,econ,"BasicMobileAgent")
#obs=sb3Converter.reset()
#vecenv=EconVecEnv(env_config=env_config)
@@ -261,7 +272,7 @@ while True:
#market=eval_econ.get_component("ContinuousDoubleAuction")
market=eval_econ.get_component("ContinuousDoubleAuction")
craft=eval_econ.get_component("SimpleCraft")
# trades=market.get_dense_log()
build=craft.get_dense_log()