class EvaluationsIterator(_abc.Iterator):
Constructor: EvaluationsIterator(data, return_filter)
return iteratively evals, fval, successes
where evals and successes are numpy arrays and fval is a
float.
The input argument data is an array where the first column, data[:, 0], contains the monotonuously increasing evaluations and the remaining columns, data[:,1:], are the recorded respective f-values (which do not need to be monotonuous) for several runs.
Each output fval value reflects an entry in the input data[:,1:]. When the f-values are monotonously decreasing in the data row index, each value appears exactly once in the iterator sequence (in decreasing order). Increased f-values are in effect ignored.
The first output element, evals, gives for each run the number of evaluations when fval was reached for the first time or, in case fval was not reached in the run, the last evaluations with a finite (exisiting) f-value of this run.
The third output element, successes, is a bool array indicating
whether the fval was reached.
Details: this class is an iterator mainly to allow for more efficient memory use, because we usually do not need to keep all evaluations (of each run) for each fval. When evals and successes are directly consumed and not stored, passing return_filter=None additionally avoids the unnecessary copy of both.
Examples
>>> import cma.optimization_tools as ot >>> data = [[1, 44.2, 33.3, 22.2], ... [2, 32, 22.1, 33], ... [3, 11, 22, 66]]
The shortest usage example is:
>>> list_of_evals_fval_succs = list(ot.EvaluationsIterator(data))
which creates a list of about n x m length where n =
data.shape[0] and m = data.shape[1] - 1 (usually n>>m). Each list
element is a three-tuple with (m evaluations, 1 f-value, m bools).
A typical use case computes the ERT-value in the first "column" (as by
default argument xvalues=(ei_ert,) of ei_xy_data) and has (by
default) f-values in the second (last) "column":
>>> xy = list(ot.EvaluationsIterator(data, return_filter=ot.ei_xy_data())) >>> print(xy) # doctest: +ELLIPSIS [(1.0, 44.2), (1.33333...
Now we can plot the ERT as the x-value (with evaluations as unit):
plt.plot([d[0] for d in xy],
[d[1] for d in xy], label='ERT')
plt.xlabel('evaluations'), plt.ylabel('f-value');
An example collecting ERT, SP1, the median, the f-value, and the number of successes without creating the full new data array otherwise:
>>> list_of_xs_y_s = [(ot.ei_ert(*d), # ERT ... ot.ei_sp1(*d), # SP1 ... ot.ei_median(*d), # median ... d[1], # f-value ... sum(d[2]) # number of runs that reached d[1] ... ) for d in ot.EvaluationsIterator(data, return_filter=None)]
Passing return_filter=None avoids an unnecessary internal copy of d[0] and d[2].
We can again plot the ERT as an x-value:
plt.plot([d[0] for d in list_of_xs_y_s],
[d[3] for d in list_of_xs_y_s], label='ERT')
More conveniently, we can use arrays for plotting, here with annotation of the graph end and of successes too:
from matplotlib import pyplot as plt
xy = np.asarray(list_of_xs_y_s)
p = plt.plot(xy[:, 0], xy[:, -2], '-', label='ERT')
plt.plot(xy[-1, 0], xy[-1, -2], 'o', color=p[-1].get_color())
p = plt.plot(xy[:, 2], xy[:, -2], '-', label='median')
i = np.nonzero(np.isfinite(xy[:, 2]))[0][-1]
plt.plot(xy[i, 2], xy[i, -2], 'o', color=p[-1].get_color())
idx = np.where(np.diff(xy[:, -1], prepend=xy[0, -1]))[0] # indices where successes changed
for j, i in enumerate(idx): # annotate:
if (j == 0 or xy[i, -1] <= 5 or # first failure and 5..1 successes
xy[i, -1] <= xy[0, -1] / 2 < xy[idx[j-1], -1]): # and 50% failures
plt.text(xy[i, 0], xy[i, -2], int(xy[i, -1]))
# plt.gca().set_xscale('log')
plt.xlabel('evaluations'), plt.ylabel('f-value'), plt.legend();
A similar example as above but with an explicit for-loop instead of a list comprehension and hence longer but not more useful here:
>>> import cma.optimization_tools as ot >>> fvals, erts, med = [], [], [] >>> xy_succ = {s: None for s in [2, 5, 9]} # number for successes to annotate >>> for evals, fval, succ in ot.EvaluationsIterator(data): ... erts.append(ot.ei_ert(evals, fval, succ)) # compress all runs to one column ... med.append(ot.ei_median(evals, fval, succ)) # compress all runs to one column ... fvals.append(fval) # y-value column ... if sum(succ) < len(succ): ... for k in xy_succ: ... if xy_succ[k] is None and sum(succ) <= k: ... xy_succ[k] = erts[-1], fvals[-1]
Plotting the above:
plt.plot(data[:, 0], data[:, 1:]) # plot all data lines
plt.plot(erts, fvals) # plot an ert line
for k, xy in xy_succ.items():
plt.text(xy[0], xy[1], k)
plt.xlabel('evaluations'), plt.ylabel('f-value');
Testing:
>>> ot.EvaluationsIterator._no_asserts = False >>> x = [ot.ei_ert(*d) for d in ot.EvaluationsIterator(data)] >>> assert x == sorted(x), x >>> for d in zip(x, [1.0, 1.333333333333333, 1.666666666666667, 2.0, 4.0, 4.5, 9.0]): ... assert -1e-7 < d[0] - d[1] < 1e-7, (x, d) >>> y = [d[1] for d in ot.EvaluationsIterator(data, return_filter=None)] >>> assert y == [44.2, 33.3, 32., 22.2, 22.1, 22., 11.], y >>> xy = list(ot.EvaluationsIterator(data, return_filter=ot.ei_xy_data())) >>> for i in range(len(xy)): ... assert xy[i] == (x[i], y[i]), (xy[i], x[i], y[i]) >>> import numpy as np >>> n, m = 22, 11 >>> a = np.log(np.abs(np.random.randn(n, m+1) / np.random.randn(n, m+1))) >>> a.sort(0) >>> a[:,:] = a[-1::-1, :] >>> a[:,0] = 2 * np.arange(a.shape[0]) + 1 >>> for agg in [ot.ei_ert, ot.ei_sp1, ot.ei_median]: ... x = np.asarray([agg(*d) for d in ot.EvaluationsIterator(a)]) ... if agg == ot.ei_median: ... x = x[np.isfinite(x)] ... assert np.all(x == np.array(x, dtype=int)), x # works because all evals are odd ... assert np.all(np.diff(x) >= 0), x
| Method | __init__ |
data[:, 0] are evaluations and data[:, 1:] are f-values. |
| Method | __next__ |
increment i = self.row_indices[j] for which self.data[i, j] >= fval |
| Instance Variable | count |
Undocumented |
| Instance Variable | data |
Undocumented |
| Instance Variable | return |
Undocumented |
| Instance Variable | row |
unsuccessful data have negative indices (which is used in fval) |
| Instance Variable | successes |
success bool array, equals to row_indices >= 0 |
| Property | current |
return array of evals either reaching self.fval or the last finite evals |
| Property | fval |
current f-value, bails when no data are available anymore |
| Class Variable | _no |
Undocumented |
| Instance Variable | _current |
current evaluations, changing these in place saves 35% CPU!? |
data[:, 0] are evaluations and data[:, 1:] are f-values.
return_filter is a function applied to the iterator return value
evals, fval, successes on the fly. None and 'id' are the
identity.