Develop a new simulation (world)
A simulation is an embedded domain in which agents behave. It is
represented in NegMAS by a World
. This tutorial will take you
through the process of developing a simple simulation (world).
A World
in NegMAS is not just a multi-agent world simulation. It was
designed from the bottom up to simply the common tasks involved in
constructing negotiation driven simulations.
The simulation is divided into simulation steps (not to be confused with negotiation rounds). At every step, agents are allowed to act proactively by executing actions in the world, reading their state from the world, or requesting/running negotiations with other agents.
The negotiation process follows the following steps:
An agent requests a negotiation involving a set of agents (it may or may not be one of them) using either
request_negotiation
orrun_negotiation
methods (the later does not return until the negotiation is complete. TheWorld
can disable immediately running negotiations, or requesting negotiations that does not involve oneself, etc. The caller agent can also provide aNegotiator
to represent it in this negotiation.All partners in the negotiation are asked to accept to start the negotiation through calls to
respond_to_negotiation_request
. The can accept by returning aNegotiator
object to represent them or reject.Negotiations start depending on the rules given by the
World
. The default rule is to start negotiations only if ALL partners accepted the request but other rules like starting negotiations that are accepted by at least two partners are possible.The
Mechanism
objects are created by theWorld
and negotiators supplied by all partners (that agreed to join the negotiation) are added to it. TheWorld
designer can fix the type of the negotiation mechanism to be used (e.g.SAOMechanism
) or it can leave the choice to individual agents requesting negotiations.The negotiation is run and its result is recorded either as a failed negotiation (if no agreement is reached) or as a
Contract
if an agreement is reached.It is possible for world designers to either make
Contract
s binding upon agreement by having them automatically signed or to give agents one final chance to sign the contracts. Only signed contracts are considered binding.The
World
can also run a simulation that is affected by signed contracts. A common case is for contracts to have some executing time or a similar attribute that defines when do they become due.When a
Contract
becomes due, it is executed by theWorld
as part of the simulation it is controlling. In some cases, the contract cannot be executed and this is considered aBreach
.The world processes breaches and may decide to apply penalties to agents that caused them (if that makes sense for the specific simulation being conducted).
Agents are informed about the final state of due contracts (i.e. whether they are executed successfully or breached).
Development Process
To create a new world, you need to do the following steps:
Create a new
AgentWorldInterface
for your world (that inherits fromAWI
) and provide easy to use methods that agents can use to access your world. A common functionality of the AWI is to confirm that negotiations make sense. We will see an example later in this tutorialCreate a new
World
class that inherits from the baseWorld
and implement the required callback (your AWI from the first step will need to be registered in the world as will be shown later).Create a base
Agent
class for your world that inherits fromAgent
and provides the basic functionality shared by all agents in your world (if any)
The Trips World
Let’s consider a – not so simple – world. We have \(m\) agents representing people agreeing about where to spend their vacation repeatedly. Every holiday season, an agent can request to negotiate with one or more other agents trying to arrange a trip. Each trip is described by the following attributes:
Active: Binary issue (either an active trip with lots of hiking, running ,etc or not)
Duration: Integer issue (between 1 and 7 days)
Cost: Continuous range issue (between 0.0 and 1.0)
All agents involved in a negotiation must agree for the trip to be conducted. An agent can request at most \(n\) negotiations in a single season. Each agent has some fixed but unknown probability of not honoring its agreement and failing to show up for the trip and only when all agents involved in a trip show up does the trip actually happen with each agent receiving its utility value as defined by its unchanging utility function.
Let’s implement this world in NegMAS.
Implementing the Agent-World-Interface
We will start by defining the interface between the world and the agent as an AWI class
class AWI(AgentWorldInterface):
@property
def n_negs(self):
"""Number of negotiations an agent can start in a step (holiday season)"""
return self._world.neg_quota_step
@property
def agents(self):
"""List of all other agent IDs"""
return list(_ for _ in self._world.agents.keys() if _ != self.agent.id)
def request_negotiation(
self, partners: List[str], negotiator: SAONegotiator
) -> bool:
"""A convenient way to request negotiations"""
if self.agent.id not in partners:
partners.append(self.agent.id)
req_id = self.agent.create_negotiation_request(
issues=self._world.ISSUES,
partners=partners,
negotiator=negotiator,
annotation=dict(),
extra=dict(negotiator_id=negotiator.id),
)
return self.request_negotiation_about(
issues=self._world.ISSUES, partners=partners, req_id=req_id
)
The minimum here is to define a way for agents to request negotiations
form the world. The base AgentWorldInterface
has a
request_negotiation_about
method that can be used for this purpose
but it is too general and allows agents to set arbitrary issues and
negotiation mechanisms. Usually you will want to restrict the types of
negotiations allowed by defining a request_negotiation
method which
decides as much as possible for the agent.
This is done here using the following method:
def request_negotiation(self, partners: List[str], negotiator: SAONegotiator) -> bool:
...
Here the agent is asked to provide only a list of partners
and a
negotiator
to use.
The AWI will then make sure that the agent is added to the partners list if it was not already in it:
if self.agent.id not in partners:
partners.append(self.agent.id)
A negotiation request is then created using
create_negotiation_request
of the agent connected to the AWI which is used to keep track of which requests are out there and which are accepted/rejected
req_id = self.agent.create_negotiation_request(
issues=self._world.ISSUES,
partners=partners,
negotiator=negotiator,
annotation=dict(),
extra=dict(negotiator_id=negotiator.id),
)
Finally, the AWI requests the negotiation from the world using the base
request_negotiation_about
method.
return self.request_negotiation_about(
issues=self._world.ISSUES, partners=partners, req_id=req_id
)
Other than this commonly provided method, the AWI provides two
properties that can be accessed by the agent, agents
which returns
the IDs of all other agents in the world and n_negs
which gives
the total number of negotiations that the agent can start in a single
step (holiday season).
Implement the base world class
To implement the trips world, you will need to override the abstract
methods of World
. You will usually need also to override
__init__
to initialize your agent and join
to set up any agent
specific information you need to keep. Here is the full implementation:
class TripsWorld(World):
ISSUES = [
make_issue((0.0, 1.0), "cost"),
make_issue(2, "active"),
make_issue((1, 7), "duration"),
]
def __init__(self, *args, **kwargs):
"""Initialize the world"""
kwargs["awi_type"] = AWI
kwargs["negotiation_quota_per_step"] = kwargs.get(
"negotiation_quota_per_step", 8
)
kwargs["force_signing"] = True
kwargs["default_signing_delay"] = 0
super().__init__(*args, **kwargs)
self._contracts: Dict[int, List[Contract]] = defaultdict(list)
self._total_utility: Dict[str, float] = defaultdict(float)
self._ufuns: Dict[str, UtilityFunction] = dict()
self._breach_prob: Dict[str, float] = dict()
def join(
self,
x: Agent,
preferences: Preferences | None = None,
breach_prob: float | None = None,
**kwargs,
):
"""Define the ufun and breach-probability for each agent"""
super().join(x, **kwargs)
weights = np.random.rand(len(self.ISSUES)) - 0.5
x.ufun = (
LinearUtilityFunction(weights, reserved_value=0.0) if ufun is None else ufun
)
self._ufuns[x.id] = x.ufun
self._breach_prob[x.id] = random() * 0.1 if breach_prob is None else breach_prob
def simulation_step(self, stage: int = 0):
"""What happens in this world? Nothing"""
pass
def get_private_state(self, agent: Agent) -> dict:
"""What is the information available to agents? total utility points"""
return dict(total_utility=self._total_utility[agent.id])
def execute_action(
self, action: Action, agent: Agent, callback: Callable | None = None
) -> bool:
"""Executing actions by agents? No actions available"""
pass
def on_contract_signed(self, contract: Contract) -> None:
"""Save the contract to be executed in the following hoiday season (step)"""
super().on_contract_signed(contract)
self._contracts[self.current_step + 1].append(contract)
def executable_contracts(self):
"""What contracts are to be executed in the current step?
Ones that were signed the previous step"""
return self._contracts[self.current_step]
def order_contracts_for_execution(
self, contracts: Collection[Contract]
) -> Collection[Contract]:
"""What should be the order of contract execution? Random"""
shuffle(contracts)
return contracts
def start_contract_execution(self, contract: Contract) -> Optional[Set[Breach]]:
"""What should happen when a contract comes due?
1. Find out if it will be breached
2. If not, add to each agent its utility from the trip
"""
breaches = []
for aid in contract.partners:
if random() < self._breach_prob[aid]:
breaches.append(
Breach(
contract,
aid,
"breach",
victims=[_ for _ in contract.partners if _ != aid],
)
)
if len(breaches) > 0:
return set(breaches)
for aid in contract.partners:
self._total_utility[aid] += self._ufuns[aid](contract.agreement)
return set()
def complete_contract_execution(
self, contract: Contract, breaches: List[Breach], resolution: Contract
) -> None:
"""What happens if a breach was resolved? Nothing. They cannot"""
pass
def delete_executed_contracts(self) -> None:
"""Removes all contracts for the current step"""
if self._current_step in self._contracts.keys():
del self._contracts[self.current_step]
def contract_record(self, contract: Contract) -> Dict[str, Any]:
"""Convert the contract into a dictionary for saving"""
return to_flat_dict(contract)
def breach_record(self, breach: Breach) -> Dict[str, Any]:
"""Convert the breach into a dictionary for saving"""
return to_flat_dict(breach)
def contract_size(self, contract: Contract) -> float:
"""How good is a contract? Welfare"""
if contract.agreement is None:
return 0.0
return sum(self._ufuns[aid](contract.agreement) for aid in contract.partners)
def post_step_stats(self):
for aid, agent in self.agents.items():
self._stats[f"total_utility_{agent.name}"].append(self._total_utility[aid])
We will now inspect each of these methods in turn.
Constructing the world
The first thing to do when constructing the world in __init__
is to
call the World
class constructor forcing some of the parameters.
This is done here:
kwargs["awi_type"] = AWI
kwargs["negotiation_quota_per_step"] = kwargs.get("negotiation_quota_per_step", 8)
kwargs["force_signing"] = True
kwargs["default_signing_delay"] = 0
super().__init__(*args, **kwargs)
Of note is setting the awi_type
to the AWI
class we have just
created. This allows agents to access members of this class through
their awi
property as we will see later.
Moreover, we force the negotiation_quota_per_step
to be no more than
8 (the default is \(\inf\)) and force signing of all contracts which
will make contracts binding immediately once agreements are reached
through negotiation.
We then define four data-members that we keep track of:
self._contracts: Dict[int, List[Contract]] = defaultdict(list)
self._total_utility: Dict[str, float] = defaultdict(float)
self._ufuns: Dict[str, UtilityFunction] = dict()
self._breach_prob: Dict[str, float] = dict()
**_contracts** maps step number to the contracts to be executed in it.
**_total_utility, _ufuns, _breach_prob** maps agent ID to the total utility it currently has, its utility function and its breach probability (the probability of not showing up for a trip).
Joining the world
Agents join the world by calls to the join
method. * The first
thing to do is to call the base join
method of the World
class.
That is essential for the system to work properly. Whenever you
override a method that is not marked abstract, you must call the
base class version using super()
:
def join(
self,
x: Agent,
preferences: Preferences | None = None,
breach_prob: float | None = None,
**kwargs
):
"""Define the ufun and breach-probability for each agent"""
super().join(x, **kwargs)
...
We need to override the this method in order to set the utility function of the agent that just joined and its breach probability. In both cases, we use the value provided by the user if any and generate an appropriate random value if nothing is provided.
x.ufun = (
LinearUtilityFunction(np.random.rand(len(self.ISSUES)) - 0.5)
if ufun is None
else ufun
)
self._ufuns[x.id] = x.ufun
self._breach_prob[x.id] = random() * 0.1 if breach_prob is None else breach_prob
Simulation, action, and state
The TripsWorld
does not have a simulation. Nothing really happens in
this world. This means we can just do nothing in the simulation_step
method
def simulation_step(self, stage: int = 0):
"""What happens in this world? Nothing"""
pass
Every world needs to define what is the private state of an agent
(available to it through self.awi.state
). In our world, the private
state of an agent is the total utility it collected so far.
def get_private_state(self, agent: Agent) -> dict:
"""What is the information available to agents? total utility points"""
return dict(total_utility=self._total_utility[agent.id])
As we have no actual simulation, there are not actions that the agent
can execute in the world, so execute_action
does nothing.
def execute_action(
self, action: Action, agent: Agent, callback: Callable | None = None
) -> bool:
"""Executing actions by agents? No actions available"""
pass
Contract Management
The TripsWorld
is responsible of managing contracts. The base
World
class will take care of most of the process but it needs the
TripsWorld
to respond to some callbacks in order to manage contract
execution, storage, and optionally renegotiations of breached contracts.
The callbacks related to this are:
def on_contract_signed(self, contract: Contract) -> None:
...
def executable_contracts(self) -> Collection[Contract]:
...
def order_contracts_for_execution(
self, contracts: Collection[Contract]
) -> Collection[Contract]:
...
def start_contract_execution(self, contract: Contract) -> Optional[Set[Breach]]:
...
def complete_contract_execution(
self, contract: Contract, breaches: List[Breach], resolution: Contract
) -> None:
...
def delete_executed_contracts(self) -> None:
...
def contract_record(self, contract: Contract) -> Dict[str, Any]:
...
def breach_record(self, breach: Breach) -> Dict[str, Any]:
...
def contract_size(self, contract: Contract) -> float:
...
The names are almost self-explanatory and we will go through them one by one:
on_contract_signed This method is called whenever a contract is signed (becomes binding). This is the only non-abstract method in the contract related set here but we need to override it in order to keep track of the execution time of the contract using:
self._contracts[self.current_step + 1].append(contract)
executable_contracts Should return a list of contracts that are due at the current step. We simply use the
_contracts
mapping we updated inon_contract_signed
:
return self._contracts[self.current_step]
order_contracts_for_execution The world is responsible of deciding that order at which contracts (returned from
executable_contracts
) are executed, here we just shuffle them randomly and return them:
shuffle(contracts)
return contracts
start_contract_execution Here we decide how to execute a contract. We first check whether each agent of the partners who signed the contract is going to show up using its breach-probability and record a breach for each such event. If there are any breaches, we stop processing because the trip did not take place:
breaches = []
for aid in contract.partners:
if random() < self._breach_prob[aid]:
breaches.append(
Breach(
contract,
aid,
"breach",
victims=[_ for _ in contract.partners if _ != aid],
)
)
if len(breaches) > 0:
return set(breaches)
If there are no breaches, the trip is assumed to execute successfully and every agent (of the partners) is assigned the utility value from that trip according to its utility function:
for aid in contract.partners:
self._total_utility[aid] = self._ufuns[aid](contract.agreement)
return set()
complete_contract_execution This method is only called in worlds that allow re-negotiation of breaches. As our world does not have the concept, we just do nothing here.
delete_executed_contracts This method is responsible of cleaning up all contracts that have been processed in the current step. We again use the mapping we constructed in
__init__
and updated inon_contract_signed
.
if self._current_step in self._contracts.keys():
del self._contracts[self.current_step]
These six steps complete all processing of contracts. Nevertheless, we still need to override three other methods to define how contracts and breaches are stored and the value of a contract
contract_record should return a dictionary representing a contract. We simply convert it to a dictionary using a helper function from negmas
to_flat_dict
:
return to_flat_dict(contract)
breach_record Same as contract record but for breaches. We do the same.
return to_flat_dict(breach)
contract_size Used to specify some sense of size for contracts. This is only used for statistics and does not affect the operation of the simulation. We define the contract size here as the welfar (total utility of all partners):
if contract.agreement is None:
return 0.0
return sum(self._ufuns[aid](contract.agreement) for aid in contract.partners)
This complete the world and agent-world-interface design. We can now develop our base agent class.
Statisitcs
The base World
keeps track of negotiation related statistics
(e.g. how many negotiations were requested very step, how many
contracted were breached, etc). You can easily add to this set of
statistics by overloading post_step_stats
(and the corresponding
pre_step_stats
if needed). In our world, we just add one custom
statistic: the total utility collected by the agent so far:
for aid, agent in self.agents.items():
self._stats[f"total_utility_{agent.name}"].append(self._total_utility[aid])
Note that we used the agent name not ID to differentiate these statistics. Because the system does not know or use our statistic, we can use the name which will usually be easier to read when inspecting these statistics as we will see in the following tutorial
Base Agent (Person)
Even though it is not strictly necessary (as with the case of
agent-world-interface), it is useful to provide a base agent that hides
unnecessary details from developers of agents targeting our
TripsWorld
. This is the complete listing of our base agent:
class Person(Agent, ABC):
@abstractmethod
def step(self):
...
@abstractmethod
def init(self):
...
@abstractmethod
def respond_to_negotiation_request(
self,
initiator: str,
partners: List[str],
mechanism: NegotiatorMechanismInterface,
) -> Optional[Negotiator]:
...
def _respond_to_negotiation_request(
self,
initiator: str,
partners: List[str],
issues: List[Issue],
annotation: Dict[str, Any],
mechanism: NegotiatorMechanismInterface,
role: Optional[str],
req_id: Optional[str],
) -> Optional[Negotiator]:
return self.respond_to_negotiation_request(initiator, partners, mechanism)
def on_neg_request_rejected(self, req_id: str, by: Optional[List[str]]):
pass
def on_neg_request_accepted(
self, req_id: str, mechanism: NegotiatorMechanismInterface
):
pass
def on_negotiation_failure(
self,
partners: List[str],
annotation: Dict[str, Any],
mechanism: NegotiatorMechanismInterface,
state: MechanismState,
) -> None:
pass
def on_negotiation_success(
self, contract: Contract, mechanism: NegotiatorMechanismInterface
) -> None:
pass
def set_renegotiation_agenda(
self, contract: Contract, breaches: List[Breach]
) -> Optional[RenegotiationRequest]:
pass
def respond_to_renegotiation_request(
self, contract: Contract, breaches: List[Breach], agenda: RenegotiationRequest
) -> Optional[Negotiator]:
pass
def on_contract_executed(self, contract: Contract) -> None:
pass
def on_contract_breached(
self, contract: Contract, breaches: List[Breach], resolution: Optional[Contract]
) -> None:
pass
The first thing, our abstract-base-class (ABC) does is defining the
abstract methods that must be implemented by any agent that is
compatible with the TripsWorld
.
The first two abstract methods are init
and step
called by the
world to initialize the agent (after its AWI is created) and at every
simulation step. These methods are not abstract in the base Agent
class but we convert them to abstract methods to force all Person
based agents to provide some implementation for them
@abstractmethod
def step(self):
...
@abstractmethod
def init(self):
...
We then add a third method for responding to negotiation requests:
@abstractmethod
def respond_to_negotiation_request(
self,
initiator: str,
partners: List[str],
mechanism: AgentMechanismInterface,
) -> Optional[Negotiator]:
...
World
and TripWorld
classes know nothing about this method, our
base Person
class will call it when it receives a request to respond
to a negotiation request from the world in
_respond_to_negotiation_request
(notice the underscore which
indicates that children should not modify this method):
return self.respond_to_negotiation_request(initiator, partners, mechanism)
This arrangement removes the need to pass several parameters of
_respond_to_negotiation_request
that are not of value for our
current simulation.
We provide a do-nothing implementation of all other callbacks expected during the simulation. These are:
def on_neg_request_rejected(self, req_id: str, by: Optional[List[str]]):
...
def on_neg_request_accepted(self, req_id: str, mechanism: AgentMechanismInterface):
...
def on_negotiation_failure(
self,
partners: List[str],
annotation: Dict[str, Any],
mechanism: AgentMechanismInterface,
state: MechanismState,
):
...
def on_negotiation_success(
self, contract: Contract, mechanism: AgentMechanismInterface
):
...
def set_renegotiation_agenda(
self, contract: Contract, breaches: List[Breach]
) -> Optional[RenegotiationRequest]:
...
def respond_to_renegotiation_request(
self, contract: Contract, breaches: List[Breach], agenda: RenegotiationRequest
) -> Optional[Negotiator]:
...
def on_contract_executed(self, contract: Contract):
...
def on_contract_breached(self, contract: Contract, breaches: List[Breach]):
...
These callbacks are called by the world at key points of the process from a negotiation request to an exeucted/breached contract. The names are self-explanatory but we summarize them here:
on_neg_request_rejectect/accepted Called to tell the agent about the fate of a negotiation request it initiated (using
request_negotiation
). Agents can access the current requests using theirnegotiation_requests
property to get more information about the request if needed.on_negotiation_failure.success Called to tell the agent about the fate of negotiations it engaged in (using its own negotiators). The agent can access the current set of negotiations using its
negotiations
property.set_renegotiation_agenda/respond_to_renegotiation_request Only needed for worlds that allow re-negotiation of breached contracts.
on_contract_executed/breached Called to tell the agent about the fate of a contract it signed.
There is also an on_contracts_finalized callback that is used to tell the agent about which of its agreements have been signed by everyone and became binding and which were canceled because one or more of the partners refused to sign it. In our world, singing is forced so this callback is not necessary and it is not abstract so we did not implement it.
We now have all the ingredients to create specific agents and start simulations. In the next tutorial we will develop an agent for this world and use it to test it.
[ADVANCED] Most Important Functionality Provided by World
This section is more of a reference. You need not go through it in details in your first read
You can control several options about how your world simulation runs by
setting constructor parameters of the World
class (as we did earlier
with force_signing
). Here we discuss briefly some of the most
important options.
General
These are general parameters that do not directly affect how the world
works. The most important of these are name
to set a name for the
world, and awi_type
which controls the type of AWI used to connect
agents to it.
name: World Name
bulletin_board: A bulletin board object to use. If not given one will be created
awi_type: The type used for agent world interfaces (must descend from or behave like
AgentWorldInterface
)info: A dictionary of key-value pairs that is kept within the world but never used. It is useful for storing contextual information. For example, when running tournaments.
Simulation parameters
These options control how the simulation is run and the order of operations in each simulation step.
n_steps: Total simulation time in steps
time_limit: Real-time limit on the simulation
operations: A list of
Operations
to run in order during every simulation step. You can use this parameter to set the order of events in your simulation. For example, you can choose when negotiations run relative to thesimulation_step
of your world. Available operations include:StatsUpdate: Updating statistics stored in
_stats
by callingupdate_stats
of theWorld
class. Each time this operation is conducted a higherstage
is passed to theupdate_stats
method (the first such call will by default runpre_step_stats
and later calls will callpost_step_stats
but you can change that.SimulationStep: Calling
simulation_step
of theWorld
class. Each time this operation is conducted a higherstage
is passed tosimulation_step
.Negotiations: Running all registered negotiations
ContractSigning: Sign contracts (if enabled)
ContractExecution: Execute contracts
AgentSteps: Step all agents by calling their
step
method
Negotiation Parameters
Controls all negotiations conducted during the simulation.
negotiation_speed: The number of negotiation steps per simulation step. None means infinite
neg_n_steps: Maximum number of steps allowed for a negotiation.
neg_step_time_limit: Time limit for single step of the negotiation protocol.
neg_time_limit: Real-time limit on each single negotiation
negotiation_quota_per_step: Number of negotiations an agent is allowed to start per step
negotiation_quota_per_simulation: Number of negotiations an agent is allowed to start in the simulation
start_negotiations_immediately: If true negotiations start immediately when registered rather than waiting for the next step
mechanisms: The mechanism types allowed in this world associated with each keyword arguments to be passed to it. This is a dictionary mapping a class name defining a mechanism to the parameters to use by default for that mechanism. For example, to allow both stacked alternating offers (with some custom setting) and the veto single text mechanisms in your world, you can use something like:
super().__init__(
mechanisms={
"negmas.sao.SAOMechanism": dict(offering_is_accepting=False),
"negmas.st.STVetoMechanism": dict(),
},
...,
)
Signing parameters
After negotiations are concluded with agreements, it is possible to have an extra signing step to confirm these agreements before they become binding contracts. This gives agents central control over the agreements reached by their negotiators. You can control whether or not this step is needed for any world simulation and how confirmation (i.e. signing) is done through these parameters.
default_signing_delay: The default number of steps between contract conclusion and signing it. Only takes effect if
force_signing
isFalse
force_signing: If true, agents are not asked to sign contracts. They are forced to do so. In this case,
default_singing_delay
is not effective and signature is immediatebatch_signing: If true, contracts are signed in batches not individually
Breach Processing
When contracts fail to execute, breaches occur. You can control what happens when breaches occur using this parameter.
breach_processing: How to handle breaches. Can be any of
BreachProcessing
values. Three options are available:No processing. In this case, the breach is just reported to the breach-list on the bulletin board and that is it.
Victim, then perpetrator. In this case victims of the breach are given the chance to propose a resolution followed by the perpetrator.
Renegotiation, where a complete negotiation session is conducted to resolve the breach.
Logging
NegMAS supports both general logs through the log*
methods of the
World
class and agent specific logs through the agent_log*
methods of the AWI. These parameters control logging. The default
logging location is ~/negmas/logs
.
log_folder: Folder to save all logs
log_to_file: If true, will log to a file
log_file_name: Name of the log file
log_file_level: The log-level to save to file (WARNING, ERROR, INFO, DEBUG, CRITICAL, …)
log_ufuns: Log utility functions
log_negotiations: Log all negotiation events
log_to_screen: Whether to log to screen
log_screen_level: The log-level to show on screen (WARNING, ERROR, INFO, DEBUG, CRITICAL, …)
no_logs: If True, All logging will be disabled no matter what other options are given.
log_stats_every: If nonzero and positive, the period of saving stats
construct_graphs: If true, information needed to draw graphs using
draw
method are kept.
What to save
These settings greatly affect the memory consumption of the simulation. It tells NegMAS what exactly do you need to save in-memory.
save_signed_contracts: Save all signed contracts
save_cancelled_contracts: Save all canceled contracts
save_negotiations: Save all negotiation records
save_resolved_breaches: Save all resolved breaches
save_unresolved_breaches: Save all unresolved breaches
Exception Handling
It is inevitable that exceptions will happen in agent code or the simulation. This set of parameters control how to handle these exceptions.
ignore_agent_exceptions: Ignore agent exceptions and keep running
ignore_mechanism_exceptions: If true, all mechanism exceptions are ignored and the mechanism is aborted
ignore_simulation_exceptions: Ignore simulation exceptions and keep running
ignore_contract_execution_exceptions: Ignore contract execution exceptions and keep running
safe_stats_monitoring: Never throw an exception for a failure to save stats or because of a Stats Monitor object
Checkpoints
NegMAS can keep checkpoints of the world simulation that can be used to recover and continue the simulation later. These checkpoints are not stored by default but you can enable them and control their frequency and location using this set of parameters
checkpoint_every: The number of steps to checkpoint after. Set to <= 0 to disable
checkpoint_folder: The folder to save checkpoints into. Set to None to disable
checkpoint_filename: The base filename to use for checkpoints (multiple checkpoints will be prefixed with step number).
single_checkpoint: If true, only the most recent checkpoint will be saved.
extra_checkpoint_info: Any extra information to save with the checkpoint in the corresponding json file as a dictionary with string keys
exist_ok: IF true, checkpoints override existing checkpoints with the same filename.
We can now continue to the next tutorials in which we will develop agents for your newly created world.
Download Notebook
.
Download Notebook
.
Download Notebook
.