Ignore:
Timestamp:
Nov 11, 2016, 2:25:30 PM (8 years ago)
Author:
Frederik Heber <heber@…>
Branches:
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, 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_CodePatterns, Subpackage_JobMarket, Subpackage_LinearAlgebra, Subpackage_levmar, Subpackage_mpqc_open, Subpackage_vmg, ThirdParty_MPQC_rebuilt_buildsystem, TremoloParser_IncreasedPrecision, TremoloParser_MultipleTimesteps, Ubuntu_1604_changes, stable
Children:
c738f1
Parents:
228340
git-author:
Frederik Heber <heber@…> (10/04/16 14:05:46)
git-committer:
Frederik Heber <heber@…> (11/11/16 14:25:30)
Message:

Extractors::reorderArgumentsByParticleTypes() rewritten to scan for all induced, connected subgraphs.

  • after filtering the arguments down to the required particle types, we use the adjacency graph that is passed down from creating the SaturatedFragments, stored in various FragmentationResultContainers and finally used to state whether in Extractors::gatherAllSymmetricDistances() a distance represents a bond or not. In this graph we look for all subgraphs that are homologous to the graph specified by the derived EmpiricalPotential. For each accepted subgraph we then gather again all symmetric distances and concatenate them all.
  • TESTFIX: Marked all potential fitting regression tests as XFAIL for the moment. On the one hand the homology container format changed (edges), and on the other hand we are in the middle of refactoring above function.
  • added test using edges for ExtractorsUnitTest.
  • lib dependency fixed for ..PotentialUnitTest.
  • NOTE: boost::graph is incompatible with CodePattern's MEMDEBUG. Hence, includes are placed prior to memdebug's.
