source: src/Actions/GraphAction/ChemicalSpaceEvaluatorAction.cpp@ 7f17c7

Last change on this file since 7f17c7 was 7f17c7, checked in by Frederik Heber <frederik.heber@…>, 7 years ago

ChemicalSpaceEvaluator working for graph with 2 nodes.

  • Property mode set to 100644
File size: 19.8 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2017 Frederik Heber. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * ChemicalSpaceEvaluator.cpp
25 *
26 * Created on: Sep 26, 2017
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35//#include "CodePatterns/MemDebug.hpp"
36
37#include <iostream>
38#include <list>
39#include <map>
40#include <string>
41#include <vector>
42
43#include <boost/graph/copy.hpp>
44
45#include "CodePatterns/Log.hpp"
46
47#include "Actions/GraphAction/ChemicalSpaceEvaluatorAction.hpp"
48
49#include "Element/element.hpp"
50#include "Graph/BoostGraphCreator.hpp"
51#include "Fragmentation/Homology/HomologyContainer.hpp"
52#include "FunctionApproximation/Extractors.hpp"
53#include "World.hpp"
54
55#include <boost/graph/adjacency_list.hpp>
56
57using namespace MoleCuilder;
58
59// and construct the stuff
60#include "ChemicalSpaceEvaluatorAction.def"
61#include "Action_impl_pre.hpp"
62/** =========== define the function ====================== */
63template<class map_t, class key_t>
64static int getElementFromMap(
65 const map_t &_map,
66 const key_t &_keyname)
67{
68 typename map_t::const_iterator iter = _map.find(_keyname);
69 ASSERT( iter != _map.end(),
70 "getElementFromMap() - cannot find key "+toString(_keyname)+" in map.");
71 return iter->second;
72}
73
74typedef std::pair<BoostGraphCreator::Vertex, BoostGraphCreator::Vertex> edge_by_vertices_t;
75typedef std::map<edge_by_vertices_t, int> edge_valence_t;
76typedef std::vector<edge_by_vertices_t> edges_t;
77typedef std::vector<int> degrees_t;
78typedef std::list< degrees_t > list_of_edge_degrees_t;
79typedef std::map< unsigned int, int> type_valence_map_t;
80typedef std::map< edge_by_vertices_t , size_t> edge_index_t;
81
82static int powerset_edge_degree(
83 list_of_edge_degrees_t &list_of_edge_degrees,
84 degrees_t &current_degrees,
85 const size_t remaining_edges,
86 const size_t current_index,
87 const edges_t &edges,
88 const edge_valence_t &edge_valence)
89{
90 if (remaining_edges == 0) {
91 list_of_edge_degrees.push_back(current_degrees);
92 LOG(3, "DEBUG: Adding [" << current_degrees << "] list of degrees.");
93 return 1;
94 } else {
95 size_t no_combinations = 0;
96 // get valence
97 const edge_by_vertices_t &e = edges[current_index];
98 const int current_max_degree = getElementFromMap<
99 edge_valence_t,
100 edge_by_vertices_t>(edge_valence, e);
101 // create all combinations and recurse
102 for(current_degrees[current_index] = 1;
103 current_degrees[current_index] <= current_max_degree;
104 ++current_degrees[current_index]) {
105 no_combinations += powerset_edge_degree(
106 list_of_edge_degrees, current_degrees,
107 remaining_edges-1, current_index+1,
108 edges, edge_valence);
109 }
110 return no_combinations;
111 }
112}
113
114static int getValenceForVertex(
115 const BoostGraphCreator::name_map_t &name_map,
116 const Extractors::type_index_lookup_t &type_index_lookup,
117 const type_valence_map_t &type_valence_map,
118 const BoostGraphCreator::Vertex &v
119 )
120{
121 const atomId_t atomid_v = boost::get(name_map, v);
122 const Extractors::type_index_lookup_t::left_const_iterator iter_v = type_index_lookup.left.find(atomid_v);
123 ASSERT( iter_v != type_index_lookup.left.end(),
124 "getValenceForVertex() - cannot find "+toString(atomid_v));
125 const unsigned int elementno_v = iter_v->second;
126 return getElementFromMap<type_valence_map_t, unsigned int>(type_valence_map, elementno_v);
127}
128
129static edge_by_vertices_t getEdgeByVertices(
130 const BoostGraphCreator::Vertex& u,
131 const BoostGraphCreator::Vertex& v)
132{
133 if (u < v)
134 return std::make_pair(u,v);
135 else
136 return std::make_pair(v,u);
137}
138
139static edge_by_vertices_t getEdgeVerticesByEdge(
140 const BoostGraphCreator::Edge& e,
141 const BoostGraphCreator::UndirectedGraph &graph)
142{
143 const BoostGraphCreator::Vertex u = source(e, graph);
144 const BoostGraphCreator::Vertex v = source(e, graph);
145 return getEdgeByVertices(u,v);
146}
147
148
149struct SaturatedGraph
150{
151 //!> states whether each node fulfills the octet rule, i.e. has bonds weight by their degree equal to its valence
152 bool ValencesAllFulfilled;
153 //!> graph saturated with additional hydrogens
154 BoostGraphCreator::UndirectedGraph saturated_graph;
155 //!> type per edge in the graph
156 Extractors::type_index_lookup_t type_index_lookup;
157};
158
159template <class graph_type>
160static SaturatedGraph saturateGraph(
161 const graph_type &graph,
162 const Extractors::nodes_t &nodes,
163 const BoostGraphCreator::name_map_t &name_map,
164 const Extractors::type_index_lookup_t &type_index_lookup,
165 const type_valence_map_t &type_valence_map,
166 const edge_index_t &edge_index,
167 const edges_t &edges,
168 const degrees_t &degrees
169)
170{
171 SaturatedGraph newgraph;
172 boost::copy_graph(graph, newgraph.saturated_graph);
173 newgraph.ValencesAllFulfilled = true;
174
175 // need to translate type_index_lookup as Extractors::createHomologyKey..() only
176 // looks at the vertex index, not its name. When extracting the subgraph, we
177 // might extract node #1, but insert it as new node index #0. Therefore, we have
178 // to translate all indices in the lookup
179 const Extractors::index_map_t index_map = boost::get(boost::vertex_index, graph);
180 {
181 BoostGraphCreator::vertex_iter vp, vpend;
182 for (boost::tie(vp, vpend) = vertices(graph); vp != vpend; ++vp) {
183 BoostGraphCreator::Vertex v = *vp;
184 const atomId_t vindex = boost::get(name_map, v);
185 Extractors::type_index_lookup_t::left_const_iterator iter = type_index_lookup.left.find(vindex);
186 if (iter != type_index_lookup.left.end()) {
187 const Extractors::node_t nodeid = boost::get(index_map, v);
188 newgraph.type_index_lookup.left.insert( std::make_pair(nodeid, iter->second) );
189 LOG(4, "DEBUG: Adding type to index lookup for vertex " << iter->first);
190 }
191 }
192 }
193
194 size_t no_nodes = boost::num_vertices(newgraph.saturated_graph);
195 // step through every node, sum up its degrees
196 BoostGraphCreator::vertex_iter vp, vpend;
197 for (boost::tie(vp, vpend) = vertices(graph); vp != vpend; ++vp) {
198 BoostGraphCreator::Vertex v = *vp;
199 const atomId_t vindex = boost::get(name_map, v);
200 LOG(3, "DEBUG: Current vertex is #" << vindex);
201 const int max_valence = getValenceForVertex(name_map, type_index_lookup, type_valence_map, v);
202 int total_valence = 0;
203 typename boost::graph_traits<graph_type>::out_edge_iterator i, end;
204 for (boost::tie(i, end) = boost::out_edges(v, graph); i != end; ++i) {
205 const BoostGraphCreator::Edge &e = *i;
206 const BoostGraphCreator::Vertex e1 = source(e, graph);
207 const BoostGraphCreator::Vertex e2 = target(e, graph);
208 const edge_by_vertices_t edge_by_vertices = getEdgeByVertices(e1,e2);
209 LOG(3, "DEBUG: Current edge is (" << edge_by_vertices << ")");
210 const size_t index = getElementFromMap<edge_index_t, edge_by_vertices_t>(
211 edge_index, edge_by_vertices);
212 ASSERT( index < edges.size(),
213 "saturateGraph() - index "+toString(index)+" out of range [0, "
214 +toString(edges.size())+"]");
215 total_valence += degrees[index];
216 }
217 LOG(3, "DEBUG: Vertex #" << vindex << " has total valence of "
218 << total_valence << " and " << max_valence << " max valence.");
219 // add hydrogens till valence is depleted
220 newgraph.ValencesAllFulfilled &= (total_valence <= max_valence);
221 for (int remaining_valence = total_valence; remaining_valence < max_valence;
222 ++remaining_valence) {
223 // add hydrogen node to this node
224 LOG(4, "DEBUG: Adding node " << no_nodes << " with type " << Extractors::ParticleType_t(1));
225 newgraph.type_index_lookup.left.insert(
226 std::make_pair(no_nodes, Extractors::ParticleType_t(1)));
227 BoostGraphCreator::Vertex h = boost::add_vertex(no_nodes, newgraph.saturated_graph);
228 ++no_nodes;
229
230 // add edge to hydrogen
231 std::pair<BoostGraphCreator::Edge, bool> edge_inserter =
232 boost::add_edge(v, h, newgraph.saturated_graph);
233 }
234 LOG(2, "DEBUG: Added " << (max_valence - total_valence)
235 << " hydrogens to vertex #" << vindex);
236 }
237
238 return newgraph;
239}
240
241template <typename graph_t>
242BoostGraphCreator::UndirectedGraph extractSubgraph(
243 const graph_t &_graph,
244 const Extractors::nodes_t &nodes)
245{
246 BoostGraphCreator::UndirectedGraph subgraph;
247 const Extractors::index_map_t index_map = boost::get(boost::vertex_index, _graph);
248
249 // add nodes
250 BoostGraphCreator::vertex_iter viter, vend;
251 for (boost::tie(viter, vend) = boost::vertices(_graph); viter != vend; ++viter) {
252 const size_t nodeid = boost::get(index_map, *viter);
253 if (nodes.find(nodeid) != nodes.end()) {
254 boost::add_vertex(*viter, subgraph);
255 LOG(4, "DEBUG: Adding node " << *viter << " to subgraph.");
256 }
257 }
258
259 // add edges
260 for (boost::tie(viter, vend) = boost::vertices(_graph); viter != vend; ++viter) {
261 BoostGraphCreator::UndirectedGraph::in_edge_iterator i, end;
262 for (boost::tie(i, end) = boost::in_edges(boost::vertex(*viter, _graph), _graph);
263 i != end; ++i) {
264 const Extractors::node_t sourceindex = boost::get(index_map, boost::source(*i, _graph));
265 const Extractors::node_t targetindex = boost::get(index_map, boost::target(*i, _graph));
266 if ((nodes.find(sourceindex) != nodes.end()) && (nodes.find(targetindex) != nodes.end())
267 && (sourceindex < targetindex)) {
268 boost::add_edge(sourceindex, targetindex, subgraph);
269 LOG(4, "DEBUG: Adding edge " << sourceindex << "<->" << targetindex << " to subgraph.");
270 }
271 }
272 }
273
274 return subgraph;
275}
276
277
278ActionState::ptr GraphChemicalSpaceEvaluatorAction::performCall() {
279 /// 1. create boost::graph from graph6 string
280 BoostGraphCreator BGC;
281 BGC.createFromGraph6String(params.graph_string.get());
282
283 BoostGraphCreator::UndirectedGraph graph = BGC.get();
284
285 Extractors::index_map_t index_map = boost::get(boost::vertex_index, graph);
286 LOG(1, "INFO: We have " << boost::num_vertices(graph) << " nodes in the fragment graph.");
287
288 /// 2. associate type with a node index
289 Extractors::type_index_lookup_t type_index_lookup;
290 std::vector<unsigned int> elementnumbers;
291 elementnumbers.reserve(params.graph_elements.get().size());
292 size_t i = 0;
293 for (std::vector<const element *>::const_iterator iter = params.graph_elements.get().begin();
294 iter != params.graph_elements.get().end(); ++iter) {
295 elementnumbers.push_back((*iter)->getAtomicNumber());
296 LOG(3, "DEBUG: Adding [" << i << ", " << elementnumbers.back() << "] type to index lookup.");
297 type_index_lookup.left.insert(
298 std::make_pair(i++, Extractors::ParticleType_t(elementnumbers.back())));
299 }
300 if (i != boost::num_vertices(graph)) {
301 ELOG(1, "Not enough elements given.");
302 return Action::failure;
303 }
304
305 /// 3a. get range of valence for each node
306 type_valence_map_t type_valence_map;
307 for (std::vector<const element *>::const_iterator iter = params.graph_elements.get().begin();
308 iter != params.graph_elements.get().end(); ++iter) {
309 if (type_valence_map.count((*iter)->getAtomicNumber()) == 0) {
310 LOG(3, "DEBUG: Adding [" << (*iter)->getAtomicNumber()
311 << ", " << (*iter)->getNoValenceOrbitals() << "] type to valence lookup.");
312 type_valence_map.insert( std::make_pair(
313 (*iter)->getAtomicNumber(),
314 (*iter)->getNoValenceOrbitals()) );
315 }
316 }
317
318 // get map for retrieving vertex index for a given vertex
319 const BoostGraphCreator::name_map_t name_map = boost::get(boost::vertex_name, graph);
320
321 /// 3b. generate range of degree for each edge
322 edge_valence_t edge_valence;
323 edges_t edges;
324 edge_index_t edge_index;
325 edges.reserve(boost::num_edges(graph));
326 {
327 size_t index = 0;
328 BoostGraphCreator::edge_iter i, end;
329 for (boost::tie(i, end) = boost::edges(graph); i != end; ++i) {
330 static int MAX_DEGREE = 3;
331 const BoostGraphCreator::Edge &e = *i;
332 /// 4a. place all edges into a vector
333 const BoostGraphCreator::Vertex u = source(e, graph);
334 const BoostGraphCreator::Vertex v = target(e, graph);
335 const edge_by_vertices_t edge_by_vertices = getEdgeByVertices(u,v);
336 edges.push_back(edge_by_vertices);
337 edge_index.insert( std::make_pair(
338 edge_by_vertices, index++) );
339 const int valence_u = getValenceForVertex(name_map, type_index_lookup, type_valence_map, u);
340 const int valence_v = getValenceForVertex(name_map, type_index_lookup, type_valence_map, v);
341 const int max_degree = std::max(1,
342 std::min(
343 std::min(valence_u, MAX_DEGREE),
344 std::min(valence_v, MAX_DEGREE)
345 ));
346 edge_valence.insert(
347 std::make_pair(edge_by_vertices, max_degree)
348 );
349 LOG(3, "DEBUG: Adding [" << std::make_pair(u,v) << ", "
350 << max_degree << "] edge to valence lookup.");
351 }
352 }
353
354 /// 4. for each graph with type per node and degree per edge set, add hydrogen nodes and edges
355 // go through powerset over all edges
356
357 /// 4b. have a recursive function that puts together the powerset over all max_degree
358 list_of_edge_degrees_t list_of_edge_degrees;
359 size_t no_combinations = 0;
360 {
361 size_t remaining_edges = edges.size();
362 size_t current_index = 0;
363 degrees_t current_degrees(edges.size(), 0);
364 no_combinations = powerset_edge_degree(
365 list_of_edge_degrees, current_degrees,
366 remaining_edges, current_index,
367 edges, edge_valence);
368 }
369 LOG(1, "INFO: Added " << no_combinations << " graph degree combinations.");
370
371 /// 5. dissect graph into a list of subgraphs
372 typedef std::vector<BoostGraphCreator::UndirectedGraph> subgraphs_t;
373 subgraphs_t allsubgraphs;
374 const size_t nodes_in_graph = boost::num_vertices(graph);
375 LOG(2, "DEBUG: There are " << nodes_in_graph << " nodes in the graph.");
376 Extractors::set_of_nodes_t set_of_nodes;
377 for (size_t nodeid = 0; nodeid < nodes_in_graph; ++nodeid) {
378 const size_t &rootindex = nodeid;
379 LOG(2, "DEBUG: Current root index is " << rootindex);
380
381 /// 5a. from node in graph (with this index) perform BFS till n-1 (#nodes in binding model)
382 std::vector<size_t> distances(boost::num_vertices(graph));
383 boost::breadth_first_search(
384 graph,
385 boost::vertex(rootindex, graph),
386 boost::visitor(Extractors::record_distance(&distances[0])));
387 LOG(3, "DEBUG: BFS discovered the following distances " << distances);
388
389 /// 5b. and store all node in map with distance to root as key
390 Extractors::nodes_per_level_t nodes_per_level;
391 for (size_t i=0;i<distances.size();++i) {
392 nodes_per_level.insert( std::make_pair(Extractors::level_t(distances[i]), Extractors::node_t(i)) );
393 }
394 LOG(3, "DEBUG: Nodes per level are " << nodes_per_level);
395
396 /// 5c. construct all possible induced connected subgraphs with this map (see fragmentation)
397 Extractors::nodes_t nodes;
398 // rootindex is always contained
399 nodes.insert( rootindex );
400 Extractors::level_t level = 0;
401
402 for (size_t i=0;i<nodes_in_graph; ++i)
403 Extractors::generateAllInducedConnectedSubgraphs(
404 i, level, nodes, set_of_nodes, nodes_per_level, graph, distances, index_map);
405 LOG(2, "DEBUG: We have found " << set_of_nodes.size() << " unique induced, connected subgraphs.");
406 }
407
408 typedef std::vector<
409 std::pair<
410 BoostGraphCreator::UndirectedGraph,
411 degrees_t> > graphs_with_degrees_t;
412
413 // 6. for every combination saturate fragments for lookup and energy contribution summation
414 const HomologyContainer &container = World::getInstance().getHomologies();
415 for (list_of_edge_degrees_t::const_iterator degrees_iter = list_of_edge_degrees.begin();
416 degrees_iter != list_of_edge_degrees.end(); ++degrees_iter) {
417 const degrees_t &current_degrees = *degrees_iter;
418
419 // simply sum up the contributions from all fragments for the total energy
420 // of this particular combination, given the graph, the element for each node,
421 // and the bond degrees for each edge
422 double total_energy = 0.;
423
424 // get fragment
425 for (Extractors::set_of_nodes_t::const_iterator nodesiter = set_of_nodes.begin();
426 nodesiter != set_of_nodes.end(); ++nodesiter) {
427 const Extractors::nodes_t &current_nodes = *nodesiter;
428 LOG(2, "DEBUG: Nodes for graph are " << current_nodes);
429
430 // create subgraph
431 Extractors::UndirectedGraph newgraph = extractSubgraph(graph, current_nodes);
432
433 const BoostGraphCreator::name_map_t new_name_map =
434 boost::get(boost::vertex_name, newgraph);
435 {
436 BoostGraphCreator::vertex_iter viter, vend;
437 boost::tie(viter, vend) = boost::vertices(newgraph);
438 for (; viter != vend; ++viter) {
439 const atomId_t vindex = boost::get(new_name_map, *viter);
440 LOG(4, "DEBUG: Copied node has index " << vindex);
441 }
442 }
443
444 // saturate subgraph: add to nodes, add to type_index_lookup, index_map
445 // saturate any leftover valence with hydrogen nodes
446 SaturatedGraph fullgraph =
447 saturateGraph(newgraph, current_nodes, new_name_map,
448 type_index_lookup, type_valence_map, edge_index,
449 edges, current_degrees);
450
451 // don't add if one of the node's valence cannot be fulfilled
452 if (!fullgraph.ValencesAllFulfilled)
453 continue;
454
455 const Extractors::index_map_t subgraph_index_map =
456 boost::get(boost::vertex_index, fullgraph.saturated_graph);
457 const BoostGraphCreator::name_map_t subgraph_name_map =
458 boost::get(boost::vertex_name, fullgraph.saturated_graph);
459
460 Extractors::nodes_t saturated_nodes;
461 BoostGraphCreator::vertex_iter viter, vend;
462 boost::tie(viter, vend) = boost::vertices(fullgraph.saturated_graph);
463 for (; viter != vend; ++viter) {
464 const Extractors::node_t vindex = boost::get(subgraph_index_map, *viter);
465 LOG(4, "DEBUG: Adding node index " << vindex << " to saturated nodes list.");
466 saturated_nodes.insert(vindex);
467 }
468 /// convert its nodes into a HomologyGraph using the graph and elements (types for index)
469 const HomologyGraph nodes_graph =
470 Extractors::createHomologyGraphFromNodes(
471 saturated_nodes,
472 fullgraph.type_index_lookup,
473 fullgraph.saturated_graph,
474 subgraph_index_map);
475 LOG(2, "DEBUG: Looking for graph " << nodes_graph);
476
477 /// Query HomologyContainer for this HomologyGraph.
478 HomologyContainer::range_t range = container.getHomologousGraphs(nodes_graph);
479
480 if (range.first == range.second) {
481 // range is empty, the fragment is unknown
482 ELOG(1, "Cannot find fragment graph " << nodes_graph << " to graph " << elementnumbers);
483 } else {
484 // list first energy
485 LOG(1, "Fragment graph " << nodes_graph << " has contribution " << range.first->second.energy);
486 total_energy += range.first->second.energy;
487 }
488 }
489 LOG(1, "The graph with degrees " << *degrees_iter << " has a total energy of " << total_energy);
490 }
491
492 return Action::success;
493}
494
495ActionState::ptr GraphChemicalSpaceEvaluatorAction::performUndo(ActionState::ptr _state) {
496 return Action::success;
497}
498
499ActionState::ptr GraphChemicalSpaceEvaluatorAction::performRedo(ActionState::ptr _state){
500 return Action::success;
501}
502
503bool GraphChemicalSpaceEvaluatorAction::canUndo() {
504 return true;
505}
506
507bool GraphChemicalSpaceEvaluatorAction::shouldUndo() {
508 return true;
509}
510/** =========== end of function ====================== */
Note: See TracBrowser for help on using the repository browser.