coco-experiment 2.8.2__tar.gz → 2.8.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/.gitignore +2 -0
  2. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/PKG-INFO +7 -8
  3. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/pyproject.toml +7 -8
  4. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/__init__.py +1 -0
  5. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/_version.py +1 -1
  6. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/coco.c +22 -13
  7. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/interface.pyx +9 -2
  8. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/noiser.py +68 -24
  9. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/utilities.py +15 -6
  10. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/LICENSE +0 -0
  11. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/README.md +0 -0
  12. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/meson.build +0 -0
  13. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/scripts/run-doctest.py +0 -0
  14. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/bare_problem.pyx +0 -0
  15. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/coco.h +0 -0
  16. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/download_example.py +0 -0
  17. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/exceptions.py +0 -0
  18. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/src/cocoex/solvers.py +0 -0
  19. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/tests/bbob2009_testcases.txt +0 -0
  20. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/tests/bbob2009_testcases2.txt +0 -0
  21. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/tests/test_bare_problem.py +0 -0
  22. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/tests/test_regression.py +0 -0
  23. {coco_experiment-2.8.2 → coco_experiment-2.8.4}/tests/test_suite.py +0 -0
@@ -64,3 +64,5 @@ test/regression-test/bbob_noisy_regression_test/data_legacy/*.json
64
64
  test/regression-test/bbob_noisy_regression_test/results
65
65
  build/c/.vscode
66
66
  bare_problem.c
67
+ test/regression-test/create/exdata
68
+ test/regression-test/exdata
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: coco-experiment
3
- Version: 2.8.2
3
+ Version: 2.8.4
4
4
  Summary: Benchmarking framework for all types of black-box optimization algorithms.
5
5
  Project-URL: Homepage, https://github.com/numbbo/coco-experiment
6
6
  Project-URL: Documentation, https://numbbo.github.io/coco-doc/apidocs/cocoex/
@@ -11,14 +11,13 @@ License-File: LICENSE
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: License :: OSI Approved :: BSD License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: 3.15
19
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
20
- Requires-Python: >=3.8
21
- Requires-Dist: numpy>=1.24.0
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: numpy>=1.26.0
22
21
  Description-Content-Type: text/markdown
23
22
 
24
23
  <h1 align="center">
@@ -1,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["meson-python", "Cython >= 3.0.6", "wheel", "numpy"]
2
+ requires = ["meson-python >= 0.22", "Cython >= 3.3", "wheel", "numpy >= 2.5"]
3
3
  build-backend = 'mesonpy'
4
4
 
5
5
  [project]
@@ -22,21 +22,20 @@ authors = [
22
22
  ]
23
23
  description = 'Benchmarking framework for all types of black-box optimization algorithms.'
24
24
  readme = "README.md"
25
- requires-python = ">=3.8"
25
+ requires-python = ">=3.12"
26
26
  license = { text = "BSD-3-Clause" }
27
27
  classifiers = [
28
28
  "Development Status :: 4 - Beta",
29
29
  "License :: OSI Approved :: BSD License",
30
30
  "Programming Language :: Python",
31
- "Programming Language :: Python :: 3.8",
32
- "Programming Language :: Python :: 3.9",
33
- "Programming Language :: Python :: 3.10",
34
- "Programming Language :: Python :: 3.11",
35
31
  "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
33
+ "Programming Language :: Python :: 3.14",
34
+ "Programming Language :: Python :: 3.15",
36
35
  "Programming Language :: Python :: Implementation :: CPython",
37
36
  ]
38
37
  dependencies = [
39
- "numpy >= 1.24.0",
38
+ "numpy >= 1.26.0",
40
39
  ]
41
40
  dynamic = ["version"]
42
41
 
@@ -76,5 +75,5 @@ path = "src/cocoex/_version.py"
76
75
 
77
76
  [dependency-groups]
78
77
  dev = [
79
- "build>=1.2",
78
+ "build>=1.6",
80
79
  ]
@@ -19,6 +19,7 @@ from . import noiser # noqa: F401
19
19
  from . import solvers # noqa: F401
20
20
  from . import utilities # noqa: F401
21
21
  from . import exceptions # noqa: F401
22
+ # from . import test # noqa: F401 # gives a circular import, why???
22
23
  from .interface import Observer as _Observer
23
24
  from .interface import Problem as _Problem
24
25
  from .interface import Suite as _Suite
@@ -1,7 +1,7 @@
1
1
  # file generated by fabricate
2
2
  # Do not change, do not track in version control!
3
3
 
4
- __version__ = "2.8.2"
4
+ __version__ = "2.8.4"
5
5
  version = __version__
6
6
 
7
7
  if __name__ == "__main__":
@@ -26241,14 +26241,16 @@ static void coco_observer_targets_free(coco_observer_targets_t *targets) {
26241
26241
  * dim*1, dim*2, dim*5, 10*dim*1, 10*dim*2, 10*dim*5, 100*dim*1, 100*dim*2,
26242
26242
  * 100*dim*5, ...
26243
26243
  */
