Provides a point to explore. This function can be called multiple times to explore several points in parallel Returns ------- p.Parameter: The candidate to try on the objective function. :code:`p.Parameter` have field :code:`args` and :code:`kwargs`
(self)
| 469 | self.archive = self.pruning(self.archive) |
| 470 | |
| 471 | def ask(self) -> p.Parameter: |
| 472 | """Provides a point to explore. |
| 473 | This function can be called multiple times to explore several points in parallel |
| 474 | |
| 475 | Returns |
| 476 | ------- |
| 477 | p.Parameter: |
| 478 | The candidate to try on the objective function. :code:`p.Parameter` have field :code:`args` and :code:`kwargs` |
| 479 | which can be directly used on the function (:code:`objective_function(*candidate.args, **candidate.kwargs)`). |
| 480 | """ |
| 481 | # call callbacks for logging etc... |
| 482 | for callback in self._callbacks.get("ask", []): |
| 483 | callback(self) |
| 484 | current_num_ask = self.num_ask |
| 485 | # tentatives if a cheap constraint is available |
| 486 | max_trials = max(1, self._constraints_manager.max_trials // 2) |
| 487 | # half will be used for sub-optimization --- if the optimization method does not need/use a budget. |
| 488 | # TODO(oteytaud): actually we could do this even when the budget is known, if we are sure that |
| 489 | # exceeding the budget is not a problem. |
| 490 | # Very simple constraint solver: |
| 491 | # - we use a simple algorithm. |
| 492 | # - no memory of previous iterations. |
| 493 | # - just projection to constraint satisfaction. |
| 494 | # We try using the normal tool during half constraint budget, in order to reduce the impact on the normal run. |
| 495 | self.parametrization.tabu_fails = 0 |
| 496 | for _ in range(max_trials): |
| 497 | is_suggestion = False |
| 498 | if self._suggestions: # use suggestions if available |
| 499 | is_suggestion = True |
| 500 | candidate = self._suggestions.pop() |
| 501 | else: |
| 502 | try: # Sometimes we have a limited budget so that |
| 503 | candidate = self._internal_ask_candidate() |
| 504 | except AssertionError as e: |
| 505 | assert ( |
| 506 | self.parametrization._constraint_checkers |
| 507 | ), f"Error: {e}" # This should not happen without constraint issues. |
| 508 | candidate = self.parametrization.spawn_child() |
| 509 | if candidate.satisfies_constraints(self.parametrization): |
| 510 | break # good to go! |
| 511 | if self._penalize_cheap_violations or self.no_parallelization: |
| 512 | # Warning! This might be a tell not asked. |
| 513 | self._internal_tell_candidate(candidate, float("Inf")) # DE requires a tell |
| 514 | # updating num_ask is necessary for some algorithms which need new num to ask another point |
| 515 | self._num_ask += 1 |
| 516 | satisfies = candidate.satisfies_constraints(self.parametrization) |
| 517 | if not satisfies and self.parametrization.tabu_length == 0: |
| 518 | # still not solving, let's run sub-optimization |
| 519 | candidate = _constraint_solver(candidate, budget=max_trials) |
| 520 | if not (satisfies or candidate.satisfies_constraints(self.parametrization, no_tabu=True)): |
| 521 | self._warn( |
| 522 | f"Could not bypass the constraint after {max_trials} tentatives, " |
| 523 | "sending candidate anyway.", |
| 524 | errors.FailedConstraintWarning, |
| 525 | ) |
| 526 | if not is_suggestion: |
| 527 | if candidate.uid in self._asked: |
| 528 | raise RuntimeError( |
no test coverage detected