AndreevAA / bmstu_cc

0 stars 0 forks source link

Problem with NFA format during transmition to DFA #1

Open AndreevAA opened 8 months ago

AndreevAA commented 8 months ago

During dev. of NFA to DFA unexpoectedly has been found problems of NFA format.

It should be like this:

nfa_test = {
    'start': {'0': {'A'}, '1': {'B'}},
    'A': {'0': {'A'}, '1': {'B'}},
    'B': {'0': {'A'}, '1': {'end'}},
    'end': {}
}

But it be like this:

digraph "" {
        start   [shape=point];
        start -> 0      [label=0];
        start -> 1      [label="."];
        start -> 2      [label=5];
        end     [shape=doublecircle];
        0 -> 1  [label=ε];
        1 -> 2  [label=ε];
        2 -> end        [label=ε];
}

Permanent link to code block: https://github.com/AndreevAA/bmstu_cc/blob/d635c0fe5172c785b9f15816edbc11eb65f48153/lab_1/main_dev_4.py#L174

AndreevAA commented 8 months ago

Ошибка решилась!

import numpy as np
import numpy as np
import pygraphviz as pgv

def solve_equations(system, nonterminals):
    A = np.array(system)
    b = np.zeros(len(system))
    b[0] = 1  # Устанавливаем значение для стартового символа

    # Решаем систему уравнений методом наименьших квадратов
    solution = np.linalg.lstsq(A, b, rcond=None)[0]

    # Формируем словарь с решениями для каждого нетерминала
    solutions_dict = {nonterminals[i]: round(solution[i], 2) for i in range(len(nonterminals))}

    return solutions_dict

def identify_terminals_nonterminals(grammar):
    terminals = set()
    nonterminals = set()

    for rule in grammar:
        left, right = rule.split('->')
        left = left.strip()
        right = right.strip().split('|')

        nonterminals.add(left)

        for term in right:
            terms = term.split()
            for t in terms:
                if not t.isupper() and t != '':  # Terminal
                    terminals.add(t)
                elif t.isupper():  # Nonterminal
                    nonterminals.add(t)

    return terminals, nonterminals

def convert_to_equations(grammar):
    equations = []
    nonterminals = set()

    for rule in grammar:
        left, right = rule.split('->')
        left = left.strip()
        right = right.strip().split('|')

        for term in right:
            equation = []
            terms = term.split()

            for t in terms:
                if t.isupper():  # Nonterminal
                    nonterminals.add(t)
                    equation.append(t)
                else:  # Terminal
                    equation.append(t)

            equations.append(equation)

    # Constructing the system of equations
    system = []
    for eq in equations:
        coeff = [1 if x in eq else 0 for x in nonterminals]
        system.append(coeff)

    return system, list(nonterminals)

# Пример праволинейной грамматики
grammar = [
    "S -> a A | b B",
    "A -> a A | b",
    "B -> a B | a"
]

terminals, nonterminals = identify_terminals_nonterminals(grammar)
system, nonterminals = convert_to_equations(grammar)

print("Терминалы:")
print(terminals)

print("\nНетерминалы:")
print(nonterminals)

print("\nСистема уравнений:")
for eq in system:
    print(eq)

solutions = solve_equations(system, nonterminals)
print("\nРешения для нетерминалов:")
for key, value in solutions.items():
    print(f"{key}: {value}")

# def construct_nfa(regex_solution):
#     G = pgv.AGraph(strict=False, directed=True)
#
#     # Добавляем начальное и конечное состояния
#     G.add_node('start', shape='point')
#     G.add_node('end', shape='doublecircle')
#
#     # Преобразуем значение regex_solution в строку
#     regex_solution = str(regex_solution)
#
#     # Добавляем ребра в НКА
#     for i, char in enumerate(regex_solution):
#         G.add_node(str(i))
#         G.add_edge('start', str(i), label=char)
#         if i < len(regex_solution) - 1:
#             G.add_edge(str(i), str(i + 1), label='ε')
#         else:
#             G.add_edge(str(i), 'end', label='ε')
#
#     return G
#
# # Построение НКА по регулярному выражению
# regex_solution = list(solutions.values())[0]  # Берем первое решение как регулярное выражение
# nfa = construct_nfa(regex_solution)

def construct_nfa(regex_solution):
    G = pgv.AGraph(strict=False, directed=True)

    # Добавляем начальное и конечное состояния
    G.add_node('start', shape='point')
    G.add_node('end', shape='doublecircle')

    # Преобразуем значение regex_solution в строку
    regex_solution = str(regex_solution)

    # Добавляем ребра в НКА
    for i, char in enumerate(regex_solution):
        G.add_node(str(i))
        G.add_edge('start', str(i), label=char)
        if i < len(regex_solution) - 1:
            G.add_edge(str(i), str(i + 1), label='ε')
        else:
            G.add_edge(str(i), 'end', label='ε')

    return G

# Построение НКА по регулярному выражению
regex_solution = list(solutions.values())[0]  # Берем первое решение как регулярное выражение
nfa = construct_nfa(regex_solution)

# Отрисовка НКА в графвиз
nfa.layout(prog='dot')
nfa.draw('nfa.png')

import numpy as np
import pygraphviz as pgv

def epsilon_closure(nfa, states):
    epsilon_states = set(states)
    stack = list(epsilon_states)

    while stack:
        current_state = stack.pop()
        if current_state in nfa and 'ε' in nfa[current_state]:
            for state in nfa[current_state]['ε']:
                if state not in epsilon_states:
                    epsilon_states.add(state)
                    stack.append(state)

    return epsilon_states

def move(nfa, states, symbol):
    next_states = set()
    for state in states:
        if state in nfa and symbol in nfa[state]:
            next_states.update(nfa[state][symbol])
    return next_states

def nfa_to_dfa(nfa):
    dfa = {}
    alphabet = set()
    start_state = frozenset(epsilon_closure(nfa, {'start'}))
    dfa[start_state] = {}
    stack = [start_state]

    while stack:
        current_states = stack.pop()
        for symbol in nfa['alphabet']:
            alphabet.add(symbol)
            next_states = frozenset(epsilon_closure(nfa, move(nfa, current_states, symbol)))
            dfa[current_states][symbol] = next_states
            if next_states not in dfa:
                dfa[next_states] = {}
                stack.append(next_states)

    return dfa, alphabet

# Конвертация графа НКА в формат, совместимый с функцией nfa_to_dfa
def convert_nfa_to_dict(nfa):
    nfa_dict = {}
    nfa_dict['alphabet'] = set()
    for edge in nfa.edges():
        source = edge[0]
        target = edge[1]
        symbol = nfa.get_edge(source, target).attr['label']
        if symbol != 'ε':
            nfa_dict['alphabet'].add(symbol)
        if source not in nfa_dict:
            nfa_dict[source] = {}
        if symbol not in nfa_dict[source]:
            nfa_dict[source][symbol] = set()
        nfa_dict[source][symbol].add(target)
    return nfa_dict

# Преобразование графа НКА в словарь
nfa_dict = convert_nfa_to_dict(nfa)

# Детерминированное моделирование НКА
dfa, alphabet = nfa_to_dfa(nfa_dict)

# Отрисовка полученного ДКА в Graphviz
def construct_dfa_graph(dfa):
    G = pgv.AGraph(strict=False, directed=True)

    for state in dfa:
        for symbol in dfa[state]:
            target_state = dfa[state][symbol]
            G.add_edge(str(state), str(target_state), label=symbol)

    return G

dfa_graph = construct_dfa_graph(dfa)
dfa_graph.layout(prog='dot')
dfa_graph.draw('dfa.png')