Skip to content

Commit

Permalink
Merge pull request #354 from OnionGrief/bisim
Browse files Browse the repository at this point in the history
MFA::Bisimilar fixes
  • Loading branch information
xendalm authored Jun 19, 2024
2 parents c3675ee + 22bfa75 commit 55cd771
Show file tree
Hide file tree
Showing 20 changed files with 481 additions and 256 deletions.
19 changes: 14 additions & 5 deletions apps/MetamorphicTestsApp/src/MetamorphicTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ std::string MetamorphicTests::generate_bregex(RegexGenerator& rg, int cells_num)
condition = false;
}
if (condition)
condition &= r.check_refs();
condition &= r.check_refs_and_memory_writers_usefulness();
} while (!condition);

return rgx_str;
Expand Down Expand Up @@ -144,14 +144,23 @@ TEST(TestMFA, Fuzzing) {
}

// TEST(TestMFA, Fuzz) {
// string rgx_str = "(a[[b|]:1|]:2*[[c|]:1|]:2*&1&2)*";
// MemoryFiniteAutomaton mfa1 = BackRefRegex(rgx_str).to_mfa();
// MemoryFiniteAutomaton mfa2 = BackRefRegex(rgx_str).to_mfa_additional();
// BackRefRegex rgx("");
// MemoryFiniteAutomaton mfa1 = rgx.to_mfa();
// MemoryFiniteAutomaton mfa2 = rgx.to_mfa_additional();
//
// std::cout << mfa1.to_txt() << mfa2.to_txt();
//
// MetamorphicTests::cmp_automatons(mfa1, mfa2);
//}
// }

// TEST(TestMFA, Cmp) {
// MemoryFiniteAutomaton mfa1 = BackRefRegex().to_mfa_additional();
// MemoryFiniteAutomaton mfa2 = BackRefRegex().to_mfa_additional();
//
// std::cout << mfa1.to_txt() << mfa2.to_txt();
//
// MetamorphicTests::cmp_automatons(mfa1, mfa2);
// }

TEST(TestMFA, ToTxt) {
RegexGenerator rg(5, 3, 3, 2);
Expand Down
29 changes: 29 additions & 0 deletions apps/UnitTestsApp/src/UnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ TEST(TestParseString, FromString) {
{"a|(c^)", false, false, 8}, // a | ( c . ^ eps )
{"[b[a]:1&1]:2&2^a", true, true},
{"[b[a]:1&1]:2&2^a", true, false},
{"[a]:1[]:1&1", false, true, 9},
{"[a]:1[(|)]:1&1", true, true},
{"O1aC1R1&1", false, false, 9},
{"O1aC1X1&1", true, false},
};

for (const auto& t : tests) {
Expand Down Expand Up @@ -755,6 +759,27 @@ TEST(TestParsing, MFA_equivalence) {
});
}

TEST(TestBrgexChecker, CheckRefsAndMWs) {
using Test = std::tuple<string, bool>;
vector<Test> tests = {
{"[a]:1&1", true},
{"&1[a]:1", false},
{"(&1[a]:1)*", true},
{"&1[a]:1&1", false},
{"[a]:1&1[a]:2", false},
{"&2[a]:1&1[a]:2", false},
{"(&2[a]:1&1[a]:2)*", true},
{"(&1[a]:1[a]:1&1[a]:2)*", false},
{"(&2[a]:1&1[a]:2)*[a]:3*", false},
{"(&2[a]:1&1[a]:2)*[a]:3*&3", true},
};
for_each(tests.begin(), tests.end(), [](const Test& test) {
auto [rgx, expected_res] = test;
SCOPED_TRACE(rgx);
ASSERT_EQ(BackRefRegex(rgx).check_refs_and_memory_writers_usefulness(), expected_res);
});
}