Location:
src/FunctionApproximation
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • src/FunctionApproximation/Extractors.cpp

    r228340 r2124aa  
    3535#endif
    3636
     37#include <boost/graph/adjacency_list.hpp>
     38#include <boost/graph/breadth_first_search.hpp>
     39#include <boost/graph/subgraph.hpp>
     40
    3741#include "CodePatterns/MemDebug.hpp"
    3842
     43#include <algorithm>
     44#include <iterator>
     45#include <set>
    3946#include <sstream>
    4047#include <utility>
    4148#include <vector>
    4249#include <boost/assign.hpp>
     50#include <boost/bimap.hpp>
     51#include <boost/bimap/set_of.hpp>
     52#include <boost/bimap/multiset_of.hpp>
    4353#include <boost/bind.hpp>
    4454#include <boost/foreach.hpp>
     
    6676{
    6777  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  }
    6893
    6994  // go through current configuration and gather all other distances
     
    89114          );
    90115      arg.globalid = globalid;
    91       arg.bonded = false;
     116      arg.bonded = sorted_edges.find(arg.indices) != sorted_edges.end();
    92117      LOG(3, "DEBUG: Created argument " << arg << ".");
    93118      result.push_back(arg);
     
    161186}
    162187
     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
    163533FunctionModel::list_of_arguments_t Extractors::reorderArgumentsByParticleTypes(
    164534    const FunctionModel::list_of_arguments_t &listargs,
     
    169539{
    170540  FunctionModel::list_of_arguments_t returnargs;
    171   for (FunctionModel::list_of_arguments_t::const_iterator iter = listargs.begin();
    172       iter != listargs.end(); ++iter) {
    173     const FunctionModel::arguments_t &args = *iter;
    174     /// We  place all arguments into multimap according to particle type pair.
    175     // here, we need a special comparator such that types in key pair are always
    176     // properly ordered.
    177     typedef std::multimap<
    178         argument_t::types_t,
    179         argument_t,
    180         ParticleTypesComparator> TypePair_Argument_Map_t;
    181     TypePair_Argument_Map_t argument_map;
     541  for (FunctionModel::list_of_arguments_t::const_iterator listiter = listargs.begin();
     542      listiter != listargs.end(); ++listiter) {
     543    const FunctionModel::arguments_t &args = *listiter;
     544
     545    // deal with the case when there are no distances (ConstantPotential)
     546    if (_bindingmodel.getNodes().size() < 2) {
     547      LOG(3, "DEBUG: Potential requires only zero or no particle types, needs no distances.");
     548      continue;
     549    }
     550    if (_bindingmodel.getEdges().empty()) {
     551      LOG(3, "DEBUG: Potential represents non-bonded interactions, gets all distances.");
     552      returnargs.push_back(args);
     553      continue;
     554    }
     555
     556    /// 0. place all nodes in a lookup map for their type
     557    type_index_lookup_t type_index_lookup;
    182558    for(FunctionModel::arguments_t::const_iterator iter = args.begin();
    183559        iter != args.end(); ++iter) {
    184       argument_map.insert( std::make_pair(iter->types, *iter) );
    185     }
    186     LOG(4, "DEBUG: particle_type map is " << argument_map << ".");
    187 
    188     /// Then, we create the desired unique keys
    189     typedef std::vector<argument_t::types_t> UniqueTypes_t;
    190     UniqueTypes_t UniqueTypes;
    191     for (ParticleTypes_t::const_iterator firstiter = _types.begin();
    192         firstiter != _types.end();
    193         ++firstiter) {
    194       for (ParticleTypes_t::const_iterator seconditer = firstiter;
    195           seconditer != _types.end();
    196           ++seconditer) {
    197         if (seconditer == firstiter)
    198           continue;
    199         UniqueTypes.push_back( std::make_pair(*firstiter, *seconditer) );
    200       }
    201     }
    202     LOG(4, "DEBUG: Created unique types as keys " << UniqueTypes << ".");
    203 
    204     /// Finally, we use the unique key list to pick corresponding arguments from the map
    205     FunctionModel::arguments_t sortedargs;
    206     sortedargs.reserve(args.size());
    207     while (!argument_map.empty()) {
    208       // note that particle_types_t may be flipped, i.e. 1,8 is equal to 8,1, but we
    209       // must maintain the correct order in indices in accordance with the order
    210       // in _types, i.e. 1,8,1 must match with e.g. ids 1,0,2 where 1 has type 1,
    211       // 0 has type 8, and 2 has type 2.
    212       // In other words: We do not want to flip/modify arguments such that they match
    213       // with the specific type pair we seek but then this comes at the price that we
    214       // have flip indices when the types in a pair are flipped.
    215 
    216       typedef std::vector<size_t> indices_t;
    217       //!> here, we gather the indices as we discover them
    218       indices_t indices;
    219       indices.resize(_types.size(), (size_t)-1);
    220 
    221       // these are two iterators that create index pairs in the same way as we have
    222       // created type pairs. If a -1 is still present in indices, then the index is
    223       // still arbitrary but is then set by the next found index
    224       indices_t::iterator firstindex = indices.begin();
    225       indices_t::iterator secondindex = firstindex+1;
    226 
    227       //!> here, we gather the current bunch of arguments as we find them
    228       FunctionModel::arguments_t argumentbunch;
    229       argumentbunch.reserve(UniqueTypes.size());
    230 
    231       for (UniqueTypes_t::const_iterator typeiter = UniqueTypes.begin();
    232           typeiter != UniqueTypes.end(); ++typeiter) {
    233         // have all arguments to same type pair as list within the found range
    234         std::pair<
    235             TypePair_Argument_Map_t::iterator,
    236             TypePair_Argument_Map_t::iterator> range_t =
    237                 argument_map.equal_range(*typeiter);
    238         LOG(4, "DEBUG: Set of arguments to current key [" << typeiter->first << ","
    239             << typeiter->second << "] is " << std::list<argument_t>(
    240                 MapValueIterator<TypePair_Argument_Map_t::iterator>(range_t.first),
    241                 MapValueIterator<TypePair_Argument_Map_t::iterator>(range_t.second)
    242                 ) << ".");
    243         // the first key is always easy and is pivot which the rest has to be associated to
    244         if (typeiter == UniqueTypes.begin()) {
    245           const argument_t & arg = range_t.first->second;
    246           if ((typeiter->first == arg.types.first) && (typeiter->second == arg.types.second)) {
    247             // store in correct order
    248             *firstindex = arg.indices.first;
    249             *secondindex = arg.indices.second;
    250           } else {
    251             // store in flipped order
    252             *firstindex = arg.indices.second;
    253             *secondindex = arg.indices.first;
     560      if (type_index_lookup.left.find(iter->indices.first) == type_index_lookup.left.end())
     561        type_index_lookup.left.insert( std::make_pair(iter->indices.first, iter->types.first) );
     562      else {
     563        ASSERT(type_index_lookup.left.at(iter->indices.first) == iter->types.first,
     564            "Extractors::reorderArgumentsByParticleTypes() - entry " +toString(iter->indices.first)+
     565            " is already present with different type "+toString(iter->types.first)
     566            +" than "+toString(type_index_lookup.left.at(iter->indices.first)));
     567      }
     568      if (type_index_lookup.left.find(iter->indices.second) == type_index_lookup.left.end())
     569        type_index_lookup.left.insert( std::make_pair(iter->indices.second, iter->types.second) );
     570      else {
     571        ASSERT(type_index_lookup.left.at(iter->indices.second) == iter->types.second,
     572            "Extractors::reorderArgumentsByParticleTypes() - entry " +toString(iter->indices.second)+
     573            " is already present with different type "+toString(iter->types.second)
     574            +" than "+toString(type_index_lookup.left.at(iter->indices.second)));
     575      }
     576    }
     577    if (DoLog(3)) {
     578      std::stringstream output;
     579      for (type_index_lookup_t::left_const_iterator indexiter = type_index_lookup.left.begin();
     580          indexiter != type_index_lookup.left.end(); ++indexiter) {
     581        output << " [" << indexiter->first << "," << indexiter->second << "]";
     582      }
     583      LOG(3, "DEBUG: index to type map:" << output.str());
     584    }
     585    if (DoLog(3)) {
     586      std::stringstream output;
     587      for (type_index_lookup_t::right_const_iterator typeiter = type_index_lookup.right.begin();
     588          typeiter != type_index_lookup.right.end(); ++typeiter) {
     589        output << " [" << typeiter->first << "," << typeiter->second << "]";
     590      }
     591      LOG(3, "DEBUG: type to index map " << output.str());
     592    }
     593
     594    /// 1. construct boost::graph from arguments_t (iter)
     595    const size_t N = type_index_lookup.left.size();
     596    UndirectedGraph fragmentgraph(N);
     597    for(FunctionModel::arguments_t::const_iterator iter = args.begin();
     598        iter != args.end(); ++iter) {
     599      if (iter->bonded)
     600        boost::add_edge(iter->indices.first, iter->indices.second, fragmentgraph);
     601    }
     602    index_map_t index_map = boost::get(boost::vertex_index, fragmentgraph);
     603    LOG(2, "DEBUG: We have " << boost::num_vertices(fragmentgraph) << " nodes in the fragment graph.");
     604
     605    /// 2. grab first node of the binding model (gives a type)
     606    const FragmentNode &firstnode = _bindingmodel.getNodes().begin()->first;
     607    const size_t &firsttimes = _bindingmodel.getNodes().begin()->second;
     608    const size_t &firsttype = firstnode.getAtomicNumber(); // TODO: convert to ParticleNumber_t ?
     609
     610    /// 3. grab all candidate nodes contained in arguments_t
     611    std::pair<
     612        type_index_lookup_t::right_const_iterator,
     613        type_index_lookup_t::right_const_iterator> range = type_index_lookup.right.equal_range(firsttype);
     614#ifndef NDEBUG
     615    const size_t checktimes = std::distance(range.first, range.second);
     616    ASSERT( firsttimes == checktimes,
     617        "Extractors::reorderArgumentsByParticleTypes() - unequal amounts of same first type "
     618        +toString(firsttype)+" in HomologyGraph and in type lookup "+toString(checktimes));
     619#endif
     620
     621    /// 4. go over all candidate nodes (gives index)
     622    set_of_nodes_t set_of_nodes;
     623    for (type_index_lookup_t::right_const_iterator rangeiter = range.first;
     624        rangeiter != range.second; ++rangeiter) {
     625      const size_t &rootindex = rangeiter->second;
     626      LOG(2, "DEBUG: Current root index is " << rootindex);
     627
     628      /// 5a. from node in graph (with this index) perform BFS till n-1 (#nodes in binding model)
     629      std::vector<size_t> distances(boost::num_vertices(fragmentgraph));
     630      boost::breadth_first_search(
     631          fragmentgraph,
     632          boost::vertex(rootindex, fragmentgraph),
     633          boost::visitor(record_distance(&distances[0])));
     634      LOG(3, "DEBUG: BFS discovered the following distances " << distances);
     635
     636      /// 5b. and store all node in map with distance to root as key
     637      nodes_per_level_t nodes_per_level;
     638      for (size_t i=0;i<distances.size();++i) {
     639        nodes_per_level.insert( std::make_pair(level_t(distances[i]), node_t(i)) );
     640      }
     641      LOG(3, "DEBUG: Nodes per level are " << nodes_per_level);
     642
     643      /// 6. construct all possible induced connected subgraphs with this map (see fragmentation)
     644      nodes_t nodes;
     645      // rootindex is always contained
     646      nodes.insert( rootindex );
     647      level_t level = 0;
     648
     649      generateAllInducedConnectedSubgraphs(
     650          N-1, level, nodes, set_of_nodes, nodes_per_level, fragmentgraph, distances, index_map);
     651      LOG(2, "DEBUG: We have found " << set_of_nodes.size() << " unique induced, connected subgraphs.");
     652    }
     653
     654    /// 8. go through each induced, connected subgraph
     655    for (set_of_nodes_t::const_iterator nodesiter = set_of_nodes.begin();
     656        nodesiter != set_of_nodes.end(); ++nodesiter) {
     657      /// 9. convert its nodes into a HomologyGraph using the subgraph and arguments_t (types for index)
     658      const nodes_t &nodes = *nodesiter;
     659      const HomologyGraph nodes_graph =
     660          createHomologyGraphFromNodes(nodes, type_index_lookup, fragmentgraph, index_map);
     661
     662      /// 10. compare each converted HomologyGraph with _bindingmodel: Accept or Reject
     663      if (nodes_graph == _bindingmodel) {
     664        LOG(2, "ACCEPT: " << nodes_graph << " is identical to " << _bindingmodel);
     665        /// 11. for each accepted keyset, pick _all_ symmetric distances from arguments_t
     666        FunctionModel::arguments_t argumentbunch;
     667        argumentbunch.reserve(args.size());
     668        for(FunctionModel::arguments_t::const_iterator iter = args.begin();
     669            iter != args.end(); ++iter) {
     670          if ((nodes.count(iter->indices.first) != 0) && (nodes.count(iter->indices.second) != 0)) {
     671            argumentbunch.push_back(*iter);
    254672          }
    255           argumentbunch.push_back(arg);
    256           argument_map.erase(range_t.first);
    257           LOG(4, "DEBUG: Gathered first argument " << arg << ".");
    258         } else {
    259           // go through the range and pick the first argument matching the index constraints
    260           for (TypePair_Argument_Map_t::iterator argiter = range_t.first;
    261               argiter != range_t.second; ++argiter) {
    262             // seconditer may be -1 still
    263             const argument_t &arg = argiter->second;
    264             if (arg.indices.first == *firstindex) {
    265               if ((arg.indices.second == *secondindex) || (*secondindex == (size_t)-1)) {
    266                 if (*secondindex == (size_t)-1)
    267                   *secondindex = arg.indices.second;
    268                 argumentbunch.push_back(arg);
    269                 argument_map.erase(argiter);
    270                 LOG(4, "DEBUG: Gathered another argument " << arg << ".");
    271                 break;
    272               }
    273             } else if ((arg.indices.first == *secondindex) || (*secondindex == (size_t)-1)) {
    274               if (arg.indices.second == *firstindex) {
    275                 if (*secondindex == (size_t)-1)
    276                   *secondindex = arg.indices.first;
    277                 argumentbunch.push_back(arg);
    278                 argument_map.erase(argiter);
    279                 LOG(4, "DEBUG: Gathered another (flipped) argument " << arg << ".");
     673        }
     674        const size_t num_symmetric_distances = nodes.size()*(nodes.size()-1)/2;
     675        ASSERT( argumentbunch.size() == num_symmetric_distances,
     676            "Extractors::reorderArgumentsByParticleTypes() - only "+toString(argumentbunch.size())+
     677            " found instead of "+toString(num_symmetric_distances));
     678        LOG(3, "DEBUG: Final bunch of arguments is " << argumentbunch << ".");
     679
     680        /**
     681         * We still need to bring the arguments in the correct order for the potential.
     682         *
     683         * The potential gives the desired order via the nodes in the HomologyGraph! Their
     684         * sequence matches with the user-defined particle types and because of the properties
     685         * of the homology graph (FragmentNode stores type and number of edges) it also
     686         * matches with the desired bonding graph.
     687         */
     688
     689        /// So, we need to store for each node its type and the number of connected
     690        /// edges, which we extract from the argumentbunch.
     691        node_FragmentNode_map_t node_FragmentNode_map = fillNodeFragmentMap(argumentbunch);
     692
     693        /// Then, we step through the nodes of the bindingmodel
     694        const HomologyGraph::nodes_t bindingmodel_nodes = _bindingmodel.getNodes();
     695        argindex_to_nodeindex_t argindex_to_nodeindex;
     696        size_t nodeindex = 0;
     697        for (HomologyGraph::nodes_t::const_iterator nodeiter = bindingmodel_nodes.begin();
     698            nodeiter != bindingmodel_nodes.end(); ++nodeiter) {
     699          const FragmentNode &node = nodeiter->first;
     700          LOG(3, "DEBUG: Binding model's node #" << node << ".");
     701          // might have to pick several
     702          for (size_t pick_no = 0; pick_no < nodeiter->second; ++pick_no) {
     703            /// ... and pick for each the first (and unique) from these stored nodes.
     704            node_FragmentNode_map_t::iterator mapiter = node_FragmentNode_map.begin();
     705            for (;mapiter != node_FragmentNode_map.end(); ++mapiter) {
     706              if ((mapiter->second.first == node.getAtomicNumber())
     707                  && (mapiter->second.second == node.getConnectedEdges())) {
     708                LOG(3, "DEBUG: #" << mapiter->first << " with type " << mapiter->second.first
     709                    << " and " << mapiter->second.second << " connected edges matches as choice #"
     710                    << pick_no << ".");
    280711                break;
    281712              }
    282713            }
     714            ASSERT( mapiter != node_FragmentNode_map.end(),
     715                "Extractors::reorderArgumentsByParticleTypes() - could not find a suitable node for #"+
     716                toString(mapiter->first)+" with type "+toString(mapiter->second.first)+" and "+
     717                toString(mapiter->second.second)+" connected edges");
     718            std::pair<argindex_to_nodeindex_t::iterator, bool> inserter =
     719                argindex_to_nodeindex.insert( std::make_pair(mapiter->first, nodeindex++) );
     720            ASSERT( inserter.second,
     721                "Extractors::reorderArgumentsByParticleTypes() - node #"+toString(mapiter->first)+
     722                " is already present?");
     723            // remove to ensure uniqueness of choice
     724            node_FragmentNode_map.erase(mapiter);
    283725          }
    284726        }
    285         // move along in indices and check bounds
    286         ++secondindex;
    287         if (secondindex == indices.end()) {
    288           ++firstindex;
    289           if (firstindex != indices.end()-1)
    290             secondindex = firstindex+1;
     727        /// This gives then the desired mapping from indices in the arguments to
     728        /// the index in the order of the HomologyGraph's nodes
     729
     730        /// Finally, we only need to bring the arguments in the typical order:
     731        /// 01 02 03 04 ... 0n, 12 13 14 ... 1n, 23 24 25 ... 2n, ...,  (n-1)n
     732        const size_t num_args = argindex_to_nodeindex.size();
     733        argument_placement_map_t argument_placement_map = fillArgumentsPlacementMap(num_args);
     734        LOG(4, "DEBUG: Placement map is " << argument_placement_map);
     735        ASSERT( argument_placement_map.size() == argumentbunch.size(),
     736            "Extractors::reorderArgumentsByParticleTypes() - placement map has size "+
     737            toString(argument_placement_map.size())+" and we expected "+toString(argumentbunch.size()));
     738
     739        // and finally resort the arguments with the known placement map
     740        FunctionModel::arguments_t sortedargs(argumentbunch);
     741        for (FunctionModel::arguments_t::const_iterator argiter = argumentbunch.begin();
     742            argiter != argumentbunch.end(); ++argiter) {
     743          const argument_t &arg = *argiter;
     744          const argument_placement_map_t::const_iterator indexiter =
     745              argument_placement_map.find( translateIndices(argindex_to_nodeindex, arg.indices) );
     746          ASSERT( indexiter != argument_placement_map.end(),
     747              "Extractors::reorderArgumentsByParticleTypes() - could not find place for edge "+
     748              toString(arg.indices));
     749          sortedargs[indexiter->second] = arg;
     750          LOG(3, "DEBUG: Placed argument " << arg << " at #" << indexiter->second
     751              << " as translated indices are " << translateIndices(argindex_to_nodeindex, arg.indices));
    291752        }
    292       }
    293       ASSERT( (firstindex == indices.end()-1) && (secondindex == indices.end()),
    294           "Extractors::reorderArgumentsByParticleTypes() - we have not gathered enough arguments.");
    295       ASSERT( argumentbunch.size() == UniqueTypes.size(),
    296           "Extractors::reorderArgumentsByParticleTypes() - we have not gathered enough arguments.");
    297       // place bunch of arguments in return args
    298       LOG(3, "DEBUG: Given types " << _types << " and found indices " << indices << ".");
    299       LOG(3, "DEBUG: Final bunch of arguments is " << argumentbunch << ".");
    300       sortedargs.insert(sortedargs.end(), argumentbunch.begin(), argumentbunch.end());
    301     }
    302     returnargs.push_back(sortedargs);
     753        LOG(2, "DEBUG: Sorted arguments are " << sortedargs << ".");
     754
     755        returnargs.push_back(sortedargs);
     756      } else {
     757        LOG(2, "REJECT: " << nodes_graph << " is not identical to " << _bindingmodel);
     758      }
     759    }
    303760  }
    304761
     
    368825  FunctionModel::list_of_arguments_t singlelist_allargs;
    369826  singlelist_allargs.push_back(allargs);
    370   FunctionModel::list_of_arguments_t sortedargs =
     827  FunctionModel::list_of_arguments_t returnargs =
    371828      reorderArgumentsByParticleTypes(singlelist_allargs, _graph, _types, _bindingmodel);
    372   ASSERT( sortedargs.size() == (size_t)1,
    373       "Extractors::filterArgumentsByParticleTypes() - reordering did not generate a single list.");
    374   // then we split up the tuples of arguments and place each into single list
    375   FunctionModel::list_of_arguments_t returnargs;
    376   FunctionModel::arguments_t::const_iterator argiter = sortedargs.begin()->begin();
    377   const size_t num_types = _types.size();
    378   const size_t args_per_tuple = num_types * (num_types-1) / 2;
    379   while (argiter != sortedargs.begin()->end()) {
    380     FunctionModel::arguments_t currenttuple(args_per_tuple);
    381     const FunctionModel::arguments_t::const_iterator startiter = argiter;
    382     std::advance(argiter, args_per_tuple);
    383 #ifndef NDEBUG
    384     FunctionModel::arguments_t::const_iterator endoutiter =
    385 #endif
    386         std::copy(startiter, argiter, currenttuple.begin());
    387     ASSERT( endoutiter == currenttuple.end(),
    388         "Extractors::filterArgumentsByParticleTypes() - currenttuple not initialized to right size.");
    389     returnargs.push_back(currenttuple);
    390   }
     829//  ASSERT( sortedargs.size() == (size_t)1,
     830//      "Extractors::filterArgumentsByParticleTypes() - reordering did not generate a single list.");
     831//  // then we split up the tuples of arguments and place each into single list
     832//  FunctionModel::list_of_arguments_t returnargs;
     833//  FunctionModel::arguments_t::const_iterator argiter = sortedargs.begin()->begin();
     834//  const size_t num_types = _types.size();
     835//  const size_t args_per_tuple = num_types * (num_types-1) / 2;
     836//  while (argiter != sortedargs.begin()->end()) {
     837//    FunctionModel::arguments_t currenttuple(args_per_tuple);
     838//    const FunctionModel::arguments_t::const_iterator startiter = argiter;
     839//    std::advance(argiter, args_per_tuple);
     840//#ifndef NDEBUG
     841//    FunctionModel::arguments_t::const_iterator endoutiter =
     842//#endif
     843//        std::copy(startiter, argiter, currenttuple.begin());
     844//    ASSERT( endoutiter == currenttuple.end(),
     845//        "Extractors::filterArgumentsByParticleTypes() - currenttuple not initialized to right size.");
     846//    returnargs.push_back(currenttuple);
     847//  }
    391848
    392849  LOG(2, "DEBUG: We have generated " << returnargs.size() << " tuples of distances.");
  • src/FunctionApproximation/unittests/ExtractorsUnitTest.cpp

    r228340 r2124aa  
    9898    CPPUNIT_ASSERT( (args[i].distance >= 0) && (args[i].distance <= 4));
    9999}
     100
     101/** UnitTest for gatherAllSymmetricDistances() with edges
     102 */
     103void ExtractorsTest::gatherAllSymmetricDistances_edgesTest()
     104{
     105  // create positions
     106  Fragment::positions_t positions;
     107  Fragment::position_t pos(3, 0.);
     108  for (double i = 0; i < 5; i+=1.) {
     109    pos[0] = i;
     110    positions.push_back(pos);
     111  }
     112  FragmentationEdges::edges_t edges;
     113  for (size_t i=1;i<5;++i)
     114    edges += FragmentationEdges::edge_t(i-1, i);
     115
     116  // create charges
     117  Fragment::atomicnumbers_t atomicnumbers;
     118  atomicnumbers += 6, 6, 1, 1, 1;
     119
     120  // create distances
     121  FunctionModel::arguments_t args =
     122      Extractors::gatherAllSymmetricDistances(positions, atomicnumbers, edges, 0);
     123  CPPUNIT_ASSERT_EQUAL( (size_t)(5*4/2), args.size() );
     124
     125  // check created args
     126  for (size_t i=0; i< 5*4/2; ++i) {
     127    CPPUNIT_ASSERT( (args[i].distance >= 0) && (args[i].distance <= 4));
     128    if ((args[i].indices.first+1) == args[i].indices.second)
     129      CPPUNIT_ASSERT( args[i].bonded );
     130  }
     131}
  • src/FunctionApproximation/unittests/ExtractorsUnitTest.hpp

    r228340 r2124aa  
    2323    CPPUNIT_TEST_SUITE( ExtractorsTest) ;
    2424    CPPUNIT_TEST ( gatherAllSymmetricDistancesTest );
     25    CPPUNIT_TEST ( gatherAllSymmetricDistances_edgesTest );
    2526    CPPUNIT_TEST_SUITE_END();
    2627
     
    2930      void tearDown();
    3031      void gatherAllSymmetricDistancesTest();
     32      void gatherAllSymmetricDistances_edgesTest();
    3133
    3234private:
  • src/FunctionApproximation/unittests/Makefile.am

    r228340 r2124aa  
    2020FUNCTIONAPPROXIMATIONLIBS = \
    2121        libUnitTest.la \
    22         ../libMolecuilder.la \
    2322        ../libMolecuilderFunctionApproximation.la \
     23        ../libMolecuilderFragmentation.la \
     24        ../libMolecuilderFragmentation_getFromKeysetStub.la \
    2425        $(top_builddir)/LinearAlgebra/src/LinearAlgebra/libLinearAlgebra.la \
    2526        ${CodePatterns_LIBS} \
Note: See TracChangeset for help on using the changeset viewer.