1# Copyright 2020, 2021 PaGMO development team 2# 3# This file is part of the pygmo library. 4# 5# This Source Code Form is subject to the terms of the Mozilla 6# Public License v. 2.0. If a copy of the MPL was not distributed 7# with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 9from ._check_deps import * 10# Version setup. 11from ._version import __version__ 12# We import the sub-modules into the root namespace 13from .core import * 14from .plotting import * 15from ._py_islands import * 16from ._py_problems import * 17from ._py_bfes import * 18from ._py_algorithms import * 19# Patch the problem class. 20from . import _patch_problem 21# Patch the algorithm class. 22from . import _patch_algorithm 23# Patch the bfe class. 24from . import _patch_bfe 25# Patch the island class. 26from . import _patch_island 27# Patch the policies. 28from . import _patch_r_policy 29from . import _patch_s_policy 30# Patch the topology. 31from . import _patch_topology 32import cloudpickle as _cloudpickle 33# Explicitly import the test submodule 34from . import test 35import atexit as _atexit 36 37 38# We move into the problems, algorithms, etc. namespaces 39# all the pure python UDAs, UDPs and UDIs. 40for _item in dir(_py_islands): 41 if not _item.startswith("_"): 42 setattr(islands, _item, getattr(_py_islands, _item)) 43del _py_islands 44 45for _item in dir(_py_problems): 46 if not _item.startswith("_"): 47 setattr(problems, _item, getattr(_py_problems, _item)) 48del _py_problems 49 50for _item in dir(_py_bfes): 51 if not _item.startswith("_"): 52 setattr(batch_evaluators, _item, getattr(_py_bfes, _item)) 53del _py_bfes 54 55for _item in dir(_py_algorithms): 56 if not _item.startswith("_"): 57 setattr(algorithms, _item, getattr(_py_algorithms, _item)) 58del _py_algorithms 59 60del _item 61 62# Machinery for the setup of the serialization backend. 63_serialization_backend = _cloudpickle 64 65# Override of the translate meta-problem constructor. 66__original_translate_init = translate.__init__ 67 68# NOTE: the idea of having the translate init here instead of exposed from C++ is to allow the use 69# of the syntax translate(udp, translation) for all udps 70 71 72def _translate_init(self, prob=None, translation=[0.]): 73 """ 74 Args: 75 prob: a user-defined problem (either Python or C++), or an instance of :class:`~pygmo.problem` 76 (if *prob* is :data:`None`, a :class:`~pygmo.null_problem` will be used in its stead) 77 translation (array-like object): an array containing the translation to be applied 78 79 Raises: 80 ValueError: if the length of *translation* is not equal to the dimension of *prob* 81 82 unspecified: any exception thrown by: 83 84 * the constructor of :class:`pygmo.problem`, 85 * the constructor of the underlying C++ class, 86 * failures at the intersection between C++ and Python (e.g., type conversion errors, mismatched function 87 signatures, etc.) 88 """ 89 if prob is None: 90 # Use the null problem for default init. 91 prob = null_problem() 92 if type(prob) == problem: 93 # If prob is a pygmo problem, we will pass it as-is to the 94 # original init. 95 prob_arg = prob 96 else: 97 # Otherwise, we attempt to create a problem from it. This will 98 # work if prob is an exposed C++ problem or a Python UDP. 99 prob_arg = problem(prob) 100 __original_translate_init(self, prob_arg, translation) 101 102 103setattr(translate, "__init__", _translate_init) 104 105# Override of the decompose meta-problem constructor. 106__original_decompose_init = decompose.__init__ 107 108# NOTE: the idea of having the translate init here instead of exposed from C++ is to allow the use 109# of the syntax decompose(udp, ..., ) for all udps 110 111 112def _decompose_init(self, prob=None, weight=[0.5, 0.5], z=[0., 0.], method='weighted', adapt_ideal=False): 113 """ 114 Args: 115 prob: a user-defined problem (either Python or C++), or an instance of :class:`~pygmo.problem` 116 (if *prob* is :data:`None`, a :class:`~pygmo.null_problem` will be used in its stead) 117 weight (array-like object): the vector of weights :math:`\\boldsymbol \lambda` 118 z (array-like object): the reference point :math:`\mathbf z^*` 119 method (str): a string containing the decomposition method chosen 120 adapt_ideal (bool): when :data:`True`, the reference point is adapted at each fitness evaluation 121 to be the ideal point 122 123 Raises: 124 ValueError: if either: 125 126 * *prob* is single objective or constrained, 127 * *method* is not one of [``'weighted'``, ``'tchebycheff'``, ``'bi'``], 128 * *weight* is not of size :math:`n`, 129 * *z* is not of size :math:`n` 130 * *weight* is not such that :math:`\\lambda_i > 0, \\forall i=1..n`, 131 * *weight* is not such that :math:`\\sum_i \\lambda_i = 1` 132 133 unspecified: any exception thrown by: 134 135 * the constructor of :class:`pygmo.problem`, 136 * the constructor of the underlying C++ class, 137 * failures at the intersection between C++ and Python (e.g., type conversion errors, mismatched function 138 signatures, etc.) 139 140 """ 141 if prob is None: 142 # Use the null problem for default init. 143 prob = null_problem(nobj=2) 144 if type(prob) == problem: 145 # If prob is a pygmo problem, we will pass it as-is to the 146 # original init. 147 prob_arg = prob 148 else: 149 # Otherwise, we attempt to create a problem from it. This will 150 # work if prob is an exposed C++ problem or a Python UDP. 151 prob_arg = problem(prob) 152 __original_decompose_init(self, prob_arg, weight, z, method, adapt_ideal) 153 154 155setattr(decompose, "__init__", _decompose_init) 156 157# Override of the unconstrain meta-problem constructor. 158__original_unconstrain_init = unconstrain.__init__ 159 160# NOTE: the idea of having the unconstrain init here instead of exposed from C++ is to allow the use 161# of the syntax unconstrain(udp, ... ) for all udps 162 163 164def _unconstrain_init(self, prob=None, method="death penalty", weights=[]): 165 """ 166 Args: 167 prob: a :class:`~pygmo.problem` or a user-defined problem, either C++ or Python (if 168 *prob* is :data:`None`, a :class:`~pygmo.null_problem` will be used in its stead) 169 method (str): a string containing the unconstrain method chosen, one of [``'death penalty'``, ``'kuri'``, ``'weighted'``, ``'ignore_c'``, ``'ignore_o'``] 170 weights (array-like object): the vector of weights to be used if the method chosen is ``'weighted'`` 171 172 Raises: 173 ValueError: if either: 174 175 * *prob* is unconstrained, 176 * *method* is not one of [``'death penalty'``, ``'kuri'``, ``'weighted'``, ``'ignore_c'``, ``'ignore_o'``], 177 * *weight* is not of the same size as the problem constraints (if the method ``'weighted'`` is selected), or not empty otherwise. 178 179 unspecified: any exception thrown by: 180 181 * the constructor of :class:`pygmo.problem`, 182 * the constructor of the underlying C++ class, 183 * failures at the intersection between C++ and Python (e.g., type conversion errors, mismatched function 184 signatures, etc.) 185 186 """ 187 if prob is None: 188 # Use the null problem for default init. 189 prob = null_problem(nobj=2, nec=3, nic=4) 190 if type(prob) == problem: 191 # If prob is a pygmo problem, we will pass it as-is to the 192 # original init. 193 prob_arg = prob 194 else: 195 # Otherwise, we attempt to create a problem from it. This will 196 # work if prob is an exposed C++ problem or a Python UDP. 197 prob_arg = problem(prob) 198 __original_unconstrain_init(self, prob_arg, method, weights) 199 200 201setattr(unconstrain, "__init__", _unconstrain_init) 202 203# Override of the cstrs_self_adaptive meta-algorithm constructor. 204__original_cstrs_self_adaptive_init = cstrs_self_adaptive.__init__ 205# NOTE: the idea of having the cstrs_self_adaptive init here instead of exposed from C++ is to allow the use 206# of the syntax cstrs_self_adaptive(uda, ...) for all udas 207 208 209def _cstrs_self_adaptive_init(self, iters=1, algo=None, seed=None): 210 """ 211 Args: 212 iter (int): number of iterations (i.e., calls to the inner algorithm evolve) 213 algo: an :class:`~pygmo.algorithm` or a user-defined algorithm, either C++ or Python (if 214 *algo* is :data:`None`, a :class:`~pygmo.de` algorithm will be used in its stead) 215 seed (int): seed used by the internal random number generator (if *seed* is :data:`None`, a 216 randomly-generated value will be used in its stead) 217 218 Raises: 219 ValueError: if *iters* is negative or greater than an implementation-defined value 220 unspecified: any exception thrown by the constructor of :class:`pygmo.algorithm`, or by 221 failures at the intersection between C++ and Python (e.g., type conversion errors, mismatched function 222 signatures, etc.) 223 224 """ 225 if algo is None: 226 # Use the null problem for default init. 227 algo = de() 228 if type(algo) == algorithm: 229 # If algo is a pygmo algorithm, we will pass it as-is to the 230 # original init. 231 algo_arg = algo 232 else: 233 # Otherwise, we attempt to create an algorithm from it. This will 234 # work if algo is an exposed C++ algorithm or a Python UDA. 235 algo_arg = algorithm(algo) 236 if seed is None: 237 __original_cstrs_self_adaptive_init(self, iters, algo_arg) 238 else: 239 __original_cstrs_self_adaptive_init(self, iters, algo_arg, seed) 240 241 242setattr(cstrs_self_adaptive, "__init__", _cstrs_self_adaptive_init) 243 244 245# Override of the population constructor. 246__original_population_init = population.__init__ 247 248 249def _population_init(self, prob=None, size=0, b=None, seed=None): 250 # NOTE: the idea of having the pop init here instead of exposed from C++ is that like this we don't need 251 # to expose a new pop ctor each time we expose a new problem: in this method we will use the problem ctor 252 # from a C++ problem, and on the C++ exposition side we need only to 253 # expose the ctor of pop from pagmo::problem. 254 """ 255 Args: 256 prob: a user-defined problem (either Python or C++), or an instance of :class:`~pygmo.problem` 257 (if *prob* is :data:`None`, a default-constructed :class:`~pygmo.problem` will be used 258 in its stead) 259 size (:class:`int`): the number of individuals 260 b: a user-defined batch fitness evaluator (either Python or C++), or an instance of :class:`~pygmo.bfe` 261 (if *b* is :data:`None`, the evaluation of the population's individuals will be performed 262 in sequential mode) 263 seed (:class:`int`): the random seed (if *seed* is :data:`None`, a randomly-generated value will be used 264 in its stead) 265 266 Raises: 267 TypeError: if *size* is not an :class:`int` or *seed* is not :data:`None` and not an :class:`int` 268 OverflowError: is *size* or *seed* are negative 269 unspecified: any exception thrown by the invoked C++ constructors, by the constructor of 270 :class:`~pygmo.problem`, or the constructor of :class:`~pygmo.bfe`, or by failures at 271 the intersection between C++ and 272 Python (e.g., type conversion errors, mismatched function signatures, etc.) 273 274 """ 275 from .core import _random_device_next 276 # Check input params. 277 if not isinstance(size, int): 278 raise TypeError("the 'size' parameter must be an integer") 279 if not seed is None and not isinstance(seed, int): 280 raise TypeError("the 'seed' parameter must be None or an integer") 281 if prob is None: 282 # Problem not specified, def-construct it. 283 prob = problem() 284 elif type(prob) != problem: 285 # If prob is not a problem, we attempt to create a problem from it. This will 286 # work if prob is an exposed C++ problem or a Python UDP. 287 prob = problem(prob) 288 289 if seed is None: 290 # Seed not specified, randomly generate it 291 # with the global pagmo rng. 292 seed = _random_device_next() 293 294 if b is None: 295 # No bfe specified, init in sequential mode. 296 __original_population_init(self, prob, size, seed) 297 else: 298 # A bfe was specified. Same as above with the problem. 299 __original_population_init(self, prob, b if type( 300 b) == bfe else bfe(b), size, seed) 301 302 303setattr(population, "__init__", _population_init) 304 305 306# Override of the island constructor. 307__original_island_init = island.__init__ 308 309 310def _island_init(self, **kwargs): 311 """ 312 Keyword Args: 313 udi: a user-defined island, either Python or C++ 314 algo: a user-defined algorithm (either Python or C++), or an instance of :class:`~pygmo.algorithm` 315 pop (:class:`~pygmo.population`): a population 316 prob: a user-defined problem (either Python or C++), or an instance of :class:`~pygmo.problem` 317 b: a user-defined batch fitness evaluator (either Python or C++), or an instance of :class:`~pygmo.bfe` 318 size (:class:`int`): the number of individuals 319 r_pol: a user-defined replacement policy (either Python or C++), or an instance of :class:`~pygmo.r_policy` 320 s_pol: a user-defined selection policy (either Python or C++), or an instance of :class:`~pygmo.s_policy` 321 seed (:class:`int`): the random seed (if not specified, it will be randomly-generated) 322 323 Raises: 324 KeyError: if the set of keyword arguments is invalid 325 unspecified: any exception thrown by the invoked C++ constructors, 326 the deep copy of the UDI, the constructors of :class:`~pygmo.algorithm` and :class:`~pygmo.population`, 327 failures at the intersection between C++ and Python (e.g., type conversion errors, mismatched function 328 signatures, etc.) 329 330 """ 331 if len(kwargs) == 0: 332 # Default constructor. 333 __original_island_init(self) 334 return 335 336 # If we are not dealing with a def ctor, we always need the algo argument. 337 if not 'algo' in kwargs: 338 raise KeyError( 339 "the mandatory 'algo' parameter is missing from the list of arguments " 340 "of the island constructor") 341 algo = kwargs.pop('algo') 342 algo = algo if type(algo) == algorithm else algorithm(algo) 343 344 # Population setup. We either need an input pop, or the prob and size, 345 # plus optionally seed and b. 346 if 'pop' in kwargs and ('prob' in kwargs or 'size' in kwargs or 'seed' in kwargs or 'b' in kwargs): 347 raise KeyError( 348 "if the 'pop' argument is provided, the 'prob', 'size', 'seed' and 'b' " 349 "arguments must not be provided") 350 elif 'pop' in kwargs: 351 pop = kwargs.pop('pop') 352 elif 'prob' in kwargs and 'size' in kwargs: 353 pop = population(prob=kwargs.pop('prob'), 354 size=kwargs.pop('size'), seed=kwargs.pop('seed') if 'seed' in kwargs else None, 355 b=kwargs.pop('b') if 'b' in kwargs else None) 356 else: 357 raise KeyError( 358 "unable to construct a population from the arguments of " 359 "the island constructor: you must either pass a population " 360 "('pop') or a set of arguments that can be used to build one " 361 "('prob', 'size' and, optionally, 'seed' and 'b')") 362 363 # UDI, if any. 364 if 'udi' in kwargs: 365 args = [kwargs.pop('udi'), algo, pop] 366 else: 367 args = [algo, pop] 368 369 # Replace/selection policies, if any. 370 if 'r_pol' in kwargs: 371 r_pol = kwargs.pop('r_pol') 372 r_pol = r_pol if type(r_pol) == r_policy else r_policy(r_pol) 373 args.append(r_pol) 374 else: 375 args.append(r_policy()) 376 377 if 's_pol' in kwargs: 378 s_pol = kwargs.pop('s_pol') 379 s_pol = s_pol if type(s_pol) == s_policy else s_policy(s_pol) 380 args.append(s_pol) 381 else: 382 args.append(s_policy()) 383 384 if len(kwargs) != 0: 385 raise KeyError( 386 'unrecognised keyword arguments: {}'.format(list(kwargs.keys()))) 387 388 __original_island_init(self, *args) 389 390 391setattr(island, "__init__", _island_init) 392 393 394# Override of the mbh meta-algorithm constructor. 395__original_mbh_init = mbh.__init__ 396# NOTE: the idea of having the mbh init here instead of exposed from C++ is to allow the use 397# of the syntax mbh(uda, ...) for all udas 398 399 400def _mbh_init(self, algo=None, stop=5, perturb=1e-2, seed=None): 401 """ 402 Args: 403 algo: an :class:`~pygmo.algorithm` or a user-defined algorithm, either C++ or Python (if 404 *algo* is :data:`None`, a :class:`~pygmo.compass_search` algorithm will be used in its stead) 405 stop (int): consecutive runs of the inner algorithm that need to result in no improvement for 406 :class:`~pygmo.mbh` to stop 407 perturb (float or array-like object): the perturbation to be applied to each component 408 seed (int): seed used by the internal random number generator (if *seed* is :data:`None`, a 409 randomly-generated value will be used in its stead) 410 411 Raises: 412 ValueError: if *perturb* (or one of its components, if *perturb* is an array) is not in the 413 (0,1] range 414 unspecified: any exception thrown by the constructor of :class:`pygmo.algorithm`, or by 415 failures at the intersection between C++ and Python (e.g., type conversion errors, mismatched function 416 signatures, etc.) 417 418 """ 419 import numbers 420 if algo is None: 421 # Use the compass search algo for default init. 422 algo = compass_search() 423 if type(algo) == algorithm: 424 # If algo is a pygmo algorithm, we will pass it as-is to the 425 # original init. 426 algo_arg = algo 427 else: 428 # Otherwise, we attempt to create an algorithm from it. This will 429 # work if algo is an exposed C++ algorithm or a Python UDA. 430 algo_arg = algorithm(algo) 431 if isinstance(perturb, numbers.Number): 432 perturb = [perturb] 433 if seed is None: 434 __original_mbh_init(self, algo_arg, stop, perturb) 435 else: 436 __original_mbh_init(self, algo_arg, stop, perturb, seed) 437 438 439setattr(mbh, "__init__", _mbh_init) 440 441 442# Override of the archi constructor. 443__original_archi_init = archipelago.__init__ 444 445 446def _archi_init(self, n=0, t=topology(), **kwargs): 447 """__init__(self, n=0, t=topology(), **kwargs) 448 449 The constructor will initialise an archipelago with a topology *t* and 450 *n* islands built from *kwargs*. 451 The keyword arguments accept the same format as explained in the constructor of 452 :class:`~pygmo.island`, with the following differences: 453 454 * *size* is replaced by *pop_size*, for clarity, 455 * the *seed* argument, if present, is used to initialise a random number generator 456 that, in turn, is used to generate random seeds for each island population. In other 457 words, the *seed* argument allows to generate randomly (but deterministically) 458 the seeds of the populations in the archipelago. If *seed* is not provided, the seeds 459 of the populations will be random and non-deterministic. 460 461 This class is the Python counterpart of the C++ class :cpp:class:`pagmo::archipelago`. 462 463 Args: 464 n (:class:`int`): the number of islands in the archipelago 465 t: a user-defined topology (either Python or C++), or an instance of :class:`~pygmo.topology` 466 467 Keyword Args: 468 udi: a user-defined island, either Python or C++ 469 algo: a user-defined algorithm (either Python or C++), or an instance of :class:`~pygmo.algorithm` 470 pop (:class:`~pygmo.population`): a population 471 prob: a user-defined problem (either Python or C++), or an instance of :class:`~pygmo.problem` 472 b: a user-defined batch fitness evaluator (either Python or C++), or an instance of :class:`~pygmo.bfe` 473 pop_size (:class:`int`): the number of individuals for each island 474 r_pol: a user-defined replacement policy (either Python or C++), or an instance of :class:`~pygmo.r_policy` 475 s_pol: a user-defined selection policy (either Python or C++), or an instance of :class:`~pygmo.s_policy` 476 seed (:class:`int`): the random seed 477 478 Raises: 479 TypeError: if *n* is not an integral type 480 ValueError: if *n* is negative 481 unspecified: any exception thrown by the constructor of :class:`~pygmo.island`, 482 by the underlying C++ constructor, :func:`~pygmo.archipelago.push_back()` or 483 by the public interface of :class:`~pygmo.topology` 484 485 Examples: 486 >>> from pygmo import * 487 >>> archi = archipelago(n = 16, algo = de(), prob = rosenbrock(10), pop_size = 20, seed = 32) 488 >>> archi.evolve() 489 >>> archi #doctest: +SKIP 490 Number of islands: 16 491 Status: busy 492 <BLANKLINE> 493 Islands summaries: 494 <BLANKLINE> 495 # Type Algo Prob Size Status 496 ----------------------------------------------------------------------------------------------- 497 0 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 498 1 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 499 2 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 500 3 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 501 4 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 busy 502 5 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 503 6 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 busy 504 7 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 busy 505 8 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 506 9 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 507 10 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 508 11 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 509 12 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 510 13 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 busy 511 14 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 busy 512 15 Thread island Differential Evolution Multidimensional Rosenbrock Function 20 idle 513 <BLANKLINE> 514 >>> archi.wait() 515 >>> res = archi.get_champions_f() 516 >>> res #doctest: +SKIP 517 [array([ 125441.77328885]), 518 array([ 144025.63904164]), 519 array([ 25387.38989711]), 520 array([ 56029.44160232]), 521 array([ 47760.99082202]), 522 array([ 118552.94891993]), 523 array([ 118405.29575447]), 524 array([ 101866.81846325]), 525 array([ 166106.12039851]), 526 array([ 167408.00058506]), 527 array([ 148815.47953885]), 528 array([ 165186.74476375]), 529 array([ 326615.28881936]), 530 array([ 167301.16445135]), 531 array([ 166871.42760503]), 532 array([ 75133.38815736])] 533 534 535 """ 536 # Check n. 537 if not isinstance(n, int): 538 raise TypeError("the 'n' parameter must be an integer") 539 if n < 0: 540 raise ValueError( 541 "the 'n' parameter must be non-negative, but it is {} instead".format(n)) 542 543 # Replace the 'pop_size' kw arg with just 'size', for later use in the 544 # island ctor. 545 546 if 'size' in kwargs: 547 raise KeyError( 548 "the 'size' argument cannot appear among the named arguments of the archipelago constructor") 549 550 if 'pop_size' in kwargs: 551 # Extract 'pop_size', replace with just 'size'. 552 ps_val = kwargs.pop('pop_size') 553 kwargs['size'] = ps_val 554 555 # Call the original init, which constructs an empty archi from a topology. 556 t = t if type(t) == topology else topology(t) 557 __original_archi_init(self, t) 558 559 if 'seed' in kwargs: 560 # Special handling of the 'seed' argument. 561 from random import Random 562 from .core import _max_unsigned 563 # Create a random engine with own state. 564 RND = Random() 565 # Get the seed from kwargs. 566 seed = kwargs.pop('seed') 567 if not isinstance(seed, int): 568 raise TypeError("the 'seed' parameter must be an integer") 569 # Seed the rng. 570 RND.seed(seed) 571 u_max = _max_unsigned() 572 # Push back the islands with different seed. 573 for _ in range(n): 574 kwargs['seed'] = RND.randint(0, u_max) 575 self.push_back(**kwargs) 576 577 else: 578 # Push back islands. 579 for _ in range(n): 580 self.push_back(**kwargs) 581 582 583setattr(archipelago, "__init__", _archi_init) 584 585 586def _archi_push_back(self, *args, **kwargs): 587 """Add an island. 588 589 This method will construct an island from the supplied arguments and add it to the archipelago. 590 Islands are added at the end of the archipelago (that is, the new island will have an index 591 equal to the size of the archipelago before the call to this method). :func:`pygmo.topology.push_back()` 592 will also be called on the :class:`~pygmo.topology` associated to this archipelago, so that 593 the addition of a new island to the archipelago is mirrored by the addition of a new vertex 594 to the topology. 595 596 This method accepts either a single positional argument, or a set of 597 keyword arguments: 598 599 * if no positional arguments are provided, then the keyword arguments will 600 be used to construct a :class:`~pygmo.island` which will then be added 601 to the archipelago; otherwise, 602 * if a single positional argument is provided and no keyword arguments are 603 provided, then the positional argument is interpreted as an :class:`~pygmo.island` 604 object to be added to the archipelago. 605 606 Any other combination of positional/keyword arguments will result in an error. 607 608 Raises: 609 ValueError: if, when using positional arguments, there are more than 1 positional arguments, 610 or if keyword arguments are also used at the same time 611 TypeError: if, when using a single positional argument, the type of that argument 612 is not :class:`~pygmo.island` 613 unspecified: any exception thrown by the constructor of :class:`~pygmo.island`, 614 :func:`pygmo.topology.push_back()` or by the underlying C++ method 615 616 """ 617 from . import island 618 619 if len(args) == 0: 620 self._push_back(island(**kwargs)) 621 else: 622 if len(args) != 1: 623 raise ValueError( 624 "{} positional arguments were provided, but this method accepts only a single positional argument".format(len(args))) 625 if len(kwargs) != 0: 626 raise ValueError( 627 "if a positional argument is passed to this method, then no keyword arguments must be passed, but {} keyword arguments were passed instead".format(len(kwargs))) 628 if type(args[0]) != island: 629 raise TypeError( 630 "the positional argument passed to this method must be an island, but the type of the argument is '{}' instead".format(type(args[0]))) 631 self._push_back(args[0]) 632 633 634setattr(archipelago, "push_back", _archi_push_back) 635 636 637def _archi_set_topology(self, t): 638 """This method will wait for any ongoing evolution in the archipelago to finish, 639 and it will then set the topology of the archipelago to *t*. 640 641 Note that it is the user's responsibility to ensure that the new topology is 642 consistent with the archipelago's properties. 643 644 Args: 645 t: a user-defined topology (either Python or C++), or an instance of :class:`~pygmo.topology` 646 647 Raises: 648 unspecified: any exception thrown by copying the topology 649 650 """ 651 t = t if type(t) == topology else topology(t) 652 self._set_topology(t) 653 654 655setattr(archipelago, "set_topology", _archi_set_topology) 656 657 658# Override of the free_form constructor. 659__original_free_form_init = free_form.__init__ 660 661 662def _free_form_init(self, t=None): 663 """ 664 Args: 665 t: the object that will be used for construction 666 667 Raises: 668 ValueError: if the edges of the input :class:`networkx.DiGraph` 669 do not all have a ``weight`` attribute, or if any edge weight 670 is outside the :math:`\\left[ 0, 1 \\right]` range 671 unspecified: any exception thrown by :func:`pygmo.topology.to_networkx()`, or by 672 the construction of a :class:`~pygmo.topology` from a UDT 673 674 """ 675 import networkx as nx 676 677 if t is None: 678 # Default ctor. 679 __original_free_form_init(self) 680 elif type(t) == topology or isinstance(t, nx.DiGraph): 681 # Ctors from topology or DiGraph are exposed 682 # directly. 683 __original_free_form_init(self, t) 684 else: 685 # If t is neither a topology, nor a DiGraph, 686 # assume that it is a UDT. 687 __original_free_form_init(self, topology(t)) 688 689 690setattr(free_form, "__init__", _free_form_init) 691 692 693def set_serialization_backend(name): 694 """Set pygmo's serialization backend. 695 696 This function allows to specify the serialization backend that is used internally by pygmo 697 for the (de)serialization of pythonic user-defined entities (e.g., user-defined pythonic 698 problems, algorithms, etc.). 699 700 By default, pygmo uses the `cloudpickle <https://github.com/cloudpipe/cloudpickle>`__ 701 module, which extends the capabilities of the standard :mod:`pickle` module with support 702 for lambdas, functions and classes defined interactively in the ``__main__`` module, etc. 703 704 In some specific cases, however, different serialization backends might work better than cloudpickle, 705 and thus pygmo provides the possibility for the cognizant user to switch to another 706 serialization backend. 707 708 The valid backends are: 709 710 * ``'pickle'`` (i.e., the standard Python :mod:`pickle` module), 711 * ``'cloudpickle'``, 712 * ``'dill'`` (from the `dill <https://pypi.org/project/dill/>`__ library). 713 714 .. warning:: 715 716 Setting the serialization backend is not thread-safe: do **not** set 717 the serialization backend while concurrently setting/getting it from another thread, 718 or while asynchronous evolutions/optimisations are ongoing. 719 720 Args: 721 name (str): the name of the desired backend 722 723 Raises: 724 TypeError: if *name* is not a :class:`str` 725 ValueError: if *name* is not one of ``['pickle', 'cloudpickle', 'dill']`` 726 ImportError: if *name* is ``'dill'`` but the dill module is not installed 727 728 """ 729 if not isinstance(name, str): 730 raise TypeError( 731 "The serialization backend must be specified as a string, but an object of type {} was provided instead".format(type(name))) 732 global _serialization_backend 733 if name == "pickle": 734 import pickle 735 _serialization_backend = pickle 736 elif name == "cloudpickle": 737 _serialization_backend = _cloudpickle 738 elif name == "dill": 739 try: 740 import dill 741 _serialization_backend = dill 742 except ImportError: 743 raise ImportError( 744 "The 'dill' serialization backend was specified, but the dill module is not installed.") 745 else: 746 raise ValueError( 747 "The serialization backend '{}' is not valid. The valid backends are: ['pickle', 'cloudpickle', 'dill']".format(name)) 748 749 750def get_serialization_backend(): 751 """Get pygmo's serialization backend. 752 753 This function will return pygmo's current serialization backend (see 754 :func:`~pygmo.set_serialization_backend()` for an explanation of the 755 available backends). 756 757 Returns: 758 types.ModuleType: the current serialization backend (as a Python module) 759 760 """ 761 return _serialization_backend 762 763 764def _cleanup(): 765 mp_island.shutdown_pool() 766 mp_bfe.shutdown_pool() 767 ipyparallel_island.shutdown_view() 768 ipyparallel_bfe.shutdown_view() 769 770 771_atexit.register(_cleanup) 772