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

(#279) Рефакторинг метода to-regex класса FiniteAutomaton #282

Merged
merged 21 commits into from
Nov 25, 2023
Merged
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
928fe1c
Refactored FiniteAutomaton::to_regex #279
AngelicHedgehog Oct 23, 2023
0be5062
Removed artefact #279
AngelicHedgehog Oct 23, 2023
6d8a85d
Merge branch 'main' into vilenskii/refacts
AngelicHedgehog Nov 12, 2023
58b8a27
(#279) удалено старое содержимое метода
AngelicHedgehog Nov 12, 2023
f0fe215
(#279) Первая реализация FiniteAutomaton::to_regex
AngelicHedgehog Nov 22, 2023
7903085
(#279) Добавлено логирование
AngelicHedgehog Nov 22, 2023
7340be4
Merge branch 'main' into vilenskii/refacts
AngelicHedgehog Nov 22, 2023
b5ca6d5
(#279) Устранена ошибка ссылки на rvalue
AngelicHedgehog Nov 22, 2023
e893cb4
(#279) Удалены лишние заголовки
AngelicHedgehog Nov 22, 2023
b4d46ae
(#279) Исправлена Segmentation fault
AngelicHedgehog Nov 22, 2023
ea1e4c3
(#279) Фикс бага формата регулярки возврата
AngelicHedgehog Nov 22, 2023
a4d2e8c
Merge branch 'main' into vilenskii/refacts
AngelicHedgehog Nov 23, 2023
3fe5878
(#279) Устранено лишнее преобразование финальной регулярки
AngelicHedgehog Nov 23, 2023
13c0de8
(#279) Добавлено определение номера начального состояния
AngelicHedgehog Nov 23, 2023
ce9370f
(#279) Излишние выкладки
AngelicHedgehog Nov 23, 2023
e1b2860
(#279) Добавлена генерация афловита с языком
AngelicHedgehog Nov 23, 2023
012327f
(#279) Исправлен метод генерации языка финальной регулярки
AngelicHedgehog Nov 23, 2023
f9e69c0
(#279) Изменён способ задания языка на явный
AngelicHedgehog Nov 23, 2023
bec1d3c
Merge branch 'hotfix' into vilenskii/refacts
xendalm Nov 23, 2023
4bd2149
(#279) fixed arden test, added auto alphabet generating (new AlgExpre…
xendalm Nov 24, 2023
d4d0207
(#302) тест на генерируемых регулярках
mathhyyn Nov 24, 2023
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
318 changes: 134 additions & 184 deletions libs/Objects/src/FiniteAutomaton.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <tuple>
#include <unordered_map>

#include "Fraction/Fraction.h"
#include "InfInt/InfInt.h"
Expand Down Expand Up @@ -357,8 +357,8 @@ FiniteAutomaton FiniteAutomaton::remove_eps(iLogTemplate* log) const {
(initial_state_identifier.empty() || states[elem].identifier.empty() ? "" : ", ") +
states[elem].identifier;
}
State new_initial_state = {0, q, initial_state_identifier, false,
map<alphabet_symbol, set<int>>()};
State new_initial_state = {
0, q, initial_state_identifier, false, map<alphabet_symbol, set<int>>()};
if (q.size() > 1) {
for (auto elem : q) {
old_meta.upd(NodeMeta{states[elem].index, group_counter});
Expand Down Expand Up @@ -414,8 +414,8 @@ FiniteAutomaton FiniteAutomaton::remove_eps(iLogTemplate* log) const {
old_meta.upd(NodeMeta{states[elem].index, group_counter});
}

old_meta.mark_transitions(*this, q1, q1, alphabet_symbol::epsilon(),
group_counter);
old_meta.mark_transitions(
*this, q1, q1, alphabet_symbol::epsilon(), group_counter);
new_meta.upd(NodeMeta{new_state.index, group_counter});
group_counter++;
}
Expand Down Expand Up @@ -2285,7 +2285,8 @@ std::pair<int, bool> FiniteAutomaton::parsing_nfa_for(const string& s) const {
}
}

// Если произошёл откат по строке, то эпсилон-переходы из рассмотренных состояний больше не считаются повторными
// Если произошёл откат по строке, то эпсилон-переходы из рассмотренных состояний больше не
// считаются повторными
if (!visited_eps.empty()) {
for (auto pos : visited_eps) {
if (std::get<0>(pos) <= parsed_len)
Expand All @@ -2294,7 +2295,8 @@ std::pair<int, bool> FiniteAutomaton::parsing_nfa_for(const string& s) const {
visited_eps = aux_eps;
aux_eps.clear();
}
// Добавление тех эпсилон-переходов, по которым ещё не было разбора от этой позиции и этого состояния
// Добавление тех эпсилон-переходов, по которым ещё не было разбора от этой позиции и этого
// состояния
auto reach_eps = state.transitions[alphabet_symbol::epsilon()];
for (int eps_tr : reach_eps) {
if (visited_eps.find({parsed_len, state.index, eps_tr}) == visited_eps.end()) {
Expand Down Expand Up @@ -2446,198 +2448,146 @@ vector<expression_arden> FiniteAutomaton::arden(const vector<expression_arden>&
}
return out;
}
Regex FiniteAutomaton::to_regex(iLogTemplate* log) const {

if (log)
Regex FiniteAutomaton::to_regex(iLogTemplate* log) const {
if (log) {
log->set_parameter("oldautomaton", *this);
}

vector<int> end_state; // храним индексы принимающих состояний
vector<vector<expression_arden>> data; // все уравнения
set<alphabet_symbol> alphabet = language->get_alphabet(); // получаем
// Алфавит
// a system of linear algebraic equations
std::unordered_map<int, std::unordered_map<int, Regex>> SLAE{};
// индекс стартового состояния (должен быть среди состояний)
const int start_state_index = 0;
// индекс глобального конечного состояния (должен не быть среди состоянтй)
const int end_state_index = -1;
/*
@algorithm_sample
from/to | 0 | 1 | -1(end) from/to | 0 | 1 | -1(end)
0 | a | b | ---> 0 | | a*b | --->
1 | | a | e 1 | | a | e

from/to | 0 | 1 | -1(end) from/to | 0 | 1 | -1(end)
0 | | a*b | ---> 0 | | | a*b(a*|e))
1 | | | a*|e 1 | | |
*/

// заполнение уравнений системы актуальными значениями
for (const auto& state : states) {
SLAE.insert({state.index, std::unordered_map<int, Regex>{}});

for (int i = 0; i < states.size(); i++) {
vector<expression_arden> temp;
data.push_back(temp);
}

auto* r = new Regex; // Заполняем вход в начальное состояние
r->regex_eps();
expression_arden initial_arden = {-1, r};
data[initial_state].push_back(initial_arden);

for (int i = 0; i < states.size(); i++) { // Для всех состояний автомата заполняем уравнения
if (states[i].is_terminal) {
end_state.push_back(i);
}
if (states[i].transitions.count("eps")) { // для переходов по eps
set<int> trans = states[i].transitions.at("eps");
for (const int& index : trans) {
auto* r = new Regex;
r->regex_eps();
expression_arden temp_expression = {i, r};
data[index].push_back(temp_expression);
}
}
for (const alphabet_symbol& as : alphabet) { // для переходов по символам алфавита
if (states[i].transitions.count(as)) {
set<int> trans = states[i].transitions.at(as);
for (const int& index : trans) {
string str = as;
auto* r = new Regex(str);
expression_arden temp_expression = {i, r};
data[index].push_back(temp_expression);
// если завершающее состояние, добавляем eps-переход
if (state.is_terminal) {
SLAE[state.index].insert({end_state_index, Regex{}});
}

// итерируемся по всем путям из state
for (const auto& [symbol, states_to] : state.transitions) {

// распознание eps-перехода
Regex symbol_regex{};
if (!symbol.is_epsilon()) {
symbol_regex = {symbol};
}

for (int state_index_to : states_to) {
if (SLAE[state.index].count(state_index_to)) {
Regex new_regex{};
new_regex.regex_alt(&SLAE[state.index][state_index_to], &symbol_regex);
SLAE[state.index][state_index_to] = new_regex;
} else {
SLAE[state.index].insert({state_index_to, symbol_regex});
}
}
}
}
if (end_state.empty()) { // если нет принимающих состояний - то регулярки не будет
return Regex();
}
// // вывод всех уравнений
// for (int i = 0; i < data.size(); i++) {
// cout << i << " = ";
// for (int j = 0; j < data[i].size(); j++) {
// cout << data[i][j].fa_state_number << " "
// << data[i][j].regex_from_state->to_txt() << " ";
// }
// cout << "\n";
// }

// переносим прошлые переходы и объединяем (работаем с уравнениями)

for (int i = 0; i < data.size(); i++) { // c конца начинаем переписывать уравнения
vector<expression_arden> temp_data;
for (int j = 0; j < data[i].size(); j++) {
if (data[i][j].fa_state_number < i && data[i][j].fa_state_number != -1) {
// если ссылаемся на какие-либо еще переходы
for (int k = 0; k < data[data[i][j].fa_state_number].size(); k++) {
Regex* r;
if (data[i][j].regex_from_state->to_txt().empty()) {
r = Regex::cast(data[data[i][j].fa_state_number][k]
.regex_from_state->make_copy()); // тут 0
} else if (data[data[i][j].fa_state_number][k]
.regex_from_state->to_txt()
.empty()) {
r = Regex::cast(data[i][j].regex_from_state->make_copy()); // тут б
// continue;
} else {
r = new Regex;
r->regex_union(

data[data[i][j].fa_state_number][k].regex_from_state,
data[i][j].regex_from_state);
}
expression_arden temp_expression = {
data[data[i][j].fa_state_number][k].fa_state_number, r};
temp_data.push_back(temp_expression);
// теорема Ардена о переходах в себя
auto arden_theorem = [&SLAE](int state_index) {
// передан индекс несуществующего состояния
if (!SLAE.count(state_index)) {
return;
}

// случай отсутсвия в уранении переходов в себя
if (!SLAE[state_index].count(state_index)) {
return;
}

// подготавливаем звёздную регулярку
Regex state_self_regex{};
state_self_regex.regex_star(&SLAE[state_index][state_index]);

// добавление звёздного перехода к остальным переходам уранения
for (auto& [state_index_to, to_regex] : SLAE[state_index]) {
Regex new_regex{};
new_regex.regex_union(&state_self_regex, &to_regex);
to_regex = new_regex;
}

// удаление рассмотренного перехода состояния в себя же
SLAE[state_index].erase(state_index);
};

// решение СЛАУ методом Гаусса с применением леммы Ардена
// итерация по строкам системы уравнений
for (auto& [state_index_row, equation_row] : SLAE) {
// ссылка не константная, тк уравнения
// будут подвержены изменениям

// начальное состояние должно остаться посленим не рассмотренным
if (state_index_row == start_state_index) {
continue;
}

// устранение переходов из рассматриваемого состояния в себя же
arden_theorem(state_index_row);

// подстановка рассматриваемого уравнения в его упоминания в прочих
// итерация по всем уравнениям с переходами в исходное для подстановки регулярки
for (auto& [state_index_from, equation_from] : SLAE) {
// пропуск итерации, если в рассматриваемом уравнения нет исходного
if (!equation_from.count(state_index_row)) {
continue;
}

// итерация по всем переходам из исходного уравнения
for (auto& [state_index_col, regex_col] : equation_row) {
// регулярка перехода из рассматриваемого уравнения в исходное
Regex regex_from{};
regex_from.regex_union(&equation_from[state_index_row], &regex_col);

// объединяем полученную регулярку с имеющейся в рассматривамом уравнении
if (equation_from.count(state_index_col)) {
Regex new_regex{};
new_regex.regex_alt(&equation_from[state_index_col], &regex_from);
equation_from[state_index_col] = new_regex;
} else {
equation_from.insert({state_index_col, regex_from});
}
} else { // если не ссылаемся
expression_arden temp_expression = {data[i][j].fa_state_number,
new Regex(*data[i][j].regex_from_state)};
temp_data.push_back(temp_expression);
}
}
for (auto& v : data[i]) {
delete v.regex_from_state;
}
data[i].clear();
// объединяем одинаковые состояния
vector<expression_arden> tempdata1 = arden_minimize(temp_data);
// применяем арден
vector<expression_arden> tempdata2 = arden(tempdata1, i);
// объединяем одинаковые состояния
vector<expression_arden> tempdata3 = arden_minimize(tempdata2);
for (auto& v : temp_data) {
delete v.regex_from_state;
}
for (auto& v : tempdata1) {
delete v.regex_from_state;
}
for (auto& v : tempdata2) {
delete v.regex_from_state;
}
data[i] = tempdata3;
}
// работа с уравнениями (могли остаться ссылки на другие состояния,
// исправляем)
for (int i = data.size() - 1; i >= 0; i--) {
for (int j = 0; j < data[i].size(); j++) {
if (data[i][j].fa_state_number != -1) {
auto* ra = new Regex;
ra->regex_union(data[data[i][j].fa_state_number][0].regex_from_state,
data[i][j].regex_from_state);
data[i][j].fa_state_number = -1;
delete data[i][j].regex_from_state;
data[i][j].regex_from_state = ra;
}
}
// объединяем состояния
vector<expression_arden> tempdata3 = arden_minimize(data[i]);
for (auto& v : data[i]) {
delete v.regex_from_state;
}
data[i].clear();
data[i] = tempdata3;
}
// вывод итоговых regex
string full_logs;
for (int i = 0; i < data.size(); i++) {
if (i != 0)
full_logs += "\\\\";
full_logs += "state " + std::to_string(i) + ":"; // TODO: logs
for (auto& j : data[i]) {
// TODO: передача набора regex

full_logs += "\\ ";
if (j.regex_from_state->to_txt().empty()) {
full_logs += "eps";
} else {
full_logs += j.regex_from_state->to_txt(); // тут по идее должно быть
// без to_txt но у нас не
// принимается Regex*
}

equation_from.erase(state_index_row);
}

// обнуляем рассмотренное выражение за избыточностью подстановки последующих в данное
//// удаление элемента из словаря во время итерации привело бы к ошибке
SLAE[state_index_row].clear();
}
if (log)
log->set_parameter("step-by-step construction", full_logs);
// если у нас 1 принимающее состояние
if (end_state.size() < 2) {
Regex* r1;
r1 = Regex::cast(data[end_state[0]][0].regex_from_state->make_copy());
for (auto& i : data) {
for (auto& j : i) {
delete j.regex_from_state;
}
}
// заполняем алфавит и lang (нужно для преобразований в автоматы)
r1->set_language(alphabet);
Regex temp = *r1;
delete r1;

// применяем теорему Ардена к начальному состоянию
arden_theorem(start_state_index);

// возвращаем путь из начало в конец
if (SLAE.count(start_state_index) && SLAE[start_state_index].count(end_state_index)) {
if (log) {
log->set_parameter("result", temp);
log->set_parameter("result", SLAE[start_state_index][end_state_index].to_txt());
}
return temp;
return {SLAE[start_state_index][end_state_index].to_txt()};
}
// если принимающих состояний несколько - объединяем через альтернативу
Regex* r1;
r1 = Regex::cast(data[end_state[0]][0].regex_from_state->make_copy());
for (int i = 1; i < end_state.size(); i++) {
auto* r2 = new Regex;
r2->regex_alt(r1, data[end_state[i]][0].regex_from_state);
delete r1;
r1 = r2;
}
for (auto& i : data) {
for (auto& j : i) {
delete j.regex_from_state;
}
}
r1->set_language(alphabet);
Regex temp1 = *r1;
delete r1;

// случай недостижимости ни одного из конечных состояний или их отсуствия
if (log) {
log->set_parameter("result", temp1);
log->set_parameter("result", "Unknown");
}
return temp1;
}
return Regex{};
}