TEST(TestReverse, BRegex_Reverse) {
ASSERT_TRUE(BackRefRegex::equal(BackRefRegex("([a*b]:1&1|b&1)").reverse(),
BackRefRegex("[ba*]:1&1|&1b")));
Expand All @@ -772,6 +797,8 @@ TEST(TestBisimilar, MFA_Bisimilar) {
{"[a|a]:1*&1", "[a]:1*[a]:1*&1", true},
{"[a]:1*&1|[b]:1&1", "[b]:1&1|[a]:1*&1", true},
{"[a]:1&1(&1|[b]:1)*", "[a]:1&1(&1|[b]:1)*", true},
{"[a|b]:1*&1", "(|[a|b]:1(a|b)*)&1", true},
{"[ab*]:1*&1", "[ab*b*]:1*&1", true},
// перекрестная бисимуляция
{"[a*]:1a*&1", "a*[a*]:1&1", false},
{"b[a*]:1a*&1", "ba*[a*]:1&1", false},
Expand All @@ -780,6 +807,8 @@ TEST(TestBisimilar, MFA_Bisimilar) {
// несовпадение раскрасок
{"[a]:1*&1", "[a*]:1*&1", false},
{"[a]:1*[a*]:1&1", "[a|]:1*&1", false},
{"([a|]:1*&1)*", "([aa*|]:1&1)*", false},
{"b*[a*]:1*&1", "b*[a*]:1&1", false},
// несовпадение по решающим действиям
{"[a|b]:1c(a|b)&1", "(a|b)c[a|b]:1&1", false},
// несовпадение CG
Expand Down
43 changes: 31 additions & 12 deletions libs/AutomatonToImage/src/AutomatonToImage.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,42 @@
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <vector>

#include "AutomatonToImage/AutomatonToImage.h"

using std::cout;
using std::ifstream;
using std::ofstream;
using std::string;
using std::stringstream;
using std::vector;

AutomatonToImage::AutomatonToImage() {}

AutomatonToImage::~AutomatonToImage() {}

string replace_before_dot2tex(const string& s) {
vector<std::pair<string, string>> substrs_to_replace = {{"\\^", "#^ "}, {"&", "#&"}};

string result = s;
for (const auto& [old_substr, new_substr] : substrs_to_replace) {
std::regex re(old_substr);
result = std::regex_replace(result, re, new_substr);
}

return result;
}

void write_to_file(const string& file_name, const string& content) {
ofstream file;
file.open(file_name, ofstream::trunc);
if (file.is_open())
file << content;
file.close();
}

void remove_file(string dir, string file, bool guarded = false) {
stringstream command;
command << "cd " << dir;
Expand All @@ -32,13 +57,13 @@ void remove_file(string dir, string file, bool guarded = false) {
string AutomatonToImage::to_image(string automaton) {
remove_file("refal", "Meta_log.raux", true);
remove_file("refal", "Aux_input.raux", true);
FILE* fo;
fo = fopen("./refal/input.dot", "wt");
fprintf(fo, "%s", automaton.c_str());
fclose(fo);
ofstream fo;
write_to_file("./refal/input.dot", replace_before_dot2tex(automaton));
system("cd refal && refgo Preprocess+MathMode+FrameFormatter input.dot > "
"error_Preprocess.raux");

system("cd refal && dot2tex -ftikz -tmath \"Mod_input.dot\" > input.tex");

system("cd refal && refgo Postprocess+MathMode+FrameFormatter input.tex > "
"error_Postprocess.raux "
"2>&1");
Expand Down Expand Up @@ -67,16 +92,10 @@ string AutomatonToImage::to_image(string automaton) {

string AutomatonToImage::colorize(string automaton, string metadata) {

FILE* fo;
FILE* md;
ifstream infile_for_Final;
fo = fopen("./refal/Col_input.tex", "wt");
fprintf(fo, "%s", automaton.c_str());
fclose(fo);
write_to_file("./refal/Col_input.tex", automaton);
if (metadata != "") {
md = fopen("./refal/Meta_input.raux", "wt");
fprintf(md, "%s", metadata.c_str());
fclose(md);
write_to_file("./refal/Meta_input.raux", metadata);
system("cd refal && refgo Colorize+MathMode Col_input.tex > "
"error_Colorize.raux");
infile_for_Final.open("./refal/Final_input.tex");
Expand Down
3 changes: 2 additions & 1 deletion libs/Interpreter/src/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,8 @@ optional<Interpreter::Expression> Interpreter::scan_expression(const vector<Lexe
if (end > pos && lexems[pos].type == Lexem::regex) {
string str = lexems[pos].value;
// выбор между backref и regex
if (str.find("&") != string::npos || str.find(":") != string::npos) {
// TODO: костыль
if (str.find('&') != string::npos || str.find("]:") != string::npos) {
expr.type = ObjectType::BRefRegex;
expr.value = BackRefRegex(str);
} else {
Expand Down
12 changes: 3 additions & 9 deletions libs/Logger/src/LogTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ string LogTemplate::render() const {
automaton = std::get<FiniteAutomaton>(param.value).to_txt();
else
automaton = std::get<MemoryFiniteAutomaton>(param.value).to_txt();
automaton = replace_for_rendering(automaton);
size_t hash = hasher(automaton);
if (cache_automatons.count(hash) != 0) {
c_graph = cache_automatons[hash];
Expand Down Expand Up @@ -245,16 +244,11 @@ string LogTemplate::log_table(Table t) {
table += "$\\begin{array}{" + format + "}\\rowcolor{HeaderColor}\n";
table += cols + "\\hline\n";
for (int i = 0; i < t.rows.size(); i++) {
string r = t.rows[i] == " " ? "eps" : t.rows[i];
row = r + " & ";
row = t.rows[i] == " " ? "eps" : t.rows[i];
for (int j = 0; j < t.columns.size(); j++) {
if (j != t.columns.size() - 1) {
row = row + t.data[i * t.columns.size() + j] + " &";
} else {
row = row + t.data[i * t.columns.size() + j] + "\\\\";
}
row += " & " + replace_for_rendering(t.data[i * t.columns.size() + j]);
}
table += row + "\n";
table += row + "\\\\\n";
}
table += "\\end{array}$\n";
return table;
Expand Down
11 changes: 8 additions & 3 deletions libs/Objects/include/Objects/BackRefRegex.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class BackRefRegex : public AlgExpression {

// возвращает вектор листьев дерева
// устанавливает для них in_lin_cells, first_in_cells и last_in_cells
// линеаризует memoryWriters
void preorder_traversal(
std::vector<BackRefRegex*>& terms, // NOLINT(runtime/references)
int& lin_counter, // NOLINT(runtime/references)
Expand Down Expand Up @@ -79,15 +80,17 @@ class BackRefRegex : public AlgExpression {
CellSet>>>& // NOLINT(runtime/references)
) const;

// преобразует star в conc (раскрывает каждую итерацию один раз) и линеаризует memoryWriter
// преобразует star в conc (раскрывает каждую итерацию один раз) и линеаризует memoryWriters
void unfold_iterations(int& number); // NOLINT(runtime/references)
// рекурсивно проверяет, является ли регулярное выражение ацикличным
bool _is_acreg(
std::unordered_set<int>, std::unordered_set<int>,
std::unordered_map<int, std::unordered_set<int>>&) const; // NOLINT(runtime/references)

void linearize_refs(int& number); // NOLINT(runtime/references)
void _check_refs(std::unordered_set<int>&, std::unordered_set<int>&) const;
void _check_memory_writers(std::unordered_map<int, std::unordered_set<int>>&,
std::unordered_set<int>&, // NOLINT(runtime/references)
std::unordered_set<int>&) const; // NOLINT(runtime/references)

// меняет порядок конкатенаций в дереве (swap term_l и term_r)
void _reverse(std::unordered_map<int, BackRefRegex*>&); // NOLINT(runtime/references)
Expand Down Expand Up @@ -123,6 +126,8 @@ class BackRefRegex : public AlgExpression {
// обращение выражения (для СНФ)
BackRefRegex reverse(iLogTemplate* log = nullptr) const;
// проверяет, что каждая ссылка может следовать за записью в память (соответствующую ячейку)
bool check_refs() const;
// и что каждый memoryWriter не будет однозначно переинициализирован без возможности
// сослаться на него (существует хотя бы один путь, в котором присутствует ссылка на него)
bool check_refs_and_memory_writers_usefulness() const;
BackRefRegex rewrite_aci() const;
};
6 changes: 2 additions & 4 deletions libs/Objects/include/Objects/FiniteAutomaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ class FiniteAutomaton : public AbstractMachine {
// eps-переходам (если флаг установлен в 0 - по всем переходам)
std::set<int> closure(const std::set<int>&, bool) const;

std::vector<int> get_bisimilar_classes() const;
std::vector<int> get_bisimulation_classes() const;
// объединение эквивалентных классов (принимает на вход вектор размера states.size())
// на i-й позиции номер класса i-го состояния
std::tuple<FiniteAutomaton, std::unordered_map<int, int>> merge_equivalent_classes(
std::tuple<FiniteAutomaton, std::unordered_map<int, int>> merge_classes(
const std::vector<int>&) const;
static bool equality_checker(const FiniteAutomaton& fa1, const FiniteAutomaton& fa2);
// дополнительно возвращает в векторах номера классов состояний каждого автомата
Expand Down Expand Up @@ -118,8 +118,6 @@ class FiniteAutomaton : public AbstractMachine {
std::unordered_map<int, int>& states_mapping, // NOLINT(runtime/references)
MFATransition::MemoryActions memory_actions, int from_mfa_state) const;

FiniteAutomaton get_subautomaton(const CaptureGroup&);

public:
FiniteAutomaton();
FiniteAutomaton(int initial_state, std::vector<FAState> states,
Expand Down
20 changes: 16 additions & 4 deletions libs/Objects/include/Objects/MemoryCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <iostream>
#include <optional>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
Expand Down Expand Up @@ -66,7 +67,7 @@ struct MFATransition {
};
};

struct CaptureGroup {
class CaptureGroup {
struct State {
int index;
int class_num;
Expand All @@ -82,14 +83,25 @@ struct CaptureGroup {
std::unordered_set<std::vector<int>, VectorHasher<int>> paths;
std::unordered_set<State, State::Hasher> states;
std::unordered_set<int> state_classes;
bool is_reset;

public:
CaptureGroup() = default;
CaptureGroup(int, const std::vector<std::vector<int>>&, const std::vector<int>&,
bool reset = false);
bool is_reset = false);
bool operator==(const CaptureGroup& other) const;

std::unordered_set<int> get_states_diff(
const std::unordered_set<int>& other_state_classes) const;
bool get_is_reset() const;
bool get_cell_number() const;
int get_opening_state_index() const;

const std::unordered_set<std::vector<int>, VectorHasher<int>>& get_paths() const;
const std::unordered_set<State, State::Hasher>& get_states() const;

std::tuple<std::unordered_set<int>, std::unordered_set<int>> get_states_diff(
const CaptureGroup& other) const;

friend std::ostream& operator<<(std::ostream& os, const CaptureGroup& cg);
};

std::ostream& operator<<(std::ostream& os, const CaptureGroup& cg);
23 changes: 16 additions & 7 deletions libs/Objects/include/Objects/MemoryFiniteAutomaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class MemoryFiniteAutomaton : public AbstractMachine {
std::pair<int, bool> _parse_slow(const std::string&, Matcher*) const;
std::pair<int, bool> _parse(const std::string&, Matcher*) const;

// поиск множества состояний НКА,
// поиск множества состояний MFA,
// достижимых из множества состояний по eps-переходам
std::tuple<std::set<int>, std::unordered_set<int>, MFATransition::MemoryActions>
get_eps_closure(const std::set<int>& indices) const;
Expand All @@ -158,7 +158,7 @@ class MemoryFiniteAutomaton : public AbstractMachine {
int state_index,
std::vector<bool>& visited, // NOLINT(runtime/references)
const MemoryConfiguration& opened_cells,
std::unordered_map<int, std::unordered_set<int>>& colors, // NOLINT(runtime/references)
std::vector<std::unordered_set<int>>& state_colors, // NOLINT(runtime/references)
const std::vector<int>& ab_classes,
std::unordered_map<int, int>& ab_class_to_first_state // NOLINT(runtime/references)
) const;
Expand All @@ -168,21 +168,30 @@ class MemoryFiniteAutomaton : public AbstractMachine {
std::vector<MFAState::Transitions> get_reversed_transitions() const;

std::pair<std::vector<std::vector<int>>, std::vector<std::vector<int>>> find_cg_paths(
int state_index, std::unordered_set<int> visited, int cell, int opening_state) const;
int state_index, std::unordered_set<int> visited, int cell, int opening_state,
bool was_in_opening_state) const;
std::vector<CaptureGroup> find_capture_groups_backward(
int ref_incoming_state, int cell, const std::vector<int>& fa_classes) const;
int ref_incoming_state, int cell,
const std::vector<MFAState::Transitions>& reversed_transitions,
const std::vector<int>& fa_classes) const;

bool find_decisions(int state_index,
std::vector<int>& visited, // NOLINT(runtime/references)
const std::unordered_set<int>& states_to_check) const;
bool states_have_decisions(const std::unordered_set<int>& states_to_check) const;
const std::unordered_set<int>& states_to_check,
const std::unordered_set<int>& following_states,
const CaptureGroup& cg) const;
bool states_have_decisions(const std::unordered_set<int>& states_to_check,
const std::unordered_set<int>& following_states,
const CaptureGroup& cg) const;

FiniteAutomaton get_cg_fa(const CaptureGroup& cg) const;

static std::optional<bool> bisimilarity_checker(const MemoryFiniteAutomaton&,
const MemoryFiniteAutomaton&);

// объединение эквивалентных классов (принимает на вход вектор размера states.size())
// на i-й позиции номер класса i-го состояния
std::tuple<MemoryFiniteAutomaton, std::unordered_map<int, int>> merge_equivalent_classes(
std::tuple<MemoryFiniteAutomaton, std::unordered_map<int, int>> merge_classes(
const std::vector<int>&) const;

public:
Expand Down
6 changes: 4 additions & 2 deletions libs/Objects/include/Objects/Symbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <vector>

// Символ, по которому осуществляются переходы в автомате.
// Может быть символом-буквой (и входить в алфавит) или ссылкой (&i)
// Может быть символом-буквой (и входить ТОЛЬКО в алфавит FA) или ссылкой (&i)
class Symbol {
private:
std::vector<int> annote_numbers;
Expand Down Expand Up @@ -90,4 +90,6 @@ class MemorySymbols {
static bool is_open(const Symbol& s);

static int get_cell_number(const Symbol& s);
};
};

bool is_special_symbol(const Symbol& s);
2 changes: 1 addition & 1 deletion libs/Objects/include/Objects/Tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct TupleHasher {
size_t operator()(const std::tuple<int, int, int>& p) const;
};

using IntPairSet = std::unordered_set<std::pair<int, int>, IntPairHasher>;
using IntPairsSet = std::unordered_set<std::pair<int, int>, IntPairHasher>;

std::ostream& operator<<(std::ostream& os, const std::vector<int>& vec);

Expand Down
Loading

0 comments on commit 55cd771

Please sign in to comment.