source: src/FunctionApproximation/Extractors.cpp@ d83d64

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

Added Graph6Reader, extended BoostGraphCreator, added ChemicalSpaceEvaluatorAction.

  • TESTS: due to new option "graph6" containing a digit we needed to modify moltest_check.py to also scan for digits and not just letters.
  • Property mode set to 100644
File size: 32.8 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2012 University of Bonn. All rights reserved.
5 * Copyright (C) 2013 Frederik Heber. All rights reserved.
6 * Please see the COPYING file or "Copyright notice" in builder.cpp for details.
7 *
8 *
9 * This file is part of MoleCuilder.
10 *
11 * MoleCuilder is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * MoleCuilder is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25/*
26 * Extractors.cpp
27 *
28 * Created on: 15.10.2012
29 * Author: heber
30 */
31
32// include config.h
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36
37#include <boost/graph/adjacency_list.hpp>
38#include <boost/graph/breadth_first_search.hpp>
39#include <boost/graph/subgraph.hpp>
40
41//#include "CodePatterns/MemDebug.hpp"
42
43#include <algorithm>
44#include <iterator>
45#include <set>
46#include <sstream>
47#include <utility>
48#include <vector>
49#include <boost/assign.hpp>
50#include <boost/bimap.hpp>
51#include <boost/bimap/set_of.hpp>
52#include <boost/bimap/multiset_of.hpp>
53#include <boost/bind.hpp>
54#include <boost/foreach.hpp>
55
56#include "CodePatterns/Assert.hpp"
57#include "CodePatterns/IteratorAdaptors.hpp"
58#include "CodePatterns/Log.hpp"
59#include "CodePatterns/toString.hpp"
60
61#include "LinearAlgebra/Vector.hpp"
62
63#include "Fragmentation/Homology/HomologyGraph.hpp"
64#include "FunctionApproximation/Extractors.hpp"
65#include "FunctionApproximation/FunctionArgument.hpp"
66#include "Potentials/BindingModel.hpp"
67
68using namespace boost::assign;
69
70using namespace Extractors;
71
72FunctionModel::arguments_t
73Extractors::gatherAllSymmetricDistanceArguments(
74 const Fragment::positions_t& positions,
75 const Fragment::atomicnumbers_t& atomicnumbers,
76 const FragmentationEdges::edges_t &edges,
77 const size_t globalid)
78{
79 FunctionModel::arguments_t result;
80
81 // place edges in map
82 typedef std::set< std::pair<size_t, size_t> > sorted_edges_t;
83 sorted_edges_t sorted_edges;
84 for (FragmentationEdges::edges_t::const_iterator edgeiter = edges.begin();
85 edgeiter != edges.end(); ++edgeiter) {
86 std::pair<sorted_edges_t::iterator, bool> inserter =
87 sorted_edges.insert(
88 (edgeiter->first < edgeiter->second) ?
89 std::make_pair(edgeiter->first, edgeiter->second) :
90 std::make_pair(edgeiter->second, edgeiter->first));
91 ASSERT(inserter.second,
92 "Extractors::gatherAllSymmetricDistanceArguments() - edge "
93 +toString(*inserter.first)+" is already present");
94 }
95
96 // go through current configuration and gather all other distances
97 Fragment::positions_t::const_iterator firstpositer = positions.begin();
98 for (;firstpositer != positions.end(); ++firstpositer) {
99 Fragment::positions_t::const_iterator secondpositer = firstpositer;
100 for (; secondpositer != positions.end(); ++secondpositer) {
101 if (firstpositer == secondpositer)
102 continue;
103 argument_t arg;
104 const Vector firsttemp((*firstpositer)[0],(*firstpositer)[1],(*firstpositer)[2]);
105 const Vector secondtemp((*secondpositer)[0],(*secondpositer)[1],(*secondpositer)[2]);
106 arg.distance = firsttemp.distance(secondtemp);
107 arg.types = std::make_pair(
108 (int)atomicnumbers[ std::distance(positions.begin(), firstpositer) ],
109 (int)atomicnumbers[ std::distance(positions.begin(), secondpositer) ]
110 );
111 arg.indices = std::make_pair(
112 std::distance(
113 positions.begin(), firstpositer),
114 std::distance(
115 positions.begin(), secondpositer)
116 );
117 arg.globalid = globalid;
118 arg.bonded = sorted_edges.find(arg.indices) != sorted_edges.end();
119 LOG(3, "DEBUG: Created argument " << arg << ".");
120 result.push_back(arg);
121 }
122 }
123
124 return result;
125}
126
127Extractors::elementcounts_t
128Extractors::_detail::getElementCounts(
129 const Fragment::atomicnumbers_t elements
130 )
131{
132 elementcounts_t elementcounts;
133 for (Fragment::atomicnumbers_t::const_iterator elementiter = elements.begin();
134 elementiter != elements.end(); ++elementiter) {
135 // insert new element
136 std::pair< elementcounts_t::iterator, bool> inserter =
137 elementcounts.insert( std::make_pair( *elementiter, 1) );
138 // if already present, just increase its count
139 if (!inserter.second)
140 ++(inserter.first->second);
141 }
142 return elementcounts;
143}
144
145struct ParticleTypesComparator {
146 bool operator()(const argument_t::types_t &a, const argument_t::types_t &b)
147 {
148 if (a.first < a.second) {
149 if (b.first < b.second) {
150 if (a.first < b.first)
151 return true;
152 else if (a.first > b.first)
153 return false;
154 else
155 return (a.second < b.second);
156 } else {
157 if (a.first < b.second)
158 return true;
159 else if (a.first > b.second)
160 return false;
161 else
162 return (a.second < b.first);
163 }
164 } else {
165 if (b.first < b.second) {
166 if (a.second < b.first)
167 return true;
168 else if (a.second > b.first)
169 return false;
170 else
171 return (a.first < b.second);
172 } else {
173 if (a.second < b.second)
174 return true;
175 else if (a.second > b.second)
176 return false;
177 else
178 return (a.first < b.first);
179 }
180 }
181 }
182};
183
184std::ostream& operator<<(std::ostream &out, const argument_t::types_t &a)
185{
186 out << "[" << a.first << "," << a.second << "]";
187 return out;
188}
189
190void insertIntoNodeFragmentMap(
191 node_FragmentNode_map_t &_node_FragmentNode_map,
192 const size_t &_index,
193 const Extractors::ParticleType_t &_type)
194{
195 const node_FragmentNode_map_t::iterator mapiter = _node_FragmentNode_map.find(_index);
196 // check if already present
197 if (mapiter != _node_FragmentNode_map.end()) {
198 // assert same type and increment number of edges
199 ASSERT( mapiter->second.first == _type,
200 "insertIntoNodeFragmentMap() - different types "+toString(mapiter->second.first)+
201 " and "+toString(_type)+" for node "+toString(_index));
202 ++(mapiter->second.second);
203 } else {
204 // place new entry with a single present edge
205 _node_FragmentNode_map.insert( std::make_pair(_index, std::make_pair(_type, 1) ));
206 }
207}
208
209static node_FragmentNode_map_t fillNodeFragmentMap(
210 FunctionModel::arguments_t &argumentbunch)
211{
212 node_FragmentNode_map_t node_FragmentNode_map;
213 // place each node index with type and number of edges into map
214 for (FunctionModel::arguments_t::const_iterator argiter = argumentbunch.begin();
215 argiter != argumentbunch.end(); ++argiter) {
216 const argument_t &arg = *argiter;
217 // only consider the distances that represent a bond edge
218 if (arg.bonded) {
219 insertIntoNodeFragmentMap(node_FragmentNode_map, arg.indices.first, arg.types.first);
220 insertIntoNodeFragmentMap(node_FragmentNode_map, arg.indices.second, arg.types.second);
221 }
222 }
223
224 return node_FragmentNode_map;
225}
226
227static argument_placement_map_t fillArgumentsPlacementMap(const size_t num_args)
228{
229 argument_placement_map_t argument_placement_map;
230 size_t placement = 0;
231 for (size_t i = 0;i<num_args;++i)
232 for (size_t j = i+1;j<num_args;++j)
233 argument_placement_map.insert( std::make_pair( argument_t::indices_t(i,j), placement++) );
234 return argument_placement_map;
235}
236
237static argument_t::indices_t translateIndices(
238 const argindex_to_nodeindex_t &_argindex_to_nodeindex,
239 const argument_t::indices_t &_indices
240 )
241{
242 const argindex_to_nodeindex_t::const_iterator firstiter =
243 _argindex_to_nodeindex.find(_indices.first);
244 ASSERT( firstiter != _argindex_to_nodeindex.end(),
245 "translateIndices() - could not find #"+toString(_indices.first)+
246 " in translation map.");
247 const argindex_to_nodeindex_t::const_iterator seconditer =
248 _argindex_to_nodeindex.find(_indices.second);
249 ASSERT(seconditer != _argindex_to_nodeindex.end(),
250 "translateIndices() - could not find #"+toString(_indices.second)+
251 " in translation map.");
252 // mind increasing order of indices in pair
253 if (firstiter->second < seconditer->second)
254 return argument_t::indices_t(firstiter->second, seconditer->second);
255 else
256 return argument_t::indices_t(seconditer->second, firstiter->second);
257}
258
259/** Power set generator
260 *
261 * taken from https://rosettacode.org/wiki/Power_set#Non-recursive_version
262 *
263 */
264static powerset_type powerset(set_type const& set)
265{
266 typedef set_type::const_iterator set_iter;
267 typedef std::vector<set_iter> vec;
268
269 struct local
270 {
271 static int dereference(set_iter v) { return *v; }
272 };
273
274 powerset_type result;
275
276 vec elements;
277 do {
278 set_type tmp;
279 std::transform(elements.begin(), elements.end(),
280 std::inserter(tmp, tmp.end()),
281 local::dereference);
282 result.insert(tmp);
283 if (!elements.empty() && ++elements.back() == set.end()) {
284 elements.pop_back();
285 } else {
286 set_iter iter;
287 if (elements.empty()) {
288 iter = set.begin();
289 } else {
290 iter = elements.back();
291 ++iter;
292 }
293 for (; iter != set.end(); ++iter) {
294 elements.push_back(iter);
295 }
296 }
297 } while (!elements.empty());
298
299 return result;
300}
301
302/** Recursive function to generate all induced, connected subgraphs given a
303 * graph.
304 *
305 * \param N number of still left to pick
306 * \param depth level in \a set_of_nodes
307 * \param nodes current set of nodes that are picked already
308 * \param set_of_nodes resulting set of generated subgraphs' nodes
309 * \param nodes_per_level level-wise frontier of connected nodes around a root node
310 * \param graph graph containing the adjacency
311 * \param index_map with indices per \a graph' vertex
312 */
313void generateAllInducedConnectedSubgraphs(
314 const size_t N,
315 const level_t level,
316 const nodes_t &nodes,
317 set_of_nodes_t &set_of_nodes,
318 const nodes_per_level_t &nodes_per_level,
319 const UndirectedGraph &graph,
320 const std::vector<size_t> &_distance,
321 const index_map_t &index_map)
322{
323 ASSERT( nodes_per_level.find(level) != nodes_per_level.end(),
324 "generateAllInducedConnectedSubgraphs() - we are deeper than the graph.");
325 ASSERT( N < nodes_per_level.size(),
326 "generateAllInducedConnectedSubgraphs() - we are looking for subgraphs larger than the graph.");
327 if (N > 0) {
328 LOG(3, "DEBUG: At level " << level << " current nodes is " << nodes << ", need to find " << N << " more.");
329 // get next level's set and constrain to nodes connected to this set
330 nodes_t validnodes;
331 std::pair< nodes_per_level_t::const_iterator, nodes_per_level_t::const_iterator> range =
332 nodes_per_level.equal_range(level);
333 for (nodes_per_level_t::const_iterator rangeiter = range.first;
334 rangeiter != range.second; ++rangeiter) {
335 LOG(4, "DEBUG: Checking edges further away from node #" << rangeiter->second);
336 // get all edges connected to this node further away
337 UndirectedGraph::in_edge_iterator i, end;
338 boost::tie(i, end) = boost::in_edges(boost::vertex(rangeiter->second, graph), graph);
339 for (;i != end; ++i) {
340 // check each edge whether it's in nodes
341 const node_t sourceindex = boost::get(index_map, boost::source(*i, graph));
342 const node_t targetindex = boost::get(index_map, boost::target(*i, graph));
343 const size_t &source_distance = _distance[sourceindex];
344 const size_t &target_distance = _distance[targetindex];
345 // edge is going deeper into graph
346 if (((source_distance == level) && (target_distance == (level+1)))
347 || ((source_distance == (level+1)) && (target_distance == level))) {
348 LOG(5, "DEBUG: Candidate edge on level " << level << " is from " << sourceindex
349 << " to " << targetindex << ".");
350 // pick right index and check for not present in list yet
351 if (sourceindex == rangeiter->second) {
352 if (nodes.count(targetindex) == 0) {
353 validnodes.insert(targetindex);
354 LOG(4, "DEBUG: Inserting node #" << targetindex << " into valid nodes.");
355 }
356 } else if (targetindex == rangeiter->second) {
357 if (nodes.count(sourceindex) == 0) {
358 validnodes.insert(sourceindex);
359 LOG(4, "DEBUG: Inserting node #" << sourceindex << " into valid nodes.");
360 }
361 } else {
362 ASSERT(0,
363 "generateAllInducedConnectedSubgraphs() - neither source #"+toString(sourceindex)+
364 " nor target #"+toString(targetindex)+" is equal to #"+toString(rangeiter->second));
365 }
366 }
367 }
368 }
369 // skip this if we cannot go deeper into the graph from here
370 if (validnodes.empty()) {
371 LOG(3, "DEBUG: We did not find any more nodes to step on from " << nodes << ".");
372 return;
373 }
374
375 // go through power set
376 const powerset_type test_powerset = powerset(validnodes);
377 for (powerset_type::const_iterator iter = test_powerset.begin();
378 iter != test_powerset.end();
379 ++iter) {
380 // count set bits (#elements in *iter), must be between 1 and N
381 const size_t num_set_bits = iter->size();
382 if ((num_set_bits > 0) && (num_set_bits <= N)) {
383 // add set to nodes
384 LOG(3, "DEBUG: With present " << nodes << " the current set is " << *iter << " of "
385 << validnodes << ".");
386
387 // copy the nodes before insertion
388 nodes_t filled_nodes(nodes.begin(), nodes.end());
389 filled_nodes.insert(iter->begin(), iter->end());
390
391 // and recurse
392 generateAllInducedConnectedSubgraphs(
393 N-num_set_bits, level+1, filled_nodes, set_of_nodes, nodes_per_level, graph, _distance, index_map);
394 }
395 }
396 } else {
397 // N==0: we have a winner
398 std::pair<set_of_nodes_t::iterator, bool> inserter =
399 set_of_nodes.insert( nodes );
400 if (!inserter.second)
401 LOG(2, "DEBUG: subgraph " << nodes << " is already contained in set_of_nodes.");
402 else
403 LOG(2, "DEBUG: subgraph " << nodes << " inserted into set_of_nodes.");
404 }
405}
406
407static Extractors::ParticleType_t getParticleTypeToNode(
408 const type_index_lookup_t &type_index_lookup,
409 const size_t nodeindex)
410{
411 const type_index_lookup_t::left_const_iterator typeiter = type_index_lookup.left.find(nodeindex);
412 ASSERT( typeiter != type_index_lookup.left.end(),
413 "getParticleTypeToNode() - could not find type to node #"+toString(nodeindex));
414 return typeiter->second;
415}
416
417HomologyGraph createHomologyGraphFromNodes(
418 const nodes_t &nodes,
419 const type_index_lookup_t &type_index_lookup,
420 const UndirectedGraph &graph,
421 const index_map_t &index_map
422 )
423{
424 HomologyGraph::nodes_t graph_nodes;
425 HomologyGraph::edges_t graph_edges;
426 {
427 typedef std::set< std::pair<node_t, node_t> > graph_edges_t;
428 graph_edges_t edge_set;
429 std::pair<HomologyGraph::nodes_t::iterator, bool > inserter;
430 for (nodes_t::const_iterator nodeiter = nodes.begin();
431 nodeiter != nodes.end(); ++nodeiter) {
432 const Extractors::ParticleType_t &nodetype = getParticleTypeToNode(type_index_lookup, *nodeiter);
433
434 // count edges in constrained set for this particular node
435 size_t num_edges = 0;
436 UndirectedGraph::in_edge_iterator i, end;
437 for (boost::tie(i, end) = boost::in_edges(boost::vertex(*nodeiter, graph), graph);
438 i != end; ++i) {
439 const node_t sourceindex = boost::get(index_map, boost::source(*i, graph));
440 const node_t targetindex = boost::get(index_map, boost::target(*i, graph));
441 // check each edge whether it's in nodes
442 if ((nodes.count(sourceindex) != 0) && (nodes.count(targetindex) != 0)) {
443 // increase edge count of node
444 ++num_edges;
445 // we first store edges in a set to ensure their uniqueness (as we encounter
446 // each edge two times and we don't know if source and target will be
447 // different the second time encountered)
448 if (sourceindex < targetindex)
449 edge_set.insert( std::make_pair(sourceindex, targetindex) );
450 else
451 edge_set.insert( std::make_pair(targetindex, sourceindex) );
452 }
453 }
454 LOG(4, "DEBUG: Node #" << *nodeiter << " has " << num_edges << " edges.");
455
456 // add FragmentNode
457 inserter = graph_nodes.insert( std::make_pair(FragmentNode(nodetype, num_edges), 1) );
458 if (!inserter.second)
459 ++(inserter.first->second);
460 }
461
462 // add edges
463 for (graph_edges_t::const_iterator edgeiter = edge_set.begin();
464 edgeiter != edge_set.end(); ++edgeiter) {
465 const Extractors::ParticleType_t sourcetype =
466 getParticleTypeToNode(type_index_lookup, edgeiter->first);
467 const Extractors::ParticleType_t targettype =
468 getParticleTypeToNode(type_index_lookup, edgeiter->second);
469 // FragmentEdge takes care of proper sorting
470 FragmentEdge edge(sourcetype, targettype);
471 LOG(4, "DEBUG: Adding fragment edge " << edge);
472 std::pair<HomologyGraph::edges_t::iterator, bool > inserter;
473 inserter = graph_edges.insert( std::make_pair( edge, 1) );
474 if (!inserter.second)
475 ++(inserter.first->second);
476 }
477 }
478
479 return HomologyGraph(graph_nodes, graph_edges);
480}
481
482FunctionModel::list_of_arguments_t Extractors::filterArgumentsByBindingModel(
483 const FunctionModel::arguments_t &args,
484 const HomologyGraph &_graph,
485 const ParticleTypes_t &_types,
486 const BindingModel &_bindingmodel
487 )
488{
489 FunctionModel::list_of_arguments_t returnargs;
490
491 // deal with the case when there are no distances (ConstantPotential)
492 if (_bindingmodel.getNodes().size() < 2) {
493 LOG(3, "DEBUG: Potential requires only one or no particle types, needs no distances.");
494 return returnargs;
495 }
496 if (_bindingmodel.getGraph().getEdges().empty()) {
497 LOG(3, "DEBUG: Potential represents non-bonded interactions, gets all distances.");
498 // TODO: Here we need to constrain to all distances matching the types?
499 returnargs.push_back(args);
500 return returnargs;
501 }
502
503 /// 0. place all nodes in a lookup map for their type
504 type_index_lookup_t type_index_lookup;
505 for(FunctionModel::arguments_t::const_iterator iter = args.begin();
506 iter != args.end(); ++iter) {
507 if (type_index_lookup.left.find(iter->indices.first) == type_index_lookup.left.end())
508 type_index_lookup.left.insert( std::make_pair(iter->indices.first, iter->types.first) );
509 else {
510 ASSERT(type_index_lookup.left.at(iter->indices.first) == iter->types.first,
511 "Extractors::reorderArgumentsByParticleTypes() - entry " +toString(iter->indices.first)+
512 " is already present with different type "+toString(iter->types.first)
513 +" than "+toString(type_index_lookup.left.at(iter->indices.first)));
514 }
515 if (type_index_lookup.left.find(iter->indices.second) == type_index_lookup.left.end())
516 type_index_lookup.left.insert( std::make_pair(iter->indices.second, iter->types.second) );
517 else {
518 ASSERT(type_index_lookup.left.at(iter->indices.second) == iter->types.second,
519 "Extractors::reorderArgumentsByParticleTypes() - entry " +toString(iter->indices.second)+
520 " is already present with different type "+toString(iter->types.second)
521 +" than "+toString(type_index_lookup.left.at(iter->indices.second)));
522 }
523 }
524 if (DoLog(3)) {
525 std::stringstream output;
526 for (type_index_lookup_t::left_const_iterator indexiter = type_index_lookup.left.begin();
527 indexiter != type_index_lookup.left.end(); ++indexiter) {
528 output << " [" << indexiter->first << "," << indexiter->second << "]";
529 }
530 LOG(3, "DEBUG: index to type map:" << output.str());
531 }
532 if (DoLog(3)) {
533 std::stringstream output;
534 for (type_index_lookup_t::right_const_iterator typeiter = type_index_lookup.right.begin();
535 typeiter != type_index_lookup.right.end(); ++typeiter) {
536 output << " [" << typeiter->first << "," << typeiter->second << "]";
537 }
538 LOG(3, "DEBUG: type to index map " << output.str());
539 }
540
541 /// 1. construct boost::graph from arguments_t (iter)
542 const size_t N = type_index_lookup.left.size();
543 UndirectedGraph fragmentgraph(N);
544 for(FunctionModel::arguments_t::const_iterator iter = args.begin();
545 iter != args.end(); ++iter) {
546 if (iter->bonded)
547 boost::add_edge(iter->indices.first, iter->indices.second, fragmentgraph);
548 }
549 index_map_t index_map = boost::get(boost::vertex_index, fragmentgraph);
550 LOG(2, "DEBUG: We have " << boost::num_vertices(fragmentgraph) << " nodes in the fragment graph.");
551
552 /// 2. grab first node of the binding model (gives a type)
553 const BindingModel::vector_nodes_t::const_iterator firstiter = _bindingmodel.getNodes().begin();
554 const FragmentNode &firstnode = *firstiter;
555 const Extractors::ParticleType_t &firsttype = firstnode.getAtomicNumber();
556
557 /// 3. grab all candidate nodes contained in arguments_t
558 std::pair<
559 type_index_lookup_t::right_const_iterator,
560 type_index_lookup_t::right_const_iterator> range = type_index_lookup.right.equal_range(firsttype);
561
562 /// 4. go over all candidate nodes (gives index)
563 const size_t nodes_in_bindingmodel = _bindingmodel.getNodes().size();
564 LOG(2, "DEBUG: There are " << nodes_in_bindingmodel << " nodes in the binding model.");
565 set_of_nodes_t set_of_nodes;
566 for (type_index_lookup_t::right_const_iterator rangeiter = range.first;
567 rangeiter != range.second; ++rangeiter) {
568 const size_t &rootindex = rangeiter->second;
569 LOG(2, "DEBUG: Current root index is " << rootindex);
570
571 /// 5a. from node in graph (with this index) perform BFS till n-1 (#nodes in binding model)
572 std::vector<size_t> distances(boost::num_vertices(fragmentgraph));
573 boost::breadth_first_search(
574 fragmentgraph,
575 boost::vertex(rootindex, fragmentgraph),
576 boost::visitor(record_distance(&distances[0])));
577 LOG(3, "DEBUG: BFS discovered the following distances " << distances);
578
579 /// 5b. and store all node in map with distance to root as key
580 nodes_per_level_t nodes_per_level;
581 for (size_t i=0;i<distances.size();++i) {
582 nodes_per_level.insert( std::make_pair(level_t(distances[i]), node_t(i)) );
583 }
584 LOG(3, "DEBUG: Nodes per level are " << nodes_per_level);
585
586 /// 6. construct all possible induced connected subgraphs with this map (see fragmentation)
587 nodes_t nodes;
588 // rootindex is always contained
589 nodes.insert( rootindex );
590 level_t level = 0;
591
592 generateAllInducedConnectedSubgraphs(
593 nodes_in_bindingmodel-1, level, nodes, set_of_nodes, nodes_per_level, fragmentgraph, distances, index_map);
594 LOG(2, "DEBUG: We have found " << set_of_nodes.size() << " unique induced, connected subgraphs.");
595 }
596
597 /// 8. go through each induced, connected subgraph
598 for (set_of_nodes_t::const_iterator nodesiter = set_of_nodes.begin();
599 nodesiter != set_of_nodes.end(); ++nodesiter) {
600 /// 9. convert its nodes into a HomologyGraph using the subgraph and arguments_t (types for index)
601 const nodes_t &nodes = *nodesiter;
602 const HomologyGraph nodes_graph =
603 createHomologyGraphFromNodes(nodes, type_index_lookup, fragmentgraph, index_map);
604
605 /// 10. compare each converted HomologyGraph with _bindingmodel: Accept or Reject
606 if (nodes_graph == _bindingmodel.getGraph()) {
607 LOG(2, "ACCEPT: " << nodes_graph << " is identical to " << _bindingmodel.getGraph());
608 /// 11. for each accepted keyset, pick _all_ symmetric distances from arguments_t
609 FunctionModel::arguments_t argumentbunch;
610 argumentbunch.reserve(args.size());
611 for(FunctionModel::arguments_t::const_iterator iter = args.begin();
612 iter != args.end(); ++iter) {
613 if ((nodes.count(iter->indices.first) != 0) && (nodes.count(iter->indices.second) != 0)) {
614 argumentbunch.push_back(*iter);
615 }
616 }
617 const size_t num_symmetric_distances = nodes.size()*(nodes.size()-1)/2;
618 ASSERT( argumentbunch.size() == num_symmetric_distances,
619 "Extractors::reorderArgumentsByParticleTypes() - only "+toString(argumentbunch.size())+
620 " found instead of "+toString(num_symmetric_distances));
621 LOG(3, "DEBUG: Final bunch of arguments is " << argumentbunch << ".");
622
623 /**
624 * We still need to bring the arguments in the correct order for the potential.
625 *
626 * The potential gives the desired order via the nodes in the HomologyGraph! Their
627 * sequence matches with the user-defined particle types and because of the properties
628 * of the homology graph (FragmentNode stores type and number of edges) it also
629 * matches with the desired bonding graph.
630 */
631
632 /// So, we need to extract from the argumentbunch the information contained in each
633 /// FragmentNode, namely its type and the number of connected edges
634 node_FragmentNode_map_t node_FragmentNode_map = fillNodeFragmentMap(argumentbunch);
635
636 /// Then, we step through the nodes of the bindingmodel ...
637 /// ... and find a suitable mapping from indices in the arguments to
638 /// the index in the order of the HomologyGraph's nodes
639 const BindingModel::vector_nodes_t bindingmodel_nodes = _bindingmodel.getNodes();
640 argindex_to_nodeindex_t argindex_to_nodeindex;
641 size_t nodeindex = 0;
642 for (BindingModel::vector_nodes_t::const_iterator nodeiter = bindingmodel_nodes.begin();
643 nodeiter != bindingmodel_nodes.end(); ++nodeiter) {
644 const FragmentNode &node = *nodeiter;
645 LOG(3, "DEBUG: Binding model's node #" << node << ".");
646 /// ... and pick for each the first (and unique) from these stored nodes.
647 node_FragmentNode_map_t::iterator mapiter = node_FragmentNode_map.begin();
648 for (;mapiter != node_FragmentNode_map.end(); ++mapiter) {
649 if ((mapiter->second.first == node.getAtomicNumber())
650 && (mapiter->second.second == node.getConnectedEdges())) {
651 LOG(3, "DEBUG: #" << mapiter->first << " with type " << mapiter->second.first
652 << " and " << mapiter->second.second << " connected edges matches.");
653 break;
654 }
655 }
656 ASSERT( mapiter != node_FragmentNode_map.end(),
657 "Extractors::reorderArgumentsByParticleTypes() - could not find a suitable node for #"+
658 toString(mapiter->first)+" with type "+toString(mapiter->second.first)+" and "+
659 toString(mapiter->second.second)+" connected edges");
660 std::pair<argindex_to_nodeindex_t::iterator, bool> inserter =
661 argindex_to_nodeindex.insert( std::make_pair(mapiter->first, nodeindex++) );
662 ASSERT( inserter.second,
663 "Extractors::reorderArgumentsByParticleTypes() - node #"+toString(mapiter->first)+
664 " is already present?");
665 // remove to ensure uniqueness of choice
666 node_FragmentNode_map.erase(mapiter);
667 }
668 LOG(4, "DEBUG: argument's indices to node index map is " << argindex_to_nodeindex);
669 // i.e. this is not the arg's index in argumentbunch, but the index of the position
670 // contained in the argument_t
671
672 /// Finally, we only need to bring the arguments in the typical order:
673 /// 01 02 03 04 ... 0n, 12 13 14 ... 1n, 23 24 25 ... 2n, ..., (n-1)n
674 /// These ordering we store in a map for each argument's indices
675 const size_t num_args = argindex_to_nodeindex.size();
676 argument_placement_map_t argument_placement_map = fillArgumentsPlacementMap(num_args);
677 LOG(4, "DEBUG: Placement map is " << argument_placement_map);
678 ASSERT( argument_placement_map.size() == argumentbunch.size(),
679 "Extractors::reorderArgumentsByParticleTypes() - placement map has size "+
680 toString(argument_placement_map.size())+" and we expected "+toString(argumentbunch.size()));
681
682 // and finally resort the arguments with the constructed placement map
683 FunctionModel::arguments_t sortedargs(argumentbunch.size());
684 for (FunctionModel::arguments_t::const_iterator argiter = argumentbunch.begin();
685 argiter != argumentbunch.end(); ++argiter) {
686 const argument_t &arg = *argiter;
687 const argument_t::indices_t translatedIndices =
688 translateIndices(argindex_to_nodeindex, arg.indices);
689 const argument_placement_map_t::const_iterator indexiter =
690 argument_placement_map.find( translatedIndices );
691 ASSERT( indexiter != argument_placement_map.end(),
692 "Extractors::reorderArgumentsByParticleTypes() - could not find place for edge "+
693 toString(arg.indices));
694 sortedargs[indexiter->second] = arg;
695 LOG(3, "DEBUG: Placed argument " << arg << " at #" << indexiter->second
696 << " as translated indices are " << translatedIndices);
697 }
698 LOG(2, "DEBUG: Sorted arguments are " << sortedargs << ".");
699
700 returnargs.push_back(sortedargs);
701 } else {
702 LOG(2, "REJECT: " << nodes_graph << " is not identical to " << _bindingmodel.getGraph());
703 }
704 }
705
706 return returnargs;
707}
708
709FunctionModel::list_of_arguments_t Extractors::filterArgumentsByParticleTypes(
710 const FunctionModel::arguments_t &args,
711 const HomologyGraph &_graph,
712 const ParticleTypes_t &_types,
713 const BindingModel &_bindingmodel
714 )
715{
716 // list allows for quicker removal than vector
717 typedef std::list< argument_t > ListArguments_t;
718 ListArguments_t availableList(args.begin(), args.end());
719 LOG(2, "DEBUG: Initial list of args is " << args << ".");
720
721 // TODO: fill a lookup map such that we don't have O(M^3) scaling, if M is number
722 // of types (and we always must have M(M-1)/2 args) but O(M^2 log(M)). However, as
723 // M is very small (<=3), this is not necessary fruitful now.
724// typedef ParticleTypes_t firsttype;
725// typedef ParticleTypes_t secondtype;
726// typedef std::map< firsttype, std::map< secondtype, FunctionModel::arguments_t > > ArgsLookup_t;
727// ArgsLookup_t ArgsLookup;
728
729 ASSERT( _types.size() <= 2,
730 "Extractors::filterArgumentsByParticleTypes() - this only filters and does not reorder."
731 +std::string(" Hence, it is not useful for multiple arguments per model."));
732
733 // basically, we have two choose any two pairs out of types but only those
734 // where the first is less than the latter. Hence, we start the second
735 // iterator at the current position of the first one and skip the equal case.
736 FunctionModel::list_of_arguments_t returnargs;
737 for (ParticleTypes_t::const_iterator firstiter = _types.begin();
738 firstiter != _types.end();
739 ++firstiter) {
740 for (ParticleTypes_t::const_iterator seconditer = firstiter;
741 seconditer != _types.end();
742 ++seconditer) {
743 if (seconditer == firstiter)
744 continue;
745 LOG(3, "DEBUG: Looking for (" << *firstiter << "," << *seconditer << ") in all args.");
746
747 // search the right one in _args (we might allow switching places of
748 // firstiter and seconditer, as distance is symmetric).
749 ListArguments_t::iterator iter = availableList.begin();
750 while (iter != availableList.end()) {
751 LOG(4, "DEBUG: Current args is " << *iter << ".");
752 if ((iter->types.first == *firstiter)
753 && (iter->types.second == *seconditer)) {
754 returnargs.push_back( FunctionModel::arguments_t(1, *iter) );
755 iter = availableList.erase(iter);
756 LOG(4, "DEBUG: Accepted argument.");
757 } else if ((iter->types.first == *seconditer)
758 && (iter->types.second == *firstiter)) {
759 returnargs.push_back( FunctionModel::arguments_t(1, *iter) );
760 iter = availableList.erase(iter);
761 LOG(4, "DEBUG: Accepted (flipped) argument.");
762 } else {
763 ++iter;
764 LOG(4, "DEBUG: Rejected argument.");
765 }
766 }
767 }
768 }
769
770 LOG(2, "DEBUG: We have generated " << returnargs.size() << " tuples of distances.");
771
772 return returnargs;
773}
774
775
776FunctionModel::arguments_t Extractors::combineArguments(
777 const FunctionModel::arguments_t &firstargs,
778 const FunctionModel::arguments_t &secondargs)
779{
780 FunctionModel::arguments_t args = concatenateArguments(firstargs, secondargs);
781 std::sort(args.begin(), args.end(),
782 boost::bind(&argument_t::operator<, _1, _2));
783 FunctionModel::arguments_t::iterator iter =
784 std::unique(args.begin(), args.end(),
785 boost::bind(&argument_t::operator==, _1, _2));
786 args.erase(iter, args.end());
787 return args;
788}
789
790FunctionModel::arguments_t Extractors::concatenateArguments(
791 const FunctionModel::arguments_t &firstargs,
792 const FunctionModel::arguments_t &secondargs)
793{
794 FunctionModel::arguments_t args(firstargs);
795 args.insert(args.end(), secondargs.begin(), secondargs.end());
796 return args;
797}
798
799FunctionModel::list_of_arguments_t Extractors::concatenateListOfArguments(
800 const FunctionModel::list_of_arguments_t &firstlistargs,
801 const FunctionModel::list_of_arguments_t &secondlistargs)
802{
803 FunctionModel::list_of_arguments_t listargs(firstlistargs);
804 listargs.insert(listargs.end(), secondlistargs.begin(), secondlistargs.end());
805 return listargs;
806}
Note: See TracBrowser for help on using the repository browser.