├── src ├── global_random_generators.cpp ├── global_random_generators.h ├── Networkland.h ├── Idea.h ├── global_vector_operations.h ├── Timeline.h ├── Aether.h ├── Aether.cpp ├── global_ideas_file_reader.h ├── Networkland.cpp ├── Timeline.cpp ├── main.cpp ├── global_snap_modifications.h └── Idea.cpp ├── profiling_reminder.txt ├── .gitignore ├── .travis.yml ├── Makefile └── README.md /src/global_random_generators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::random_device rd; 4 | std::mt19937 rng(rd()); 5 | -------------------------------------------------------------------------------- /profiling_reminder.txt: -------------------------------------------------------------------------------- 1 | valgrind --tool=callgrind ./build/a.out test_data/real_graph.paj 2 | 3 | kcachegrind callgrind.out.* 4 | -------------------------------------------------------------------------------- /src/global_random_generators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern std::random_device rd; // only used once to initialise (seed) engine 8 | extern std::mt19937 rng; // random-number engine used (Mersenne-Twister in this case) 9 | 10 | inline int randunifrange(int start, int stop) { 11 | std::uniform_int_distribution uni(start, stop); 12 | return uni(rng); 13 | } 14 | 15 | inline double randunif_zero_one() { 16 | std::uniform_real_distribution uni(0, 1); 17 | return uni(rng); 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### C++ #### 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Compiled Object files 7 | *.slo 8 | *.lo 9 | *.o 10 | *.obj 11 | 12 | # Precompiled Headers 13 | *.gch 14 | *.pch 15 | 16 | # Compiled Dynamic libraries 17 | *.so 18 | *.dylib 19 | *.dll 20 | 21 | # Fortran module files 22 | *.mod 23 | *.smod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | # build directory 37 | build 38 | 39 | 40 | #### Miscellaneous #### 41 | 42 | # Text 43 | *.pdf 44 | 45 | # doxygen Documentation 46 | inst/doxygen/html/ 47 | inst/doxygen/latex/ 48 | 49 | # valgrind + callgrind 50 | callgrind.out.* 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: gcc 3 | dist: trusty 4 | 5 | before_install: 6 | # C++17 7 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 8 | # installing dependencies 9 | - sudo apt-get -qq update 10 | 11 | install: 12 | # C++17 13 | - sudo apt-get install -qq g++-6 14 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 90 15 | # snap 16 | - sudo apt-get install -y libboost-all-dev libiomp-dev zip unzip 17 | - cd .. 18 | - wget "http://snap.stanford.edu/releases/Snap-4.0.zip" 19 | - unzip Snap-4.0.zip -d snap_raw 20 | - mkdir snap 21 | - cp -r snap_raw/Snap-4.0/* snap 22 | - cd snap 23 | - make 24 | - cd .. 25 | - cd gluesless 26 | 27 | script: make 28 | -------------------------------------------------------------------------------- /src/Networkland.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Snap.h" 4 | #include "vector" 5 | #include 6 | 7 | //! Real world represented with a network - ABM environment 8 | /*! 9 | Could represent a spatial, cultural etc. network. 10 | */ 11 | 12 | class Networkland { 13 | 14 | public: 15 | Networkland(const TStr& pajek_file_path); 16 | Networkland(PUndirNet newgraph); 17 | 18 | std::vector get_neighboring_nodes(int node); 19 | void delete_nodes(int node); 20 | int get_number_of_nodes(); 21 | bool does_node_exist(int node); 22 | bool does_edge_exist(int first_node, int second_node); 23 | int get_edge_weight(int first_node, int second_node); 24 | PUndirNet get_graph(); 25 | int is_node_occupied(int node); 26 | void set_node_occupation_flag(int node); 27 | 28 | private: 29 | PUndirNet graph; 30 | }; 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC ?= gluesless 2 | 3 | BUILD_DIR ?= ./build 4 | SRC_DIRS ?= ./src 5 | 6 | LDFLAGS := ../snap/snap-core/Snap.o 7 | LDLIBS += -fopenmp 8 | 9 | SRCS := $(shell find $(SRC_DIRS) -name *.cpp) 10 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) 11 | DEPS := $(OBJS:.o=.d) 12 | 13 | INC_DIRS := $(shell find $(SRC_DIRS) -type d) 14 | INC_FLAGS := $(addprefix -I,$(INC_DIRS)) 15 | 16 | CPPFLAGS ?= $(INC_FLAGS) -MMD -MP -std=c++17 -I../snap/snap-core -I../snap/snap-adv -I../snap/glib-core -I../snap/snap-exp -Wfatal-errors 17 | 18 | $(BUILD_DIR)/$(TARGET_EXEC): $(OBJS) 19 | $(CXX) $(OBJS) -o $@ $(LDFLAGS) $(LDLIBS) 20 | 21 | # c++ source 22 | $(BUILD_DIR)/%.cpp.o: %.cpp 23 | $(MKDIR_P) $(dir $@) 24 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ 25 | 26 | 27 | .PHONY: clean 28 | 29 | clean: 30 | $(RM) -r $(BUILD_DIR) 31 | 32 | -include $(DEPS) 33 | 34 | MKDIR_P ?= mkdir -p 35 | -------------------------------------------------------------------------------- /src/Idea.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Networkland.h" 7 | #include "global_random_generators.h" 8 | 9 | //! Idea/Innovation/Meme/Memeplex - ABM agent 10 | /*! 11 | An Idea is a simple representation of a human idea or 12 | innovation. It lifes in an Aether, mutates and 13 | interacts with other ideas and a Networkland. 14 | */ 15 | 16 | class Idea { 17 | 18 | public: 19 | Idea( 20 | std::string identity, 21 | Networkland* realworld, 22 | std::vector start_nodes 23 | ); 24 | 25 | void live(); 26 | void expand(); 27 | std::vector get_all_neighboring_nodes(); 28 | std::vector select_nodes_to_convert(std::vector neighbors); 29 | std::vector get_nodes(); 30 | std::string get_identity(); 31 | int get_number_of_nodes(); 32 | int get_number_of_dead_nodes(); 33 | 34 | private: 35 | //! stores identity of an Idea 36 | std::string identity; 37 | //! reference to Networkland 38 | Networkland* realworld; 39 | //! 40 | std::vector current_nodes; 41 | //! 42 | std::vector dead_nodes; 43 | }; 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/nevrome/gluesless.svg?token=vxsQ9RjxoGASGtX4Q8jc&branch=master)](https://travis-ci.com/nevrome/gluesless) 2 | 3 | ## gluesless 4 | 5 | gluesless is an idea expansion simulation. 6 | 7 | ``` 8 | gluesless -pi -ii -o 9 | 10 | Options: 11 | -pi --pajekfile text file that describes network in .pajek format 12 | -ii --ideasfile text file that describes idea as produced by popgenerator 13 | -o --outputfile output text file 14 | -q --quiet 15 | ``` 16 | 17 | ## Installation 18 | 19 | ``` 20 | sudo apt-get install -y libboost-all-dev libiomp-dev zip unzip 21 | wget "http://snap.stanford.edu/releases/Snap-4.0.zip" 22 | unzip Snap-4.0.zip -d snap_raw 23 | mkdir snap 24 | cp -r snap_raw/Snap-4.0/* snap 25 | cd snap 26 | make 27 | cd .. 28 | git clone git@github.com:nevrome/gluesless.git 29 | cd gluesless 30 | make 31 | ``` 32 | 33 | ## Citation 34 | 35 | ``` 36 | @Manual{clemens_schmid_gluesless_2019, 37 | title = {Gluesless: An Idea Expansion Simulation}, 38 | author = {Clemens Schmid}, 39 | year = {2019} 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /src/global_vector_operations.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Snap.h" 4 | 5 | template 6 | inline std::vector remove_duplicates(std::vector &v) { 7 | std::sort(v.begin(), v.end()); 8 | auto it = std::unique(v.begin(), v.end()); // 10 20 30 20 10 ? ? ? ? 9 | v.resize(std::distance(v.begin(), it)); // 10 20 30 20 10 10 | return(v); 11 | } 12 | 13 | template 14 | inline std::vector erase_elements_of_second_vector_from_the_first(std::vector &first, std::vector &second) { 15 | first.erase( 16 | remove_if( 17 | first.begin(), first.end(), 18 | [&](auto x){ return find(second.begin(), second.end() , x) != second.end(); } 19 | ), 20 | first.end() 21 | ); 22 | return(first); 23 | } 24 | 25 | inline TIntV combine_vectors_to_TIntV(std::vector &first, std::vector &second) { 26 | TIntV new_vector; 27 | new_vector.Reserve(first.size() + second.size()); 28 | for (auto& i : first) { 29 | new_vector.Add(i); 30 | } 31 | for (auto& i : second) { 32 | new_vector.Add(i); 33 | } 34 | return(new_vector); 35 | } 36 | -------------------------------------------------------------------------------- /src/Timeline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Aether.h" 7 | 8 | //! Sequence of Aether statuses 9 | /*! 10 | A Timeline controls and documents the development of 11 | an Aether and the Ideas within it. 12 | Every Timeline is linked to an Aether. 13 | */ 14 | 15 | class Timeline { 16 | 17 | public: 18 | Timeline(Aether* overmind); 19 | 20 | //! go to the next timestep 21 | /*! 22 | Jumps from on timestep to the next by calling the Aether 23 | development function. After the development, the 24 | current status of the Aether is queried and stored. 25 | */ 26 | void develop(); 27 | 28 | int get_iteration_count(); 29 | std::vector get_graph_size_over_time(); 30 | 31 | //! export model development 32 | /*! 33 | Exports the complete model documentation in a specially 34 | prepared text file. This file contains data for every 35 | timestep. 36 | */ 37 | void export_to_text_file(std::string file_path); 38 | 39 | private: 40 | Aether* overmind; 41 | int iteration_count; 42 | std::vector graph_size_over_time; 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /src/Aether.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Idea.h" 6 | #include "Networkland.h" 7 | 8 | //! World where Idea instances exist 9 | /*! 10 | An Aether is a world, where Idea instances life and 11 | interact. The status of an Aether can be queried with 12 | multiple getter functions. 13 | Every Aether is linked to a Networkland. 14 | */ 15 | 16 | class Aether { 17 | 18 | public: 19 | Aether(Networkland* real); 20 | Aether(Networkland* real, std::string ideas_file_path); 21 | 22 | //! go to the next timestep 23 | /*! 24 | Develops the Aether from one timestep to the next. This function controls 25 | the major developement steps like creation, growth, splitting and death of 26 | Ideas. 27 | */ 28 | void develop(); 29 | 30 | void add_idea_to_mindspace(Idea* new_idea); 31 | int get_current_graph_size(); 32 | int get_number_of_nodes_in_graph(); 33 | std::vector get_mindspace(); 34 | 35 | private: 36 | //! vector that stores references to Idea instances 37 | std::vector mindspace; 38 | //! reference to Networkland 39 | Networkland* realworld; 40 | }; 41 | -------------------------------------------------------------------------------- /src/Aether.cpp: -------------------------------------------------------------------------------- 1 | #include "Aether.h" 2 | 3 | #include "global_ideas_file_reader.h" 4 | 5 | Aether::Aether(Networkland* real) { 6 | this->realworld = real; 7 | } 8 | 9 | Aether::Aether(Networkland* real, std::string ideas_file_path) { 10 | this->realworld = real; 11 | this->mindspace = ideas_file_to_ideas_vector(ideas_file_path, real); 12 | } 13 | 14 | void Aether::develop() { 15 | 16 | // let every idea act in random order 17 | auto& v = this->mindspace; 18 | std::shuffle(v.begin(), v.end(), rng); 19 | 20 | // find weaker idea 21 | // std::vector number_of_nodes; 22 | // for (auto& current_idea : v) { 23 | // number_of_nodes.push_back(current_idea->get_number_of_dead_nodes()); 24 | // } 25 | // auto minIt = std::min_element(number_of_nodes.begin(), number_of_nodes.end()); 26 | // Idea* weaker_idea = v[*minIt]; 27 | 28 | // let ideas live 29 | for (auto& current_idea : v) { 30 | current_idea->live(); 31 | // if (current_idea == weaker_idea) { //&& 50 > randunifrange(0, 100)) { 32 | // current_idea->live(); 33 | // } 34 | } 35 | 36 | } 37 | 38 | void Aether::add_idea_to_mindspace(Idea* new_idea) { 39 | this->mindspace.push_back(new_idea); 40 | } 41 | 42 | int Aether::get_current_graph_size() { 43 | return(this->realworld->get_number_of_nodes()); 44 | } 45 | 46 | int Aether::get_number_of_nodes_in_graph() { 47 | return(this->realworld->get_number_of_nodes()); 48 | } 49 | 50 | std::vector Aether::get_mindspace() { 51 | return(this->mindspace); 52 | } 53 | -------------------------------------------------------------------------------- /src/global_ideas_file_reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Idea.h" 10 | 11 | inline void split(const std::string& s, char c, std::vector& v) { 12 | int i = 0; 13 | int j = s.find(c); 14 | while (j >= 0) { 15 | v.push_back(s.substr(i, j-i)); 16 | i = ++j; 17 | j = s.find(c, j); 18 | 19 | if (j < 0) { 20 | v.push_back(s.substr(i, s.length())); 21 | } 22 | } 23 | } 24 | 25 | inline void read_delim(std::string file_path, std::vector*>& data, char delimiter) { 26 | 27 | std::ifstream in(file_path); 28 | std::vector* p = NULL; 29 | std::string tmp; 30 | 31 | while (!in.eof()) { 32 | std::getline(in, tmp, '\n'); 33 | 34 | p = new std::vector(); 35 | split(tmp, delimiter, *p); 36 | 37 | data.push_back(p); 38 | 39 | //std::cout << tmp << '\n'; 40 | tmp.clear(); 41 | } 42 | 43 | data.erase(data.end() - 1); 44 | 45 | } 46 | 47 | inline std::vector ideas_file_to_ideas_vector(std::string ideas_file_path, Networkland* real) { 48 | 49 | std::vector*> data; 50 | 51 | read_delim(ideas_file_path, data, ';'); 52 | 53 | std::vector mindspace; 54 | for(auto& p : data) { 55 | std::stringstream starting_nodes_string(p->at(1)); 56 | int number; 57 | std::vector starting_nodes; 58 | while (starting_nodes_string >> number) { starting_nodes.push_back( number ); } 59 | 60 | Idea* new_idea = new Idea(p->at(0), real, starting_nodes); 61 | mindspace.push_back(new_idea); 62 | } 63 | 64 | return(mindspace); 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/Networkland.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Networkland.h" 4 | #include 5 | 6 | #include "global_snap_modifications.h" 7 | 8 | Networkland::Networkland(const TStr& pajek_file_path) { 9 | this->graph = TSnap::pajek_file_to_PUndirNet(pajek_file_path); 10 | } 11 | 12 | Networkland::Networkland(PUndirNet newgraph) { 13 | this->graph = newgraph; 14 | } 15 | 16 | std::vector Networkland::get_neighboring_nodes(int node) { 17 | const TUndirNet::TNodeI nodei = this->graph->GetNI(node); 18 | int amount_of_neighbors = nodei.GetDeg(); 19 | std::vector neighboring_nodes; 20 | neighboring_nodes.reserve(amount_of_neighbors); 21 | for (int i = 0; i < amount_of_neighbors; i++) { 22 | neighboring_nodes.push_back(nodei.GetNbrNId(i)); 23 | } 24 | return(neighboring_nodes); 25 | } 26 | 27 | int Networkland::get_number_of_nodes() { 28 | return(this->graph->GetNodes()); 29 | } 30 | 31 | void Networkland::delete_nodes(int node) { 32 | this->graph->DelNode(node); 33 | } 34 | 35 | bool Networkland::does_node_exist(int node) { 36 | return(this->graph->IsNode(node)); 37 | } 38 | 39 | bool Networkland::does_edge_exist(int first_node, int second_node) { 40 | return(this->graph->IsEdge(first_node, second_node)); 41 | } 42 | 43 | int Networkland::get_edge_weight(int first_node, int second_node) { 44 | TInt a; 45 | this->graph->GetSAttrDatE(first_node, second_node, "weight", a); 46 | return((int) a); 47 | } 48 | 49 | PUndirNet Networkland::get_graph() { 50 | return(this->graph); 51 | } 52 | 53 | int Networkland::is_node_occupied(int node){ 54 | TInt a; 55 | this->graph->GetSAttrDatN(node, "occupied", a); 56 | return((int) a); 57 | } 58 | 59 | void Networkland::set_node_occupation_flag(int node){ 60 | this->graph->AddSAttrDatN( (TInt) node, (TStr) "occupied", (TInt) 1); 61 | } 62 | -------------------------------------------------------------------------------- /src/Timeline.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Timeline.h" 6 | 7 | Timeline::Timeline(Aether* overmind) { 8 | this->overmind = overmind; 9 | this->iteration_count = 0; 10 | this->graph_size_over_time.push_back(this->overmind->get_number_of_nodes_in_graph()); 11 | } 12 | 13 | void Timeline::develop() { 14 | 15 | // develop Aether 16 | this->overmind->develop(); 17 | 18 | // store information for every time step 19 | this->iteration_count += 1; 20 | this->graph_size_over_time.push_back(this->overmind->get_number_of_nodes_in_graph()); 21 | 22 | } 23 | 24 | int Timeline::get_iteration_count() { 25 | return(this->iteration_count); 26 | } 27 | 28 | std::vector Timeline::get_graph_size_over_time() { 29 | return(this->graph_size_over_time); 30 | } 31 | 32 | void Timeline::export_to_text_file(std::string file_path) { 33 | 34 | // remove old result file 35 | std::remove(file_path.c_str()); 36 | 37 | // open a file in write mode. 38 | std::ofstream outfile; 39 | outfile.open(file_path); 40 | 41 | // Number of iterations 42 | outfile << "Number of iterations:" << std::endl; 43 | outfile << this->iteration_count << std::endl << std::endl; 44 | 45 | // Number of remaining nodes after this iteration 46 | outfile << "Number of remaining nodes after this iteration:" << std::endl; 47 | for (auto& i : this->graph_size_over_time) { 48 | outfile << i << " "; 49 | } 50 | outfile << std::endl << std::endl; 51 | 52 | // Nodes per Idea 53 | outfile << "Nodes per Idea:" << std::endl; 54 | std::vector all_ideas = overmind->get_mindspace(); 55 | for (auto& p1 : all_ideas) { 56 | outfile << p1->get_identity() << std::endl; 57 | std::vector all_nodes_of_idea = p1->get_nodes(); 58 | for (auto& p2 : all_nodes_of_idea) { 59 | outfile << p2 << " "; 60 | } 61 | outfile << std::endl; 62 | } 63 | outfile << std::endl; 64 | 65 | // close connection to file 66 | outfile.close(); 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Snap.h" 10 | 11 | #include "Timeline.h" 12 | 13 | //! main 14 | /*! 15 | main method 16 | */ 17 | 18 | int main(int argc, char* argv[]){ 19 | 20 | // manage input arguments 21 | TStr pajek_file_path; 22 | std::string ideas_file_path; 23 | std::string output_file_path; 24 | bool quiet = false; 25 | 26 | for (int i = 1; i < argc; i++) { 27 | if (strcmp(argv[i], "--pajekfile") == 0 | strcmp(argv[i], "-pi") == 0) { 28 | pajek_file_path = argv[i + 1]; 29 | i++; 30 | } 31 | if (strcmp(argv[i], "--ideasfile") == 0 | strcmp(argv[i], "-ii") == 0) { 32 | ideas_file_path = argv[i + 1]; 33 | i++; 34 | } 35 | if (strcmp(argv[i], "--outputfile") == 0 | strcmp(argv[i], "-o") == 0) { 36 | output_file_path = argv[i + 1]; 37 | i++; 38 | } 39 | if (strcmp(argv[i], "--quiet") == 0 | strcmp(argv[i], "-q") == 0) { 40 | quiet = true; 41 | } 42 | } 43 | 44 | // create world 45 | Networkland* real = new Networkland(pajek_file_path); 46 | Aether* overmind = new Aether(real, ideas_file_path); 47 | Timeline* thyme = new Timeline(overmind); 48 | 49 | // develop 50 | int graph_size; 51 | while (true) { 52 | 53 | thyme->develop(); 54 | 55 | int new_graph_size = thyme->get_graph_size_over_time().back(); 56 | if (graph_size == new_graph_size) { 57 | break; 58 | } 59 | graph_size = new_graph_size; 60 | 61 | if (!quiet) { 62 | printf("remaining nodes: "); 63 | printf(std::to_string(graph_size).c_str()); 64 | printf(" "); 65 | printf("\r"); 66 | fflush(stdout); 67 | } 68 | 69 | } 70 | 71 | // file output 72 | thyme->export_to_text_file(output_file_path); 73 | 74 | // console output 75 | if (!quiet) { 76 | printf("remaining nodes: "); 77 | printf(std::to_string(thyme->get_graph_size_over_time().back()).c_str()); 78 | printf(" (final)"); 79 | printf("\n"); 80 | } 81 | 82 | // cleaning up 83 | delete real; 84 | delete overmind; 85 | delete thyme; 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /src/global_snap_modifications.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace TSnap { 4 | 5 | inline PUndirNet pajek_file_to_PUndirNet(const TStr& InFNm) { 6 | PUndirNet Graph = PUndirNet::New(); 7 | TSsParser Ss(InFNm, ssfSpaceSep, true, true, true); 8 | while ((Ss.Len()==0 || strstr(Ss[0], "*vertices") == NULL) && ! Ss.Eof()) { 9 | Ss.Next(); Ss.ToLc(); 10 | } 11 | // nodes 12 | bool EdgeList = true; 13 | EAssert(strstr(Ss[0], "*vertices") != NULL); 14 | while (Ss.Next()) { 15 | Ss.ToLc(); 16 | if (Ss.Len()>0 && Ss[0][0] == '%') { continue; } // comment 17 | if (strstr(Ss[0], "*arcslist")!=NULL || strstr(Ss[0],"*edgeslist")!=NULL) { EdgeList=false; break; } 18 | if (strstr(Ss[0], "*arcs")!=NULL || strstr(Ss[0],"*edges")!=NULL) { break; } // arcs are directed, edges are undirected 19 | Graph->AddNode(Ss.GetInt(0)); 20 | // Node attributes 21 | const TInt& time = Ss.GetInt(1); 22 | Graph->TUndirNet::AddSAttrDatN(Ss.GetInt(0), "time", time); 23 | const TInt& social = Ss.GetInt(2); 24 | Graph->TUndirNet::AddSAttrDatN(Ss.GetInt(0), "social", social); 25 | } 26 | // edges 27 | while (Ss.Next()) { 28 | if (Ss.Len()>0 && Ss[0][0] == '%') { continue; } // comment 29 | if (Ss.Len()>0 && Ss[0][0] == '*') { break; } 30 | if (EdgeList) { 31 | // [ ] 32 | if (Ss.Len() >= 2 && Ss.IsInt(0) && Ss.IsInt(1) && (const TInt&)Ss.IsInt(2)) { 33 | Graph->AddEdge(Ss.GetInt(0), Ss.GetInt(1)); 34 | // Edge attributes 35 | const TInt& value = Ss.GetInt(2); 36 | Graph->TUndirNet::AddSAttrDatE(Ss.GetInt(0), Ss.GetInt(1), "weight", value); 37 | } 38 | } 39 | } 40 | return Graph; 41 | } 42 | 43 | inline PUndirNet get_subgraph_PUndirNet(const PUndirNet& Graph, const TIntV& NIdV) { 44 | //if (! RenumberNodes) { return TSnap::GetSubGraph(Graph, NIdV); } 45 | PUndirNet NewGraphPt = TUndirNet::New(); 46 | TUndirNet& NewGraph = *NewGraphPt; 47 | NewGraph.Reserve(NIdV.Len(), -1); 48 | TIntSet NIdSet(NIdV.Len()); 49 | for (int n = 0; n < NIdV.Len(); n++) { 50 | if (Graph->IsNode(NIdV[n])) { 51 | NIdSet.AddKey(NIdV[n]); 52 | NewGraph.AddNode(NIdV[n]); 53 | } 54 | } 55 | for (int n = 0; n < NIdSet.Len(); n++) { 56 | const int SrcNId = NIdSet[n]; 57 | const TUndirNet::TNodeI NI = Graph->GetNI(SrcNId); 58 | for (int edge = 0; edge < NI.GetOutDeg(); edge++) { 59 | const int OutNId = NI.GetOutNId(edge); 60 | if (NIdSet.IsKey(OutNId)) { 61 | NewGraph.AddEdge(SrcNId, OutNId); 62 | TInt a; 63 | Graph->TUndirNet::GetSAttrDatE(SrcNId, OutNId, "weight", a); 64 | NewGraph.TUndirNet::AddSAttrDatE(SrcNId, OutNId, "weight", a); 65 | } 66 | } 67 | } 68 | return NewGraphPt; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/Idea.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Idea.h" 6 | #include "global_vector_operations.h" 7 | #include "global_snap_modifications.h" 8 | 9 | Idea::Idea( 10 | std::string identity, 11 | Networkland* realworld, 12 | std::vector start_nodes 13 | ){ 14 | this->current_nodes.reserve(1000); 15 | this->dead_nodes.reserve(100000); 16 | 17 | this->identity = identity; 18 | this->realworld = realworld; 19 | this->current_nodes = start_nodes; 20 | } 21 | 22 | void Idea::live() { 23 | this->expand(); 24 | } 25 | 26 | void Idea::expand() { 27 | 28 | // get all neighboring nodes 29 | std::vector all_neighbors = this->get_all_neighboring_nodes(); 30 | // remove duplicates from neighbors 31 | std::vector neighbors_without_doubles = remove_duplicates(all_neighbors); 32 | // remove the current nodes from neighbors 33 | std::vector neighbors = erase_elements_of_second_vector_from_the_first( 34 | neighbors_without_doubles, this->current_nodes 35 | ); 36 | 37 | // make decision, which neighbors can be converted 38 | std::vector converted = this->select_nodes_to_convert(neighbors); 39 | 40 | // delete current nodes from graph 41 | for (auto& i : this->current_nodes) { 42 | if(this->realworld->does_node_exist(i)) { 43 | this->realworld->delete_nodes(i); 44 | this->dead_nodes.push_back(i); 45 | } 46 | } 47 | 48 | // forget current nodes and make neighbors new current nodes 49 | this->current_nodes.clear(); 50 | this->current_nodes.insert(this->current_nodes.end(), converted.begin(), converted.end()); 51 | 52 | // set occupation flag for all new nodes 53 | for(auto& new_node : this->current_nodes) { 54 | this->realworld->set_node_occupation_flag(new_node); 55 | } 56 | 57 | } 58 | 59 | std::vector Idea::get_all_neighboring_nodes() { 60 | 61 | std::vector neighbors; 62 | neighbors.reserve(1000); 63 | for (auto& i : this->current_nodes) { 64 | if(this->realworld->does_node_exist(i)) { 65 | std::vector new_neighbors = this->realworld->get_neighboring_nodes(i); 66 | neighbors.insert(neighbors.end(), new_neighbors.begin(), new_neighbors.end()); 67 | } 68 | } 69 | 70 | return(neighbors); 71 | 72 | } 73 | 74 | std::vector Idea::select_nodes_to_convert(std::vector neighbors) { 75 | 76 | TIntV all_nodes_involved = combine_vectors_to_TIntV(neighbors, this->current_nodes); 77 | PUndirNet small_subgraph = TSnap::get_subgraph_PUndirNet(this->realworld->get_graph(), all_nodes_involved); 78 | 79 | // determine connection number, mean edge weight and occupation state per neighbor 80 | std::vector> all_neighbors_information(neighbors.size()); 81 | for (auto& p1 : neighbors) { 82 | int number_of_edges = 0; 83 | int max_weight = 0; 84 | bool occuppied = false; 85 | 86 | TInt b; 87 | small_subgraph->GetSAttrDatN(p1, "occupied", b); 88 | occuppied = (bool) b; 89 | 90 | for (auto& p2 : this->current_nodes) { 91 | if(small_subgraph->IsEdge(p1, p2)) { 92 | number_of_edges += 1; 93 | // weight_per_edge += this->realworld->get_edge_weight(p1, p2); 94 | TInt a; 95 | small_subgraph->GetSAttrDatE(p1, p2, "weight", a); 96 | //weight_per_edge += (int) a; 97 | if ( (double) a > max_weight) { 98 | max_weight = (double) a; 99 | } 100 | } 101 | } 102 | std::tuple one_neighbors_information = std::make_tuple( 103 | p1, max_weight, number_of_edges, occuppied 104 | ); 105 | all_neighbors_information.push_back(one_neighbors_information); 106 | } 107 | 108 | // make random decision to convert or ignore a node based on the edge weight 109 | std::vector> success_per_neighbor(neighbors.size()); 110 | for (auto& i : all_neighbors_information) { 111 | // get variables from tuple 112 | double max_weight = std::get<1>(i); 113 | int number_of_edges = std::get<2>(i); 114 | bool occupied = std::get<3>(i); 115 | // if the node is already occupied, it's more difficult 116 | double resistance; 117 | if (occupied) { resistance = randunifrange(50, 100); } else { resistance = randunifrange(0, 50); } 118 | // if more than one contact, then there's a convincing bonus 119 | double bonus = log10((double) number_of_edges); 120 | double group_effect_multiplier = 1 + bonus; 121 | double power = max_weight * group_effect_multiplier; 122 | // make decision 123 | std::pair success; 124 | success = std::make_pair(std::get<0>(i), power > resistance); 125 | // store decision 126 | success_per_neighbor.push_back(success); 127 | } 128 | 129 | // collect converted nodes into a result vector 130 | std::vector converted; 131 | converted.reserve(neighbors.size()); 132 | for (auto& i : success_per_neighbor) { 133 | if (i.second) { 134 | converted.push_back(i.first); 135 | } 136 | } 137 | 138 | return(converted); 139 | 140 | } 141 | 142 | std::vector Idea::get_nodes() { 143 | 144 | auto& dn = this->dead_nodes; 145 | auto& cn = this->current_nodes; 146 | dn.insert(dn.end(), cn.begin(), cn.end()); 147 | 148 | return(dn); 149 | 150 | } 151 | 152 | std::string Idea::get_identity() { 153 | return(this->identity); 154 | } 155 | 156 | int Idea::get_number_of_nodes() { 157 | return(this->current_nodes.size()); 158 | } 159 | 160 | int Idea::get_number_of_dead_nodes() { 161 | return(this->dead_nodes.size()); 162 | } 163 | 164 | --------------------------------------------------------------------------------