Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def _pop(self, key):
"Pop a retry proposal draft from the in-flight dictionary"
return self._in_flight.pop(id(key))
def _rekey(self, old_key, new_key):
"Change the key on a retry proposal draft."
logger.debug("Rekey retry proposal")
self._set(new_key, self._pop(old_key))
def _resubmit(self, key):
"Recycle a previously-submitted retry proposal."
logger.debug("Resubmitting retry proposal")
self.rput(self._pop(key))
class FixedSampleStrategy(BaseStrategy):
"""Sample at a fixed set of points.
The fixed sampling strategy is appropriate for any non-adaptive
sampling scheme. Since the strategy is non-adaptive, we can
employ as many available workers as we have points to process. We
keep trying any evaluation that fails, and suggest termination
only when all evaluations are complete. The points in the
experimental design can be provided as any iterable object (e.g. a
list or a generator function). One can use a generator for an
infinite sequence if the fixed sampling strategy is used in
combination with a strategy that provides a termination criterion.
"""
def __init__(self, points):
"""Initialize the sampling scheme.
"Propose an action based on outstanding points."
try:
if self.retry.empty():
logger.debug("Getting new point from fixed schedule")
point = next(self.point_generator)
proposal = self.propose_eval(point)
self.retry.rput(proposal)
return self.retry.get()
except StopIteration:
logger.debug("Done fixed schedule; {0} outstanding evals".format(
self.retry.num_eval_outstanding))
if self.retry.num_eval_outstanding == 0:
return self.propose_terminate()
class CoroutineStrategy(BaseStrategy):
"""Event-driven to serial adapter using coroutines.
The coroutine strategy runs a standard sequential optimization algorithm
in a Python coroutine, with which the strategy communicates via send/yield
directives. The optimization coroutine yields the parameters at which
it would like function values; the strategy then requests the function
evaluation and returns the value with a send command when it becomes
available.
Attributes:
coroutine: Optimizer coroutine
rvalue: Function to map records to values
retry: Retry manager
"""
def __init__(self, coroutine, rvalue=lambda r: r.value):
def on_reply_accept(self, proposal):
"If proposal accepted, wait for result."
proposal.record.batch_id = proposal.batch_id
def on_complete(self, record):
"Return or re-request on completion or cancellation."
logger.debug("Got batch value ({0}/{1})".format(
record.batch_id, len(self.results)))
self.results[record.batch_id] = self.rvalue(record)
class RunTerminatedException(Exception):
pass
class PromiseStrategy(BaseStrategy):
"""Provides a promise-based asynchronous evaluation interface.
A promise (aka a future) is a common concurrent programming abstraction.
A caller requests an asynchronous evaluation, and the call returns
immediately with a promise object. The caller can check the promise
object to see if it has a value ready, or wait for the value to become
ready. The callee can set the value when it is ready.
Attributes:
proposalq: Queue of proposed actions caused by optimizer.
valueq: Queue of function values from proposed actions.
rvalue: Function to extract return value from a record
block: Flag whether to block on proposal pop
"""
class Promise(object):
def kill(self, record, retry=False):
"Request a function evaluation be killed"
self._propose(self.propose_kill(record), retry)
def terminate(self, retry=True):
"Request termination of the optimization"
self._propose(self.propose_terminate(), retry)
def propose_action(self):
if not self.retry.empty():
return self.retry.get()
return self.decorate_proposal(self.strategy.propose_action())
class AddArgStrategy(BaseStrategy):
"""Add extra arguments to evaluation proposals.
Decorates evaluation proposals with extra arguments. These may
reflect termination criteria, advice to workers about which of
several methods to use, etc. The extra arguments can be static
(via the extra_args attribute), or they may be returned from the
get_extra_args function, which child classes can override.
The strategy *overwrites* any existing list of extra arguments,
so it is probably a mistake to compose strategies in a way that
involves multiple objects of this type.
Attributes:
strategy: Parent strategy
extra_args: Extra arguments to be added
"""
if not self.retry.empty():
return self.retry.get()
def on_complete(self, record):
"Return point to coroutine"
try:
logger.debug("Return value to coroutine")
params = self.coroutine.send(self.rvalue(record))
logger.debug("Set up next point proposal from coroutine")
self.retry.rput(self.propose_eval(params))
except StopIteration:
logger.debug("Coroutine returned without yield")
self.retry.rput(self.propose_terminate())
class CoroutineBatchStrategy(BaseStrategy):
"""Event-driven to synchronous parallel adapter using coroutines.
The coroutine strategy runs a synchronous parallel optimization
algorithm in a Python coroutine, with which the strategy
communicates via send/yield directives. The optimization
coroutine yields batches of parameters (as lists) at which it
would like function values; the strategy then requests the
function evaluation and returns the records associated with those
function evaluations when all have been completed.
NB: Within a given batch, function evaluations are attempted in
the reverse of the order in which they are specified. This should
make no difference for most things (the batch is not assumed to have
any specific order), but it is significant for testing.
Attributes:
elif record.is_done:
logger.debug("Received record killed at base strategy")
self.on_kill(record)
else:
logger.debug("Intermediate record update at base strategy")
def on_complete(self, record):
"Process completed record."
pass
def on_kill(self, record):
"Process killed or cancelled record."
pass
class RetryStrategy(BaseStrategy):
"""Retry strategy class.
The RetryStrategy class manages a queue of proposals to be retried,
either because they were rejected or because they correspond to
a function evaluation that was killed.
If a proposal is submitted to a retry strategy and it has already
been marked with the .retry attribute, we do not attempt to manage
retries, as another object is assumed to have taken responsibility
for retrying the proposal on failure. This prevents us from having
redundant retries.
When a proposal is submitted to the retry class, we make a copy
(the proposal draft) for safe-keeping. If at any point it is
necessary to resubmit, we submit the draft copy to the retry
class. The draft will have any modifications or callbacks that
"On every completion, increment the counter."
if record.is_completed:
self.counter += 1
logger.debug("MaxEvalStrategy: completed {0}/{1}".format(
self.counter, self.max_counter))
def propose_action(self):
"Propose termination once the eval counter is high enough."
if self.counter >= self.max_counter:
logger.debug("MaxEvalStrategy: request termination")
return Proposal('terminate')
else:
return None
class InputStrategy(BaseStrategy):
"""Insert requests from the outside world (e.g. from a GUI)."""
def __init__(self, controller, strategy):
self.controller = controller
self.strategy = strategy
self.retry = RetryStrategy()
def _propose(self, proposal, retry):
if retry:
self.retry.rput(proposal)
else:
self.retry.put(proposal)
self.controller.ping()
def eval(self, params, retry=True):
"Request a new function evaluation"