26244
- static coco_observer_evaluations_t *coco_observer_evaluations(const char *base_evaluations, const size_t dimension) {
26244
+ static coco_observer_evaluations_t *coco_observer_evaluations(const size_t number_evaluation_triggers,
26245
+ const char *base_evaluations,
26246
+ const size_t dimension) {
26245
26247
 
26246
26248
  coco_observer_evaluations_t *evaluations = (coco_observer_evaluations_t *)coco_allocate_memory(sizeof(*evaluations));
26247
26249
 
26248
26250
  /* First trigger */
26249
26251
  evaluations->value1 = 1;
26250
26252
  evaluations->exponent1 = 0;
26251
- evaluations->number_of_triggers = OBSERVER_DEFAULT_NUMBER_OF_EVALUATION_TRIGGERS;
26253
+ evaluations->number_of_triggers = number_evaluation_triggers;
26252
26254
 
26253
26255
  /* Second trigger */
26254
26256
  evaluations->base_evaluations =
@@ -26381,6 +26383,12 @@ static coco_observer_t *coco_observer_allocate(
26381
26383
  observer->logger_free_function = NULL;
26382
26384
  observer->restart_function = NULL;
26383
26385
  observer->is_active = 1;
26386
+ coco_debug("Function coco_observer_allocate() called with values: result_folder: %s, observer_name: %s, algorithm_name: %s, "
26387
+ "algorithm_info: %s, settings: %s, number_target_triggers: %lu, log_target_precision: %e, lin_target_precision: %e, "
26388
+ "number_evaluation_triggers: %lu, base_evaluation_triggers: %s, precision_x: %d, precision_f: %d, precision_g: %d, "
26389
+ "log_discrete_as_int: %d", result_folder, observer_name, algorithm_name, algorithm_info, settings, (unsigned long)number_target_triggers,
26390
+ log_target_precision, lin_target_precision, (unsigned long)number_evaluation_triggers, base_evaluation_triggers, precision_x, precision_f,
26391
+ precision_g, log_discrete_as_int);
26384
26392
  return observer;
26385
26393
  }
26386
26394
 
@@ -27252,8 +27260,8 @@ static coco_problem_t *logger_bbob(coco_observer_t *observer, coco_problem_t *in
27252
27260
  /* Initialize triggers performance and evaluation triggers depending on the observer option */
27253
27261
  logger_data->targets = coco_observer_targets(suite->known_optima, observer->lin_target_precision,
27254
27262
  observer->number_target_triggers, observer->log_target_precision);
27255
- logger_data->evaluations =
27256
- coco_observer_evaluations(observer->base_evaluation_triggers, inner_problem->number_of_variables);
27263
+ logger_data->evaluations = coco_observer_evaluations(
27264
+ observer->number_evaluation_triggers, observer->base_evaluation_triggers, inner_problem->number_of_variables);
27257
27265
  logger_data->mdat_targets = coco_observer_targets(suite->known_optima, observer->lin_target_precision,
27258
27266
  observer->number_target_triggers, observer->log_target_precision);
27259
27267
 
@@ -27266,7 +27274,7 @@ static coco_problem_t *logger_bbob(coco_observer_t *observer, coco_problem_t *in
27266
27274
  observer_data->observed_problem = problem;
27267
27275
  return problem;
27268
27276
  }
27269
- #line 510 "coco_observer.c"
27277
+ #line 518 "coco_observer.c"
27270
27278
  #line 1 "logger_bbob_old.c"
27271
27279
  /**
27272
27280
  * @file logger_bbob_old.c
@@ -27919,8 +27927,8 @@ static coco_problem_t *logger_bbob_old(coco_observer_t *observer, coco_problem_t
27919
27927
 
27920
27928
  /* Initialize triggers based on target values and number of evaluations */
27921
27929
  logger_data->targets = coco_observer_log_targets(observer->number_target_triggers, observer->log_target_precision);
27922
- logger_data->evaluations =
27923
- coco_observer_evaluations(observer->base_evaluation_triggers, inner_problem->number_of_variables);
27930
+ logger_data->evaluations = coco_observer_evaluations(
27931
+ observer->number_evaluation_triggers, observer->base_evaluation_triggers, inner_problem->number_of_variables);
27924
27932
 
27925
27933
  problem =
27926
27934
  coco_problem_transformed_allocate(inner_problem, logger_data, logger_bbob_old_free, observer->observer_name);
@@ -27929,7 +27937,7 @@ static coco_problem_t *logger_bbob_old(coco_observer_t *observer, coco_problem_t
27929
27937
  bbob_old_logger_is_open = 1;
27930
27938
  return problem;
27931
27939
  }
27932
- #line 511 "coco_observer.c"
27940
+ #line 519 "coco_observer.c"
27933
27941
  #line 1 "logger_biobj.c"
27934
27942
  /**
27935
27943
  * @file logger_biobj.c
@@ -29616,7 +29624,8 @@ static logger_biobj_indicator_t *logger_biobj_indicator(const logger_biobj_data_
29616
29624
 
29617
29625
  indicator->targets = coco_observer_targets(suite->known_optima, observer->lin_target_precision,
29618
29626
  observer->number_target_triggers, observer->log_target_precision);
29619
- indicator->evaluations = coco_observer_evaluations(observer->base_evaluation_triggers, problem->number_of_variables);
29627
+ indicator->evaluations = coco_observer_evaluations(observer->number_evaluation_triggers, observer->base_evaluation_triggers,
29628
+ problem->number_of_variables);
29620
29629
 
29621
29630
  /* Prepare the info file */
29622
29631
  path_name = coco_allocate_string(COCO_PATH_MAX + 1);
@@ -30253,7 +30262,7 @@ static coco_problem_t *logger_biobj(coco_observer_t *observer, coco_problem_t *i
30253
30262
  observer_data->observed_problem = problem;
30254
30263
  return problem;
30255
30264
  }
30256
- #line 512 "coco_observer.c"
30265
+ #line 520 "coco_observer.c"
30257
30266
  #line 1 "logger_rw.c"
30258
30267
  /**
30259
30268
  * @file logger_rw.c
@@ -30589,7 +30598,7 @@ static coco_problem_t *logger_rw(coco_observer_t *observer, coco_problem_t *inne
30589
30598
 
30590
30599
  return problem;
30591
30600
  }
30592
- #line 513 "coco_observer.c"
30601
+ #line 521 "coco_observer.c"
30593
30602
  #line 1 "logger_toy.c"
30594
30603
  /**
30595
30604
  * @file logger_toy.c
@@ -30778,7 +30787,7 @@ static coco_problem_t *logger_toy(coco_observer_t *observer, coco_problem_t *inn
30778
30787
 
30779
30788
  return problem;
30780
30789
  }
30781
- #line 514 "coco_observer.c"
30790
+ #line 522 "coco_observer.c"
30782
30791
 
30783
30792
  /**
30784
30793
  * Currently, four observers are supported:
@@ -31544,7 +31553,7 @@ void coco_archive_free(coco_archive_t *archive) {
31544
31553
  * don't change, don't track in version control!
31545
31554
  */
31546
31555
 
31547
- const char *coco_version = "2.8.2";
31556
+ const char *coco_version = "2.8.4";
31548
31557
  #line 1 "coco_runtime_c.c"
31549
31558
  /**
31550
31559
  * @file coco_runtime_c.c
@@ -83,6 +83,7 @@ cdef extern from "coco.h":
83
83
  void bbob_problem_best_parameter_print(const coco_problem_t *problem)
84
84
  void bbob_biobj_problem_best_parameter_print(const coco_problem_t *problem)
85
85
 
86
+ cdef extern double coco_problem_get_best_value(coco_problem_t *p) # copied from bare_problem.pyx
86
87
 
87
88
  cdef bytes _bstring(s):
88
89
  if type(s) is bytes:
@@ -424,7 +425,7 @@ cdef class Suite:
424
425
  % (self.name, self.instance, self.options,
425
426
  len(self), '' if len(self) == 1 else 's',
426
427
  '' if len(self.dimensions) == 1 else 's',
427
- '%d=%d' % (min(self.dimensions), max(self.dimensions)))
428
+ '%d-%d' % (min(self.dimensions), max(self.dimensions)))
428
429
  def __len__(self):
429
430
  return len(self._indices)
430
431
 
@@ -556,7 +557,7 @@ cdef class Problem:
556
557
  raise RuntimeError("Problem already initialized")
557
558
  if problem == NULL:
558
559
  raise ValueError("in Problem._initialize(problem,...): problem is NULL")
559
- self.problem = problem
560
+ self.problem = problem # should probably be private ._problem?
560
561
  self._problem_index = coco_problem_get_suite_dep_index(self.problem)
561
562
  self._do_free = free
562
563
  self._list_of_observers = []
@@ -783,6 +784,12 @@ cdef class Problem:
783
784
  """
784
785
  assert(self.problem)
785
786
  return bool(coco_problem_final_target_hit(self.problem))
787
+ @property
788
+ def _best_value(self):
789
+ """return best f-value
790
+ """
791
+ assert(self.problem)
792
+ return coco_problem_get_best_value(self.problem)
786
793
  #@property
787
794
  #def final_target_fvalue1(self):
788
795
  # assert(self.problem)
@@ -8,8 +8,12 @@ import json
8
8
  import numpy as np # for checking finite values
9
9
 
10
10
  _frozen_noise = True
11
- math_floor = int
12
11
  parameters_file = 'noiser_parameters.json'
12
+ '''default file name where to save current parameters of `Noisifier`'''
13
+ save_parameters = False
14
+ '''flag whether `Noisifier.__init__` saves noise parameters in file'''
15
+
16
+ math_floor = int
13
17
 
14
18
  def rand(x, i=0):
15
19
  """return a uniform random value seeded with ``x[:2]`` and `i`"""
@@ -20,7 +24,7 @@ def randn(x, i=0):
20
24
  def randc(x, i=0):
21
25
  """return a Cauchy random value seeded with ``x[:2]`` and `i`.
22
26
 
23
- We observe that median |Z| = 1 and p(pi x Z / 2 > a) = 1/a.
27
+ We observe that ``median(|Z|) == 1`` and ``p(pi x Z / 2 > a) == 1/a``.
24
28
  """
25
29
  return _cauchy(1, _seed(x, i))[0]
26
30
 
@@ -52,11 +56,12 @@ class Noisifier:
52
56
  ### prepare
53
57
  suite = cocoex.Suite(suite_name, "", "")
54
58
  observer = cocoex.Observer(suite_name, "")
59
+ noisifier = cocoex.noiser.Noisifier() # wrapper without internal state
55
60
 
56
61
  ### go
57
62
  for problem in suite: # this loop may take several minutes or more
58
63
  problem.observe_with(observer) # generates the data for cocopp
59
- problem = cocoex.noiser.Noisifier().noisify(problem) # a Noisifier
64
+ problem = noisifier.noisify(problem) # wrap problem as noisy
60
65
  fmin(problem, problem.initial_solution, disp=False)
61
66
 
62
67
  The `problem` is now a `Noisifier` but it still abides by the interface
@@ -70,13 +75,35 @@ class Noisifier:
70
75
  noise generator and the numbers scaled by pi/2 such that P(number > k)
71
76
  equals about 1/k when k >= 5.
72
77
 
78
+ Parameter settings are by default saved to the file indicated by the module
79
+ global variable `cocoex.noiser.parameters_file` if
80
+ ``bool(cocoex.noiser.save_parameters) is True``.
81
+
82
+ A minimal regression test:
83
+
84
+ >>> import cocoex
85
+ >>> def eq(x, y):
86
+ ... return [cocoex.utilities.about_equal(xi, yi)
87
+ ... for xi, yi in zip(x, y)]
88
+ >>> def fun(x):
89
+ ... return 2 * x[0], 3 * x[1]
90
+
91
+ >>> n = cocoex.noiser.Noisifier(p_add=.5)
92
+ >>> f = n.noisify(fun)
93
+ >>> assert all(eq(f([1, 2]), [2, 41.39010606165791])
94
+ ... ), f([1, 2])
95
+ >>> assert all(eq(f([2, 3, 4]), [11.518074565992698, 9.705537168513859])
96
+ ... ), f([2, 3, 4]) # x[2:] have no influence here
97
+ >>> assert all(eq(f([-1, 2]), [-2, 6])
98
+ ... ), f([-1, 2])
99
+
73
100
  See also https://github.com/numbbo/coco-experiment/issues/36 and
74
101
  https://github.com/numbbo/coco-experiment/blob/main/build/python/example/example_experiment_complete.py
75
102
 
76
103
  """
77
104
  p_add_single_default = 0.2
78
105
  def __init__(self, p_add=None, p_subtract=0.0, p_epsilon=0.0, epsilon=1e-4,
79
- rands=(rand, randn, randc)):
106
+ rands=(rand, randn, randc), filename=None):
80
107
  """constructor with 4 optional parameters for the noise model,
81
108
 
82
109
  `p_add`: probability for adding a positive heavy tail random value
@@ -89,6 +116,11 @@ class Noisifier:
89
116
  `p_epsilon`: probability for adding a Gaussian random value
90
117
 
91
118
  `epsilon`: standard deviation of the Gaussian random value
119
+
120
+ `filename`: for saving parameter setting, if not given use module global
121
+ variable `cocoex.noiser.parameters_file`, saving depends on the module
122
+ variable `save_parameters`.
123
+
92
124
  """
93
125
  if p_add is None:
94
126
  if p_subtract == p_epsilon == 0:
@@ -97,15 +129,17 @@ class Noisifier:
97
129
  p_add = 0.0
98
130
  self._params = {k: v for k, v in locals().items() if k != 'self'}
99
131
  if sum(self._params[p] for p in ('p_subtract', 'p_add')) > 1:
100
- warnings.warn("p_subtract={0} + p_add={1} > 1, hence p_add is interpreted"
101
- " as ``1-p_subtract``"
132
+ warnings.warn("p_subtract={0} + p_add={1} > 1, hence p_add" # noqa: UP030
133
+ " is interpreted as ``1-p_subtract``"
102
134
  .format(self._params['p_subtract'], self._params['p_add']))
103
135
  if p_add < 0 or p_subtract < 0 or p_epsilon < 0 or epsilon < 0:
104
- raise ValueError("parameters cannot be negative but were {0}"
105
- .format(self._params))
136
+ raise ValueError("parameters cannot be negative" # noqa: UP030, UP032
137
+ " but were {0}".format(self._params))
106
138
  if p_epsilon > 0 and epsilon == 0:
107
- warnings.warn("p_epsilon = {0} > 0 is not effective because epsilon = 0"
108
- .format(p_epsilon))
139
+ warnings.warn("p_epsilon = {0} > 0 is not effective" # noqa: UP030, UP032
140
+ " because epsilon = 0".format(p_epsilon))
141
+ if save_parameters:
142
+ self.save_parameters()
109
143
 
110
144
  def noisify(self, problem):
111
145
  """wrap `problem` with frozen noise"""
@@ -116,23 +150,19 @@ class Noisifier:
116
150
  def parameters(self):
117
151
  return {k: v for (k, v) in self._params.items() if k.startswith(('p_', 'eps'))}
118
152
 
119
- def _dump_params(self):
120
- with open(parameters_file, 'wt') as fp:
153
+ def save_parameters(self, filename=None):
154
+ """save parameter `dict` to a `json` file"""
155
+ with open(filename or self._params.get('filename')
156
+ or parameters_file, 'wt') as fp:
121
157
  json.dump(self.parameters, fp)
122
158
 
123
- def _load_params(self, filename=parameters_file, warn_errors=(FileNotFoundError,), ignore_errors=()):
124
- try:
125
- with open(parameters_file, 'rt') as fp:
126
- return json.load(fp)
127
- except warn_errors as e:
128
- warnings.warn('NoisifyProblem._load_params raised the exception "{0}"'.format(e))
129
- except ignore_errors:
130
- pass
131
- return {}
159
+ def _set_params(self, filename=None, **kwargs):
160
+ """set parameters from file `filename` or filename passed at
132
161
 
133
- def _set_params(self, filename=parameters_file, **kwargs):
134
- """set parameters from file `filename`"""
135
- self._params.update(self._load_params(filename), **kwargs)
162
+ instantiation or `cocoex.noiser.parameters_file`.
163
+ """
164
+ self._params.update(_read_params(filename or self._params.get('filename')
165
+ or parameters_file), **kwargs)
136
166
 
137
167
  def __getattr__(self, name):
138
168
  """pretend to be a COCO `problem` as passed in `noisify`"""
@@ -192,6 +222,20 @@ class Noisifier:
192
222
  assert np.isfinite(n)
193
223
  return n
194
224
 
225
+
226
+ def _read_params(filename=None,
227
+ warn_errors=(FileNotFoundError,), ignore_errors=()):
228
+ """read parameters from a json file and return a parameters `dict`"""
229
+ try:
230
+ with open(filename or parameters_file, 'rt') as fp:
231
+ return json.load(fp)
232
+ except warn_errors as e:
233
+ warnings.warn('noiser._read_params raised the exception: ' + str(e))
234
+ except ignore_errors:
235
+ pass
236
+ return {}
237
+
238
+
195
239
  _seed_weights = [1.23468, 2.34579] # otherwise, coordinate search would be noisefree
196
240
  _seeds = collections.deque(maxlen=99) # for the record only
197
241
  def _seed(x, i=0):
@@ -269,7 +269,7 @@ class ProblemNonAnytime:
269
269
  ``p_unobserved.evaluations >= p_observed.evaluations``.
270
270
 
271
271
  """
272
- inherited_constant_attributes = [
272
+ inherited_constant_attributes = [ # easier and better solved by defining __getattr__!?
273
273
  'dimension',
274
274
  'id',
275
275
  'index',
@@ -377,7 +377,7 @@ class SameFunction:
377
377
  >>> from cocoex.utilities import SameFunction
378
378
  >>>
379
379
  >>> suite = cocoex.Suite('bbob', '', '')
380
- >>> already_seen = SameFunction()
380
+ >>> already_seen = seen = SameFunction()
381
381
  >>> processed = 0
382
382
  >>> for problem in suite:
383
383
  ... if already_seen(problem.id) > 5:
@@ -600,6 +600,9 @@ class ExperimentRepeater:
600
600
  sweeps until some given budget is exhaused, namely, ``budget_multiplier
601
601
  * dimension``.
602
602
 
603
+ CAVEAT: this works as desired only when all instances of the same problem
604
+ are run in the same batch, as it is the case with `BatchScheduler`.
605
+
603
606
  Example code snippet::
604
607
 
605
608
  [...]
@@ -934,6 +937,9 @@ class ExperimentRepeater:
934
937
  class BatchScheduler:
935
938
  """Facilitate to run a benchmarking experiment in independent batches.
936
939
 
940
+ All instances of the same ``(function, dimension)`` belong to the same
941
+ batch, in order to allow for uniform instance repetitions.
942
+
937
943
  The batch scheduler crucially assumes that in each batch the same
938
944
  problems are given *in the same order* when calling `is_in_batch`.
939
945
 
@@ -953,10 +959,13 @@ class BatchScheduler:
953
959
  ``batch_to_execute in (0,1,2,3)`` to generate the full experimental
954
960
  data.
955
961
 
956
- Details: to get a more even time distribution over all batches, it
957
- seems advisable that the number of functions is not divisible by the
958
- number of batches. That is, 4 (or 6 or 8 or 12) batches is not likely
959
- to be ideal on the `'bbob'` testbed of 24 functions.
962
+ Details: while making the number of functions not divisible by the number of
963
+ batches may sometimes achieve a more even time distribution over all
964
+ batches, in practice, it is advisable to keep each function in a single or a
965
+ few batches as it is enforced by the `BatchScheduler`. Hence, for the 24
966
+ functions of the `'bbob'` testbed, having 48 or 24 or 12 or 8 or 6 or 4
967
+ batches is advisable. Additionally, it can be useful to run the largest
968
+ dimension in a separate launch (with at most 24 batches in this case).
960
969
  """
961
970
  def __init__(self, number_of_batches, batch_to_execute):
962
971
  """distribute over `number_of_batches` batches and execute
File without changes