Skip to content

Commit

Permalink
adding ordering_fun for dynamic ordering runall
Browse files Browse the repository at this point in the history
  • Loading branch information
yasserfarouk committed Nov 15, 2024
1 parent da192d4 commit c935552
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 16 deletions.
39 changes: 30 additions & 9 deletions src/negmas/mechanisms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,7 @@ def runall(
keep_order=True,
method: str = "serial",
ordering: tuple[int, ...] | None = None,
ordering_fun: Callable[[int, list[TState | None]], int] | None = None,
) -> list[TState | None]:
"""Runs all mechanisms.
Expand All @@ -1285,6 +1286,9 @@ def runall(
at every step. This is only allowed if the method is serial
method: the method to use for running all the sessions. Acceptable options are: sequential, serial, threads, processes
ordering: Controls the order of advancing the negotiations with the "serial" method.
ordering_fun: A function to implement dynamic ordering for the "serial" method.
This function receives a list of states and returns the index of the next mechanism to step.
Note that a state may be None if the corresponding mechanism was None and it should never be stepped
Returns:
- list of states of all mechanisms after completion
Expand All @@ -1295,6 +1299,9 @@ def runall(
- serial means stepping mechanisms in some order which can be controlled by `ordering`. If no ordering is given, the ordering is just round-robin
"""
if method == "sequential":
if not keep_order:
mechanisms = [_ for _ in mechanisms]
random.shuffle(mechanisms)
for mechanism in mechanisms:
mechanism.run()
states = [_.state for _ in mechanisms]
Expand All @@ -1307,20 +1314,34 @@ def runall(
assert (
len(notmentioned) == 0
), f"Mechanisms {notmentioned} are never mentioned in the ordering."
while not all(completed):
if not keep_order:
random.shuffle(indices)
for i in indices:
done, mechanism = completed[i], mechanisms[i]
if done:
if ordering_fun:
j = 0
while not all(completed):
states = [_.state for _ in mechanisms]
i = ordering_fun(j, states)
j += 1
mechanism = mechanisms[i]
if completed[i]:
continue
result = mechanism.step()
if result.running:
continue
completed[i] = True
states[i] = mechanism.state
if all(completed):
break
else:
while not all(completed):
if not keep_order:
random.shuffle(indices)
for i in indices:
mechanism = mechanisms[i]
if completed[i]:
continue
result = mechanism.step()
if result.running:
continue
completed[i] = True
states[i] = mechanism.state
if all(completed):
break
elif method == "threads":
raise NotImplementedError()
elif method == "processes":
Expand Down
89 changes: 82 additions & 7 deletions tests/optional/test_sao_slow.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,48 @@
]


@st.composite
def generate_data(draw):
values = list(range(10))
sub_values = (
st.lists(st.sampled_from(values))
.map(lambda sublist: sublist + values)
.flatmap(st.permutations)
)
return draw(sub_values)


@given(
ordering=generate_data(),
offering_is_accepting=st.booleans(),
n_negotiators=st.integers(2, 4),
)
def test_mechanism_runall_ordering_with_fun(
ordering, offering_is_accepting, n_negotiators
):
n_outcomes = 5
mechanisms = []

def orderer(indx: int, states: list[SAOState | None]) -> int:
return ordering[indx % len(ordering)]

for _ in range(10):
assert _ in ordering
mechanism = SAOMechanism(
outcomes=n_outcomes,
n_steps=random.randint(3, 20),
offering_is_accepting=offering_is_accepting,
)
ufuns = MappingUtilityFunction.generate_random(1, outcomes=n_outcomes)
for i in range(n_negotiators):
mechanism.add(AspirationNegotiator(name=f"agent{i}"), preferences=ufuns[0])
mechanisms.append(mechanism)

states = SAOMechanism.runall(mechanisms, method="serial", ordering_fun=orderer)
assert len(states) == 10
assert not any(_ is not None and _.running for _ in states)


class MyRaisingNegotiator(SAONegotiator):
def propose(self, state: SAOState, dest: str | None = None) -> Outcome | None:
_ = state
Expand Down Expand Up @@ -308,8 +350,8 @@ def test_neg_run_no_waiting():
assert v >= waste * n_steps


@mark.parametrize(["keep_order"], [(True,), (False,)])
def test_neg_sync_loop(keep_order):
@mark.parametrize(["keep_order", "method"], [(True, "serial"), (False, "serial")])
def test_neg_sync_loop_sync(keep_order, method):
# from pprint import pprint

n_outcomes, n_steps = 10, 10
Expand All @@ -334,7 +376,7 @@ def test_neg_sync_loop(keep_order):
c2.create_negotiator(preferences=ufuns[1], id=f"1-{m}", name=f"1-{m}") # type: ignore It will be SAOControlled
)
mechanisms.append(mechanism)
SAOMechanism.runall(mechanisms, keep_order=keep_order)
SAOMechanism.runall(mechanisms, method=method, keep_order=keep_order)

for mechanism in mechanisms:
assert mechanism.state.started
Expand Down Expand Up @@ -472,22 +514,55 @@ def test_mechanism_runs_with_offering_not_accepting(n_negotiators, oia):
assert mechanism.agreement is not None


@mark.parametrize("n_negotiators,oia", [(2, True), (3, True), (2, False), (3, False)])
def test_mechanism_runall(n_negotiators, oia):
@mark.parametrize(
"n_negotiators,offering_is_accepting,method,keep_order",
[
_
for _ in itertools.product(
(2, 3, 4), (True, False), ("sequential", "serial"), (True, False)
)
],
)
def test_mechanism_runall(n_negotiators, offering_is_accepting, method, keep_order):
n_outcomes = 5
mechanisms = []
for _ in range(10):
mechanism = SAOMechanism(
outcomes=n_outcomes,
n_steps=random.randint(3, 20),
offering_is_accepting=oia,
offering_is_accepting=offering_is_accepting,
)
ufuns = MappingUtilityFunction.generate_random(1, outcomes=n_outcomes)
for i in range(n_negotiators):
mechanism.add(AspirationNegotiator(name=f"agent{i}"), preferences=ufuns[0])
mechanisms.append(mechanism)

states = SAOMechanism.runall(mechanisms)
states = SAOMechanism.runall(mechanisms, method=method, keep_order=keep_order)
assert len(states) == 10
assert not any(_ is not None and _.running for _ in states)


@given(
ordering=generate_data(),
offering_is_accepting=st.booleans(),
n_negotiators=st.integers(2, 4),
)
def test_mechanism_runall_ordering(ordering, offering_is_accepting, n_negotiators):
n_outcomes = 5
mechanisms = []
for _ in range(10):
assert _ in ordering
mechanism = SAOMechanism(
outcomes=n_outcomes,
n_steps=random.randint(3, 20),
offering_is_accepting=offering_is_accepting,
)
ufuns = MappingUtilityFunction.generate_random(1, outcomes=n_outcomes)
for i in range(n_negotiators):
mechanism.add(AspirationNegotiator(name=f"agent{i}"), preferences=ufuns[0])
mechanisms.append(mechanism)

states = SAOMechanism.runall(mechanisms, method="serial", ordering=ordering)
assert len(states) == 10
assert not any(_ is not None and _.running for _ in states)

Expand Down

0 comments on commit c935552

Please sign in to comment.