Generally speaking migrating code from Vita isn't too hard: Ultra has a simpler interface but the backbone structure is similar in many respects.
Many of the examples have been ported to Ultra and are a good starting point for understanding the differences.
General aspects
The majority of class specializations have their own namespace. So instead of vita::de_problem
, vita::de_search
, vita::de_individual
, ultra::src_search
you should use ultra:de::problem
, ultra:de::search
, ultra::de::individual
, ultra::src::search
.
You usually haven't to specify template arguments since they're automatically deduced:
vita::de_search<decltype(neg_rastrigin)> search(prob, neg_rastrigin);
is now the simpler:
de::search search(prob, neg_rastrigin);
environment
class has been renamed to parameters
and parameters have been grouped by category. E.g.
environment env;
env.p_mutation = 0.6;
env.tournament_size = 3;
becomes
parameters params;
params.evolution.p_mutation = 0.6;
params.evolution.tournament_size = 3;
Return results of a search are contained in a different structure (search_stats
defined in kernel/search.h
). So, for example, the usual access to the best individual / fitness changes from:
const auto res(search.run());
const auto solution(res.best.solution);
const auto value(res.best.score.fitness);
to
const auto res(search.run());
const auto solution(res.best_individual);
const auto value(*res.best_measurements.fitness);
Note that scores contained in the search_stats struct
are optional values hence the *
to get the fitness.
The threshold for identifying successful runs isn't specified anymore in the environment / parameters structure but is passed to the search::run
member functions:
environment env;
env.threshold.fitness = -0.5;
// ...
src_search s(prob);
const auto result(s.run(10));
should be replaced with
model_measurements<double> threshold;
mm.fitness = -0.5;
// ...
src::search s(prob);
const auto result(s.run(10, threshold));
Differences deriving from design choices
Concurrency-related aspects
All the search strategies can be used enabling multiple layers / subgroups (prob.params.population.init_subgroups > 1
). Evolution happens in parallel for every subgroup. Many algorithms take advantage of this organization to explore different search spaces, possibly using different parameters; anyway the can be executed in single thread mode.
ALPS, otherwise, is conceived to exchange information among subgroups and, even if started with a single layer, will add further layers during the evolution (thus enabling concurrency).
Concurrency is a great performance boost but makes evolution non-repeatable.
Fitness-related changes
There isn't anymore a fixed type for representing fitness. User can employ the type more appropriate for the specific task provided that it satisfies the Fitness
concept. However a fitnd
class is available for filling the gap.
Serialisation-related changes
Serialisation of floating point values now uses hexadecimal format (std::hexfloat
) for impreoved precision and performance:
- no rounding occurs when writing or reading a value formatted in this way;
- operations on such values can be faster with a well tuned I/O library;
- fewer digits are required to represent values exactly.
Existing serialisation files have to accomodate this change.