void GeneticAlgorithm::run_optimization(std::vector<exp_signal> const& signals_exp,
	                                    Spin const& spinA, Spin const& spinB,
										std::vector<int> const& param_numbers,
										std::vector<int> const& param_modes,
										std::vector<bound> const& param_bounds,
										genetic_parameters const& genetic_param,
										output_parameters const& output_param) const
{
	// Initialize the calculator of PELDOR signals
	PeldorCalculator* peldor_calc = new PeldorCalculator(genetic_param.num_avg);
	peldor_calc->calculate_spinA_excitation(signals_exp, spinA);
	// Calculate the spectrum of the spin system
	if (output_param.record_spectrum) {
		std::cout << "  Recording the spectrum... ";
		peldor_calc->save_spectrum(signals_exp, spinA, spinB, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Start optimization
	int n = 1;
	std::cout << "  Optimization step " << n << "/" << genetic_param.num_generations_max << std::endl;
	// Create a first generation
	Generation* generation = new Generation(genetic_param);
	generation->create_initial_generation(param_bounds, genetic_param);
	// Score first generation
	generation->score_chromosomes(*peldor_calc, signals_exp, spinA, spinB, param_numbers, param_modes, genetic_param);
	generation->sort_chromosomes();
	std::vector<double> best_fitness; best_fitness.reserve(genetic_param.num_generations_max);
	best_fitness.push_back(generation->chromosomes[0].fitness);
	// Proceed with next generations
	for (int n = 2; n <= genetic_param.num_generations_max; ++n) { // loop over generations
		std::cout << "  Optimization step " << n << "/" << genetic_param.num_generations_max << std::endl;
		// Create next generation
		generation->produce_offspring(genetic_param, param_bounds);
		// Score the generation
		generation->score_chromosomes(*peldor_calc, signals_exp, spinA, spinB, param_numbers, param_modes, genetic_param);
		generation->sort_chromosomes();
		best_fitness.push_back(generation->chromosomes[0].fitness);
	}
	// Save the fitness of the best chromosome
	if (output_param.record_score) {
		std::cout << "  Recording the fitness vs the number of optimization steps... ";
		record_score(best_fitness, genetic_param, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Save the calculated fit to the PELDOR signals for the best chromosome
	if (output_param.record_fit) {
		std::cout << "  Recording the fit to the PELDOR signals... ";
		record_fit(*peldor_calc, generation->chromosomes[0], signals_exp, spinA, spinB, param_numbers, param_modes, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Save the genes of the best chromosome
	if (output_param.record_parameters) {
		std::cout << "  Recording the best values of fitting parameters... ";
		record_parameters(generation->chromosomes[0], param_numbers, param_modes, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Record the calculated for-factors for the PELDOR signals
	if (output_param.record_form_factor) {
		std::cout << "  Recording the form-factors of PELDOR signals... ";
		record_form_factor(*peldor_calc, generation->chromosomes[0], signals_exp, spinA, spinB,
			               param_numbers, param_modes, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Record the symmetry-related sets of fitting parameters
	if (output_param.record_symmetric_solutions) {
		std::cout << "  Recording the symmetry-related sets of fitting parameters... ";
		record_symmetric_parameters(*peldor_calc, generation->chromosomes[0], signals_exp, spinA, spinB, 
			                        param_numbers, param_modes, param_bounds, genetic_param, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Record the error plots
	if (output_param.record_error_plot) {
		std::cout << "  Recording the error plot... ";
		record_error_plot(*peldor_calc, generation->chromosomes[0], signals_exp, spinA, spinB, 
			              param_numbers, param_modes, param_bounds, genetic_param, output_param);
		std::cout << "Done!" << std::endl;
	}
	// Clean up
	delete peldor_calc;
	delete generation;
}