- Wprowadzenie
- Struktura projektu
- Algorytmy
- Komponenty
- Konfiguracja
- Interfejsy
- Szczegółowa dokumentacja
Program implementuje rozwiązanie problemu składania DNA (DNA Assembly Problem) przy użyciu algorytmów metaheurystycznych, w szczególności algorytmu genetycznego. Program zawiera zaawansowane mechanizmy optymalizacji i adaptacji parametrów. Zadanie na labolatoria do dr. Radoma - Bioinformatyka 3 semestr, optymalizacja kombinatoryczna.
.
├── include/ # Pliki nagłówkowe
│ ├── metaheuristics/ # Implementacje metaheurystyk
│ ├── interfaces/ # Interfejsy
│ ├── dna/ # Obsługa instancji DNA
│ ├── utils/ # Narzędzia pomocnicze
│ └── configuration/ # Konfiguracja
├── src/ # Pliki źródłowe
└── test/ # Testy jednostkowe
Główna klasa implementująca algorytm genetyczny dla problemu DNA Assembly.
-
Inicjalizacja:
initializePopulation(int popSize, const DNAInstance& instance)
- Tworzy początkową populację rozwiązań
- Parametry:
popSize
: Rozmiar populacjiinstance
: Instancja problemu DNA
-
Ewaluacja:
evaluatePopulation()
- Ocenia jakość każdego osobnika w populacji
- Wykorzystuje funkcję przystosowania opartą na grafie
-
Selekcja: Implementowana przez interfejs
ISelection
- Wybiera osobniki do reprodukcji
- Dostępne strategie:
- Selekcja turniejowa
- Selekcja rankingowa
- Selekcja ruletki
-
Krzyżowanie: Implementowane przez interfejs
ICrossover
- Tworzy nowe rozwiązania przez kombinację istniejących
- Dostępne operatory:
- Krzyżowanie jednopunktowe
- Krzyżowanie dwupunktowe
- Krzyżowanie adaptacyjne (AdaptiveCrossover)
-
Mutacja: Implementowana przez interfejs
IMutation
- Wprowadza losowe zmiany w rozwiązaniach
- Dostępne operatory:
- Mutacja przez zamianę
- Mutacja przez inwersję
- Mutacja przez przesunięcie
Implementowana w klasie PermutationRepresentation
:
- Reprezentacja permutacyjna fragmentów DNA
- Operacje na permutacjach
- Walidacja poprawności rozwiązań
Klasa reprezentująca instancję problemu:
class DNAInstance {
std::vector<std::string> fragments; // Fragmenty DNA
int k; // Parametr k dla odległości Levenshteina
std::string originalSequence; // Oryginalna sekwencja (jeśli znana)
};
Reprezentacja pojedynczego osobnika w populacji:
class Individual {
std::vector<int> genes; // Reprezentacja rozwiązania
double fitness; // Wartość funkcji przystosowania
std::string dna; // Złożona sekwencja DNA
};
Implementacje funkcji przystosowania:
OptimizedGraphBasedFitness
: Ocena bazująca na grafieLevenshteinBasedFitness
: Ocena bazująca na odległości Levenshteina
Konfiguracja algorytmu genetycznego:
struct GAConfig {
int populationSize; // Rozmiar populacji
int maxGenerations; // Maksymalna liczba generacji
double crossoverProbability; // Prawdopodobieństwo krzyżowania
double mutationProbability; // Prawdopodobieństwo mutacji
SelectionType selectionType; // Typ selekcji
CrossoverType crossoverType; // Typ krzyżowania
MutationType mutationType; // Typ mutacji
};
Bazowy interfejs dla wszystkich algorytmów:
class IAlgorithm {
virtual std::string run(const DNAInstance& instance) = 0;
virtual ~IAlgorithm() = default;
};
Interfejs reprezentacji rozwiązania:
class IRepresentation {
virtual std::vector<int> decode(const std::string& dna) = 0;
virtual std::string encode(const std::vector<int>& genes) = 0;
virtual bool isValid(const std::vector<int>& genes) = 0;
};
Interfejs selekcji osobników:
class ISelection {
virtual std::vector<std::shared_ptr<Individual>> select(
const std::vector<std::shared_ptr<Individual>>& population,
int count) = 0;
virtual SelectionType getType() const = 0;
};
Interfejs operatora krzyżowania:
class ICrossover {
virtual std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) = 0;
virtual CrossoverType getType() const = 0;
};
Interfejs operatora mutacji:
class IMutation {
virtual void mutate(std::shared_ptr<Individual> individual) = 0;
virtual MutationType getType() const = 0;
virtual void setMutationProbability(double probability) = 0;
};
Interfejs strategii wymiany pokoleń:
class IReplacement {
virtual std::vector<std::shared_ptr<Individual>> replace(
const std::vector<std::shared_ptr<Individual>>& oldPopulation,
const std::vector<std::shared_ptr<Individual>>& offspring) = 0;
virtual ReplacementType getType() const = 0;
};
Interfejs funkcji przystosowania:
class IFitness {
virtual double calculate(const std::vector<int>& genes,
const DNAInstance& instance) = 0;
virtual FitnessType getType() const = 0;
};
Interfejs kryterium zatrzymania:
class IStopping {
virtual bool shouldStop(int generation, double bestFitness) = 0;
virtual void reset() = 0;
virtual StoppingType getType() const = 0;
};
Interfejs cache'owania populacji:
class IPopulationCache {
virtual void addToCache(const std::vector<int>& genes, double fitness) = 0;
virtual bool isInCache(const std::vector<int>& genes) const = 0;
virtual double getCachedFitness(const std::vector<int>& genes) const = 0;
virtual void clear() = 0;
virtual size_t size() const = 0;
};
Interfejs dla benchmarków:
class IBenchmark {
virtual void run() = 0;
virtual void generateReport(const std::string& outputFile) = 0;
virtual BenchmarkType getType() const = 0;
};
TournamentSelection
: Selekcja turniejowaRouletteSelection
: Selekcja metodą ruletkiRankSelection
: Selekcja rankingowa
SinglePointCrossover
: Krzyżowanie jednopunktoweTwoPointCrossover
: Krzyżowanie dwupunktoweUniformCrossover
: Krzyżowanie równomierneAdaptiveCrossover
: Krzyżowanie adaptacyjne
SwapMutation
: Mutacja przez zamianęInversionMutation
: Mutacja przez inwersjęScrambleMutation
: Mutacja przez przemieszanie
GenerationalReplacement
: Wymiana generacyjnaElitistReplacement
: Wymiana elitarnaSteadyStateReplacement
: Wymiana w stanie ustalonym
GraphBasedFitness
: Ocena bazująca na grafieLevenshteinFitness
: Ocena bazująca na odległości LevenshteinaHybridFitness
: Ocena hybrydowa
GenerationLimit
: Limit generacjiFitnessThreshold
: Próg funkcji przystosowaniaTimeLimit
: Limit czasowyStagnationDetection
: Wykrywanie stagnacji
-
GeneticAlgorithm(std::unique_ptr<IRepresentation> representation, GAConfig& config, bool debugMode = false)
- Konstruktor inicjalizujący algorytm genetyczny
- Parametry:
representation
: Implementacja reprezentacji rozwiązańconfig
: Konfiguracja algorytmudebugMode
: Tryb debugowania
-
std::string run(const DNAInstance& instance)
- Uruchamia algorytm genetyczny
- Zwraca: Najlepsze znalezione rozwiązanie (sekwencję DNA)
-
double getBestFitness()
- Zwraca wartość przystosowania najlepszego rozwiązania
-
void initializePopulation(int popSize, const DNAInstance& instance)
- Inicjalizuje populację początkową
-
void evaluatePopulation()
- Ocenia wszystkie osobniki w populacji
-
bool updateGlobalBest()
- Aktualizuje najlepsze znalezione rozwiązanie
-
void logGenerationStats()
- Zapisuje statystyki generacji
Implementuje adaptacyjny operator krzyżowania:
class AdaptiveCrossover : public ICrossover {
void updateCrossoverRates(); // Aktualizacja współczynników
void adaptParameters(); // Adaptacja parametrów
double calculateSuccess(); // Obliczenie skuteczności
};
Analizuje ścieżki w grafie reprezentującym problem:
class PathAnalyzer {
std::vector<PreprocessedEdge> findBestPath();
double evaluatePath(const std::vector<int>& path);
void preprocessEdges();
};
Główny plik programu zawierający logikę sterującą i obsługę trybów działania:
debug
- Tryb debugowania z domyślnymi ustawieniamigenerate_instance
- Generowanie instancji testowychtest_instance
- Rozwiązywanie problemu rekonstrukcji DNA z pliku wejściowegotuning
- Strojenie parametrów algorytmutuning_hybrid
- Zaawansowane strojenie hybrydowe (1+lambda) ES + Racing
void printUsage()
// Wyświetla instrukcję użycia programu i dostępne opcje
DNAInstance generateInstance(int n, int k, int deltaK, int lNeg, int lPoz)
// Generuje instancję problemu DNA o zadanych parametrach
// Parametry:
// n - długość DNA (300-700)
// k - długość oligonukleotydów (7-10)
// deltaK - zakres Delta K (0-2)
// lNeg - błędy negatywne (0 lub >=10)
// lPoz - błędy pozytywne (0 lub >=10)
void runGeneticAlgorithm(const DNAInstance& instance,
const std::string& outputFile,
int processId,
const std::string& configFile,
bool debugMode)
// Uruchamia algorytm genetyczny dla danej instancji
// Parametry:
// instance - instancja problemu DNA
// outputFile - plik wyjściowy dla wyników
// processId - identyfikator procesu
// configFile - plik konfiguracyjny
// debugMode - tryb debugowania
double runGeneticAlgorithmWithFitness(...)
// Wersja zwracająca wartość funkcji przystosowania
void runParameterTuningWithRacing(const DNAInstance& instance)
// Uruchamia strojenie parametrów z wykorzystaniem metody Racing
TuningResult runParameterTuning(...)
// Przeprowadza strojenie parametrów i zwraca wyniki
### Generator DNA (generator/)
#### DNAGenerator
Klasa odpowiedzialna za generowanie instancji problemu:
```cpp
class DNAGenerator {
std::string generateRandomDNA(int length);
std::vector<std::string> generateFragments(const std::string& dna, int k);
void introduceErrors(std::vector<std::string>& fragments, int lNeg, int lPoz);
};
Zarządza procesem strojenia parametrów:
class ParameterTuningManager {
void tuneParameters();
void evaluateConfiguration(const ParameterSet& params);
void saveResults(const std::string& outputFile);
};
Implementacja metody Racing do selekcji konfiguracji:
class Racing {
void runRace(std::vector<Configuration>& configs);
void eliminateConfigurations();
Configuration getBestConfiguration();
};
Parsowanie i walidacja parametrów strojenia:
class ParameterParser {
// Metody parsowania
ParameterSet parseConfigFile(const std::string& filename);
void validateParameters(const ParameterSet& params);
// Obsługa różnych typów parametrów
int parseIntParameter(const std::string& value, const std::string& name);
double parseDoubleParameter(const std::string& value, const std::string& name);
std::string parseEnumParameter(const std::string& value, const std::vector<std::string>& allowedValues);
};
Struktury danych używane w procesie strojenia:
struct ParameterSet {
std::map<std::string, std::variant<int, double, std::string>> parameters;
double fitness;
int evaluations;
};
struct Configuration {
ParameterSet params;
std::vector<double> results;
double mean;
double stdDev;
};
struct TuningResult {
ParameterSet bestParams;
double bestFitness;
int generations;
double tuningTime;
};
Metaewolucyjny algorytm do strojenia parametrów:
class MetaEA {
// Główne metody
void evolveParameters();
void evaluatePopulation();
void selectBestConfigurations();
// Operatory
ParameterSet crossover(const ParameterSet& parent1, const ParameterSet& parent2);
void mutate(ParameterSet& params);
// Parametry meta-algorytmu
const int META_POPULATION_SIZE = 20;
const int META_GENERATIONS = 50;
const double META_MUTATION_RATE = 0.1;
};
Testy wydajności operatorów krzyżowania:
class CrossoverBenchmark {
void runBenchmark();
void compareCrossoverOperators();
void generateStatistics();
};
Testy wydajności adaptacyjnego operatora krzyżowania:
class AdaptiveCrossoverBenchmark {
void testAdaptation();
void measureConvergence();
};
Klasy do wprowadzania błędów w sekwencjach DNA:
class NegativeErrorIntroducer {
void introduceErrors(std::vector<std::string>& fragments, int count);
};
class PositiveErrorIntroducer {
void introduceErrors(std::vector<std::string>& fragments, int count);
};
Generator liczb pseudolosowych:
class Random {
// Generowanie liczb losowych
int getRandomInt(int min, int max);
double getRandomDouble(double min, double max);
bool getRandomBool(double probability);
// Operacje na kolekcjach
template<typename T>
void shuffle(std::vector<T>& vec);
template<typename T>
T getRandomElement(const std::vector<T>& vec);
};
Pomiar czasu wykonania:
class Timer {
void start();
void stop();
double getElapsedTime() const;
void reset();
static std::string formatTime(double seconds);
};
System logowania:
class Logger {
// Poziomy logowania
enum class Level {
DEBUG,
INFO,
WARNING,
ERROR
};
// Metody logowania
void log(Level level, const std::string& message);
void setLogLevel(Level level);
void setLogFile(const std::string& filename);
// Konfiguracja
void enableConsoleOutput(bool enable);
void enableFileOutput(bool enable);
};
Narzędzie do profilowania wydajności:
class Profiler {
// Pomiar wydajności
void startSection(const std::string& name);
void endSection(const std::string& name);
// Raportowanie
void generateReport();
void saveReport(const std::string& filename);
// Statystyki
struct SectionStats {
double totalTime;
int calls;
double avgTime;
double minTime;
double maxTime;
};
};
Implementacja hashowania Zobrista dla efektywnego cache'owania:
class ZobristHasher {
// Inicjalizacja
void initialize(size_t size);
// Hashowanie
size_t hash(const std::vector<int>& genes);
void updateHash(size_t& currentHash, int pos, int oldVal, int newVal);
// Parametry
static constexpr size_t HASH_SIZE = 64;
};
Zbiór funkcji pomocniczych:
namespace Utils {
// Operacje na plikach
std::string readFile(const std::string& filename);
void writeFile(const std::string& filename, const std::string& content);
// Operacje na stringach
std::vector<std::string> split(const std::string& str, char delimiter);
std::string trim(const std::string& str);
// Konwersje
template<typename T>
std::string toString(const T& value);
template<typename T>
T fromString(const std::string& str);
// Operacje matematyczne
double calculateMean(const std::vector<double>& values);
double calculateStdDev(const std::vector<double>& values);
// Operacje na DNA
int calculateLevenshteinDistance(const std::string& s1, const std::string& s2);
double calculateSequenceSimilarity(const std::string& s1, const std::string& s2);
}
population_size=100
max_generations=1000
crossover_probability=0.8
mutation_probability=0.1
selection_type=tournament
crossover_type=adaptive
mutation_type=swap
tournament_size=3
elite_size=2
300 8 # N=300 (długość DNA), K=8 (długość oligonukleotydów)
ACGTACGT # Fragment 1
CGTACGTA # Fragment 2
...
fragment_n
gdzie:
- n: liczba fragmentów
- k: parametr k dla odległości Levenshteina
- fragment_i: i-ty fragment DNA
{
"population_size": 100,
"max_generations": 1000,
"crossover_probability": 0.8,
"mutation_probability": 0.1,
"selection_type": "tournament",
"crossover_type": "adaptive",
"mutation_type": "swap"
}
mkdir build
cd build
cmake ..
make
./dna_assembly <plik_instancji> <plik_konfiguracji>
n k
fragment_1
fragment_2
...
fragment_n
{
"population_size": 100,
"max_generations": 1000,
"crossover_probability": 0.8,
"mutation_probability": 0.1,
"selection_type": "tournament",
"crossover_type": "adaptive",
"mutation_type": "swap"
}
- C++17 lub nowszy
- CMake 3.10 lub nowszy
- Boost (dla testów jednostkowych)
Implementacja naiwnego podejścia do problemu rekonstrukcji DNA:
class NaiveReconstruction {
// Główne metody
std::string reconstructDNA(const std::vector<std::string>& fragments);
// Metody pomocnicze
double calculateOverlap(const std::string& s1, const std::string& s2);
std::pair<int, int> findBestMatch(const std::vector<std::string>& fragments);
void mergePair(std::vector<std::string>& fragments, int i, int j);
};
Algorytm działa poprzez:
- Znajdowanie par fragmentów o największym pokryciu
- Łączenie znalezionych par
- Powtarzanie procesu aż do uzyskania jednej sekwencji
Parsowanie i walidacja parametrów strojenia:
class ParameterParser {
// Metody parsowania
ParameterSet parseConfigFile(const std::string& filename);
void validateParameters(const ParameterSet& params);
// Obsługa różnych typów parametrów
int parseIntParameter(const std::string& value, const std::string& name);
double parseDoubleParameter(const std::string& value, const std::string& name);
std::string parseEnumParameter(const std::string& value, const std::vector<std::string>& allowedValues);
};
Struktury danych używane w procesie strojenia:
struct ParameterSet {
std::map<std::string, std::variant<int, double, std::string>> parameters;
double fitness;
int evaluations;
};
struct Configuration {
ParameterSet params;
std::vector<double> results;
double mean;
double stdDev;
};
struct TuningResult {
ParameterSet bestParams;
double bestFitness;
int generations;
double tuningTime;
};
Metaewolucyjny algorytm do strojenia parametrów:
class MetaEA {
// Główne metody
void evolveParameters();
void evaluatePopulation();
void selectBestConfigurations();
// Operatory
ParameterSet crossover(const ParameterSet& parent1, const ParameterSet& parent2);
void mutate(ParameterSet& params);
// Parametry meta-algorytmu
const int META_POPULATION_SIZE = 20;
const int META_GENERATIONS = 50;
const double META_MUTATION_RATE = 0.1;
};
Implementacja cache'owania populacji dla przyspieszenia obliczeń:
class PopulationCacheImpl : public IPopulationCache {
// Metody zarządzania cache'm
void addToCache(const std::vector<int>& genes, double fitness);
bool isInCache(const std::vector<int>& genes) const;
double getCachedFitness(const std::vector<int>& genes) const;
// Parametry cache'a
const size_t MAX_CACHE_SIZE = 10000;
void cleanCache();
};
Klasa zarządzająca uruchomieniem algorytmu genetycznego:
class GeneticAlgorithmRunner {
// Główne metody
void run(const DNAInstance& instance);
void initialize();
void evolve();
void finalize();
// Metody pomocnicze
void updateStatistics();
void logProgress();
void checkStoppingCriteria();
// Parametry wykonania
const int CHECKPOINT_INTERVAL = 100;
const int LOG_INTERVAL = 10;
};
// Inicjalizacja
auto instance = DNAInstance::fromFile("instance.txt");
auto config = GAConfig::fromFile("config.txt");
auto ga = GeneticAlgorithm(std::make_unique<PermutationRepresentation>(), config);
// Uruchomienie
std::string result = ga.run(instance);
std::cout << "Zrekonstruowana sekwencja: " << result << std::endl;
// Konfiguracja operatorów
auto selection = std::make_unique<TournamentSelection>(3);
auto crossover = std::make_unique<AdaptiveCrossover>();
auto mutation = std::make_unique<SwapMutation>(0.1);
// Konfiguracja algorytmu
GAConfig config;
config.setPopulationSize(100)
.setMaxGenerations(1000)
.setSelection(std::move(selection))
.setCrossover(std::move(crossover))
.setMutation(std::move(mutation));
// Uruchomienie z logowaniem
Logger::getInstance().setLogLevel(Logger::Level::DEBUG);
auto ga = GeneticAlgorithm(std::make_unique<PermutationRepresentation>(), config);
ga.run(instance);
// Konfiguracja strojenia
auto tuner = ParameterTuningManager("tuning_results.csv");
tuner.addParameter("population_size", {50, 100, 200})
.addParameter("crossover_rate", {0.7, 0.8, 0.9})
.addParameter("mutation_rate", {0.01, 0.05, 0.1});
// Uruchomienie strojenia
tuner.tune(instance, 30); // 30 iteracji dla każdej konfiguracji
// Tworzenie instancji problemu
auto instance = DNAInstance::fromFile("instance.txt");
// Konfiguracja w trybie debug
GAConfig config;
config.setPopulationSize(50) // Mniejsza populacja dla szybszego debugowania
.setMaxGenerations(100) // Mniej generacji
.setDebugMode(true) // Włączenie trybu debug
.setLogLevel(Logger::Level::DEBUG);
// Inicjalizacja loggera
Logger::getInstance().enableFileOutput(true)
.setLogFile("debug_output.txt")
.enableConsoleOutput(true);
// Inicjalizacja profilera
Profiler::getInstance().startSection("main");
void GeneticAlgorithm::initializePopulation() {
Logger::debug("Inicjalizacja populacji początkowej");
Profiler::startSection("initialization");
// Tworzenie początkowej populacji
for (int i = 0; i < config.populationSize; i++) {
auto individual = createRandomIndividual();
population.push_back(individual);
Logger::debug(f"Osobnik {i}: {individual->toString()}");
}
Profiler::endSection("initialization");
}
void GeneticAlgorithm::evolve() {
for (int generation = 0; generation < config.maxGenerations; generation++) {
Profiler::startSection("generation");
// Selekcja rodziców
Logger::debug("Generacja {generation}: Selekcja");
auto parents = selection->select(population, config.selectionSize);
// Krzyżowanie
Logger::debug("Krzyżowanie wybranych osobników");
auto offspring = crossover->crossover(parents);
// Mutacja
Logger::debug("Mutacja potomstwa");
for (auto& child : offspring) {
mutation->mutate(child);
}
// Ewaluacja
Logger::debug("Obliczanie przystosowania");
evaluatePopulation(offspring);
// Wymiana pokoleń
Logger::debug("Wymiana pokoleń");
population = replacement->replace(population, offspring);
// Statystyki i debug info
logDebugInfo(generation);
Profiler::endSection("generation");
}
}
void GeneticAlgorithm::evaluatePopulation() {
Profiler::startSection("evaluation");
for (auto& individual : population) {
// Obliczanie przystosowania
double fitness = fitnessCalculator->calculate(individual);
individual->setFitness(fitness);
// Debug info
Logger::debug(f"Fitness: {fitness}, DNA: {individual->getDNA()}");
}
// Statystyki populacji
double avgFitness = calculateAverageFitness();
double bestFitness = findBestFitness();
Logger::debug(f"Średnie przystosowanie: {avgFitness}");
Logger::debug(f"Najlepsze przystosowanie: {bestFitness}");
Profiler::endSection("evaluation");
}
Używany do śledzenia przebiegu algorytmu:
// Poziomy logowania
Logger::debug("Szczegółowe informacje dla debugowania");
Logger::info("Ogólne informacje o postępie");
Logger::warning("Ostrzeżenia");
Logger::error("Błędy krytyczne");
Mierzy wydajność poszczególnych sekcji:
// Przykładowy raport profilera
Section | Total Time | Calls | Avg Time
-----------------|------------|-------|----------
initialization | 0.123s | 1 | 0.123s
generation | 15.432s | 100 | 0.154s
evaluation | 10.234s | 100 | 0.102s
selection | 2.345s | 100 | 0.023s
crossover | 1.876s | 100 | 0.019s
mutation | 0.987s | 100 | 0.010s
void GeneticAlgorithm::visualizeProgress() {
// Wykres fitness w czasie
plotFitnessProgress();
// Wizualizacja różnorodności populacji
plotPopulationDiversity();
// Statystyki operatorów
plotOperatorStatistics();
}
struct DebugStatistics {
double bestFitness;
double averageFitness;
double worstFitness;
double populationDiversity;
int generationsToConvergence;
double executionTime;
std::map<std::string, double> operatorEffectiveness;
};
void GeneticAlgorithm::validateSolution() {
auto bestSolution = getBestIndividual();
// Sprawdzenie poprawności DNA
bool isValid = validator->validateDNA(bestSolution->getDNA());
Logger::debug(f"Poprawność rozwiązania: {isValid}");
// Szczegółowa analiza błędów
auto errors = validator->analyzeErrors(bestSolution->getDNA());
for (const auto& error : errors) {
Logger::debug(f"Błąd: {error.description} w pozycji {error.position}");
}
}
int main() {
// Inicjalizacja z trybem debug
auto ga = GeneticAlgorithm(config, true);
// Uruchomienie z pełnym logowaniem
Logger::setLogLevel(Logger::Level::DEBUG);
Profiler::getInstance().enable();
// Wykonanie algorytmu
auto result = ga.run(instance);
// Analiza wyników
ga.validateSolution();
ga.visualizeProgress();
// Generowanie raportu
Profiler::getInstance().generateReport("profiling_results.txt");
Logger::saveLog("debug_log.txt");
}
Framework do automatycznego testowania rozwiązań na różnych instancjach:
class SBHTestingFramework:
def __init__(self, config_path: str):
"""
Inicjalizacja frameworku testowego
:param config_path: Ścieżka do pliku konfiguracyjnego
"""
self.config = self._load_config(config_path)
self.results_db = ResultsDatabase()
self.test_cases = TestCaseManager()
def run_test_suite(self, test_suite: str):
"""
Uruchomienie zestawu testów
:param test_suite: Nazwa zestawu testów (np. 'easy', 'medium', 'hard')
"""
for test_case in self.test_cases.get_suite(test_suite):
self._run_single_test(test_case)
self._collect_metrics(test_case)
def generate_report(self, output_path: str):
"""
Generowanie raportu z testów
:param output_path: Ścieżka do pliku wyjściowego
"""
self.results_db.generate_report(output_path)
class TestCase:
"""Reprezentacja pojedynczego przypadku testowego"""
def __init__(self, dna_length: int, oligo_length: int,
neg_errors: int, pos_errors: int):
self.dna_length = dna_length
self.oligo_length = oligo_length
self.neg_errors = neg_errors
self.pos_errors = pos_errors
self.original_sequence = None
self.fragments = []
System do tworzenia rankingu rozwiązań:
class RankingSystem:
def __init__(self, results_db_path: str):
"""
Inicjalizacja systemu rankingowego
:param results_db_path: Ścieżka do bazy wyników
"""
self.results = self._load_results(results_db_path)
self.metrics = MetricsCalculator()
def calculate_rankings(self):
"""Obliczanie rankingów dla różnych kategorii"""
return {
'overall': self._calculate_overall_ranking(),
'by_size': self._calculate_size_based_ranking(),
'by_error_type': self._calculate_error_based_ranking(),
'by_time': self._calculate_time_based_ranking()
}
def generate_ranking_report(self, output_path: str):
"""
Generowanie raportu rankingowego
:param output_path: Ścieżka do pliku wyjściowego
"""
rankings = self.calculate_rankings()
self._generate_report(rankings, output_path)
class MetricsCalculator:
"""Kalkulator metryk dla rankingu"""
def calculate_solution_score(self, solution: Solution) -> float:
"""
Obliczanie wyniku dla pojedynczego rozwiązania
Uwzględnia:
- Dokładność rekonstrukcji
- Czas wykonania
- Zużycie pamięci
- Stabilność wyników
"""
accuracy = self._calculate_accuracy(solution)
time_score = self._calculate_time_score(solution)
memory_score = self._calculate_memory_score(solution)
stability = self._calculate_stability(solution)
return self._combine_scores(accuracy, time_score,
memory_score, stability)
Narzędzie do wizualizacji wyników testów:
class VisualizationManager:
def __init__(self, results_data: pd.DataFrame):
"""
Inicjalizacja menedżera wizualizacji
:param results_data: DataFrame z wynikami
"""
self.data = results_data
self.plt_style = self._configure_plot_style()
def plot_performance_comparison(self):
"""Generowanie wykresów porównawczych wydajności"""
self._plot_accuracy_comparison()
self._plot_time_comparison()
self._plot_memory_usage()
self._plot_convergence_rates()
def generate_error_analysis(self):
"""Analiza i wizualizacja błędów"""
self._plot_error_distribution()
self._plot_error_correlation()
self._generate_error_heatmap()
# Inicjalizacja i uruchomienie testów
framework = SBHTestingFramework('config/test_config.yaml')
framework.run_test_suite('comprehensive')
framework.generate_report('results/test_report.pdf')
# Tworzenie rankingu rozwiązań
ranking = RankingSystem('results/results_database.db')
rankings = ranking.calculate_rankings()
ranking.generate_ranking_report('results/ranking_report.pdf')
# Wizualizacja wyników
results_data = pd.read_csv('results/test_results.csv')
viz = VisualizationManager(results_data)
viz.plot_performance_comparison()
viz.generate_error_analysis()
test_suites:
easy:
dna_length: [300, 400]
oligo_length: [7, 8]
neg_errors: [0, 10]
pos_errors: [0, 10]
repetitions: 5
medium:
dna_length: [500, 600]
oligo_length: [8, 9]
neg_errors: [20, 30]
pos_errors: [20, 30]
repetitions: 10
hard:
dna_length: [700, 800]
oligo_length: [9, 10]
neg_errors: [40, 50]
pos_errors: [40, 50]
repetitions: 15
metrics:
accuracy:
weight: 0.4
threshold: 0.95
time:
weight: 0.3
max_time: 3600
memory:
weight: 0.2
max_memory: 1024
stability:
weight: 0.1
min_repetitions: 5
Test Suite Results
-----------------
Suite: Comprehensive
Total test cases: 150
Success rate: 92%
Detailed Results:
1. Easy instances (50 cases)
- Success rate: 98%
- Avg. time: 12.3s
- Avg. memory: 256MB
2. Medium instances (50 cases)
- Success rate: 94%
- Avg. time: 45.7s
- Avg. memory: 512MB
3. Hard instances (50 cases)
- Success rate: 84%
- Avg. time: 187.2s
- Avg. memory: 1024MB
Algorithm Rankings
-----------------
Overall Ranking:
1. AdaptiveGA v2.1 (Score: 0.92)
2. StandardGA v1.5 (Score: 0.87)
3. HybridGA v1.0 (Score: 0.85)
Category Rankings:
1. Best for small instances:
- AdaptiveGA v2.1 (Score: 0.95)
2. Best for large instances:
- HybridGA v1.0 (Score: 0.88)
3. Best time performance:
- StandardGA v1.5 (Score: 0.91)
tests/
├── google_tests/ # Testy jednostkowe z Google Test
│ ├── genetic/ # Testy modułu genetycznego
│ │ ├── crossover_test.cpp # Testy operatorów krzyżowania
│ │ ├── mutation_test.cpp # Testy operatorów mutacji
│ │ ├── selection_test.cpp # Testy metod selekcji
│ │ └── fitness_test.cpp # Testy funkcji przystosowania
│ ├── dna/ # Testy modułu DNA
│ │ ├── instance_test.cpp # Testy instancji DNA
│ │ └── error_test.cpp # Testy wprowadzania błędów
│ └── utils/ # Testy narzędzi
│ ├── random_test.cpp # Testy generatora liczb losowych
│ └── timer_test.cpp # Testy pomiaru czasu
├── integration_tests/ # Testy integracyjne
│ ├── algorithm_test.cpp # Testy przepływu algorytmu
│ └── system_test.cpp # Testy całego systemu
└── performance_tests/ # Testy wydajnościowe
├── benchmark_test.cpp # Testy wydajności
└── stress_test.cpp # Testy obciążeniowe
TEST(CrossoverTest, SinglePointCrossoverMaintainsValidPermutation) {
// Arrange
auto parent1 = std::make_shared<Individual>(std::vector<int>{1,2,3,4,5});
auto parent2 = std::make_shared<Individual>(std::vector<int>{5,4,3,2,1});
auto crossover = std::make_unique<SinglePointCrossover>();
// Act
auto [child1, child2] = crossover->crossover(parent1, parent2);
// Assert
EXPECT_TRUE(isValidPermutation(child1->getGenes()));
EXPECT_TRUE(isValidPermutation(child2->getGenes()));
EXPECT_EQ(child1->getGenes().size(), 5);
EXPECT_EQ(child2->getGenes().size(), 5);
}
TEST(FitnessTest, OptimalSolutionHasMaxFitness) {
// Arrange
auto instance = createTestInstance();
auto solution = createOptimalSolution(instance);
auto fitness = std::make_unique<GraphBasedFitness>();
// Act
double fitnessValue = fitness->calculate(solution->getGenes(), instance);
// Assert
EXPECT_NEAR(fitnessValue, 1.0, 0.001);
}
TEST(MutationTest, SwapMutationChangesExactlyTwoPositions) {
// Arrange
auto individual = std::make_shared<Individual>(std::vector<int>{1,2,3,4,5});
auto originalGenes = individual->getGenes();
auto mutation = std::make_unique<SwapMutation>(1.0); // 100% szansa na mutację
// Act
mutation->mutate(individual);
auto mutatedGenes = individual->getGenes();
// Assert
int differences = 0;
for (size_t i = 0; i < originalGenes.size(); ++i) {
if (originalGenes[i] != mutatedGenes[i]) differences++;
}
EXPECT_EQ(differences, 2);
}
TEST(StressTest, HandlesLargeInstances) {
// Arrange
const int NUM_TESTS = 100;
const int DNA_LENGTH = 1000;
auto ga = setupGeneticAlgorithm();
// Act & Assert
for (int i = 0; i < NUM_TESTS; ++i) {
auto instance = generateLargeInstance(DNA_LENGTH);
auto start = std::chrono::high_resolution_clock::now();
auto result = ga.run(instance);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::seconds>
(end - start).count();
EXPECT_LE(duration, 60); // Max 1 minuta na instancję
EXPECT_GE(ga.getBestFitness(), 0.85);
}
}
#!/bin/bash
# run_tests.sh
# Kompilacja
cd build
cmake -DBUILD_TESTING=ON ..
make -j$(nproc)
# Testy jednostkowe
echo "Running unit tests..."
./tests/google_tests/unit_tests --gtest_output="xml:unit_test_results.xml"
# Testy integracyjne
echo "Running integration tests..."
./tests/integration_tests/integration_tests --gtest_output="xml:integration_test_results.xml"
# Testy wydajnościowe
echo "Running performance tests..."
./tests/performance_tests/performance_tests --gtest_output="xml:performance_test_results.xml"
# Analiza wyników
python3 ../scripts/analyze_test_results.py
# analyze_test_results.py
def analyze_test_results():
unit_results = parse_xml_results('unit_test_results.xml')
integration_results = parse_xml_results('integration_test_results.xml')
performance_results = parse_xml_results('performance_test_results.xml')
generate_report(unit_results, integration_results, performance_results)
# CMakeLists.txt
enable_testing()
# Google Test
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# Testy jednostkowe
add_executable(unit_tests
tests/google_tests/genetic/crossover_test.cpp
tests/google_tests/genetic/mutation_test.cpp
tests/google_tests/genetic/selection_test.cpp
tests/google_tests/genetic/fitness_test.cpp
# ... pozostałe pliki testów
)
target_link_libraries(unit_tests
PRIVATE
GTest::GTest
GTest::Main
${PROJECT_NAME}_lib
)
# Dodanie testów do CTest
add_test(NAME unit_tests COMMAND unit_tests)
add_test(NAME integration_tests COMMAND integration_tests)
add_test(NAME performance_tests COMMAND performance_tests)
# .github/workflows/tests.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgtest-dev cmake
- name: Build and test
run: |
mkdir build && cd build
cmake -DBUILD_TESTING=ON ..
make
ctest --output-on-failure --verbose
# Instalacja lcov
sudo apt-get install lcov
# Kompilacja z flagami pokrycia
cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON ..
make
# Generowanie raportu pokrycia
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
genhtml coverage.info --output-directory coverage_report

-
Organizacja testów
- Jeden plik testowy na jeden plik źródłowy
- Grupowanie testów w logiczne kategorie
- Jasne nazewnictwo testów (Given_When_Then)
-
Struktura testu
- Arrange (przygotowanie)
- Act (działanie)
- Assert (sprawdzenie)
-
Testowanie brzegowych przypadków
- Puste wejście
- Maksymalne wartości
- Nieprawidłowe dane
- Błędy alokacji pamięci
-
Mockowanie i stubbing
- Używanie mock objects dla zewnętrznych zależności
- Stubbing dla deterministycznych testów
- Izolacja testowanego kodu
-
Wydajność testów
- Szybkie testy jednostkowe
- Dłuższe testy integracyjne w osobnej grupie
- Testy wydajnościowe uruchamiane na żądanie
## Operatory krzyżowania (Crossover)
### 1. Krzyżowanie jednopunktowe (SinglePointCrossover)
```cpp
class SinglePointCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
};
- Wybierany jest losowy punkt przecięcia (k) w zakresie [1, n-1], gdzie n to długość chromosomu
- Pierwszy potomek otrzymuje geny [1..k] od pierwszego rodzica i [k+1..n] od drugiego
- Drugi potomek otrzymuje geny [1..k] od drugiego rodzica i [k+1..n] od pierwszego
- Wykonywana jest naprawa permutacji, aby zachować poprawność rozwiązania
Rodzic 1: [1 2 3 | 4 5 6]
Rodzic 2: [6 5 4 | 3 2 1]
Punkt przecięcia: 3
Przed naprawą:
Potomek 1: [1 2 3 | 3 2 1]
Potomek 2: [6 5 4 | 4 5 6]
Po naprawie:
Potomek 1: [1 2 3 | 6 5 4]
Potomek 2: [6 5 4 | 1 2 3]
class TwoPointCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
};
- Wybierane są dwa losowe punkty przecięcia (k1, k2) gdzie k1 < k2
- Pierwszy potomek otrzymuje segmenty [1..k1] i [k2+1..n] od pierwszego rodzica i [k1+1..k2] od drugiego
- Drugi potomek otrzymuje segmenty [1..k1] i [k2+1..n] od drugiego rodzica i [k1+1..k2] od pierwszego
- Wykonywana jest naprawa permutacji
Rodzic 1: [1 2 | 3 4 | 5 6]
Rodzic 2: [6 5 | 4 3 | 2 1]
Punkty przecięcia: 2, 4
Przed naprawą:
Potomek 1: [1 2 | 4 3 | 5 6]
Potomek 2: [6 5 | 3 4 | 2 1]
Po naprawie:
Potomek 1: [1 2 | 4 3 | 5 6]
Potomek 2: [6 5 | 3 4 | 2 1]
class UniformCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
private:
double crossoverProbability = 0.5;
};
- Dla każdej pozycji i w chromosomie:
- Z prawdopodobieństwem p (domyślnie 0.5) gen pochodzi od pierwszego rodzica
- Z prawdopodobieństwem (1-p) gen pochodzi od drugiego rodzica
- Wykonywana jest naprawa permutacji dla obu potomków
Rodzic 1: [1 2 3 4 5 6]
Rodzic 2: [6 5 4 3 2 1]
Maska: [1 0 1 0 1 0] (1: gen z rodzica 1, 0: gen z rodzica 2)
Przed naprawą:
Potomek 1: [1 5 3 3 5 1]
Potomek 2: [6 2 4 4 2 6]
Po naprawie:
Potomek 1: [1 5 3 4 2 6]
Potomek 2: [6 2 4 3 5 1]
class AdaptiveCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
private:
void updateCrossoverRates();
void adaptParameters();
std::vector<CrossoverOperator> operators;
std::vector<double> successRates;
};
- Utrzymuje pulę różnych operatorów krzyżowania
- Monitoruje skuteczność każdego operatora
- Adaptuje prawdopodobieństwa użycia operatorów na podstawie ich skuteczności
- Wybiera operator do krzyżowania na podstawie zaktualizowanych prawdopodobieństw
Początkowe prawdopodobieństwa:
- SinglePoint: 0.33
- TwoPoint: 0.33
- Uniform: 0.33
- Cycle: 0.33
- PMX: 0.33
- EdgeRecombination: 0.33
Po 100 generacjach:
- SinglePoint: 0.45 (najlepsze wyniki)
- TwoPoint: 0.35
- Uniform: 0.20 (najgorsze wyniki)
class CycleCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
private:
std::vector<int> findCycle(const std::vector<int>& p1,
const std::vector<int>& p2);
};
- Znajduje cykle w permutacjach rodziców
- Zachowuje kompletne cykle z pierwszego rodzica dla pierwszego potomka
- Wypełnia pozostałe pozycje genami z drugiego rodzica
- Dla drugiego potomka proces jest odwrotny
Rodzic 1: [1 2 3 4 5 6]
Rodzic 2: [2 4 5 1 6 3]
Cykl 1: 1->2->4->1
Cykl 2: 3->5->6->3
Potomek 1: [1 2 5 4 6 3]
Potomek 2: [2 4 3 1 5 6]
class OrderCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
};
- Wybiera losowy podciąg z pierwszego rodzica
- Kopiuje ten podciąg do potomka na te same pozycje
- Wypełnia pozostałe pozycje genami z drugiego rodzica w kolejności ich występowania
- Dla drugiego potomka proces jest odwrotny
Rodzic 1: [1 2 3 4 5 6]
Rodzic 2: [6 5 4 3 2 1]
Wybrany podciąg: [2 3 4]
Potomek 1: [6 2 3 4 5 1]
Potomek 2: [1 5 4 3 2 6]
class PermutationRepair {
public:
static void repair(std::vector<int>& genes) {
std::vector<bool> used(genes.size(), false);
std::vector<int> missing;
// Znajdź duplikaty i brakujące elementy
for (int i = 0; i < genes.size(); i++) {
if (used[genes[i]]) {
missing.push_back(i);
} else {
used[genes[i]] = true;
}
}
// Znajdź brakujące wartości
std::vector<int> values;
for (int i = 0; i < used.size(); i++) {
if (!used[i]) {
values.push_back(i);
}
}
// Napraw permutację
for (int i = 0; i < missing.size(); i++) {
genes[missing[i]] = values[i];
}
}
};
struct CrossoverEffectiveness {
std::string name;
double avgFitness;
double successRate;
double avgExecutionTime;
int validOffspringRate;
};
std::vector<CrossoverEffectiveness> results = {
{"SinglePoint", 0.85, 0.92, 0.12, 95},
{"TwoPoint", 0.87, 0.90, 0.15, 93},
{"Uniform", 0.82, 0.88, 0.18, 90},
{"Adaptive", 0.89, 0.94, 0.20, 96},
{"Cycle", 0.84, 0.89, 0.14, 94},
{"Order", 0.86, 0.91, 0.13, 92}
};
class PMXCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
private:
void createMapping(const std::vector<int>& section1,
const std::vector<int>& section2,
std::map<int, int>& mapping);
};
- Wybiera dwa punkty przecięcia, definiując sekcję do mapowania
- Kopiuje sekcję z pierwszego rodzica do pierwszego potomka
- Tworzy mapowanie między elementami w sekcji
- Używa mapowania do wypełnienia pozostałych pozycji
- Powtarza proces dla drugiego potomka w odwrotnej kolejności
Rodzic 1: [1 2 3 | 4 5 6 | 7 8 9]
Rodzic 2: [9 8 7 | 1 3 2 | 6 5 4]
Punkty przecięcia: 3, 6
Mapowanie:
4 ↔ 1
5 ↔ 3
6 ↔ 2
Przed mapowaniem:
Potomek 1: [_ _ _ | 4 5 6 | _ _ _]
Potomek 2: [_ _ _ | 1 3 2 | _ _ _]
Po mapowaniu:
Potomek 1: [3 1 2 | 4 5 6 | 7 8 9]
Potomek 2: [9 8 7 | 1 3 2 | 4 5 6]
class EdgeRecombinationCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
private:
struct EdgeList {
std::vector<std::set<int>> adjacency;
void buildFrom(const std::vector<int>& p1, const std::vector<int>& p2);
std::set<int> getNeighbors(int node) const;
void removeNode(int node);
};
};
- Tworzy listę krawędzi (sąsiedztwa) z obu rodziców
- Rozpoczyna od losowego miasta z jednego z rodziców
- Iteracyjnie wybiera następne miasto z najmniejszą liczbą pozostałych sąsiadów
- W przypadku remisu, wybiera losowo z dostępnych opcji
- Aktualizuje listę krawędzi po każdym wyborze
Rodzic 1: [1 2 3 4 5]
Rodzic 2: [5 3 1 2 4]
Lista sąsiedztwa:
1: {2, 3, 5} // Sąsiedzi z obu rodziców
2: {1, 3, 4}
3: {1, 2, 4, 5}
4: {2, 3, 5}
5: {1, 3, 4}
Proces budowy potomka:
Start: 1
Lista sąsiadów: 2(2), 3(3), 5(2) → Wybierz 2
Lista sąsiadów: 3(2), 4(1) → Wybierz 3
Lista sąsiadów: 4(1), 5(1) → Wybierz 4
Pozostało: 5
Potomek: [1 2 3 4 5]
class EnhancedAdaptiveCrossover : public ICrossover {
std::pair<std::shared_ptr<Individual>, std::shared_ptr<Individual>>
crossover(const std::shared_ptr<Individual>& parent1,
const std::shared_ptr<Individual>& parent2) override;
private:
std::vector<std::shared_ptr<ICrossover>> m_crossovers;
std::vector<double> m_weights;
void updateWeights(const std::vector<double>& fitnessDiffs);
std::shared_ptr<ICrossover> selectOperator();
};
-
Utrzymuje pulę wszystkich dostępnych operatorów krzyżowania:
- SinglePointCrossover
- TwoPointCrossover
- UniformCrossover
- CycleCrossover
- PMXCrossover
- EdgeRecombinationCrossover
-
Adaptacyjnie dostosowuje wagi operatorów na podstawie ich skuteczności:
void updateWeights(const std::vector<double>& fitnessDiffs) { double total = 0.0; for (size_t i = 0; i < m_crossovers.size(); ++i) { m_weights[i] = m_weights[i] * 0.9 + fitnessDiffs[i] * 0.1; total += m_weights[i]; } // Normalizacja wag for (auto& weight : m_weights) { weight /= total; } }
Początkowe wagi:
- SinglePoint: 0.167
- TwoPoint: 0.167
- Uniform: 0.167
- Cycle: 0.167
- PMX: 0.167
- EdgeRecombination: 0.167
Po 100 generacjach:
- SinglePoint: 0.15
- TwoPoint: 0.12
- Uniform: 0.10
- Cycle: 0.20
- PMX: 0.30
- EdgeRecombination: 0.20
struct DetailedCrossoverEffectiveness {
std::string name;
double avgFitness;
double successRate;
double avgExecutionTime;
int validOffspringRate;
double diversityMaintenance;
double convergenceSpeed;
};
std::vector<DetailedCrossoverEffectiveness> extendedResults = {
{"SinglePoint", 0.85, 0.92, 0.12, 95, 0.75, 0.82},
{"TwoPoint", 0.87, 0.90, 0.15, 93, 0.78, 0.85},
{"Uniform", 0.82, 0.88, 0.18, 90, 0.85, 0.78},
{"Cycle", 0.84, 0.89, 0.14, 94, 0.80, 0.83},
{"PMX", 0.89, 0.94, 0.16, 96, 0.88, 0.90},
{"EdgeRecombination", 0.88, 0.93, 0.20, 95, 0.90, 0.87},
{"AdaptiveMix", 0.91, 0.95, 0.22, 97, 0.92, 0.92}
};
Wnioski z porównania:
- PMX i EdgeRecombination najlepiej zachowują strukturę rozwiązania
- Adaptive Mix osiąga najlepsze wyniki dzięki dynamicznemu doborowi operatorów
- EdgeRecombination najlepiej zachowuje różnorodność populacji
- PMX ma najlepszy stosunek jakości do czasu wykonania
- Operatory proste (SinglePoint, TwoPoint) są najszybsze, ale mniej skuteczne
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/benchmark/adaptive_crossover_benchmark.cpp | tests/google_tests/benchmark/adaptive_crossover_benchmark_test.cpp | ❌ |
src/benchmark/crossover_benchmark.cpp | tests/google_tests/benchmark/crossover_benchmark_test.cpp | ❌ |
src/benchmark/naive_benchmark.cpp | tests/google_tests/benchmark/naive_benchmark_test.cpp | ❌ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/configuration/genetic_algorithm_configuration.cpp | tests/google_tests/configuration/genetic_algorithm_configuration_test.cpp | ❌ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/dna/dna_generator.cpp | tests/google_tests/dna/dna_generator_test.cpp | ✅ |
src/dna/dna_instance.cpp | tests/google_tests/dna/dna_instance_test.cpp | ✅ |
src/dna/dna_instance_builder.cpp | tests/google_tests/dna/dna_instance_builder_test.cpp | ✅ |
src/dna/dna_instance_io.cpp | tests/google_tests/dna/dna_instance_io_test.cpp | ✅ |
src/dna/error_introduction.cpp | tests/google_tests/dna/error_introduction_test.cpp | ✅ |
src/dna/spectrum_generator.cpp | tests/google_tests/dna/spectrum_generator_test.cpp | ✅ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/generator/dna_generator.cpp | tests/google_tests/generator/dna_generator_test.cpp | ✅ |
src/generator/spectrum_generator.cpp | tests/google_tests/generator/spectrum_generator_test.cpp | ✅ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/main.cpp | tests/google_tests/main/main_test.cpp | ❌ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/metaheuristics/adaptive_crossover.cpp | tests/google_tests/metaheuristics/adaptive_crossover_test.cpp | ✅ |
src/metaheuristics/crossover.cpp | tests/google_tests/metaheuristics/crossover_test.cpp | ❌ |
src/metaheuristics/crossover_impl.cpp | tests/google_tests/metaheuristics/crossover_impl_test.cpp | ❌ |
src/metaheuristics/edge_table.cpp | tests/google_tests/metaheuristics/edge_table_test.cpp | ❌ |
src/metaheuristics/fitness.cpp | tests/google_tests/metaheuristics/fitness_test.cpp | ❌ |
src/metaheuristics/fitness_impl.cpp | tests/google_tests/metaheuristics/fitness_impl_test.cpp | ❌ |
src/metaheuristics/genetic_algorithm.cpp | tests/google_tests/metaheuristics/genetic_algorithm_test.cpp | ✅ |
src/metaheuristics/genetic_algorithm_runner.cpp | tests/google_tests/metaheuristics/genetic_algorithm_runner_test.cpp | ❌ |
src/metaheuristics/individual.cpp | tests/google_tests/metaheuristics/individual_test.cpp | ❌ |
src/metaheuristics/mutation.cpp | tests/google_tests/metaheuristics/mutation_test.cpp | ❌ |
src/metaheuristics/mutation_impl.cpp | tests/google_tests/metaheuristics/mutation_impl_test.cpp | ❌ |
src/metaheuristics/path_analyzer.cpp | tests/google_tests/metaheuristics/path_analyzer_test.cpp | ❌ |
src/metaheuristics/permutation_representation.cpp | tests/google_tests/metaheuristics/permutation_representation_test.cpp | ❌ |
src/metaheuristics/population_cache.cpp | tests/google_tests/metaheuristics/population_cache_test.cpp | ❌ |
src/metaheuristics/selection.cpp | tests/google_tests/metaheuristics/selection_test.cpp | ❌ |
src/metaheuristics/selection_impl.cpp | tests/google_tests/metaheuristics/selection_impl_test.cpp | ❌ |
src/metaheuristics/stopping_criteria.cpp | tests/google_tests/metaheuristics/stopping_criteria_test.cpp | ❌ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/naive/naive_reconstruction.cpp | tests/google_tests/naive/naive_reconstruction_test.cpp | ❌ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/tuning/parameter_parser.cpp | tests/google_tests/tuning/parameter_parser_test.cpp | ❌ |
src/tuning/parameter_tuning_manager.cpp | tests/google_tests/tuning/parameter_tuning_manager_test.cpp | ❌ |
src/tuning/tuning_structures.cpp | tests/google_tests/tuning/tuning_structures_test.cpp | ❌ |
Plik źródłowy | Plik testowy | Status |
---|---|---|
src/utils/logging.cpp | tests/google_tests/utils/logging_test.cpp | ❌ |
src/utils/performance_profilling_framework.cpp | tests/google_tests/utils/performance_profilling_test.cpp | ❌ |
src/utils/utility_functions.cpp | tests/google_tests/utils/utility_functions_test.cpp | ❌ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/common.h | tests/google_tests/common/common_test.cpp | ❌ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/config/genetic_config.h | tests/google_tests/config/genetic_config_test.cpp | ❌ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/interfaces/i_algorithm.h | tests/google_tests/interfaces/i_algorithm_test.cpp | ❌ |
include/interfaces/i_benchmark.h | tests/google_tests/interfaces/i_benchmark_test.cpp | ❌ |
include/interfaces/i_crossover.h | tests/google_tests/interfaces/i_crossover_test.cpp | ❌ |
include/interfaces/i_fitness.h | tests/google_tests/interfaces/i_fitness_test.cpp | ❌ |
include/interfaces/i_mutation.h | tests/google_tests/interfaces/i_mutation_test.cpp | ❌ |
include/interfaces/i_population_cache.h | tests/google_tests/interfaces/i_population_cache_test.cpp | ❌ |
include/interfaces/i_replacement.h | tests/google_tests/interfaces/i_replacement_test.cpp | ❌ |
include/interfaces/i_representation.h | tests/google_tests/interfaces/i_representation_test.cpp | ❌ |
include/interfaces/i_selection.h | tests/google_tests/interfaces/i_selection_test.cpp | ❌ |
include/interfaces/i_stopping.h | tests/google_tests/interfaces/i_stopping_test.cpp | ❌ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/io/instance_io.h | tests/google_tests/io/instance_io_test.cpp | ✅ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/metaheuristics/adaptive_mutation.h | tests/google_tests/metaheuristics/adaptive_mutation_test.cpp | ❌ |
include/metaheuristics/concrete_fitness.h | tests/google_tests/metaheuristics/concrete_fitness_test.cpp | ❌ |
include/metaheuristics/dna_utils.h | tests/google_tests/metaheuristics/dna_utils_test.cpp | ❌ |
include/metaheuristics/preprocessed_edge.h | tests/google_tests/metaheuristics/preprocessed_edge_test.cpp | ❌ |
include/metaheuristics/replacement_impl.h | tests/google_tests/metaheuristics/replacement_impl_test.cpp | ❌ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/tuning/meta_ea.h | tests/google_tests/tuning/meta_ea_test.cpp | ❌ |
include/tuning/parameters.h | tests/google_tests/tuning/parameters_test.cpp | ❌ |
include/tuning/racing.h | tests/google_tests/tuning/racing_test.cpp | ❌ |
Plik nagłówkowy | Plik testowy | Status |
---|---|---|
include/utils/random.h | tests/google_tests/utils/random_test.cpp | ✅ |
include/utils/timer.h | tests/google_tests/utils/timer_test.cpp | ❌ |
include/utils/zobrist_hasher.h | tests/google_tests/utils/zobrist_hasher_test.cpp | ❌ |
- Total Files: 52
- Files with Tests: 11
- Coverage Percentage: 21.15%
- DNA Module: 100% (6/6)
- Generator Module: 100% (2/2)
- Metaheuristics Module: 11.76% (2/17)
- Utils Module: 33.33% (1/3)
- Other Modules: 0% (0/24)