libgsjj  1
Deterministic finite automata learning library
BinaryCNFMethod.cpp
Go to the documentation of this file.
1 
8 
9 #include <iostream>
10 #include <cmath>
11 
12 #include "MapleCOMSPS_LRB/utils/System.h"
13 
14 #include "gsjj/binary.h"
15 
16 using namespace Minisat;
17 
18 namespace gsjj {
19  namespace passive {
20  namespace CNF {
21  BinaryCNFMethod::BinaryCNFMethod(const std::set<std::string> &SpSet, const std::set<std::string> &SmSet, const std::set<std::string> &SSet, const std::set<std::string> &prefixesSet, const std::set<char> &alphabetSet, unsigned int n) :
22  Method(SpSet, SmSet, SSet, prefixesSet, alphabetSet, n)
23  {
24  // Defaults values for the options (taken from the bcsat code)
25  m_simplify_opts.constant_folding = true;
26  m_simplify_opts.downward_bcp = true;
27  m_simplify_opts.remove_duplicate_children = true;
28  m_simplify_opts.remove_g_not_g_children = true;
29  m_simplify_opts.inline_equivalences = true;
30  m_simplify_opts.misc_reductions = true;
31  m_simplify_opts.use_coi = true;
32  m_simplify_opts.absorb_children = SimplifyOptions::CHILDABSORB_NONE;
33  verbose = false;
34 
35  m_binarySize = std::ceil(std::log2(n));
36 
38  }
39 
41 
42  }
43 
45  // We explicitly handles the case where n = 1 <=> m = 0
46  if (m_numberStates == 1) {
47  return m_hasSolution = (m_Sp.size() == 0 || m_Sm.size() == 0);
48  }
49 
50  // If needed, we create the solver
51  if (!m_solver) {
52  std::atomic_bool falseBool(false);
53  m_solver = std::make_unique<BC>(falseBool);
54  }
55 
57  m_solver->force_true(createFormula());
58 
59  m_triedSolve = true;
60  m_cpuTimeStart = cpuTime();
61  // minisat_solver returns 1 iff the formula is sat
62  m_hasSolution = m_solver->minisat_solve(true, m_simplify_opts, false, false, false, false, 0) == 1;
63  m_cpuTimeEnd = cpuTime();
64  return m_hasSolution;
65  }
66 
67  std::unique_ptr<DFA<char>> BinaryCNFMethod::constructDFA() {
68  // First, we explicitly handles the case where n = 1
69  if (m_numberStates == 1) {
70  auto dfa = std::make_unique<DFA<char>>(0, m_Sp.size() != 0);
71  for (const char &a : m_alphabet) {
72  dfa->addTransition(0, a, 0);
73  }
74  return dfa;
75  }
76 
77  // We try to solve if it is not yet done
78  if (!m_triedSolve) {
79  if (!solve()) {
80  return nullptr;
81  }
82  }
83 
84  // If the formula is not satisfiable, we return a null pointer
85  if (!hasSolution()) {
86  return nullptr;
87  }
88  else {
89  return toDFA();
90  }
91  }
92 
94  return "binary";
95  }
96 
97  void BinaryCNFMethod::setStopTrigger(const std::chrono::seconds &timeLimit, std::atomic_bool &stopTrigger, const bool *stopPointer) {
98  m_solver = std::make_unique<BC>(stopTrigger);
99  }
100 
102  for (const auto &u : m_prefixes) {
103  for (unsigned int i = 0 ; i < m_binarySize ; i++) {
104  Gate *var = m_solver->new_VAR();
105  m_stateToVar.emplace(std::make_pair(u, i), var);
106  }
107  }
108  }
109 
111  Gate *formula = nullptr;
112 
113  // For every ua, va in prefixes such that the last letter of ua is the same as the last letter of va
114  for (const auto &ua : m_prefixes) {
115  if (ua.size() > 0) {
116  const char &a = ua.at(ua.size() - 1);
117  const std::string &u = ua.substr(0, ua.size() - 1);
118 
119  for (const auto &va : m_prefixes) {
120  if (va.size() > 0 && va.at(va.size() - 1) == a) {
121  const std::string &v = va.substr(0, va.size() - 1);
122  // We must force that x_u = x_v => x_{ua} = x_{va}
123  // In other words, we force that the transition function is correct and coherent
124  // So, we create a formula for NOT (x_u = x_v) OR x_ua = x_va
125  Gate *different = phi_different(u, v);
126  Gate *eq = equal(ua, va);
127  Gate *full = m_solver->new_OR(different, eq);
128 
129  if (formula) {
130  formula = m_solver->new_AND(formula, full);
131  }
132  else {
133  formula = full;
134  }
135  }
136  }
137  }
138  }
139 
140  // Now, we want to express that for every u in Sp and every v in Sm, x_u is different than x_v
141  // In other words, u and v can not end in the same state (since u must be accepted and v rejected)
142  for (const auto &u : m_Sp) {
143  for (const auto &v : m_Sm) {
144  formula = m_solver->new_AND(formula, phi_different(u, v));
145  }
146  }
147 
148  // Finally, we must make sure that the encoding is correct even if the number of states is not a power of 2
149  // http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
150  for (const auto &u : m_prefixes) {
151  for (unsigned int i = m_numberStates ; i < pow(2, m_binarySize) ; i++) {
152  formula = m_solver->new_AND(formula, phi_not_q_i(u, i));
153  }
154  }
155 
156  return formula;
157  }
158 
159  Gate* BinaryCNFMethod::equal(const std::string &u, const std::string &v) {
160  return m_solver->new_NOT(phi_different(u, v));
161  }
162 
163  Gate* BinaryCNFMethod::phi_different(const std::string &u, const std::string &v) {
164  Gate *gate = nullptr;
165 
166  for (unsigned int j = 0 ; j < m_binarySize ; j++) {
167  Gate *x_uj = m_stateToVar.find(std::make_pair(u, j))->second;
168  Gate *x_vj = m_stateToVar.find(std::make_pair(v, j))->second;
169  Gate *left = m_solver->new_AND(x_uj, m_solver->new_NOT(x_vj));
170  Gate *right = m_solver->new_AND(m_solver->new_NOT(x_uj), x_vj);
171  Gate *total = m_solver->new_OR(left, right);
172  // If gate already exists, we just add an OR operation
173  if (gate) {
174  gate = m_solver->new_OR(gate, total);
175  }
176  else {
177  gate = total;
178  }
179  }
180 
181  return gate;
182  }
183 
184  Gate* BinaryCNFMethod::phi_not_q_i(const std::string &u, unsigned int i) {
185  Gate *gate = nullptr;
186 
187  std::vector<bool> bin_i = decimal_to_binary(i, m_binarySize);
188 
189  for (unsigned int j = 0 ; j < m_binarySize ; j++) {
190  Gate *x_uj = m_stateToVar.find(std::make_pair(u, j))->second;
191  Gate *l_uj = x_uj;
192 
193  if (bin_i.at(j)) {
194  l_uj = m_solver->new_NOT(x_uj);
195  }
196 
197  if (gate) {
198  gate = m_solver->new_OR(gate, l_uj);
199  }
200  else {
201  gate = l_uj;
202  }
203  }
204 
205  return gate;
206  }
207 
208  unsigned int BinaryCNFMethod::toState(const std::string &u) const {
209  unsigned int state = 0;
210  for (int j = m_binarySize - 1 ; j >= 0 ; j--) {
211  state += std::pow(2, j) * m_stateToVar.at(std::make_pair(u, j))->value;
212  }
213  return state;
214  }
215 
216  std::unique_ptr<DFA<char>> BinaryCNFMethod::toDFA() const {
217  // First, we seek the initial state
218  unsigned int initial = toState("");
219 
220  std::unique_ptr<DFA<char>> dfa = std::make_unique<DFA<char>>(initial, m_Sp.find("") != m_Sp.end());
221 
222  // Now, we create the other states
223  for (unsigned int i = 0 ; i < m_numberStates ; i++) {
224  if (i != initial) {
225  bool accepting = false;
226 
227  // The state is accepting if there exists a word u in Sp that ends in the state u
228  for (const auto &u : m_Sp) {
229  if (i == toState(u)) {
230  accepting = true;
231  break;
232  }
233  }
234 
235  dfa->addState(i, accepting);
236  }
237  }
238 
239  // Finally, we create the transitions
240  for (unsigned int i = 0 ; i < m_numberStates ; i++) {
241  for (const char &a : m_alphabet) {
242  bool found = false;
243  for (unsigned int j = 0 ; j < m_numberStates ; j++) {
244  for (const auto &ua : m_prefixes) {
245  if (ua.size() > 0 && ua.at(ua.size() - 1) == a){
246  std::string u = ua.substr(0, ua.size() - 1);
247 
248  if (i == toState(u) && j == toState(ua)) {
249  found = true;
250  dfa->addTransition(i, a, j);
251  }
252  }
253  }
254  }
255 
256  if (!found) {
257  dfa->addTransition(i, a, i);
258  }
259  }
260  }
261  return dfa;
262  }
263  }
264  }
265 }
const std::set< char > m_alphabet
Definition: Method.h:121
The namespace of the library.
Definition: binary.h:11
unsigned int toState(const std::string &u) const
double m_cpuTimeStart
Definition: Method.h:126
const std::set< std::string > m_Sm
The set of words to reject .
Definition: Method.h:105
const std::set< std::string > m_prefixes
Definition: Method.h:117
This file declares the function to convert a number in its decimal form to its binary form and vice-v...
This file declares the Biermann method that relies on a CNF SAT solver.
std::unique_ptr< DFA< char > > constructDFA() override
Constructs the DFA induced by the variables according to the method.
bool solve() override
Solves the SAT/SMT problem induced by the method.
static bool s_registered
Useful to make sure the template is correctly used by the compiler.
Definition: MethodFactory.h:97
std::vector< bool > decimal_to_binary(unsigned int dec_number, unsigned int size)
Converts a number in decimal form to binary form.
Definition: binary.cpp:12
bool hasSolution() const
If solve() has been called and if there exists a solution, return True.
Definition: Method.h:60
std::unique_ptr< DFA< char > > toDFA() const
const unsigned int m_numberStates
The number of states in the DFA to construct.
Definition: Method.h:109
The base class of all passive methods.
Definition: Method.h:40
const std::set< std::string > m_Sp
The set of words to accept .
Definition: Method.h:101
Gate * equal(const std::string &u, const std::string &v)
virtual void setStopTrigger(const std::chrono::seconds &timeLimit, std::atomic_bool &stopTrigger, const bool *stopPointer) override
Sets the stop trigger for this method.
Gate * phi_not_q_i(const std::string &u, unsigned int i)
Gate * phi_different(const std::string &u, const std::string &v)