source: src/FunctionApproximation/Extractors.cpp@ 4464ef

Action_Thermostats Add_AtomRandomPerturbation Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_ChronosMutex Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids_IntegrationTest JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo StoppableMakroAction Subpackage_levmar Subpackage_vmg ThirdParty_MPQC_rebuilt_buildsystem TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes stable
Last change on this file since 4464ef was 1155ba, checked in by Frederik Heber <heber@…>, 8 years ago

Converted filterArguments... into faster filter, renamed other to ..ByBindingModel.

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