Skip to content

Commit

Permalink
Temporary commit while extending propose
Browse files Browse the repository at this point in the history
  • Loading branch information
yasserfarouk committed Nov 14, 2024
1 parent 1c03918 commit 03ec804
Show file tree
Hide file tree
Showing 35 changed files with 3,802 additions and 3,638 deletions.
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ Developing a novel negotiator slightly more difficult by is still doable in few
class MyAwsomeNegotiator(SAONegotiator):
def propose(self, state):
def propose(self, state, dest=None):
"""Your code to create a proposal goes here"""
By just implementing `propose()`, this negotiator is now capable of engaging in alternating offers
Expand All @@ -176,7 +176,7 @@ Developing a novel negotiation protocol is actually even simpler:
class MyNovelProtocol(Mechanism):
def __call__(self, state: MechanismState):
def __call__(self, state: MechanismState, action=None):
"""One round of the protocol"""
By implementing the single `__call__()` function, a new protocol is created. New negotiators can be added to the
Expand Down
28 changes: 14 additions & 14 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ specific modules, advanced and helper modules.
finding the pareto-frontier, sampling outcomes with given
utilities from the outcome space, etc.
3. **negotiators** This module represents basic negotiation agent
implementation and provides basic interfaces to be overriden
implementation and provides basic interfaces to be overridden
(implemented) by higher specialized modules
4. **mechanisms** This module represents the most basic conceptual
view of a negotiation protocol supporting both mediate and
Expand Down Expand Up @@ -114,7 +114,7 @@ specific modules, advanced and helper modules.
the library including mixins for logging.
- **inout** Provides functions to load and store XML Genius domains
and utility functions.
- **java** [Depricated] Provides an interface to JNegMAS allowing
- **java** [Deprecated] Provides an interface to JNegMAS allowing
agents and negotiators to be developed in Java.
- **tournaments** Supports creating and running tournaments to
compare agents and negotiators.
Expand Down Expand Up @@ -672,11 +672,11 @@ set of issues and can be created using ``make_os`` function:


A special case of ``CartesianOutcomeSpace`` is a
``DiscreteCartesianOutcomeSpace`` (see the examle above) which represent
``DiscreteCartesianOutcomeSpace`` (see the example above) which represent
a Cartesian outcome-space with discrete issues (i.e. no issues are
continuous).

``OutcomeSpace`` provide convenient methods for gettin information about
``OutcomeSpace`` provide convenient methods for getting information about
the outcome-space or manipulating it. Some of the most important
examples are:

Expand Down Expand Up @@ -788,7 +788,7 @@ Ordinal and Cardinal Preferences

The most general ``Preferences`` type in NegMAS is ``Ordinal``
``Preferences`` which can only represent partial ordering of outcomes in
the outcome-space throgh the ``is_not_worse()`` method. An entity with
the outcome-space through the ``is_not_worse()`` method. An entity with
this kind of preferences can compare two outcomes but it gets one bit of
information out of this comparison (which is better for the entity) and
has no way to know *how much* is the difference
Expand All @@ -808,7 +808,7 @@ Crisp and Prob Preferences

NegMAS usually implements two versions of each ``Preferences`` type
(other than ``Ordinal``) that represent a probabilistic version (ending
with ``Prob``) returing ``Distribution``\ s when queried, and a crisp
with ``Prob``) returning ``Distribution``\ s when queried, and a crisp
version (ending with ``Crisp``) returning a ``float``. This simplifies
the development of agents and negotiators working with probability
distributions.
Expand All @@ -822,7 +822,7 @@ allowed to change. The entity having non-stationary preferences usually
faces a harder problem achieving its goals as it needs to take into
account this possible change. Entities interacting with other entities
with non-stationary ``Preferences`` are also in reatively harder
situation comapred with those dealing with entities with stationary
situation compared with those dealing with entities with stationary
``Preferences``.

Stationary Preference type names start with ``Stationary``
Expand Down Expand Up @@ -961,7 +961,7 @@ Notice that (as the last example shows) utility functions can return
``None`` to indicate that the utility value cannot be inferred for this
outcome/offer.

Preferences Protcols
Preferences Protocols
~~~~~~~~~~~~~~~~~~~~

The ``preferences`` module provide a set of other python protocols that
Expand Down Expand Up @@ -1041,15 +1041,15 @@ real-valued number (utility of this issue).
Notice that despite the name, this type of utiliy functions can
represent nonlinear relation between issue values and utility values.
The linearity is in how these possibly nonlinear mappings are being
combind to generate a utility value for the outcome.
combined to generate a utility value for the outcome.

Note that a utility function needs to know the outcome-space over which
is it defined. There are three ways to pass this to the
``UtilityFunction`` constructor:

