source: src/Graph/Graph6Writer.cpp@ 1c0b0b

Candidate_v1.7.0 stable
Last change on this file since 1c0b0b was 1c0b0b, checked in by Frederik Heber <frederik.heber@…>, 3 years ago

Graph6Writer::write_elementlist uses BFS from boundary atom.

  • the elementlist is not stable as the set of atoms is arbitrary to any kind of permutation. However, the underlying bond graph is not, even though it may also have some symmetries.
  • Therefore, we use a BFS from a non-hydrogen atom on the boundary.
  • FIX: BoosGraphCreator gets vector of const atom*.
  • Property mode set to 100644
File size: 6.0 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2021 Frederik Heber. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * Graph6Writer.cpp
25 *
26 * Created on: Apr 2, 2021
27 * Author: heber
28 */
29
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include "Graph6Writer.hpp"
37
38#include "CodePatterns/Assert.hpp"
39#include "CodePatterns/Log.hpp"
40
41#include <cassert>
42#include <cmath>
43#include <iostream>
44
45#include "Atom/atom.hpp"
46#include "Descriptors/AtomIdDescriptor.hpp"
47#include "Element/element.hpp"
48#include "Graph/BoostGraphCreator.hpp"
49#include "Graph/BreadthFirstSearchGatherer.hpp"
50#include "World.hpp"
51
52//#include "CodePatterns/MemDebug.hpp"
53
54Graph6Writer::Graph6Writer(const std::vector<const atom *> atoms):
55 _atoms(atoms)
56{}
57
58void Graph6Writer::write_n(std::ostream& out) {
59 const unsigned long n = _atoms.size();
60
61 if (n<62) {
62 out << ((unsigned char)(n+63));
63 return;
64 }
65
66 out << ((unsigned char)126);
67 int num_bytes = 2;
68 if (n> 258047) {
69 out << ((unsigned char)126);
70 num_bytes = 3;
71 }
72 for(int value=num_bytes; value>=0; value--) {
73 unsigned char c = 0;
74 int n_pos = 6*(value+1)-1;
75 for(int c_pos=5; c_pos>=0; n_pos--, c_pos--) {
76 c += (n & (1<<n_pos))>>((int)n_pos/6);
77 }
78 out << (c+63);
79 }
80
81}
82
83/* Given an iterator over the adjacency matrix in the order (0,1),(0,2),(1,2),(0,3),(1,3),(2,3),...,(n-1,n)
84 this writes a graph6 representation to out. */
85void Graph6Writer::write_graph6(std::ostream& out) {
86 write_n(out);
87
88 const unsigned long n = _atoms.size();
89
90 unsigned char value = 0;
91 int byte_pos = 5;
92 unsigned int bytes_written = 0;
93 for (size_t j=0; j<n; ++j)
94 for (size_t i=0; i<j; ++i) {
95 // std::cout << "\t\n" << (int)value << " " << byte_pos << std::endl;
96
97 unsigned int bit = _atoms[i]->IsBondedTo(_atoms[j]);
98 LOG(2, "DEBUG: (" << i << "," << j << ") = " << bit << "," << value << " | " << bit << " << " << byte_pos << " = " << (unsigned int)value << " | " << (bit << byte_pos));
99 value = value | (bit << byte_pos--);
100 if (byte_pos < 0) {
101 LOG(2, "DEBUG: Writing byte " << value << " into range [" << (unsigned char)63 << "," << (unsigned char)126 << "]");
102 ASSERT( (value+63) <= 126,
103 "Graph6Writer::write_graph6() - char to write is outside "+toString((unsigned char)63)
104 +" and "+toString((unsigned char)126));
105 out << (unsigned char)(value+63);
106 bytes_written++;
107 value = 0;
108 byte_pos = 5;
109 }
110 }
111 if (byte_pos!=5) {
112 ASSERT( (value+63) <= 126,
113 "Graph6Writer::write_graph6() - char to write is outside "+toString((unsigned char)63)
114 +" and "+toString((unsigned char)126));
115 LOG(2, "DEBUG: Writing byte " << value << " into range [" << (unsigned char)63 << "," << (unsigned char)126 << "]");
116 out << (unsigned char)(value+63);
117 bytes_written++;
118 value=0;
119 }
120 ASSERT( value==0,
121 "Graph6Writer::write_graph6() - byte is not null, i.e. chars left to write?");
122 ASSERT( bytes_written == (unsigned int)ceil(n*(n-1)/12.0f),
123 "Graph6Writer::write_graph6() - unexpected number of bytes written");
124}
125
126/**
127 * Picks a non-hydrogen from all hydrogen atoms in the current set
128 * of atoms.
129 *
130 * Returns -1 if none could be found.
131 */
132atomId_t Graph6Writer::getBoundaryNonHydrogen() const {
133 atomId_t start_atom_id = -1;
134 int bond_degree = 16;
135 for(std::vector<const atom *>::const_iterator iter = _atoms.begin();
136 iter != _atoms.end(); ++iter) {
137 const atom *walker = *iter;
138 if (walker->getElement().getSymbol() == "H") {
139 const BondList& bond_list = walker->getListOfBonds();
140 assert(bond_list.size() == 1);
141 const atom *other = bond_list.front().get()->GetOtherAtom(walker);
142 const int number_of_bonds = other->getListOfBonds().size();
143 if ((other->getElement().getSymbol() != "H") && (bond_degree > number_of_bonds)) {
144 start_atom_id = other->getId();
145 bond_degree = number_of_bonds;
146 }
147 }
148 }
149 return start_atom_id;
150}
151
152bool OnlyNonHydrogens(const bond &_bond) {
153 return _bond.HydrogenBond == 0;
154}
155
156void Graph6Writer::write_elementlist(std::ostream& out) {
157 /** Execute a Breadth-First Search discovery from one terminal atom (e.g.,
158 * pick random hydrogen and then it's bond-neighbor if it is non-hydrogen).
159 * Then return the element list in that ordering.
160 *
161 * The graph6 string does not account for the inherent graph symmetries
162 * (e.g., BW having 123<->321 but not 123<->132 symmetry).
163 */
164 // pick bond neighbor of a hydrogen atom
165 atomId_t start_atom_id = getBoundaryNonHydrogen();
166 if (start_atom_id == (unsigned int)-1) {
167 // fall back to first atom in list
168 start_atom_id = _atoms.front()->getId();
169 }
170
171 // do an unlimited BFS and get set of nodes, ordered by discovery level
172 BoostGraphCreator graphCreator;
173 graphCreator.createFromAtoms(_atoms, OnlyNonHydrogens);
174 BreadthFirstSearchGatherer gatherer(graphCreator);
175 const std::vector<atomId_t> gathered_atoms = gatherer(start_atom_id);
176
177 // print all nodes
178 const World& world = World::getConstInstance();
179 for (std::vector<atomId_t>::const_iterator iter = gathered_atoms.begin();
180 iter != gathered_atoms.end(); ++iter) {
181 if (iter != gathered_atoms.begin())
182 out << ' ';
183 const atom* walker = world.getAtom(AtomById(*iter));
184 assert(walker != NULL);
185 out << walker->getElement().getSymbol();
186 }
187}
188
Note: See TracBrowser for help on using the repository browser.