pastebin - collaborative debugging

pastebin is a collaborative debugging tool allowing you to share and modify code snippets while chatting on IRC, IM or a message board.

This site is developed to XHTML and CSS2 W3C standards. If you see this paragraph, your browser does not support those standards and you need to upgrade. Visit WaSP for a variety of options.

pastebin - collaborative debugging tool View Help


Posted by timvdm on Wed 16 Sep 14:44
report abuse | download | new post

  1. /***************************************************************************
  2.  *   Copyright (C) 2009 by Tim Vandermeersch                               *
  3.  *                                                                         *
  4.  *   This program is free software; you can redistribute it and/or modify  *
  5.  *   it under the terms of the GNU General Public License as published by  *
  6.  *   the Free Software Foundation; either version 2 of the License, or     *
  7.  *   (at your option) any later version.                                   *
  8.  *                                                                         *
  9.  *   This program is distributed in the hope that it will be useful,       *
  10.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
  11.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
  12.  *   GNU General Public License for more details.                          *
  13.  *                                                                         *
  14.  *   You should have received a copy of the GNU General Public License     *
  15.  *   along with this program; if not, write to the                         *
  16.  *   Free Software Foundation, Inc.,                                       *
  17.  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  18.  ***************************************************************************/
  19. #include <iostream>
  20. #include <vector>
  21. #include <algorithm>
  22. #include <cassert>
  23.  
  24. #include <Eigen/Core>
  25.  
  26. #include <openbabel/mol.h>
  27. #include <openbabel/obconversion.h>
  28. #include <openbabel/graphsym.h>
  29.  
  30. /**
  31.  * This class represents a shortened mapping for permutations.
  32.  *
  33.  * There are three ways to symbolically represent permutations:
  34.  @verbatim
  35.  cycle notation:  (1 2 3)    (rotate to the left: 1 2 3 -> 2 3 1)
  36.  
  37.  standard mapping:  1 2 3    (first line are the original numbers in ascending order)
  38.                     2 3 1    (second line contains the element to which it is permuted)
  39.  
  40.  shortened mapping:  2 3 1   (avoid first line in standard mapping: efficient storage)
  41.  @endverbatim
  42.  *
  43.  * For efficiency reasons, the shortened mapping notation is used to internally store
  44.  * the permutations. The elements are always in the range \f$0-n\f$ where \f$n\f$ is the
  45.  * number of elements (i.e. atoms).
  46.  *
  47.  * Multiple Permutations can be combined in a PermutationGroup. The permutation
  48.  * group \f$S_n\f$, is the group containing all \f$n!\f$ permutations.
  49.  *
  50.  * The automorphism group is a subgroup of \f$S_n\f$. It consists of all vertex (i.e. atom)
  51.  * permutations which conserve the edge connections (i.e. bonds). There is always
  52.  * at least one permutation in the automorphism group, namely the identity permutation.
  53.  * Since it doesn't change any connections, it fulfills the previously given definition.
  54.  * To check if other permuations belong to the automorphism group, the following matrix
  55.  * multiplication equation can be used
  56.  \f[
  57.    P^T A P = A
  58.  \f]
  59.  * For small molecules (less than 10 atoms) it is possible to generate all the
  60.  * permutations in the \f$S_n\f$ group and apply the formula. However, to have reasonable
  61.  * performance for larger molecules, it is needed to limit the number of permutations
  62.  * that need to be checked. In practise, a Graph Invariant Atom Partitioning (GIAP)
  63.  * algorithm is used for this (e.g. Morgan's Extended Connectivity (EC)). See the
  64.  * OBGraphSym class documentation for more details. [note: an orbit is the group of atoms
  65.  * in a molecule that belong to the same partitioned group -- or the atoms with the same
  66.  * symmetry class]
  67.  *
  68.  * Once the initial atom partitioning in orbits is obtained, this can be used to generate
  69.  * the subgroup of \f$S_n\f$ knowing that permutations between orbits are not allowed
  70.  * (i.e. these permutations will never satisfy the matrix multiplication equation).
  71.  * Orbits with only one atom will never be changed in any permutation belonging to the
  72.  * automorphisms group and will therefor be ignored. The number of permutations to be
  73.  * tested depends on the number of remaining orbits and the number of particles contained
  74.  * in them. For example a molecule with 12 atoms, 2 orbits, each containing 3 atoms will require
  75.  * \f$3! \times 3! = 36\f$ tests. [note: \f$12! = 4790 001 600\f$]
  76.  *
  77.  * @image html 12a2o3a.png
  78.  *
  79.  */
  80. struct Permutation
  81. {
  82.   /**
  83.    * Default constructor.
  84.    */
  85.   Permutation()
  86.   {}
  87.  
  88.   /**
  89.    * Constructor taking a @p map as argument.
  90.    */
  91.   Permutation(const std::vector<unsigned int> &_map) : map(_map)
  92.   {}
  93.  
  94.   /**
  95.    * Constructor taking a @p matrix as argument.
  96.    */
  97.   Permutation(const Eigen::MatrixXi &matrix)
  98.   {
  99.     setMatrix(matrix);
  100.   }
  101.  
  102.   /**
  103.    * Copy constructor.
  104.    */
  105.   Permutation(const Permutation &other)
  106.   {
  107.     this->map = other.map;
  108.   }
  109.  
  110.   /**
  111.    * Print the permutation to std::cout in shortened notation.
  112.    */
  113.   void print() const
  114.   {
  115.     std::vector<unsigned int>::const_iterator i;
  116.     for (i = map.begin(); i != map.end(); ++i)
  117.       std::cout << *i << " ";
  118.     std::cout << std::endl;    
  119.   }
  120.  
  121.   /**
  122.    * Compute the permutation matrix for this Permutation.
  123.    */
  124.   Eigen::MatrixXi matrix() const
  125.   {
  126.     int n = map.size();
  127.     Eigen::MatrixXi P = Eigen::MatrixXi::Zero(n, n);
  128.  
  129.     for (int i = 0; i < n; ++i) {
  130.       P(map.at(i)-1, i) = 1;
  131.       P(i, map.at(i)-1) = 1;
  132.     }
  133.  
  134.     return P;
  135.   }
  136.  
  137.   /**
  138.    * Set the permutation matrix for this permutation. The matrix is
  139.    * converted to a shortened notation permutation.
  140.    */
  141.   void setMatrix(const Eigen::MatrixXi &m)
  142.   {
  143.     if (m.rows() != m.cols())
  144.       return;
  145.  
  146.     int size = m.rows();
  147.     map.resize(size);
  148.  
  149.     for (int i = 0; i < size; ++i)
  150.       for (int j = 0; j < size; ++j)
  151.         if (m(i,j))
  152.           map[i] = j + 1;  
  153.   }
  154.  
  155.   /**
  156.    * Multiply two permutation matrices and return the resulting permutation.
  157.    * This is the same as applying the two permutations consecutively.
  158.    */
  159.   Permutation operator*(const Permutation &rhs) const
  160.   {
  161.     Eigen::MatrixXi res = rhs.matrix() * matrix();
  162.     return Permutation(res);
  163.   }
  164.  
  165.   /**
  166.    * Equality operator. Two permutations are equal if their shortened notations
  167.    * are the same (compared element-by-element).
  168.    */
  169.   bool operator==(const Permutation &rhs) const
  170.   {
  171.     if (map.size() != rhs.map.size())
  172.       return false;
  173.    
  174.     std::vector<unsigned int>::const_iterator i1 = map.begin();
  175.     std::vector<unsigned int>::const_iterator i2 = rhs.map.begin();
  176.     for (; i1 != map.end(); ++i1, ++i2)
  177.       if (*i1 != *i2)
  178.         return false;
  179.  
  180.     return true;  
  181.   }
  182.  
  183.   std::vector<unsigned int> map; //!< The actual mapping in shortened notation
  184. };
  185.  
  186. struct PermutationGroup
  187. {
  188.   /**
  189.    * Default constructor.
  190.    */
  191.   PermutationGroup()
  192.   {}
  193.  
  194.   /**
  195.    * Constructor taking vector of permutations as argument.
  196.    */
  197.   PermutationGroup(const std::vector<Permutation> &_permutations) : permutations(_permutations)
  198.   {}
  199.  
  200.   /**
  201.    * @return The number of permutations in this group.
  202.    */
  203.   unsigned int size() const
  204.   {
  205.     return permutations.size();
  206.   }
  207.  
  208.   /**
  209.    * Add permutation @p p to this permutation group.
  210.    */
  211.   void add(const Permutation &p)
  212.   {
  213.     permutations.push_back(p);
  214.   }
  215.  
  216.   /**
  217.    * @return A constant reference to the @p index-th permutation.
  218.    */
  219.   const Permutation& at(unsigned int index)
  220.   {
  221.     return permutations.at(index);
  222.   }
  223.  
  224.   /**
  225.    * @return True if this permutation group contains permutation @p p.
  226.    */
  227.   bool contains(const Permutation &p) const
  228.   {
  229.     std::vector<Permutation>::const_iterator i;
  230.     for (i = permutations.begin(); i != permutations.end(); ++i)
  231.       if (*i == p)
  232.         return true;
  233.     return false;
  234.   }
  235.  
  236.   std::vector<Permutation> permutations; //!< The actual permutations in the group
  237. };
  238.  
  239.  
  240.  
  241. struct Orbit
  242. {
  243.   Orbit(const std::vector<unsigned int> &_ordered) : ordered(_ordered), current(ordered)
  244.   {
  245.   }
  246.  
  247.   Orbit(const Orbit &other) : ordered(other.ordered), current(other.ordered)
  248.   {
  249.   }
  250.  
  251.   std::vector<unsigned int> ordered;
  252.   std::vector<unsigned int> current;
  253. };
  254.  
  255. typedef std::vector<Orbit> Orbits;
  256.  
  257. /**
  258.  * Compute the \f$S_n\f$ group consisting of all \f$n!\f$ permutations.
  259.  */
  260. PermutationGroup computeAllPermutations(const Permutation &input)
  261. {
  262.   PermutationGroup Sn;
  263.   Permutation p = input;
  264.   Sn.add(p);
  265.          
  266.   while (std::next_permutation(p.map.begin(), p.map.end())) {
  267.     Sn.add(p);
  268.   }
  269.  
  270.   return Sn;
  271. }
  272.  
  273. Orbits computeOrbits(const std::vector<unsigned int> &symClasses)
  274. {
  275.   Orbits orbits;
  276.   std::vector<bool> visit(symClasses.size(), false);
  277.  
  278.   std::vector<unsigned int>::const_iterator symClass;
  279.   for (symClass = symClasses.begin(); symClass != symClasses.end(); ++symClass) {
  280.     if (visit.at(*symClass))
  281.       continue;
  282.     visit[*symClass] = true;
  283.  
  284.     unsigned int n = std::count(symClasses.begin(), symClasses.end(), *symClass);
  285.     if (n >= 2) {
  286.       std::vector<unsigned int> orbit;
  287.      
  288.       for (unsigned int i = 0; i < symClasses.size(); ++i)
  289.         if (symClasses.at(i) == *symClass)
  290.           orbit.push_back(i);
  291.  
  292.       orbits.push_back(Orbit(orbit));
  293.     }
  294.   }
  295.  
  296.   return orbits;
  297. }
  298.  
  299. Permutation processOrbitPermutations(const Orbit &orbit, const Permutation &E)
  300. {
  301.   Permutation p = E;
  302.   for (unsigned int i = 0; i < orbit.ordered.size(); ++i) {
  303.     std::cout << "p.map.at(" << orbit.ordered.at(i) << ") = E.map.at(" << orbit.current.at(i) << ")" << std::endl;
  304.     p.map.at(orbit.ordered.at(i)) = E.map.at(orbit.current.at(i));
  305.   }
  306.   return p;
  307. }
  308.  
  309. bool sameMatrix(const Eigen::MatrixXi &m1, const Eigen::MatrixXi &m2)
  310. {
  311.   if (m1.cols() != m2.cols())
  312.     return false;
  313.   if (m1.rows() != m2.rows())
  314.     return false;
  315.  
  316.   for (int i = 0; i < m1.rows(); ++i)
  317.     for (int j = 0; j < m1.cols(); ++j)
  318.       if (m1(j,i) != m2(j,i))
  319.         return false;
  320.  
  321.   return true;
  322. }
  323.  
  324. PermutationGroup computeAutomorphisms(const PermutationGroup &g, const Eigen::MatrixXi &A)
  325. {
  326.   PermutationGroup G;
  327.  
  328.   std::vector<Permutation>::const_iterator p;
  329.   for (p = g.permutations.begin(); p != g.permutations.end(); ++p) {
  330.     Eigen::MatrixXi P = (*p).matrix();
  331.     Eigen::MatrixXi A2 = P.transpose() * A *  P;
  332.  
  333.     if (sameMatrix(A, A2)) {
  334.       G.add(*p);
  335.       (*p).print();
  336.     }
  337.   }
  338.  
  339.   return G;
  340. }
  341.  
  342.  
  343. /**
  344.  * Compute the adjacency matrix for the specified molecule. This matrix
  345.  * represents the connectivity in the molecule. It can be used to determine
  346.  * if a Permutation is part of the automorphism group.
  347.  */
  348. Eigen::MatrixXi adjacencyMatrix(OpenBabel::OBMol *mol)
  349. {
  350.   int n = mol->NumAtoms();
  351.   Eigen::MatrixXi A = Eigen::MatrixXi::Zero(n, n);
  352.  
  353.   using OpenBabel::OBMolBondIter;
  354.   std::cout << "bonds:" << std::endl;
  355.   FOR_BONDS_OF_MOL (bond, mol)
  356.     std::cout << "  " << bond->GetBeginAtomIdx() << "-" << bond->GetEndAtomIdx() << std::endl;
  357.  
  358.   for (int i = 0; i < n; ++i) {
  359.     OpenBabel::OBAtom *ai = mol->GetAtom(i+1);
  360.     for (int j = 0; j < n; ++j) {
  361.       if (i == j)
  362.         continue;
  363.       if (ai->IsConnected(mol->GetAtom(j+1)))
  364.         A(i, j) = 1;
  365.     }
  366.   }
  367.  
  368.   return A;
  369. }
  370.  
  371. ////////////////////////////////////////////////////////////////////////////////
  372. //
  373. //
  374. //  O P E N B A B E L   S P E C I F I C   C O D E
  375. //
  376. //
  377. ////////////////////////////////////////////////////////////////////////////////
  378.  
  379. OpenBabel::OBMol* ReadMolecule(const std::string &filename)
  380. {
  381.   OpenBabel::OBConversion conv;
  382.   OpenBabel::OBFormat *format = conv.FormatFromExt(filename.c_str());
  383.   if (!format || !conv.SetInFormat(format)) {
  384.     std::cout << "ERROR: Could not detect file format for: " << filename << std::endl;
  385.     return 0;
  386.   }
  387.  
  388.   std::ifstream ifs;
  389.   ifs.open(filename.c_str());
  390.   if (!ifs) {
  391.     std::cout << "ERROR: Could not open file: " << filename << std::endl;
  392.     return 0;  
  393.   }
  394.  
  395.   OpenBabel::OBMol *obmol = new OpenBabel::OBMol;
  396.   if (!conv.Read(obmol, &ifs)) {
  397.     std::cout << "ERROR: Could not read molecule from file: " << filename << std::endl;
  398.     delete obmol;
  399.     ifs.close();
  400.     return 0;
  401.   }
  402.  
  403.   ifs.close();
  404.   return obmol;
  405. }
  406.  
  407. ////////////////////////////////////////////////////////////////////////////////
  408. //
  409. //
  410. //  U N I T   T E S T S
  411. //
  412. //
  413. ////////////////////////////////////////////////////////////////////////////////
  414.  
  415.  
  416.  
  417. void testPermutation()
  418. {
  419.   std::vector<unsigned int> e;
  420.   e.push_back(1);
  421.   e.push_back(2);
  422.   e.push_back(3);
  423.   e.push_back(4);
  424.   Permutation p1(e), p2(e);
  425.  
  426.   assert(p1 == p2);
  427. }
  428.  
  429. void testPermutationGroup()
  430. {
  431.   Permutation p;
  432.   p.map.push_back(1);
  433.   p.map.push_back(2);
  434.   p.map.push_back(3);
  435.   p.map.push_back(4);
  436.  
  437.   PermutationGroup group;
  438.   assert( !group.contains(p) );
  439.  
  440.   group.add(p);
  441.   assert( group.contains(p) );
  442. }
  443.  
  444. void unitTests()
  445. {
  446.   testPermutation();
  447.   testPermutationGroup();
  448.  
  449. }
  450.  
  451. int main(int argc, char **argv)
  452. {
  453.   unitTests();
  454.  
  455.   if (argc < 2) {
  456.     std::cout << "Usage: " << argv[0] << " <filename>" << std::endl;
  457.     return -1;
  458.   }
  459.  
  460.   OpenBabel::OBMol *obmol = ReadMolecule(argv[1]);
  461.   if (!obmol)
  462.     return -1;
  463.  
  464.   std::vector<unsigned int> elements;
  465.   for (int i = 0; i < obmol->NumAtoms(); ++i) {
  466.     elements.push_back(i+1);
  467.   }
  468.  
  469.   Permutation E(elements); // identity
  470.  
  471.   OpenBabel::OBGraphSym graphSym(obmol);
  472.   std::vector<unsigned int> symClasses;
  473.   graphSym.GetSymmetry(symClasses);
  474.   const Orbits sortedOrbits = computeOrbits(symClasses);
  475.  
  476.   PermutationGroup G0, G1;
  477.  
  478.   Orbits orbits = sortedOrbits;;
  479.   for (unsigned int i = 0; i < orbits.size(); ++i) {
  480.     // print the orbit
  481.     std::cout << "orbit: ";
  482.     for (unsigned int j = 0; j < orbits.at(i).ordered.size(); ++j) {
  483.       std::cout << orbits.at(i).ordered.at(j) << " ";
  484.     }
  485.     std::cout << std::endl;
  486.  
  487.     // sorted orbit
  488.     Orbit e = orbits.at(i);
  489.  
  490.     Permutation p = processOrbitPermutations(orbits.at(i), E);
  491.     if (!G0.contains(p))
  492.       G0.add(p);
  493.     while (std::next_permutation(orbits.at(i).current.begin(), orbits.at(i).current.end())) {
  494.       p = processOrbitPermutations(orbits.at(i), E);
  495.  
  496.       if (!G0.contains(p))
  497.         G0.add(p);
  498.     }
  499.     std::cout << std::endl;
  500.   }
  501.  
  502.   for (unsigned int i = 0; i < G0.size(); ++i) {
  503.     for (unsigned int j = 0; j < G0.size(); ++j) {
  504.       if (i == j)
  505.         continue;
  506.  
  507.       Permutation p = G0.at(i) * G0.at(j);
  508.       if (!G1.contains(p))
  509.         G1.add(p);
  510.     }
  511.   }
  512.  
  513.   for (unsigned int i = 0; i < G1.size(); ++i)
  514.     if (!G0.contains(G1.at(i)))
  515.       G0.add(G1.at(i));
  516.  
  517.   G1.permutations.clear();
  518.  
  519.   // print automorphisms
  520.   std::cout << "G0:" << std::endl;
  521.   for (unsigned int i = 0; i < G0.size(); ++i)
  522.     G0.at(i).print();
  523.   // print automorphisms
  524.   std::cout << "G1:" << std::endl;
  525.   for (unsigned int i = 0; i < G1.size(); ++i)
  526.     G1.at(i).print();
  527.  
  528.  
  529.   // compute adjacency matrix
  530.   Eigen::MatrixXi A = adjacencyMatrix(obmol);
  531.   // compute the permutation group of all n! permutations
  532.   PermutationGroup Sn = computeAllPermutations(E);
  533.   // compute automorphisms starting with the limited group
  534.   PermutationGroup G3 = computeAutomorphisms(G0, A);
  535.   // compute automorphisms starting with entire permutation group
  536.   PermutationGroup G4 = computeAutomorphisms(Sn, A);
  537.  
  538.  
  539.   if (G3.size() != G4.size()) {
  540.     std::cout << "NOT ALL AUTOMORPHISMS ARE FOUND!!" << std::endl;
  541.     return 0;
  542.   }
  543.   bool success = true;
  544.   for (unsigned int i = 0; i < G4.size(); ++i)
  545.     if (!G3.contains(G4.at(i))) {
  546.       std::cout << "COULD NOT FIND AUTOMORPHISM: ";
  547.       G3.at(i).print();
  548.       std::cout << std::endl;
  549.       success = false;
  550.     }
  551.  
  552.   if (success) {
  553.     std::cout << "ALL AUTOMORPHISMS FOUND!!" << std::endl;
  554.   }
  555.  
  556.   return 0;
  557. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with @@


Remember me so that I can delete my post