1. **issues=…** pass a list of issues (usually made using
``make_issue``)
2. **outcome_space=…** pass an ``OutcomeSpace`` type (usualy made using
2. **outcome_space=…** pass an ``OutcomeSpace`` type (usually made using
``make_os``)
3. **outcomes=…** pass a list of outcomes.

Expand Down Expand Up @@ -1168,7 +1168,7 @@ more readable:
Nonlinear Aggregation Utility Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A direct generalization of the linear agggregation utility functions is
A direct generalization of the linear aggregation utility functions is
provided by the ``NonLinearAggregationUtilityFunction`` which represents
the following function:

Expand Down Expand Up @@ -1620,7 +1620,7 @@ The most important points to notice about this figure are the following:

- Almost all entities are ``NamedObject``\ s which means they have a
*user assigned* name used for debugging, printing, and logging, and a
*system assigned* id used when programatically accessing the object.
*system assigned* id used when programmatically accessing the object.
For example, agents request negotiations with other agents from the
world using the partner’s *id* not *name*.
- ``Controller`` objects can access neither worlds nor mechanisms
Expand All @@ -1630,7 +1630,7 @@ The most important points to notice about this figure are the following:
mathematical function but it can have state, access the mechanism
state or settings (through its own ``AgentMechanismInterface``) and
can change its returned value for the same output during the
negotiation. Ufuns need not be dyanmic in this sense but they can be.
negotiation. Ufuns need not be dynamic in this sense but they can be.

Mechanisms (Negotiations)
-------------------------
Expand Down Expand Up @@ -1703,7 +1703,7 @@ respond to every offer in parallel.
self.state.current_offer = None
self.current_offerer = -1
def __call__(self, state):
def __call__(self, state, action=None):
n_agents = len(self.negotiators)
nxt = (self.current_offerer + 1) % n_agents
current = self.negotiators[nxt]
Expand Down
24 changes: 12 additions & 12 deletions docs/tutorials/03.develop_new_negotiator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ can immediately see the issue: uninformed concession. Consider the buyer
agent. It started with offering the best outcome for itself (The green
offers above) and repeated it for a while (as expected) then started
conceding (i.e. offering outcomes with lower utility for itself.
Nevertheless, when it did conceed, it did not consider its partner at
Nevertheless, when it did concede, it did not consider its partner at
all. The figure below shows the same figure focusing on one specific
choice the buyer did: |Uninformed Concession|

Expand Down Expand Up @@ -140,7 +140,7 @@ will simply offer randomly.
.. code:: ipython3
class RandomNegotiator(SAONegotiator):
def propose(self, state):
def propose(self, state, dest: str | None = None):
return self.nmi.random_outcomes(1)[0]
Let’s define a helper function for testing our negotiator that replaces
Expand Down Expand Up @@ -233,15 +233,15 @@ negotiation but replacing only the buyer


The seller behaves as the time-based aspiration negotiator is expected
to behave. It starts at its best outcome then it conceeds slowly. Our
to behave. It starts at its best outcome then it concedes slowly. Our
random buyer agent also seems to behave as expected, it offers outcomes
all over the place. What happened in this case, is that the buyer
accepted some offer from the seller. How did it decide to do so? We did
not implement a way for our negotiator to make this decision.

The default acceptance strategy in NegMAS is to accept an outcome **if
and only if it has a utility for the negotiator better or equal to
whatever offer it would hace proposed at this negotiation state**.
whatever offer it would have proposed at this negotiation state**.

So this is what happened, the buyer agent received some offer from the
aspriation negotiator, it called our ``propose`` method to see what
Expand Down Expand Up @@ -379,7 +379,7 @@ Negotiation execution state:

Negotiation end state:

- **bronken** Did a negotiator end the negotiation (by returning
- **broken** Did a negotiator end the negotiation (by returning
``ResponseType.END_NEGOTIATION`` from its ``respond()`` method).
- **timedout** The negotiation timed out without agreement.
- **agreement** The final agreement (or ``None`` if broken or
Expand All @@ -390,7 +390,7 @@ Timing state:
- **step** The current negotiation step (here it is *9* out of the *20*
steps allowed)
- **time** The real time that passed since the negotiation stareted
- **relative_time** The fraction of teh negotiation that passed (here
- **relative_time** The fraction of the negotiation that passed (here
it is :math:`(9+1)/(20+1=0.476...`).

There are also SAO specific state variables:
Expand Down Expand Up @@ -458,7 +458,7 @@ negotiator will need to re-calculate the utility value associated with
each outcome at every ufun change. It can do that in the
``on_preferences_changed()`` callback.

Moreover, we need some way to calcualate the current utility level we
Moreover, we need some way to calculate the current utility level we
are willing to accept (and to offer around). Here we can use another
component from NegMAS called ``PolyAspiration`` which is designed
exactly for that. Let’s see what the negotiator looks like and then
Expand All @@ -485,7 +485,7 @@ explain it:
self._asp = PolyAspiration(1.0, "boulware")
def on_preferences_changed(self, changes):
# create an initiaze an invertor for my ufun
# create an initialize an invertor for my ufun
changes = [_ for _ in changes if _.type not in (PreferencesChangeType.Scale,)]
if not changes:
return
Expand All @@ -495,7 +495,7 @@ explain it:
# find worst and best outcomes for me
worest, self._best = self.ufun.extreme_outcomes()
# and the correponding utility values
# and the corresponding utility values
self._min, self._max = self.ufun(worest), self.ufun(self._best)
# MUST call parent to avoid being called again for no reason
Expand All @@ -512,7 +512,7 @@ explain it:
# accept if the offer is not worse for me than what I would have offered
return super().respond(state, source)
def propose(self, state):
def propose(self, state, dest: str | None = None):
# calculate my current aspiration level (utility level at which I will offer and accept)
a = (self._max - self._min) * self._asp.utility_at(
state.relative_time
Expand All @@ -524,7 +524,7 @@ explain it:
if not outcomes:
return self._best
# else if I did not recieve anything from the partner, offer any outcome above the aspiration level
# else if I did not receive anything from the partner, offer any outcome above the aspiration level
if not self._partner_first:
return choice(outcomes)
Expand All @@ -537,7 +537,7 @@ explain it:
return nearest
Let’s look at this negotiator in details. We override four methods: -
**init\ ()** to initialize the negotiator. This method should **alwyas**
**init\ ()** to initialize the negotiator. This method should **always**
call ``super().__init__()`` to correctly initialize the negotiator.
Moreover, we initialize the aspiration mixin to slowly concede from
zero. - **on_preferences_changed(changes)** to update the ufun inverter,
Expand Down
16 changes: 8 additions & 8 deletions docs/tutorials/04.develop_new_mechanism.rst
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ What happens if the utility value of a negotiator had a different slope:


Notice that in this case, the both negotiators always get their maximum
possible utility which leads to a linear increas in welfare with slope.
possible utility which leads to a linear increase in welfare with slope.
There is a small exception though at slope zero. Try running the last
simulation several times. Does the peculiar result at slope zero
persist? Does it lead to the same welfare every time? Can you explain
Expand Down Expand Up @@ -379,7 +379,7 @@ We can then define the mechanism class itself:
# getting what it offered itself. This is not a part of the
# Rubinstein protocol (hence extended). It was added to avoid
# very long negotiations as the original mechanism is supposed to
# allow an infinte time.
# allow an infinite time.
if (
outcomes[0][0] <= outcomes[1][0] + 1e-5
and outcomes[1][1] <= outcomes[0][1] + 1e-5
Expand Down Expand Up @@ -428,12 +428,12 @@ We can now develop the base negotiator type for this mechanism:
self.my_index = indx
@abstractmethod
def propose(self, state: RubinsteinMechanismState) -> Outcome:
def propose(self, state: RubinsteinMechanismState, dest: str | None = None) -> Outcome:
"""Proposes an outcome which is a tuple of two numbers between zero and one"""
The base negotiator here implements ``set_index`` so that specific
negotiators need not bother about it. It defines a single abstract
method to be overriden by any compatible negotiator.
method to be overridden by any compatible negotiator.

We will first define a utility function to plot what happens in a
negotiation
Expand Down Expand Up @@ -472,7 +472,7 @@ discounting) and otherwise returns a random apportionment of the pie.
.. code:: ipython3
class RandomRubinsteinNegotiator(RubinsteinNegotiator):
def propose(self, state: RubinsteinMechanismState) -> Outcome:
def propose(self, state: RubinsteinMechanismState, dest: str | None = None) -> Outcome:
if self.ufun((1.0, 1.0)) < 0.0:
return None
r = random()
Expand Down Expand Up @@ -508,7 +508,7 @@ We can implement the optimal negotiator for this mechanism as follows:
.. code:: ipython3
class OptimalRubinsteinNegotiator(RubinsteinNegotiator):
def propose(self, state: RubinsteinMechanismState) -> Outcome:
def propose(self, state: RubinsteinMechanismState, dest: str | None = None) -> Outcome:
first = (1 - state.discounts[1]) / (1 - state.discounts[1] * state.discounts[0])
return first, 1 - first
Expand Down Expand Up @@ -543,7 +543,7 @@ other agent’s
super().__init__(*args, **kwargs)
self._asp = PolyAspiration(max_aspiration, aspiration_type)
def propose(self, state: RubinsteinMechanismState) -> Outcome:
def propose(self, state: RubinsteinMechanismState, dest: str | None = None) -> Outcome:
if self.ufun((1.0, 1.0)) < 0.0:
return None
r = self._asp.utility_at(state.relative_time)
Expand Down Expand Up @@ -688,7 +688,7 @@ What if we tried our boulware here:

The behavior did not change from the case with equal time pressure.
There is no mystry here. The boulware agent does not take its own
time-pressure (discount) into accout. Can you fix that?
time-pressure (discount) into account. Can you fix that?



Expand Down
Loading

0 comments on commit 03ec804

Please sign in to comment.