diff --git a/mo/src/problems/partition/moBinaryPartition.h b/mo/src/problems/partition/moBinaryPartition.h index 2fedb50ef..7fd53bce0 100644 --- a/mo/src/problems/partition/moBinaryPartition.h +++ b/mo/src/problems/partition/moBinaryPartition.h @@ -4,30 +4,76 @@ #include +/** A partition of a binary space. + * + * This data structure defines a grouping of the elements of a multi-dimensional + * set in a space of boolean numbers. + * \f[ + * \mathrm{1}^n = \bigcup_{i=1}^n \{0,1\}_i + * \f] + * Elements of the set may be either "selected" (in the set S) or "rejected" (in the set R). + * \f[ + * (S \in \mathrm{1}^m) \cup (R \in \mathrm{1}^k) \in \mathrm{1}^n,\; n=m+k + * \f] + * Elements are referred to by their index in the set (hereby named "atoms"). + * + * This representation is useful if your problem can be defined has selecting + * a subset of elements that optimize some objective function. + * + * The core data structures are two ordered sets of unique atoms, + * the union of which is guaranteed to have the correct dimension. + */ template class moBinaryPartition : public EO { public: + /** The type for indices. */ using AtomType = size_t; + + /** The data structures holding the indices. */ using ContainerType = std::set; + /** The set of selected atoms. */ ContainerType selected; + + /** The set of not-selected atoms. */ ContainerType rejected; - /** Constructor + /** Consistent constructor * - * @param total_nb_genes Total number of possible genes from whith to select. + * Put all `total_nb_atoms` indices in the @ref rejected set. + * Indices starts at zero and fill the set in increasing order. + * + * @param total_nb_atoms Total number of possible atoms from whith to select. */ - moBinaryPartition( const size_t total_nb_genes ) + moBinaryPartition( const size_t total_nb_atoms ) { // Fill the rejected list with all possible gene indices, // starting from zero. - for(size_t i = 0; i < total_nb_genes; ++i) { + for(size_t i = 0; i < total_nb_atoms; ++i) { rejected.insert(i); } - // No selected. + // None selected. } + /** Empty constructor + * + * Do not fill the @ref rejected set. + * You are responsible for making it consistent after instantiation. + * + * @warning If you do not fill at least the @ref rejected set, + * errors will be raised whe trying to @ref select or @ref reject. + */ + moBinaryPartition() + { } + + /** Move one atom in the @ref selected set. + * + * That is: erase the atom from @ref rejected, + * insert it in @ref selected. + * + * @note In debug mode, double check that elements were actually moved. + */ void select(const size_t atom) { assert(not selected.contains(atom)); @@ -44,6 +90,13 @@ class moBinaryPartition : public EO assert(has_inserted); } + /** Move one atom in the @ref rejected set. + * + * That is: insert the atom in @ref rejected, + * erase it from @ref selected. + * + * @note In debug mode, double check that elements were actually moved. + */ void reject(const size_t atom) { assert(not rejected.contains(atom)); @@ -95,6 +148,7 @@ class moBinaryPartition : public EO assert(rejected.size() == size); } + /** Returns true if all sets are equals. */ bool operator==(const moBinaryPartition& other) { return this->selected == other.selected and this->rejected == other.rejected; diff --git a/mo/src/problems/partition/moBinaryPartitionSwapNeighbor.h b/mo/src/problems/partition/moBinaryPartitionSwapNeighbor.h index 86e97a74d..af7449398 100644 --- a/mo/src/problems/partition/moBinaryPartitionSwapNeighbor.h +++ b/mo/src/problems/partition/moBinaryPartitionSwapNeighbor.h @@ -6,25 +6,53 @@ #include "moBinaryPartition.h" +/** Stable neighbor for a binary partition. + * + * Models how to move from a solution to a neighbor, + * by swaping one selected atom for one rejected atom. + * The number of selected atoms is thus guaranteed to be stable. + * + * The core data structure is two atoms: + * - the selected one, + * - the rejected one. + */ template class moBinaryPartitionSwapNeighbor : public moBackableNeighbor//, - // public moIndexNeighbor + // public moIndexNeighbor // FIXME see if we can model that. { public: + /** Shortcut for Atom’s type. */ using AtomType = typename EOT::AtomType; + + /** Shortcut for container’s type. */ using ContainerType = typename EOT::ContainerType; + + /** Shortcut for fitness. */ using moBackableNeighbor::fitness; + // using moIndexNeighbor::key; // using moIndexNeighbor::index; + /** Consistent constructor. + * + * Will ensure that the dimension of the partition does not change. + * + * @param _selected_nb Number of selected atoms to maintain. + */ moBinaryPartitionSwapNeighbor( const size_t _selected_nb ) : - selected_nb(_selected_nb), - is_set(false) + selected_nb(_selected_nb) + #ifndef NDEBUG + , is_set(false) + #endif { assert(selected_nb > 0); } + /** Apply the currently stored move. + * + * That is: reject one atom and select one other. + */ virtual void move(EOT& solution) override { assert(is_set); // Swap the two atoms. @@ -35,6 +63,10 @@ class moBinaryPartitionSwapNeighbor : solution.invalidate(); } + /** Apply the opposite of the currently stored move. + * + * That is: reject the selected atom, and select the rejected one. + */ virtual void moveBack(EOT& solution) override { assert(is_set); solution.reject(this->select); @@ -44,6 +76,11 @@ class moBinaryPartitionSwapNeighbor : solution.invalidate(); } + /** Set the considered atoms. + * + * @param in The selected atom. + * @param out The rejected atom. + */ void set(AtomType in, AtomType out) { this->select = in; this->reject = out; @@ -52,11 +89,16 @@ class moBinaryPartitionSwapNeighbor : #endif } + /** Get the considered atom. + * + * @returns A pair of atoms, the first being the selected atom, the second being the rejected one. + */ std::pair get() { assert(is_set); return std::make_pair(select, reject); } + /** Returns true if this neighbor has the same selected & rejected atoms than the given neighbor. */ virtual bool equals(moBinaryPartitionSwapNeighbor& neighbor) { auto [in, out] = neighbor.get(); return this->select == in and this->reject == out; @@ -66,6 +108,7 @@ class moBinaryPartitionSwapNeighbor : return "moBinaryPartitionSwapNeighbor"; } + /** Fancy print. */ virtual void printOn(std::ostream& out) const override { assert(is_set); out << selected_nb @@ -78,10 +121,21 @@ class moBinaryPartitionSwapNeighbor : #else protected: #endif + /** Fixed dimension of the handled solutions. */ const size_t selected_nb; + + /** Selected atom. */ AtomType select; + + /** Rejected atom. */ AtomType reject; + #ifndef NDEBUG + /** Sanity flag. + * + * Used in debug builds to ensure that the neighbor + * have been set before being used. + */ bool is_set; #endif }; diff --git a/mo/src/problems/partition/moBinaryPartitionSwapNeighborhood.h b/mo/src/problems/partition/moBinaryPartitionSwapNeighborhood.h index e92382357..694576732 100644 --- a/mo/src/problems/partition/moBinaryPartitionSwapNeighborhood.h +++ b/mo/src/problems/partition/moBinaryPartitionSwapNeighborhood.h @@ -5,13 +5,32 @@ #include #include "moBinaryPartition.h" +/** Stable neighborhood for binary partitions. + * + * This generates all neighbors of a binary partition + * that have the same dimension than the considered solution. + * I.e. it enumerates all the swaps of two atoms + * between the selected and rejected sets. + * + * The core data structure is two indices: + * - one for the position within the selected set of a binary partition, + * - the other for the position within the rejected set. + * + * The neighborhood is defined as enumerating the neighbors, + * first by going over the rejected atoms (outer loop), + * then by iterating over the selected atoms (inner loop). + */ template class moBinaryPartitionSwapNeighborhood : public moNeighborhood > { public: + /** Shortcut for neighbor's type. */ using Neighbor = moBinaryPartitionSwapNeighbor; + + /** Shortcut for Atom’s type. */ using AtomType = typename EOT::AtomType; + /** Get the currently pointed selected atom. */ AtomType selected(EOT& from, const size_t i_select) { typename EOT::ContainerType::iterator it = std::begin(from.rejected); @@ -19,6 +38,7 @@ class moBinaryPartitionSwapNeighborhood : public moNeighborhood 0; } @@ -103,6 +132,9 @@ class moBinaryPartitionSwapNeighborhood : public moNeighborhood