Skip to content

Commit cdf1f97

Browse files
committed
move static crossover helper methods in base class
1 parent 2bcfc1f commit cdf1f97

File tree

3 files changed

+49
-45
lines changed

3 files changed

+49
-45
lines changed

include/operon/operators/crossover.hpp

+11-22
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,21 @@
2020
namespace Operon {
2121
// crossover takes two parent trees and returns a child
2222
struct CrossoverBase : public OperatorBase<Tree, const Tree&, const Tree&> {
23+
using Limits = std::pair<std::size_t, std::size_t>;
24+
static auto FindCompatibleSwapLocations(Operon::RandomGenerator& random, Tree const& lhs, Tree const& rhs, size_t maxDepth, size_t maxLength, double internalProbability = 1.0) -> std::pair<size_t, size_t>;
25+
static auto SelectRandomBranch(Operon::RandomGenerator& random, Tree const& tree, double internalProb, Limits length, Limits level, Limits depth) -> size_t;
26+
static auto Cross(const Tree& lhs, const Tree& rhs, /* index of subtree 1 */ size_t i, /* index of subtree 2 */ size_t j) -> Tree;
2327
};
2428

2529
class OPERON_EXPORT SubtreeCrossover : public CrossoverBase {
2630
public:
27-
SubtreeCrossover(double p, size_t d, size_t l)
28-
: internalProbability_(p)
29-
, maxDepth_(d)
30-
, maxLength_(l)
31-
{
32-
}
31+
SubtreeCrossover(double internalProbability, size_t maxDepth, size_t maxLength)
32+
: internalProbability_(internalProbability)
33+
, maxDepth_(maxDepth)
34+
, maxLength_(maxLength)
35+
{ }
36+
3337
auto operator()(Operon::RandomGenerator& random, const Tree& lhs, const Tree& rhs) const -> Tree override;
34-
auto FindCompatibleSwapLocations(Operon::RandomGenerator& random, const Tree& lhs, const Tree& rhs) const -> std::pair<size_t, size_t>;
35-
36-
static inline auto Cross(const Tree& lhs, const Tree& rhs, /* index of subtree 1 */ size_t i, /* index of subtree 2 */ size_t j) -> Tree
37-
{
38-
auto const& left = lhs.Nodes();
39-
auto const& right = rhs.Nodes();
40-
Operon::Vector<Node> nodes;
41-
using signed_t = std::make_signed<size_t>::type; // NOLINT
42-
nodes.reserve(right[j].Length - left[i].Length + left.size());
43-
std::copy_n(left.begin(), i - left[i].Length, back_inserter(nodes));
44-
std::copy_n(right.begin() + static_cast<signed_t>(j) - right[j].Length, right[j].Length + 1, back_inserter(nodes));
45-
std::copy_n(left.begin() + static_cast<signed_t>(i) + 1, left.size() - (i + 1), back_inserter(nodes));
46-
47-
auto child = Tree(nodes).UpdateNodes();
48-
return child;
49-
}
5038

5139
[[nodiscard]] auto InternalProbability() const -> double { return internalProbability_; }
5240
[[nodiscard]] auto MaxDepth() const -> size_t { return maxDepth_; }
@@ -57,5 +45,6 @@ class OPERON_EXPORT SubtreeCrossover : public CrossoverBase {
5745
size_t maxDepth_;
5846
size_t maxLength_;
5947
};
48+
6049
} // namespace Operon
6150
#endif

source/operators/crossover.cpp

+37-22
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,39 @@
55
#include <random>
66

77
#include "operon/core/contracts.hpp"
8+
#include "operon/formatter/formatter.hpp"
89
#include "operon/operators/crossover.hpp"
910
#include "operon/random/random.hpp"
1011

1112
namespace Operon {
12-
1313
namespace {
14-
using Limits = std::pair<size_t, size_t>;
15-
14+
using Limits = std::pair<std::size_t, std::size_t>;
1615
auto NotIn(Limits t, size_t v) -> bool {
1716
auto [a, b] = t;
1817
return v < a || b < v;
1918
}
2019
} // namespace
2120

22-
static auto SelectRandomBranch(Operon::RandomGenerator& random, Tree const& tree, double internalProb, Limits length, Limits level, Limits depth) -> size_t
21+
auto CrossoverBase::FindCompatibleSwapLocations(Operon::RandomGenerator& random, Tree const& lhs, Tree const& rhs, size_t maxDepth, size_t maxLength, double internalProbability) -> std::pair<size_t, size_t>
22+
{
23+
using Signed = std::make_signed_t<size_t>;
24+
auto diff = static_cast<Signed>(lhs.Length() - maxLength + 1); // +1 to account for at least one node that gets swapped in
25+
26+
auto i = SelectRandomBranch(random, lhs, internalProbability, Limits{std::max(diff, Signed{1}), lhs.Length()}, Limits{size_t{1}, lhs.Depth()}, Limits{size_t{1}, lhs.Depth()});
27+
// we have to make some small allowances here due to the fact that the provided trees
28+
// might actually be larger than the maxDepth and maxLength limits given here
29+
auto maxBranchDepth = static_cast<Signed>(maxDepth - lhs[i].Level);
30+
maxBranchDepth = std::max(maxBranchDepth, Signed{1});
31+
32+
auto partialTreeLength = (lhs.Length() - (lhs[i].Length + 1));
33+
auto maxBranchLength = static_cast<Signed>(maxLength - partialTreeLength);
34+
maxBranchLength = std::max(maxBranchLength, Signed{1});
35+
36+
auto j = SelectRandomBranch(random, rhs, internalProbability, Limits{1UL, maxBranchLength}, Limits{1UL, rhs.Depth()}, Limits{1UL, maxBranchDepth});
37+
return std::make_pair(i, j);
38+
}
39+
40+
auto CrossoverBase::SelectRandomBranch(Operon::RandomGenerator& random, Tree const& tree, double internalProb, Limits length, Limits level, Limits depth) -> size_t
2341
{
2442
if (tree.Length() == 1) {
2543
return 0;
@@ -54,31 +72,27 @@ static auto SelectRandomBranch(Operon::RandomGenerator& random, Tree const& tree
5472
return *Operon::Random::Sample(random, candidates.rbegin(), tail);
5573
}
5674
return *Operon::Random::Sample(random, candidates.begin(), head);
57-
5875
}
5976

60-
auto SubtreeCrossover::FindCompatibleSwapLocations(Operon::RandomGenerator& random, Tree const& lhs, Tree const& rhs) const -> std::pair<size_t, size_t>
61-
{
62-
using Signed = std::make_signed<size_t>::type;
63-
auto diff = static_cast<Signed>(lhs.Length() - maxLength_ + 1); // +1 to account for at least one node that gets swapped in
6477

65-
auto i = SelectRandomBranch(random, lhs, internalProbability_, Limits{std::max(diff, Signed{1}), lhs.Length()}, Limits{size_t{1}, lhs.Depth()}, Limits{size_t{1}, lhs.Depth()});
66-
// we have to make some small allowances here due to the fact that the provided trees
67-
// might actually be larger than the maxDepth and maxLength limits given here
68-
auto maxBranchDepth = static_cast<Signed>(maxDepth_ - lhs[i].Level);
69-
maxBranchDepth = std::max(maxBranchDepth, Signed{1});
70-
71-
auto partialTreeLength = (lhs.Length() - (lhs[i].Length + 1));
72-
auto maxBranchLength = static_cast<Signed>(maxLength_ - partialTreeLength);
73-
maxBranchLength = std::max(maxBranchLength, Signed{1});
74-
75-
auto j = SelectRandomBranch(random, rhs, internalProbability_, Limits{1UL, maxBranchLength}, Limits{1UL, rhs.Depth()}, Limits{1UL, maxBranchDepth});
76-
return std::make_pair(i, j);
78+
auto CrossoverBase::Cross(const Tree& lhs, const Tree& rhs, /* index of subtree 1 */ size_t i, /* index of subtree 2 */ size_t j) -> Tree
79+
{
80+
auto const& left = lhs.Nodes();
81+
auto const& right = rhs.Nodes();
82+
Operon::Vector<Node> nodes;
83+
using Signed = std::make_signed_t<size_t>;
84+
nodes.reserve(right[j].Length - left[i].Length + left.size());
85+
std::copy_n(left.begin(), i - left[i].Length, back_inserter(nodes));
86+
std::copy_n(right.begin() + static_cast<Signed>(j) - right[j].Length, right[j].Length + 1, back_inserter(nodes));
87+
std::copy_n(left.begin() + static_cast<Signed>(i) + 1, left.size() - (i + 1), back_inserter(nodes));
88+
89+
auto child = Tree(nodes).UpdateNodes();
90+
return child;
7791
}
7892

7993
auto SubtreeCrossover::operator()(Operon::RandomGenerator& random, const Tree& lhs, const Tree& rhs) const -> Tree
8094
{
81-
auto [i, j] = FindCompatibleSwapLocations(random, lhs, rhs);
95+
auto [i, j] = FindCompatibleSwapLocations(random, lhs, rhs, maxDepth_, maxLength_, internalProbability_);
8296
auto child = Cross(lhs, rhs, i, j);
8397

8498
auto maxDepth{std::max(maxDepth_, lhs.Depth())};
@@ -90,3 +104,4 @@ auto SubtreeCrossover::operator()(Operon::RandomGenerator& random, const Tree& l
90104
return child;
91105
}
92106
} // namespace Operon
107+

test/source/implementation/crossover.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ TEST_CASE("Crossover")
7070
//auto p2 = btc(rng, maxLength, 1, maxDepth);
7171
auto p2 = p1;
7272

73-
auto [i, j] = cx.FindCompatibleSwapLocations(rng, p1, p2);
73+
auto [i, j] = Operon::SubtreeCrossover::FindCompatibleSwapLocations(rng, p1, p2, maxDepth, maxLength);
7474
c1[i]++;
7575
c2[j]++;
7676

0 commit comments

Comments
 (0)