Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MFA::Bisimilar fixes #354

Merged
merged 16 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/UnitTestsApp/src/UnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ 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},
// перекрестная бисимуляция
{"[a*]:1a*&1", "a*[a*]:1&1", false},
{"b[a*]:1a*&1", "ba*[a*]:1&1", false},
Expand All @@ -780,6 +781,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
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);
18 changes: 13 additions & 5 deletions libs/Objects/include/Objects/MemoryFiniteAutomaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 @@ -170,19 +170,27 @@ class MemoryFiniteAutomaton : public AbstractMachine {
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;
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
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
58 changes: 14 additions & 44 deletions libs/Objects/src/FiniteAutomaton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ FiniteAutomaton FiniteAutomaton::minimize(bool is_trim, iLogTemplate* log) const
classes[groups[i][j]] = i;
}
}
auto [minimized_dfa, class_to_index] = dfa.merge_equivalent_classes(classes);
auto [minimized_dfa, class_to_index] = dfa.merge_classes(classes);

// кэширование
language->set_min_dfa(minimized_dfa);
Expand Down Expand Up @@ -1361,7 +1361,7 @@ bool FiniteAutomaton::is_one_unambiguous(iLogTemplate* log) const {
return true;
}

tuple<FiniteAutomaton, unordered_map<int, int>> FiniteAutomaton::merge_equivalent_classes(
tuple<FiniteAutomaton, unordered_map<int, int>> FiniteAutomaton::merge_classes(
const vector<int>& classes) const {
map<int, vector<int>> class_to_indexes;
for (int i = 0; i < classes.size(); i++)
Expand Down Expand Up @@ -1398,7 +1398,7 @@ tuple<FiniteAutomaton, unordered_map<int, int>> FiniteAutomaton::merge_equivalen
return {{class_to_index.at(classes[initial_state]), new_states, language}, class_to_index};
}

vector<int> FiniteAutomaton::get_bisimilar_classes() const {
vector<int> FiniteAutomaton::get_bisimulation_classes() const {
vector<RLGrammar::Item> fa_items;
vector<RLGrammar::Item*> nonterminals;
vector<RLGrammar::Item*> terminals;
Expand All @@ -1418,8 +1418,8 @@ vector<int> FiniteAutomaton::get_bisimilar_classes() const {

FiniteAutomaton FiniteAutomaton::merge_bisimilar(iLogTemplate* log) const {
MetaInfo old_meta, new_meta;
vector<int> classes = get_bisimilar_classes();
auto [result, class_to_index] = merge_equivalent_classes(classes);
vector<int> classes = get_bisimulation_classes();
auto [result, class_to_index] = merge_classes(classes);

for (int i = 0; i < classes.size(); i++) {
for (int j = 0; j < classes.size(); j++)
Expand Down Expand Up @@ -1602,9 +1602,9 @@ bool FiniteAutomaton::equality_checker(const FiniteAutomaton& fa1, const FiniteA
if (t != 0)
return false;

vector<int> bisimilar_classes(nonterminals.size());
vector<int> bisimulation_classes(nonterminals.size());
for (int i = 0; i < nonterminals.size(); i++)
bisimilar_classes[i] = nonterminals[i]->class_number;
bisimulation_classes[i] = nonterminals[i]->class_number;

// биективная бисимуляция обратных грамматик
vector<vector<vector<RLGrammar::Item*>>> fa1_reverse_rules = RLGrammar::get_reverse_grammar(
Expand All @@ -1623,22 +1623,22 @@ bool FiniteAutomaton::equality_checker(const FiniteAutomaton& fa1, const FiniteA
RLGrammar::get_bisimilar_grammar(
reverse_rules, nonterminals, reverse_bisimilar_nonterminals, class_to_nonterminals);
// сопоставление состояний 1 к 1
vector<int> reverse_bisimilar_classes;
vector<int> reverse_bisimulation_classes;
for (RLGrammar::Item* nont : nonterminals) {
reverse_bisimilar_classes.push_back(nont->class_number);
reverse_bisimulation_classes.push_back(nont->class_number);
nont->class_number = -1;
}

// устанавливаем классы нетерминалов-состояний (1 к 1), чтобы после сопоставить переходы
int new_class = 0;
for (int i = 0; i < bisimilar_classes.size(); i++) {
for (int i = 0; i < bisimulation_classes.size(); i++) {
if (nonterminals[i]->class_number != -1)
continue;
nonterminals[i]->class_number = new_class;
// поиск нетерминалов с классом, как у i-го
for (int j = i + 1; j < bisimilar_classes.size(); j++) {
if (bisimilar_classes[j] == bisimilar_classes[i])
if (reverse_bisimilar_classes[j] == reverse_bisimilar_classes[i])
for (int j = i + 1; j < bisimulation_classes.size(); j++) {
if (bisimulation_classes[j] == bisimulation_classes[i])
if (reverse_bisimulation_classes[j] == reverse_bisimulation_classes[i])
nonterminals[j]->class_number = new_class;
}
new_class++;
Expand Down Expand Up @@ -2768,34 +2768,4 @@ std::vector<std::unordered_set<int>> FiniteAutomaton::get_SCCs() {
}

return SCCs;
}

FiniteAutomaton FiniteAutomaton::get_subautomaton(const CaptureGroup& cg) {
int n = cg.states.size();
vector<FAState> sub_states;
sub_states.reserve(cg.states.size());
Alphabet alphabet;

unordered_set<int> terminal_states;
for (const auto& path : cg.paths)
terminal_states.insert(path[path.size() - 1]);

unordered_map<int, int> indexes;
int idx = 0;
for (auto st : cg.states) {
indexes[st.index] = idx;
sub_states.emplace_back(idx, states[st.index].identifier, terminal_states.count(st.index));
idx++;
}

for (const auto& st : cg.states)
for (const auto& [symbol, symbol_transitions] : states[st.index].transitions)
for (const auto& to : symbol_transitions)
if (indexes.count(to)) {
alphabet.insert(symbol);
sub_states[indexes.at(st.index)].add_transition(indexes.at(to), symbol);
}

// начальное состояние общее у всех cg.paths
return {indexes.at((*cg.paths.begin())[0]), sub_states, alphabet};
}
}
42 changes: 32 additions & 10 deletions libs/Objects/src/MemoryCommon.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Objects/MemoryCommon.h"

using std::tuple;
using std::unordered_set;
using std::vector;

Expand Down Expand Up @@ -40,12 +41,12 @@ bool CaptureGroup::State::operator==(const State& other) const {
}

CaptureGroup::CaptureGroup(int cell, const vector<vector<int>>& _paths,
const vector<int>& _state_classes, bool reset)
: cell(cell) {
const vector<int>& _state_classes, bool is_reset)
: cell(cell), is_reset(is_reset) {
for (const auto& path : _paths) {
paths.insert(path);
for (auto st : path) {
int class_num = (reset) ? State::reset_class : _state_classes[st];
int class_num = (is_reset) ? State::reset_class : _state_classes[st];
states.insert({st, class_num});
state_classes.insert(class_num);
}
Expand All @@ -56,19 +57,40 @@ bool CaptureGroup::operator==(const CaptureGroup& other) const {
return cell == other.cell && states == other.states;
}

unordered_set<int> CaptureGroup::get_states_diff(
const unordered_set<int>& other_state_classes) const {
bool CaptureGroup::get_is_reset() const {
return is_reset;
}

bool CaptureGroup::get_cell_number() const {
return cell;
}

int CaptureGroup::get_opening_state_index() const {
return (*paths.begin())[0];
}

const std::unordered_set<std::vector<int>, VectorHasher<int>>& CaptureGroup::get_paths() const {
return paths;
}

const unordered_set<CaptureGroup::State, CaptureGroup::State::Hasher>& CaptureGroup::get_states()
const {
return states;
}

tuple<unordered_set<int>, unordered_set<int>> CaptureGroup::get_states_diff(
const CaptureGroup& other) const {
unordered_set<int> diff;
for (auto st : states)
if (st.class_num != State::reset_class && !other_state_classes.count(st.class_num))
if (st.class_num != State::reset_class && !other.state_classes.count(st.class_num))
diff.insert(st.index);

unordered_set<int> res(diff);
unordered_set<int> following(diff);
for (const auto& path : paths)
for (size_t i = path.size() - 1; i > 0; i--)
if (diff.count(path[i - 1]))
res.insert(path[i]);
return res;
following.insert(path[i]);
return {diff, following};
}

std::ostream& operator<<(std::ostream& os, const CaptureGroup& cg) {
Expand All @@ -79,4 +101,4 @@ std::ostream& operator<<(std::ostream& os, const CaptureGroup& cg) {
for (const auto& i : cg.states)
os << "{" << i.index << ": " << i.class_num << "} ";
return os << "]\n";
}
}
Loading
Loading