Skip to content

Commit

Permalink
Merge pull request #105 from emilydolson/master
Browse files Browse the repository at this point in the history
Fix dangerous memory usage in Lineage Tracker
  • Loading branch information
mercere99 authored May 15, 2017
2 parents d7e6d14 + ebf6cff commit 5df10a1
Show file tree
Hide file tree
Showing 15 changed files with 597 additions and 201 deletions.
335 changes: 226 additions & 109 deletions evo/LineageTracker.h

Large diffs are not rendered by default.

88 changes: 46 additions & 42 deletions evo/OEE.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "../base/array.h"
#include "../base/vector.h"
#include "../tools/stats.h"

#include "../tools/memo_function.h"
#include "LineageTracker.h"
#include "StatsManager.h"

Expand Down Expand Up @@ -63,7 +63,6 @@ namespace evo{
using skeleton_type = emp::vector<GENOME_ELEMENT>;

static constexpr bool separate_generations = POP_MANAGER::emp_has_separate_generations;

std::unordered_set<skeleton_type > novel;

int generations = OeeConfig.GENERATIONS(); //How far back do we look for persistance?
Expand All @@ -81,7 +80,7 @@ namespace evo{
using StatsManager_Base<POP_MANAGER>::emp_is_stats_manager;
using lineage_type = LineageTracker<POP_MANAGER>;
lineage_type * lineage;
std::function<double(org_ptr)> fit_fun;
std::function<double(const ORG)> fit_fun;

template <typename WORLD>
OEEStatsManager(WORLD * w,
Expand Down Expand Up @@ -123,9 +122,10 @@ namespace evo{
output_location << delimiter << var_name;
}
output_location << std::endl;

}

void SetDefaultFitnessFun(std::function<double(org_ptr)> fit){
void SetDefaultFitnessFun(std::function<double(const ORG)> fit){
fit_fun = fit;
}

Expand Down Expand Up @@ -157,15 +157,19 @@ namespace evo{

ecology = EcologyMetric(persist_skeletons);

complexity = ComplexityMetric(persist_skeletons,
[this](skeleton_type skel){
// for (auto el : skel) {
// std::cout << el.GetSymbol();
// }
int nulls = std::count(skel.begin(), skel.end(), NULL_VAL);
// std::cout << " Nulls: " << nulls << std::endl;
return (double)(skel.size() - nulls);
});
complexity = ComplexityMetric(persist_skeletons, [this](const skeleton_type skel) {
// for (auto el : skel) {
// std::cout << el.GetSymbol();
// }
int nulls = std::count(skel.begin(), skel.end(), NULL_VAL);
// std::cout << " Nulls: " << nulls << std::endl;
return (double)(skel.size() - nulls);
});

if (do_skeletonize.size() > 100000) {
do_skeletonize.Clear();
}

}

emp::vector<double> results = emp::vector<double>({(double)change, (double)novelty, (double)ecology, (double)complexity});
Expand Down Expand Up @@ -205,24 +209,36 @@ namespace evo{
// }
//
//Convert a container of orgs to skeletons containing only informative sites
//TODO: Currently assumes bit org


emp::memo_function<skeleton_type(const ORG)> do_skeletonize = [this](const ORG org){
double fitness = fit_fun(org);
skeleton_type skeleton(org.size());
ORG test = ORG(org);
// std::cout << "Size: " << org.size() << std::endl;
for (int i = 0; i < org.size(); i++) {
test[i] = NULL_VAL;
// std::cout << "Fitness: " << fit_fun(test) << std::endl;
if (fit_fun(test) >= fitness){
skeleton[i] = NULL_VAL;
} else {
skeleton[i] = org[i];
}
test[i] = org[i];
// std::cout << "Skeleton: ";
// for (auto el : skeleton) {
// std::cout << " " << el.GetSymbol();
// }
// std::cout << std::endl;
}
return skeleton;
};

template <template <typename> class C >
C<skeleton_type> Skeletonize (C<ORG> orgs){
C<skeleton_type> skeletons;
for (auto org : orgs) {
double fitness = fit_fun(&org);
skeleton_type skeleton(org.size());
ORG test = ORG(org);

for (size_t i = 0; i < org.size(); i++) {
test[i] = NULL_VAL;
if (fit_fun(&test) >= fitness){
skeleton[i] = NULL_VAL;
} else {
skeleton[i] = org[i];
}
test[i] = org[i];
}
skeleton_type skeleton = do_skeletonize(org);
skeletons.insert(skeleton);
}
return skeletons;
Expand All @@ -231,20 +247,7 @@ namespace evo{
emp::vector<skeleton_type> Skeletonize (emp::vector<ORG> orgs){
emp::vector<skeleton_type> skeletons;
for (auto org : orgs) {
double fitness = fit_fun(&org);
skeleton_type skeleton(org.size());
ORG test = ORG(org);
for (size_t i = 0; i < org.size(); i++) {
test[i] = NULL_VAL;
double fit = fit_fun(&test);
//std::cout << i << " " << fit << " " << fitness << std::endl;
if (fit >= fitness){
skeleton[i] = NULL_VAL;
} else {
skeleton[i] = org[i];
}
test[i] = org[i];
}
skeleton_type skeleton = do_skeletonize(org);
// std::cout << "skeletonized" << std::endl;
skeletons.push_back(skeleton);
}
Expand All @@ -253,7 +256,7 @@ namespace evo{

//Find most complex skeleton in the given vector.
double ComplexityMetric(emp::vector<skeleton_type> & persist,
std::function<double(skeleton_type)> complexity_fun) {
emp::memo_function<double(skeleton_type)> complexity_fun) {

double most_complex = complexity_fun(*(persist.begin()));

Expand Down Expand Up @@ -369,6 +372,7 @@ namespace evo{
persist.push_back(id);
break;
}

id = lineage->nodes[id].parent->id;
}
}
Expand Down
3 changes: 3 additions & 0 deletions evo/PopulationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
#ifndef EMP_EVO_POPULATION_MANAGER_H
#define EMP_EVO_POPULATION_MANAGER_H

#include <map>

#include "../tools/random_utils.h"
#include "../tools/Range.h"
#include "../control/SignalManager.h"

#include "PopulationIterator.h"

Expand Down
89 changes: 89 additions & 0 deletions evo/World.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ namespace evo {
// Argument: Update number (sequentially increasing)
Signal<void(size_t)> on_update_sig;

// Trigger: Location of org about to be killed
// Argument: Pointer to organism about to die
Signal<void(int)> on_death_sig;


// Determine the callback type; by default this will be OrgSignals_NONE, but it can be
// overridden by setting the type callback_t in the organism class.
EMP_CHOOSE_MEMBER_TYPE(callback_t, callback_t, OrgSignals_NONE, ORG)
Expand Down Expand Up @@ -227,6 +232,7 @@ namespace evo {
, inject_ready_sig(to_string(w_name,"::inject-ready"), control)
, org_placement_sig(to_string(w_name,"::org-placement"), control)
, on_update_sig(to_string(w_name,"::on-update"), control)
, on_death_sig(to_string(w_name,"::on-death"), control)
, callbacks(w_name)
, world_name(w_name)
{
Expand Down Expand Up @@ -278,6 +284,7 @@ namespace evo {
SignalKey OnInjectReady(const std::function<void(ORG *)> & fun) { return inject_ready_sig.AddAction(fun); }
SignalKey OnOrgPlacement(const std::function<void(size_t)> & fun) { return org_placement_sig.AddAction(fun); }
SignalKey OnUpdate(const std::function<void(size_t)> & fun) { return on_update_sig.AddAction(fun); }
SignalKey OnOrgDeath(const std::function<void(int)> & fun) { return on_death_sig.AddAction(fun); }

// Forward any link requests to the SignalControl object.
template <typename... ARGS>
Expand Down Expand Up @@ -605,6 +612,88 @@ namespace evo {
EcoSelect(fit_fun, extra_funs, pools, t_size, tourny_count);
}

// EcoSelect works like Tournament Selection, but also uses a vector of supplimentary fitness
// functions. The best individuals on each supplemental function divide up a resource pool.
// NOTE: You must turn off the FitnessCache for this function to work properly.
void EcoSelectGradation(fit_fun_t fit_fun, const emp::vector<fit_fun_t> & extra_funs,
const emp::vector<double> & pool_sizes, size_t t_size, size_t tourny_count=1)
{
emp_assert(fit_fun);
emp_assert(t_size > 0 && t_size <= popM.size(), t_size, popM.size());
emp_assert(random_ptr != nullptr && "EcoSelect() requires active random_ptr");
emp_assert(fitM.IsCached() == false, "Ecologies mean constantly changing fitness!");

// Setup info to track fitnesses.
emp::vector<double> base_fitness(popM.size());
emp::vector< emp::vector<double> > extra_fitnesses(extra_funs.size());
emp::vector<double> max_extra_fit(extra_funs.size(), 0.0);
emp::vector<double> resource_left = pool_sizes;
emp::vector<size_t> max_count(extra_funs.size(), 0);
for (size_t i=0; i < extra_funs.size(); i++) {
extra_fitnesses[i].resize(popM.size());
}
emp::vector<size_t> ordering(popM.size());


// Collect all fitness info.
for (size_t org_id = 0; org_id < popM.size(); org_id++) {
base_fitness[org_id] = popM.CalcFitness(org_id, fit_fun);
ordering[org_id] = org_id;
for (size_t ex_id = 0; ex_id < extra_funs.size(); ex_id++) {
double cur_fit = popM.CalcFitness(org_id, extra_funs[ex_id]);
cur_fit = cur_fit;
extra_fitnesses[ex_id][org_id] = Pow2(cur_fit/32);
}
}

for (size_t ex_id = 0; ex_id < extra_funs.size(); ex_id++) {
std::sort(ordering.begin(), ordering.end(),
[&extra_fitnesses, &ex_id](int x, int y){return extra_fitnesses[ex_id][x] > extra_fitnesses[ex_id][y];});
for (size_t org_id : ordering) {
double bonus = .05 * extra_fitnesses[ex_id][org_id] * resource_left[ex_id];
extra_fitnesses[ex_id][org_id] = bonus;
resource_left[ex_id] -= bonus;
base_fitness[org_id] += bonus;
}

}

// for (size_t ex_id = 0; ex_id < extra_funs.size(); ex_id++) {
// std::cout << "Bonus " << ex_id << " = " << extra_fitnesses[ex_id]
// << std::endl;
// }


emp::vector<size_t> entries;
for (size_t T = 0; T < tourny_count; T++) {
entries.resize(0);
for (size_t i=0; i<t_size; i++) entries.push_back( popM.GetRandomOrg() ); // Allows replacement!

double best_fit = base_fitness[entries[0]];
size_t best_id = entries[0];

// Search for a higher fit org in the tournament.
for (size_t i = 1; i < t_size; i++) {
const double cur_fit = base_fitness[entries[i]];
if (cur_fit > best_fit) {
best_fit = cur_fit;
best_id = entries[i];
}
}

// Place the highest fitness into the next generation!
InsertBirth( *(popM[best_id]), best_id, 1 );
}
}

/// EcoSelect can be provided a single value if all pool sizes are identical.
void EcoSelectGradation(fit_fun_t fit_fun, const emp::vector<fit_fun_t> & extra_funs,
double pool_sizes, size_t t_size, size_t tourny_count=1)
{
emp::vector<double> pools(extra_funs.size(), pool_sizes);
EcoSelectGradation(fit_fun, extra_funs, pools, t_size, tourny_count);
}

// LexicaseSelect runs through multiple fitness functions in a random order for
// EACH offspring produced.
// NOTE: You must turn off the FitnessCache for this function to work properly.
Expand Down
9 changes: 6 additions & 3 deletions examples/evo/LineageViz.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright (C) Michigan State University, 2016-2017.
// Released under the MIT Software license; see doc/LICENSE

#include "tools/BitSet.h"
#include "../../tools/BitSet.h"
#include "tools/Random.h"

#include "web/init.h"
Expand Down Expand Up @@ -30,10 +30,13 @@
#include <functional>


emp::web::SpatialGridLineageVisualization lineage_viz(6000, 5000);
emp::web::SpatialGridTreeVisualization<> lineage_viz(10000, 10000);
emp::web::Document doc("lineage_viz");

int main() {
lineage_viz.grid_width = 60;
lineage_viz.grid_height = 60;
lineage_viz.legend_cell_size = 5;
doc << lineage_viz;
lineage_viz.LoadDataFromFile("test.json");
lineage_viz.LoadDataFromFile("lineage.json");
}
10 changes: 6 additions & 4 deletions examples/evo/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ CFLAGS_native_opt := $(CFLAGS_all) $(OFLAGS_native_opt)
CFLAGS_native_debug := $(CFLAGS_all) $(OFLAGS_native_debug)
CFLAGS_native_grumpy := $(CFLAGS_all) $(OFLAGS_native_grumpy)

CFLAGS_web_debug := $(CFLAGS_all) $(OFLAGS_web_debug) --js-library ../../web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1
CFLAGS_web_opt := $(CFLAGS_all) $(OFLAGS_web_opt) --js-library ../../web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1
#CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web) --js-library ../../web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1
CFLAGS_web_debug := $(CFLAGS_all) $(OFLAGS_web_debug) --js-library ../../web/library_emp.js --js-library ../../web/d3/library_d3.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1
CFLAGS_web_opt := $(CFLAGS_all) $(OFLAGS_web_opt) --js-library ../../web/library_emp.js --js-library ../../web/d3/library_d3.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s NO_EXIT_RUNTIME=1
CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web) --js-library ../../web/library_emp.js -s --js-library ../../web/d3/library_d3.js EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1

TARGETS := EcoEA Fitness_Share_NK Grid Lexicase link_test MultiNK NK NK-constexpr NK-const-config NK-Serial Roulette Symbulation World bit_evolve

JS_TARGETS := LineageViz.js

EVO_DEPEND := ../../evo/PopulationManager.h ../../evo/World.h

default: native
Expand Down Expand Up @@ -53,7 +55,7 @@ $(TARGETS): % : %.cc $(EVO_DEPEND)
$(CXX) $(CFLAGS_version) $(CFLAGS) $< -o $@

$(JS_TARGETS): %.js : %.cc
$(CXX_web) $(CFLAGS_web) $< -o $@
$(CXX_web) $(CFLAGS_version) $(CFLAGS_web) $< -o $@

debug-%: $*.cc $(EVO_DEPEND)
$(CXX) $(CFLAGS_version) $(CFLAGS_native_debug) $< -o $@
Expand Down
2 changes: 2 additions & 0 deletions tools/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#ifndef EMP_CONST_H
#define EMP_CONST_H

#include <cstdint>

namespace emp {

constexpr const double E = 2.71828; // e
Expand Down
2 changes: 0 additions & 2 deletions tools/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#include <ctime>
#include <functional>
#include <initializer_list>
#include <iostream>
#include <type_traits>
#include <sstream>
Expand Down Expand Up @@ -60,7 +59,6 @@ namespace emp {
template <typename T, size_t N>
constexpr size_t GetSize(T (&)[N]) { return N; }


// Build a function that will always return a unique value (and trip an assert if it can't...)
static size_t UniqueVal() {
static size_t val = 0;
Expand Down
Loading

0 comments on commit 5df10a1

Please sign in to comment.