Skip to content

Commit

Permalink
adding step time out
Browse files Browse the repository at this point in the history
  • Loading branch information
yasserfarouk committed Mar 22, 2019
1 parent 3a21090 commit 32118bf
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 26 deletions.
31 changes: 24 additions & 7 deletions negmas/apps/scml/world.py
Expand Up @@ -56,6 +56,7 @@ def __init__(self
, time_limit=60 * 90
, neg_n_steps=100
, neg_time_limit=3 * 60
, neg_step_time_limit=60
, negotiation_speed=10
# bank parameters
, minimum_balance=0
Expand Down Expand Up @@ -137,6 +138,7 @@ def __init__(self
, log_file_name=log_file_name, awi_type='negmas.apps.scml.SCMLAWI'
, default_signing_delay=default_signing_delay
, start_negotiations_immediately=start_negotiations_immediately
, neg_step_time_limit=neg_step_time_limit
, name=name)
if balance_at_max_interest is None:
balance_at_max_interest = initial_wallet_balances
Expand Down Expand Up @@ -166,6 +168,8 @@ def __init__(self
self.bulletin_board.record(section='settings', key='negotiation_speed_multiple'
, value=negotiation_speed)
self.bulletin_board.record(section='settings', key='negotiation_n_steps', value=neg_n_steps)
self.bulletin_board.record(section='settings', key='negotiation_step_time_limit', value=neg_step_time_limit)
self.bulletin_board.record(section='settings', key='negotiation_time_limit', value=neg_time_limit)
self.bulletin_board.record(section='settings', key='transportation_delay', value=transportation_delay)
self.avg_process_cost_is_public = avg_process_cost_is_public
self.catalog_prices_are_public = catalog_prices_are_public
Expand Down Expand Up @@ -934,8 +938,8 @@ def _execute_contract(self, contract: Contract) -> Set[Breach]:
if not partner.confirm_contract_execution(contract=contract):
self.logdebug(
f'{partner.name} refused execution og Contract {contract.id}')
breaches.add(Breach(contract=contract, perpetrator=partner # type: ignore
, victims=list(partners - {partner})
breaches.add(Breach(contract=contract, perpetrator=partner.id # type: ignore
, victims=[_.id for _ in list(partners - {partner})]
, level=1.0, type='refusal'))
if len(breaches) > 0:
return breaches
Expand Down Expand Up @@ -1032,10 +1036,10 @@ def _execute_contract(self, contract: Contract) -> Set[Breach]:
# pay penalties if there are any. Notice that penalties apply only to to seller. It makes no sense to have a
# penalty on the buyer who already have no money to pay the contract value anyway.
if penalty_breach_society is not None:
breaches.add(Breach(contract=contract, perpetrator=seller, victims=[]
breaches.add(Breach(contract=contract, perpetrator=seller.id, victims=[]
, level=penalty_breach_society, type='penalty_society', step=self.current_step))
if penalty_breach_victim is not None:
breaches.add(Breach(contract=contract, perpetrator=seller, victims=[buyer]
breaches.add(Breach(contract=contract, perpetrator=seller.id, victims=[buyer.id]
, level=penalty_breach_victim, type='penalty_society', step=self.current_step))

# check the buyer
Expand All @@ -1057,7 +1061,7 @@ def _execute_contract(self, contract: Contract) -> Set[Breach]:
if product_breach is not None:
# apply insurances if they exist
# register the breach independent of insurance
breaches.add(Breach(contract=contract, perpetrator=seller, victims=[buyer]
breaches.add(Breach(contract=contract, perpetrator=seller.id, victims=[buyer.id]
, level=product_breach, type='product', step=self.current_step))
if self.insurance_company.pay_insurance(contract=contract, perpetrator=seller):
# if the buyer has an insurance against the seller for this contract, then just give him the missing
Expand Down Expand Up @@ -1088,7 +1092,7 @@ def _execute_contract(self, contract: Contract) -> Set[Breach]:

if money_breach is not None:
# apply insurances if they exist.
breaches.add(Breach(contract=contract, perpetrator=buyer, victims=[seller]
breaches.add(Breach(contract=contract, perpetrator=buyer.id, victims=[seller.id]
, level=money_breach, type='money', step=self.current_step))
if self.insurance_company.pay_insurance(contract=contract, perpetrator=buyer):
# if the seller has an insurance against the buyer for this contract, then just give him the missing
Expand Down Expand Up @@ -1132,7 +1136,17 @@ def _execute_contract(self, contract: Contract) -> Set[Breach]:
# quantity = int(math.floor(money / unit_price))

if money > 0 or quantity > 0:
self._move_product(buyer=buyer, seller=seller, quantity=quantity, money=money, product_id=pind)
perpetrators = set([b.perpetrator for b in breaches])
execute = True
victims = {}
if 0 < len(perpetrators) < len(partners):
victims = set(partners) - set(perpetrators)
execute = all(victim.confirm_partial_execution(contract=contract, breaches=list(breaches))
for victim in victims)
if execute:
self._move_product(buyer=buyer, seller=seller, quantity=quantity, money=money, product_id=pind)
else:
self.logdebug(f'Contract {contract.id}: one of {[_.id for _ in victims]} refused partial execution.')
else:
self.logdebug(f'Contract {contract.id} has no transfers')
return breaches
Expand All @@ -1156,6 +1170,9 @@ def _move_product(self, buyer: SCMLAgent, seller: SCMLAgent, product_id: int, qu
self.logdebug(f'Moved {quantity} units of {self.products[product_id].name} from {seller.name} to {buyer.name} '
f'for {money} dollars')

def _complete_contract_execution(self, contract: Contract, breaches: List[Breach], resolved: bool):
pass

def _move_product_force(self, buyer: SCMLAgent, seller: SCMLAgent, product_id: int, quantity: int, money: float):
"""Moves as much product and money between the buyer and seller"""
seller_factory, buyer_factory = self.a2f[seller.id], self.a2f[buyer.id]
Expand Down
8 changes: 6 additions & 2 deletions negmas/common.py
Expand Up @@ -64,6 +64,8 @@ class MechanismState:
"""Does the mechanism have any errors"""
error_details: str = ''
"""Details of the error if any"""
info: 'MechanismInfo' = None
"""Mechanism information"""

@property
def ended(self):
Expand Down Expand Up @@ -103,7 +105,7 @@ def asdict(self):
return {_.name: self.__dict__[_.name] for _ in fields(self)}

class Java:
implements = ['jnegmas.ProductionFailure']
implements = ['jnegmas.common.MechanismState']


@dataclass
Expand All @@ -119,6 +121,8 @@ class MechanismInfo:
"""A lit of *all possible* outcomes for a negotiation. None if the number of outcomes is uncountable"""
time_limit: float
"""The time limit in seconds for this negotiation session. None indicates infinity"""
step_time_limit: float
"""The time limit in seconds for each step of this negotiation session. None indicates infinity"""
n_steps: int
"""The allowed number of steps for this negotiation. None indicates infinity"""
dynamic_entry: bool
Expand Down Expand Up @@ -242,7 +246,7 @@ def asdict(self):
return {_.name: self.__dict__[_.name] for _ in fields(self)}

class Java:
implements = ['jnegmas.ProductionFailure']
implements = ['jnegmas.common.PyMechanismInfo']


def register_all_mechanisms(mechanisms: typing.Dict[str, 'Mechanism']) -> None:
Expand Down
13 changes: 12 additions & 1 deletion negmas/mechanisms.py
Expand Up @@ -62,6 +62,7 @@ def __init__(
outcomes: Union[int, List['Outcome']] = None,
n_steps: int = None,
time_limit: float = None,
step_time_limit: float = None,
max_n_agents: int = None,
dynamic_entry=False,
cache_outcomes=True,
Expand Down Expand Up @@ -160,6 +161,7 @@ def __init__(
, outcomes=__outcomes
, time_limit=time_limit
, n_steps=n_steps
, step_time_limit = step_time_limit
, dynamic_entry=dynamic_entry
, max_n_agents=max_n_agents
, annotation=annotation
Expand Down Expand Up @@ -571,6 +573,10 @@ def step(self) -> MechanismState:
- There is another function (`run()`) that runs the whole mechanism in blocking mode
"""
if self.time > self.time_limit:
self._agreement, self._broken, self._timedout = None, False, True
self._history.append(self.state)
return self.state
if len(self._agents) < 2:
if self.info.dynamic_entry:
self._history.append(self.state)
Expand Down Expand Up @@ -611,11 +617,16 @@ def step(self) -> MechanismState:

for agent in self._agents:
agent.on_round_start(state=self.state)
step_start = time.perf_counter()
result = self.step_()
step_time = time.perf_counter() - step_start
self._error, self._error_details = result.error, result.error_details
if self._error:
self.on_mechanism_error()
self._broken, self._timedout, self._agreement = result.broken, result.timedout, result.agreement
if step_time > self.info.step_time_limit:
self._broken, self._timedout, self._agreement = False, True, None
else:
self._broken, self._timedout, self._agreement = result.broken, result.timedout, result.agreement
if (self._agreement is not None) or self._broken or self._timedout:
self._running = False
self._step += 1
Expand Down
17 changes: 11 additions & 6 deletions negmas/sao.py
Expand Up @@ -8,7 +8,7 @@

from negmas.common import *
from negmas.events import Notification
from negmas.java import JNegmasGateway, JavaCallerMixin, to_java
from negmas.java import JNegmasGateway, JavaCallerMixin, to_dict
from negmas.mechanisms import MechanismRoundResult, Mechanism
from negmas.negotiators import Negotiator, AspirationMixin, Controller
from negmas.outcomes import sample_outcomes, Outcome, outcome_is_valid, ResponseType, outcome_as_dict
Expand Down Expand Up @@ -63,6 +63,7 @@ def __init__(
outcomes=None,
n_steps=None,
time_limit=None,
step_time_limit=None,
max_n_agents=None,
dynamic_entry=True,
keep_issue_names=True,
Expand All @@ -77,6 +78,7 @@ def __init__(
outcomes=outcomes,
n_steps=n_steps,
time_limit=time_limit,
step_time_limit=step_time_limit,
max_n_agents=max_n_agents,
dynamic_entry=dynamic_entry,
keep_issue_names=keep_issue_names,
Expand Down Expand Up @@ -600,6 +602,9 @@ def propose_(self, state: MechanismState) -> Optional['Outcome']:
"""

class Java:
implements = ['jnegmas.sao.PySAONegotiator']


class RandomNegotiator(Negotiator, RandomResponseMixin, RandomProposalMixin):
"""A negotiation agent that responds randomly in a single negotiation."""
Expand Down Expand Up @@ -1023,23 +1028,23 @@ def __init__(self, java_class_name: Optional[str]
)
if java_class_name is not None:
self.init_java_bridge(java_class_name=java_class_name, auto_load_java=auto_load_java)
self.java_object.fromMap(to_java(self))
self.java_object.fromMap(to_dict(self))

@classmethod
def from_java(cls, java_object, *args, parent: Controller = None) -> 'JavaSAONegotiator':
def from_dict(cls, java_object, *args, parent: Controller = None) -> 'JavaSAONegotiator':
"""Creates a Java negotiator from an object returned from the JVM implementing PySAONegotiator"""
ufun = java_object.getUtilityFunction()
if ufun is not None:
ufun = JavaUtilityFunction.from_java(java_object=ufun)
return JavaCallerMixin.from_java(java_object, name=java_object.getName()
ufun = JavaUtilityFunction.from_dict(java_object=ufun)
return JavaCallerMixin.from_dict(java_object, name=java_object.getName()
, assume_normalized=java_object.getAssumeNormalized()
, rational_proposal=java_object.getRationalProposal()
, parent=parent
, ufun=ufun)

def on_notification(self, notification: Notification, notifier: str):
super().on_notification(notification=notification, notifier=notifier)
jnotification = {'type': notification.type, 'data': to_java(notification.data)}
jnotification = {'type': notification.type, 'data': to_dict(notification.data)}
self.java_object.on_notification(jnotification, notifier)

def respond_(self, state: MechanismState, offer: 'Outcome'):
Expand Down

0 comments on commit 32118bf

Please sign in to comment.