diff --git a/include/mata/nfa/algorithms.hh b/include/mata/nfa/algorithms.hh index f3b993ba0..b39b70a9e 100644 --- a/include/mata/nfa/algorithms.hh +++ b/include/mata/nfa/algorithms.hh @@ -137,7 +137,7 @@ Nfa concatenate_eps(const Nfa& lhs, const Nfa& rhs, const Symbol& epsilon, bool * @param[in] nfa NFA to reduce * @param[out] state_renaming Map mapping original states to the reduced states. */ -Nfa reduce_simulation(const Nfa& nfa, StateRenaming &state_renaming); +Nfa reduce_simulation(const Nfa& nfa, StateRenaming &state_renaming, ReductionDirection direction = ReductionDirection::FORWARD); /** * @brief Reduce NFA using residual construction. @@ -148,7 +148,7 @@ Nfa reduce_simulation(const Nfa& nfa, StateRenaming &state_renaming); * @param[in] direction Direction of the residual construction (values: "forward", "backward"). */ Nfa reduce_residual(const Nfa& nfa, StateRenaming &state_renaming, - const std::string& type, const std::string& direction); + const std::string& type, ReductionDirection direction); /** * @brief Reduce NFA using residual construction. diff --git a/include/mata/nfa/nfa.hh b/include/mata/nfa/nfa.hh index 70b272469..bef7aff85 100644 --- a/include/mata/nfa/nfa.hh +++ b/include/mata/nfa/nfa.hh @@ -788,6 +788,9 @@ Nfa determinize( const Nfa& aut, std::unordered_map *subset_map = nullptr, std::optional> macrostate_discover = std::nullopt); +enum class ReductionAlgorithm { SIMULATION, RESIDUAL_AFTER, RESIDUAL_WITH }; +enum class ReductionDirection { FORWARD, BACKWARD }; + /** * @brief Reduce the size of the automaton. * @@ -801,7 +804,8 @@ Nfa determinize( * @return Reduced automaton. */ Nfa reduce(const Nfa &aut, StateRenaming *state_renaming = nullptr, - const ParameterMap& params = {{ "algorithm", "simulation" }, { "type", "after" }, { "direction", "forward" } }); + ReductionAlgorithm reduction_algorithm = ReductionAlgorithm::SIMULATION, + ReductionDirection direction = ReductionDirection::FORWARD); /** * @brief Checks inclusion of languages of two NFAs: @p smaller and @p bigger (smaller <= bigger). diff --git a/include/mata/nfa/plumbing.hh b/include/mata/nfa/plumbing.hh index 94f39ee46..dc45884b9 100644 --- a/include/mata/nfa/plumbing.hh +++ b/include/mata/nfa/plumbing.hh @@ -44,8 +44,9 @@ inline void determinize(Nfa* result, const Nfa& aut, std::unordered_map; */ void segs_one_initial_final( const std::vector& segments, bool include_empty, const State& unused_state, - std::map, std::shared_ptr>& out); - -/** - * @brief Create noodles from segment automaton @p aut. - * - * Segment automaton is a chain of finite automata (segments) connected via ε-transitions. - * A noodle is a vector of pointers to copy of the segments automata created as if there was exactly one ε-transition - * between each two consecutive segments. - * - * @param[in] automaton Segment automaton to noodlify. - * @param[in] epsilon Epsilon symbol to noodlify for. - * @param[in] include_empty Whether to also include empty noodles. - * @return A list of all (non-empty) noodles. - */ -std::vector noodlify(const SegNfa& aut, Symbol epsilon, bool include_empty = false); + std::map, std::shared_ptr>& out, nfa::ReductionAlgorithm red_alg); /** * @brief Create noodles from segment automaton @p aut. @@ -337,55 +323,7 @@ std::vector noodlify(const SegNfa& aut, Symbol epsilon, bool include_emp * @param[in] include_empty Whether to also include empty noodles. * @return A list of all (non-empty) noodles. */ -std::vector noodlify_mult_eps(const SegNfa& aut, const std::set& epsilons, bool include_empty = false); - -/** - * @brief Create noodles for left and right side of equation. - * - * Segment automaton is a chain of finite automata (segments) connected via ε-transitions. - * A noodle is a copy of the segment automaton with exactly one ε-transition between each two consecutive segments. - * - * Mata cannot work with equations, queries etc. Hence, we compute the noodles for the equation, but represent - * the equation in a way that libMata understands. The left side automata represent the left side of the equation - * and the right automaton represents the right side of the equation. To create noodles, we need a segment automaton - * representing the intersection. That can be achieved by computing a product of both sides. First, the left side - * has to be concatenated over an epsilon transitions into a single automaton to compute the intersection on, though. - * - * @param[in] lhs_automata Sequence of segment automata for left side of an equation to noodlify. - * @param[in] rhs_automaton Segment automaton for right side of an equation to noodlify. - * @param[in] include_empty Whether to also include empty noodles. - * @param[in] params Additional parameters for the noodlification: - * - "reduce": "false", "forward", "backward", "bidirectional"; Execute forward, backward or bidirectional simulation - * minimization before noodlification. - * @return A list of all (non-empty) noodles. - */ -std::vector noodlify_for_equation(const std::vector>& lhs_automata, - const Nfa& rhs_automaton, - bool include_empty = false, const ParameterMap& params = {{ "reduce", "false"}}); - -/** - * @brief Create noodles for left and right side of equation. - * - * Segment automaton is a chain of finite automata (segments) connected via ε-transitions. - * A noodle is a copy of the segment automaton with exactly one ε-transition between each two consecutive segments. - * - * Mata cannot work with equations, queries etc. Hence, we compute the noodles for the equation, but represent - * the equation in a way that libMata understands. The left side automata represent the left side of the equation - * and the right automaton represents the right side of the equation. To create noodles, we need a segment automaton - * representing the intersection. That can be achieved by computing a product of both sides. First, the left side - * has to be concatenated over an epsilon transitions into a single automaton to compute the intersection on, though. - * - * @param[in] lhs_automata Sequence of pointers to segment automata for left side of an equation to noodlify. - * @param[in] rhs_automaton Segment automaton for right side of an equation to noodlify. - * @param[in] include_empty Whether to also include empty noodles. - * @param[in] params Additional parameters for the noodlification: - * - "reduce": "false", "forward", "backward", "bidirectional"; Execute forward, backward or bidirectional simulation - * minimization before noodlification. - * @return A list of all (non-empty) noodles. - */ -std::vector noodlify_for_equation( - const std::vector& lhs_automata, const Nfa& rhs_automaton, bool include_empty = false, - const ParameterMap& params = {{ "reduce", "false"}}); +std::vector noodlify_mult_eps(const SegNfa& aut, const std::set& epsilons, nfa::ReductionAlgorithm red_alg, bool include_empty = false); /** * @brief Create noodles for left and right side of equation (both sides are given as a sequence of automata). @@ -401,7 +339,8 @@ std::vector noodlify_for_equation( std::vector noodlify_for_equation( const std::vector>& lhs_automata, const std::vector>& rhs_automata, - bool include_empty = false, const ParameterMap& params = {{ "reduce", "false"}}); + nfa::ReductionAlgorithm red_alg, + bool reduce_intersection = true); struct TransducerNoodleElement { std::shared_ptr transducer; @@ -420,7 +359,8 @@ std::vector noodlify_for_transducer( std::shared_ptr nft, const std::vector>& input_automata, const std::vector>& output_automata, - bool reduce_intersection = false + nfa::ReductionAlgorithm red_alg, + bool reduce_intersection = true ); /** diff --git a/src/nfa/operations.cc b/src/nfa/operations.cc index 0a5f8b85b..611ec0827 100644 --- a/src/nfa/operations.cc +++ b/src/nfa/operations.cc @@ -1073,40 +1073,26 @@ Simlib::Util::BinaryRelation mata::nfa::algorithms::compute_relation(const Nfa& } } -Nfa mata::nfa::reduce(const Nfa &aut, StateRenaming *state_renaming, const ParameterMap& params) { - if (!haskey(params, "algorithm")) { - throw std::runtime_error(std::to_string(__func__) + - " requires setting the \"algorithm\" key in the \"params\" argument; " - "received: " + std::to_string(params)); - } +Nfa mata::nfa::reduce(const Nfa &aut, StateRenaming *state_renaming, ReductionAlgorithm reduction_algorithm, ReductionDirection direction) { + static unsigned next_file_id = 0; + aut.print_to_mata(std::to_string(next_file_id++) + std::string(".mata")); Nfa result; std::unordered_map reduced_state_map; - const std::string& algorithm = params.at("algorithm"); - if ("simulation" == algorithm) { - result = algorithms::reduce_simulation(aut, reduced_state_map); - } - else if ("residual" == algorithm) { - // reduce type either 'after' or 'with' creation of residual automaton - if (!haskey(params, "type")) { - throw std::runtime_error(std::to_string(__func__) + - " requires setting the \"type\" key in the \"params\" argument; " - "received: " + std::to_string(params)); - } - // forward or backward canonical residual automaton - if (!haskey(params, "direction")) { - throw std::runtime_error(std::to_string(__func__) + - " requires setting the \"direction\" key in the \"params\" argument; " - "received: " + std::to_string(params)); - } - const std::string& residual_type = params.at("type"); - const std::string& residual_direction = params.at("direction"); - - result = algorithms::reduce_residual(aut, reduced_state_map, residual_type, residual_direction); - } else { - throw std::runtime_error(std::to_string(__func__) + - " received an unknown value of the \"algorithm\" key: " + algorithm); + switch(reduction_algorithm) { + case ReductionAlgorithm::SIMULATION: + result = algorithms::reduce_simulation(aut, reduced_state_map, direction); + break; + case ReductionAlgorithm::RESIDUAL_AFTER: + result = algorithms::reduce_residual(aut, reduced_state_map, "after", direction); + break; + case ReductionAlgorithm::RESIDUAL_WITH: + result = algorithms::reduce_residual(aut, reduced_state_map, "with", direction); + break; + default: + throw std::runtime_error(std::to_string(__func__) + + " received an unknown value of the \"reduction_algorithm\" key"); } if (state_renaming) { @@ -1545,10 +1531,13 @@ std::optional mata::nfa::get_word_from_lang_difference(const Nfa & n }).get_word(); } -Nfa mata::nfa::algorithms::reduce_simulation(const Nfa& aut, StateRenaming &state_renaming) { +Nfa mata::nfa::algorithms::reduce_simulation(const Nfa& aut, StateRenaming &state_renaming, ReductionDirection direction) { Nfa result; - const auto sim_relation = algorithms::compute_relation( - aut, ParameterMap{{ "relation", "simulation"}, { "direction", "forward"}}); + if (direction != ReductionDirection::FORWARD) { + throw std::runtime_error(std::to_string(__func__) + + " can only reduce simulation by forward direction (for now)"); + } + const auto sim_relation = algorithms::compute_relation(aut, ParameterMap{{ "relation", "simulation"}, { "direction", "forward"}}); auto sim_relation_symmetric = sim_relation; sim_relation_symmetric.restrict_to_symmetric(); @@ -1617,20 +1606,15 @@ Nfa mata::nfa::algorithms::reduce_simulation(const Nfa& aut, StateRenaming &stat return result; } -Nfa mata::nfa::algorithms::reduce_residual(const Nfa& nfa, StateRenaming &state_renaming, const std::string& type, const std::string& direction) { +Nfa mata::nfa::algorithms::reduce_residual(const Nfa& nfa, StateRenaming &state_renaming, const std::string& type, ReductionDirection direction) { Nfa back_determinized = nfa; Nfa result; - if (direction != "forward" && direction != "backward"){ - throw std::runtime_error(std::to_string(__func__) + - " received an unknown value of the \"direction\" key: " + direction); - } - // forward canonical residual automaton is firstly backward determinized and // then the residual construction is done forward, for backward residual automaton // is it the opposite, so the automaton is reverted once more before and after // construction, however the first two reversion negate each other out - if (direction == "forward") + if (direction == ReductionDirection::FORWARD) back_determinized = revert(back_determinized); back_determinized = revert(determinize(back_determinized)); // backward deteminization @@ -1653,7 +1637,7 @@ Nfa mata::nfa::algorithms::reduce_residual(const Nfa& nfa, StateRenaming &state_ " received an unknown value of the \"type\" key: " + type); } - if (direction == "backward") + if (direction == ReductionDirection::BACKWARD) result = revert(result); return result.trim(); diff --git a/src/strings/nfa-noodlification.cc b/src/strings/nfa-noodlification.cc index fca368807..8f4f9fa65 100644 --- a/src/strings/nfa-noodlification.cc +++ b/src/strings/nfa-noodlification.cc @@ -56,101 +56,21 @@ Nfa concatenate_with(const std::vector>& nfas, mata::Symbol } // namespace -std::vector seg_nfa::noodlify(const SegNfa& aut, const Symbol epsilon, bool include_empty) { - - const std::set epsilons({epsilon}); - // return noodlify_reach(aut, epsilons, include_empty); - - Segmentation segmentation{ aut, epsilons }; - const auto& segments{ segmentation.get_untrimmed_segments() }; - - if (segments.size() == 1) { - std::shared_ptr segment = std::make_shared(segments[0]); - segment->trim(); - if (segment->num_of_states() > 0 || include_empty) { - return {{ segment }}; - } else { - return {}; - } - } - - State unused_state = aut.num_of_states(); // get some State not used in aut - std::map, std::shared_ptr> segments_one_initial_final; - segs_one_initial_final(segments, include_empty, unused_state, segments_one_initial_final); - - const auto& epsilon_depths{ segmentation.get_epsilon_depths() }; - - // Compute number of all combinations of ε-transitions with one ε-transitions from each depth. - size_t num_of_permutations{ get_num_of_permutations(epsilon_depths) }; - size_t epsilon_depths_size{ epsilon_depths.size() }; - - std::vector noodles{}; - // noodle of epsilon transitions (each from different depth) - std::vector epsilon_noodle(epsilon_depths_size); - // for each combination of ε-transitions, create the automaton. - // based on https://stackoverflow.com/questions/48270565/create-all-possible-combinations-of-multiple-vectors - for (size_t index{ 0 }; index < num_of_permutations; ++index) { - size_t temp{ index }; - for (size_t depth{ 0 }; depth < epsilon_depths_size; ++depth) { - size_t num_of_trans_at_cur_depth = epsilon_depths.at(depth).size(); - size_t computed_index = temp % num_of_trans_at_cur_depth; - temp /= num_of_trans_at_cur_depth; - epsilon_noodle[depth] = epsilon_depths.at(depth)[computed_index]; - } - - Noodle noodle; - - // epsilon_noodle[0] for sure exists, as we sorted out the case of only one segment at the beginning - auto first_segment_iter = segments_one_initial_final.find(std::make_pair(unused_state, epsilon_noodle[0].source)); - if (first_segment_iter != segments_one_initial_final.end()) { - noodle.push_back(first_segment_iter->second); - } else { - continue; - } - - bool all_segments_exist = true; - for (auto iter = epsilon_noodle.begin(); iter + 1 != epsilon_noodle.end(); ++iter) { - auto next_iter = iter + 1; - auto segment_iter = segments_one_initial_final.find(std::make_pair(iter->target, next_iter->source)); - if (segment_iter != segments_one_initial_final.end()) { - noodle.push_back(segment_iter->second); - } else { - all_segments_exist = false; - break; - } - } - - if (!all_segments_exist) { - continue; - } - - auto last_segment_iter = segments_one_initial_final.find( - std::make_pair(epsilon_noodle.back().target, unused_state)); - if (last_segment_iter != segments_one_initial_final.end()) { - noodle.push_back(last_segment_iter->second); - } else { - continue; - } - - noodles.push_back(noodle); - } - return noodles; -} - //todo: is this taking all final times all initial? // can it be done more efficiently? (only connected combinations through dfs) void seg_nfa::segs_one_initial_final( const std::vector& segments, bool include_empty, const State& unused_state, - std::map, std::shared_ptr>& out) { + std::map, std::shared_ptr>& out, + ReductionAlgorithm red_alg) { for (auto iter = segments.begin(); iter != segments.end(); ++iter) { if (iter == segments.begin()) { // first segment will always have all initial states in noodles for (const State final_state: iter->final) { Nfa segment_one_final = *iter; segment_one_final.final = {final_state }; - segment_one_final = reduce(segment_one_final.trim()); + segment_one_final = reduce(segment_one_final.trim(), nullptr, red_alg); if (segment_one_final.num_of_states() > 0 || include_empty) { out[std::make_pair(unused_state, final_state)] = std::make_shared(segment_one_final); @@ -160,7 +80,7 @@ void seg_nfa::segs_one_initial_final( for (const State init_state: iter->initial) { Nfa segment_one_init = *iter; segment_one_init.initial = {init_state }; - segment_one_init = reduce(segment_one_init.trim()); + segment_one_init = reduce(segment_one_init.trim(), nullptr, red_alg); if (segment_one_init.num_of_states() > 0 || include_empty) { out[std::make_pair(init_state, unused_state)] = std::make_shared(segment_one_init); @@ -172,7 +92,7 @@ void seg_nfa::segs_one_initial_final( Nfa segment_one_init_final = *iter; segment_one_init_final.initial = {init_state }; segment_one_init_final.final = {final_state }; - segment_one_init_final = reduce(segment_one_init_final.trim()); + segment_one_init_final = reduce(segment_one_init_final.trim(), nullptr, red_alg); if (segment_one_init_final.num_of_states() > 0 || include_empty) { out[std::make_pair(init_state, final_state)] = std::make_shared(segment_one_init_final); } @@ -182,7 +102,7 @@ void seg_nfa::segs_one_initial_final( } } -std::vector seg_nfa::noodlify_mult_eps(const SegNfa& aut, const std::set& epsilons, bool include_empty) { +std::vector seg_nfa::noodlify_mult_eps(const SegNfa& aut, const std::set& epsilons, ReductionAlgorithm red_alg, bool include_empty) { Segmentation segmentation{ aut, epsilons }; const auto& segments{ segmentation.get_untrimmed_segments() }; @@ -204,7 +124,7 @@ std::vector seg_nfa::noodlify_mult_eps(const State unused_state = aut.num_of_states(); // get some State not used in aut std::map, std::shared_ptr> segments_one_initial_final; - segs_one_initial_final(segments, include_empty, unused_state, segments_one_initial_final); + segs_one_initial_final(segments, include_empty, unused_state, segments_one_initial_final, red_alg); const auto& epsilon_depths_map{ segmentation.get_epsilon_depth_trans_map() }; @@ -271,97 +191,11 @@ std::vector seg_nfa::noodlify_mult_eps(const return noodles; } -std::vector seg_nfa::noodlify_for_equation( - const std::vector>& lhs_automata, const Nfa& rhs_automaton, - bool include_empty, const ParameterMap& params) { - const auto lhs_aut_begin{ lhs_automata.begin() }; - const auto lhs_aut_end{ lhs_automata.end() }; - for (auto lhs_aut_iter{ lhs_aut_begin }; lhs_aut_iter != lhs_aut_end; - ++lhs_aut_iter) { - (*lhs_aut_iter).get().unify_initial(); - (*lhs_aut_iter).get().unify_final(); - } - - if (lhs_automata.empty() || rhs_automaton.is_lang_empty()) { return {}; } - - // Automaton representing the left side concatenated over epsilon transitions. - Nfa concatenated_lhs{ *lhs_aut_begin }; - for (auto next_lhs_aut_it{ lhs_aut_begin + 1 }; next_lhs_aut_it != lhs_aut_end; - ++next_lhs_aut_it) { - concatenated_lhs = concatenate(concatenated_lhs, *next_lhs_aut_it, EPSILON); - } - - auto product_pres_eps_trans{ - intersection(concatenated_lhs, rhs_automaton).trim() }; - if (product_pres_eps_trans.is_lang_empty()) { - return {}; - } - if (utils::haskey(params, "reduce")) { - const std::string& reduce_value = params.at("reduce"); - if (reduce_value == "forward" || reduce_value == "bidirectional") { - product_pres_eps_trans = reduce(product_pres_eps_trans); - } - if (reduce_value == "backward" || reduce_value == "bidirectional") { - product_pres_eps_trans = revert(product_pres_eps_trans); - product_pres_eps_trans = reduce(product_pres_eps_trans); - product_pres_eps_trans = revert(product_pres_eps_trans); - } - } - return noodlify(product_pres_eps_trans, EPSILON, include_empty); -} - -std::vector seg_nfa::noodlify_for_equation( - const std::vector& lhs_automata, const Nfa& rhs_automaton, bool include_empty, - const ParameterMap& params) { - const auto lhs_aut_begin{ lhs_automata.begin() }; - const auto lhs_aut_end{ lhs_automata.end() }; - - std::string reduce_value{}; - if (utils::haskey(params, "reduce")) { - reduce_value = params.at("reduce"); - } - - if (!reduce_value.empty()) { - if (reduce_value == "forward" || reduce_value == "backward" || reduce_value == "bidirectional") { - for (auto lhs_aut_iter{ lhs_aut_begin }; lhs_aut_iter != lhs_aut_end; - ++lhs_aut_iter) { - (*lhs_aut_iter)->unify_initial(); - (*lhs_aut_iter)->unify_final(); - } - } - } - - if (lhs_automata.empty() || rhs_automaton.is_lang_empty()) { return {}; } - - // Automaton representing the left side concatenated over epsilon transitions. - Nfa concatenated_lhs{ *(*lhs_aut_begin) }; - for (auto next_lhs_aut_it{ lhs_aut_begin + 1 }; next_lhs_aut_it != lhs_aut_end; - ++next_lhs_aut_it) { - concatenated_lhs = concatenate(concatenated_lhs, *(*next_lhs_aut_it), EPSILON); - } - - auto product_pres_eps_trans{ - intersection(concatenated_lhs, rhs_automaton).trim() }; - if (product_pres_eps_trans.is_lang_empty()) { - return {}; - } - if (!reduce_value.empty()) { - if (reduce_value == "forward" || reduce_value == "bidirectional") { - product_pres_eps_trans = reduce(product_pres_eps_trans); - } - if (reduce_value == "backward" || reduce_value == "bidirectional") { - product_pres_eps_trans = revert(product_pres_eps_trans); - product_pres_eps_trans = reduce(product_pres_eps_trans); - product_pres_eps_trans = revert(product_pres_eps_trans); - } - } - return noodlify(product_pres_eps_trans, EPSILON, include_empty); -} - - std::vector seg_nfa::noodlify_for_equation( const std::vector>& lhs_automata, - const std::vector>& rhs_automata, bool include_empty, const ParameterMap& params) { + const std::vector>& rhs_automata, + ReductionAlgorithm red_alg, + bool reduce_intersection) { if (lhs_automata.empty() || rhs_automata.empty()) { return {}; } std::unordered_set> unified_nfas; // Unify each automaton only once. @@ -378,18 +212,10 @@ std::vector seg_nfa::noodlify_for_equation( if (product_pres_eps_trans.is_lang_empty()) { return {}; } - if (utils::haskey(params, "reduce")) { - const std::string& reduce_value = params.at("reduce"); - if (reduce_value == "forward" || reduce_value == "bidirectional") { - product_pres_eps_trans = reduce(product_pres_eps_trans); - } - if (reduce_value == "backward" || reduce_value == "bidirectional") { - product_pres_eps_trans = revert(product_pres_eps_trans); - product_pres_eps_trans = reduce(product_pres_eps_trans); - product_pres_eps_trans = revert(product_pres_eps_trans); - } + if (reduce_intersection) { + product_pres_eps_trans = reduce(product_pres_eps_trans, nullptr, red_alg); } - return noodlify_mult_eps(product_pres_eps_trans, { EPSILON, EPSILON-1 }, include_empty); + return noodlify_mult_eps(product_pres_eps_trans, { EPSILON, EPSILON-1 }, red_alg); } seg_nfa::VisitedEpsilonsCounterVector seg_nfa::process_eps_map(const VisitedEpsilonsCounterMap& eps_cnt) { @@ -404,6 +230,7 @@ std::vector seg_nfa::noodlify_for_transducer( std::shared_ptr nft, const std::vector>& input_automata, const std::vector>& output_automata, + ReductionAlgorithm red_alg, bool reduce_intersection ) { if (input_automata.empty() || output_automata.empty()) { return {}; } @@ -530,7 +357,7 @@ std::vector seg_nfa::noodlify_for_transducer( std::vector result; std::map,TransducerNoodleElement> seg_nfa_to_transducer_el; // we create for each segment NFAi only one NFTi and keep it here - for (const auto& noodle : noodlify_mult_eps(intersection_nfa, {INPUT_DELIMITER, OUTPUT_DELIMITER}, false)) { + for (const auto& noodle : noodlify_mult_eps(intersection_nfa, {INPUT_DELIMITER, OUTPUT_DELIMITER}, red_alg)) { TransducerNoodle new_noodle; for (const auto& element : noodle) { // element.first is NFAi @@ -554,9 +381,9 @@ std::vector seg_nfa::noodlify_for_transducer( TransducerNoodleElement transd_el{element_nft, // the language of the input automaton is the projection to input track - std::make_shared(nfa::reduce(nfa::remove_epsilon(nft::project_to(*element_nft, 0)))), element.second[0], + std::make_shared(nfa::reduce(nfa::remove_epsilon(nft::project_to(*element_nft, 0)), nullptr, red_alg)), element.second[0], // the language of the output automaton is the projection to output track - std::make_shared(nfa::reduce(nfa::remove_epsilon(nft::project_to(*element_nft, 1)))), element.second[1] + std::make_shared(nfa::reduce(nfa::remove_epsilon(nft::project_to(*element_nft, 1)), nullptr, red_alg)), element.second[1] }; seg_nfa_to_transducer_el.insert({element_aut, transd_el}); new_noodle.push_back(transd_el); diff --git a/tests/nfa/nfa.cc b/tests/nfa/nfa.cc index cfbe7845f..b89d37c29 100644 --- a/tests/nfa/nfa.cc +++ b/tests/nfa/nfa.cc @@ -3288,19 +3288,11 @@ TEST_CASE("mata::nfa::algorithms::minimize_hopcroft()") { TEST_CASE("mata::nfa::reduce_size_by_residual()") { Nfa aut; StateRenaming state_renaming; - ParameterMap params_after, params_with; - params_after["algorithm"] = "residual"; - params_with["algorithm"] = "residual"; SECTION("empty automaton") { - params_after["type"] = "after"; - params_after["direction"] = "forward"; - params_with["type"] = "with"; - params_with["direction"] = "forward"; - - Nfa result_after = reduce(aut, &state_renaming, params_after); - Nfa result_with = reduce(aut, &state_renaming, params_with); + Nfa result_after = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_AFTER); + Nfa result_with = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_WITH); REQUIRE(result_after.delta.empty()); REQUIRE(result_after.initial.empty()); @@ -3311,16 +3303,12 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { SECTION("simple automaton") { - params_after["type"] = "after"; - params_after["direction"] = "forward"; - params_with["type"] = "with"; - params_with["direction"] = "forward"; aut.add_state(2); aut.initial.insert(1); aut.final.insert(2); - Nfa result_after = reduce(aut, &state_renaming, params_after); - Nfa result_with = reduce(aut, &state_renaming, params_with); + Nfa result_after = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_AFTER); + Nfa result_with = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_WITH); REQUIRE(result_after.num_of_states() == 0); REQUIRE(result_after.initial.empty()); @@ -3330,8 +3318,8 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { REQUIRE(are_equivalent(aut, result_after)); aut.delta.add(1, 'a', 2); - result_after = reduce(aut, &state_renaming, params_after); - result_with = reduce(aut, &state_renaming, params_with); + result_after = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_AFTER); + result_with = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_WITH); REQUIRE(result_after.num_of_states() == 2); REQUIRE(result_after.initial[0]); @@ -3343,10 +3331,6 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { SECTION("medium automaton") { - params_after["type"] = "after"; - params_after["direction"] = "forward"; - params_with["type"] = "with"; - params_with["direction"] = "forward"; aut.add_state(4); aut.initial = { 1 }; @@ -3359,8 +3343,8 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { aut.delta.add(3, 'a', 3); aut.delta.add(2, 'a', 1); - Nfa result_after = reduce(aut, &state_renaming, params_after); - Nfa result_with = reduce(aut, &state_renaming, params_with); + Nfa result_after = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_AFTER); + Nfa result_with = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_WITH); REQUIRE(result_after.num_of_states() == 4); REQUIRE(result_after.initial[0]); @@ -3380,10 +3364,6 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { SECTION("big automaton") { - params_after["type"] = "after"; - params_after["direction"] = "forward"; - params_with["type"] = "with"; - params_with["direction"] = "forward"; aut.add_state(7); aut.initial = { 0 }; @@ -3423,8 +3403,8 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { aut.delta.add(6, 'c', 1); aut.delta.add(6, 'd', 1); - Nfa result_after = reduce(aut, &state_renaming, params_after); - Nfa result_with = reduce(aut, &state_renaming, params_with); + Nfa result_after = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_AFTER); + Nfa result_with = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_WITH); REQUIRE(result_after.num_of_states() == 5); REQUIRE(result_after.initial[0]); @@ -3485,11 +3465,6 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { SECTION("backward residual big automaton") { - params_after["type"] = "after"; - params_after["direction"] = "backward"; - params_with["type"] = "with"; - params_with["direction"] = "backward"; - aut.add_state(7); aut.initial = { 0 }; @@ -3529,8 +3504,8 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { aut.delta.add(6, 'c', 1); aut.delta.add(6, 'd', 1); - Nfa result_after = reduce(aut, &state_renaming, params_after); - Nfa result_with = reduce(aut, &state_renaming, params_with); + Nfa result_after = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_AFTER, ReductionDirection::BACKWARD); + Nfa result_with = reduce(aut, &state_renaming, ReductionAlgorithm::RESIDUAL_WITH, ReductionDirection::BACKWARD); REQUIRE(result_after.num_of_states() == 6); REQUIRE(result_after.initial[0]); @@ -3562,28 +3537,6 @@ TEST_CASE("mata::nfa::reduce_size_by_residual()") { REQUIRE(are_equivalent(aut, result_after)); } - - SECTION("error checking") - { - CHECK_THROWS_WITH(reduce(aut, &state_renaming, params_after), - Catch::Matchers::ContainsSubstring("requires setting the \"type\" key in the \"params\" argument;")); - - params_after["type"] = "bad_type"; - CHECK_THROWS_WITH(reduce(aut, &state_renaming, params_after), - Catch::Matchers::ContainsSubstring("requires setting the \"direction\" key in the \"params\" argument;")); - - params_after["direction"] = "unknown_direction"; - CHECK_THROWS_WITH(reduce(aut, &state_renaming, params_after), - Catch::Matchers::ContainsSubstring("received an unknown value of the \"direction\" key")); - - params_after["direction"] = "forward"; - CHECK_THROWS_WITH(reduce(aut, &state_renaming, params_after), - Catch::Matchers::ContainsSubstring("received an unknown value of the \"type\" key")); - - params_after["type"] = "after"; - CHECK_NOTHROW(reduce(aut, &state_renaming, params_after)); - - } } TEST_CASE("mata::nfa::union_norename()") { diff --git a/tests/strings/nfa-noodlification.cc b/tests/strings/nfa-noodlification.cc index 768f61ffa..7df2b39ef 100644 --- a/tests/strings/nfa-noodlification.cc +++ b/tests/strings/nfa-noodlification.cc @@ -55,692 +55,3 @@ using namespace mata::nfa::builder; x.delta.add(14, 'b', 12); \ // }}} - -template void unused(const T &) {} - -TEST_CASE("mata::nfa::SegNfa::noodlify()") -{ - Nfa aut{20}; - - SECTION("Small automaton") { - aut.initial.insert(0); - aut.final.insert(1); - aut.delta.add(0, 'a', 1); - - Nfa noodle{2 }; - noodle.initial.insert(0); - noodle.delta.add(0, 'a', 1); - noodle.final.insert(1); - auto result{ seg_nfa::noodlify(aut, 'c') }; - REQUIRE(result.size() == 1); - REQUIRE(result[0].size() == 1); - CHECK(are_equivalent(*result[0][0], noodle)); - - auto result_segments{ seg_nfa::noodlify_for_equation({ aut } , aut) }; - REQUIRE(result_segments.size() == 1); - REQUIRE(result_segments[0].size() == 1); - CHECK(are_equivalent(*result_segments[0][0], noodle)); - } - - SECTION("1-2-3 epsilon transitions") - { - aut.initial.insert(0); - aut.final.insert({4, 5, 6, 7}); - aut.delta.add(0, 'e', 1); - aut.delta.add(1, 'e', 2); - aut.delta.add(1, 'e', 3); - aut.delta.add(2, 'e', 4); - aut.delta.add(2, 'e', 5); - aut.delta.add(2, 'e', 6); - aut.delta.add(3, 'e', 7); - - auto noodles{ seg_nfa::noodlify(aut, 'e') }; - - CHECK(noodles.size() == 4); - } - - SECTION("6-5-6 epsilon transitions") - { - aut.initial.insert({0, 1, 2}); - aut.final.insert({11, 12, 13, 14, 15, 16}); - aut.delta.add(0, 'e', 3); - aut.delta.add(0, 'e', 4); - aut.delta.add(0, 'e', 5); - aut.delta.add(1, 'e', 3); - aut.delta.add(1, 'e', 4); - aut.delta.add(2, 'e', 5); - - aut.delta.add(3, 'e', 6); - aut.delta.add(3, 'e', 7); - aut.delta.add(4, 'e', 8); - aut.delta.add(4, 'e', 9); - aut.delta.add(5, 'e', 10); - - aut.delta.add(6, 'e', 11); - aut.delta.add(7, 'e', 12); - aut.delta.add(8, 'e', 13); - aut.delta.add(8, 'e', 14); - aut.delta.add(9, 'e', 15); - aut.delta.add(10, 'e', 16); - - auto noodles{ seg_nfa::noodlify(aut, 'e') }; - - CHECK(noodles.size() == 12); - } - - SECTION("1-2-3-3 epsilon transitions") - { - aut.initial.insert(0); - aut.final.insert(7); - aut.delta.add(0, 'e', 1); - - aut.delta.add(1, 'e', 2); - aut.delta.add(1, 'e', 3); - - aut.delta.add(2, 'e', 4); - aut.delta.add(3, 'e', 5); - aut.delta.add(3, 'e', 6); - - aut.delta.add(4, 'e', 7); - aut.delta.add(5, 'e', 7); - aut.delta.add(6, 'e', 7); - - auto noodles{ seg_nfa::noodlify(aut, 'e') }; - - CHECK(noodles.size() == 3); - } -} - -TEST_CASE("mata::nfa::SegNfa::noodlify_for_equation()") { - SECTION("Empty input") { - CHECK(seg_nfa::noodlify_for_equation(std::vector>{}, Nfa{}).empty()); - } - - SECTION("Empty left side") { - Nfa right{1}; - right.initial.insert(0); - right.final.insert(0); - CHECK(seg_nfa::noodlify_for_equation(std::vector>{}, right).empty()); - } - - SECTION("Empty right side") { - Nfa left{ 1}; - left.initial.insert(0); - left.final.insert(0); - CHECK(seg_nfa::noodlify_for_equation({ left }, Nfa{}).empty()); - } - - SECTION("Small automata without initial and final states") { - Nfa left{ 1}; - Nfa right{ 2}; - CHECK(seg_nfa::noodlify_for_equation({ left }, right).empty()); - } - - SECTION("Small automata") { - Nfa left1{ 1 }; - left1.initial.insert(0); - left1.final.insert(0); - Nfa left2{ 1 }; - left2.initial.insert(0); - left2.final.insert(0); - Nfa right{ 2 }; - right.initial.insert(0); - right.final.insert(0); - - Nfa noodle{ 2 }; - noodle.initial.insert(0); - noodle.delta.add(0, 0, 1); - noodle.final.insert(1); - auto result{ seg_nfa::noodlify_for_equation({ left1, left2 }, right) }; - REQUIRE(result.size() == 1); - } - - SECTION("Larger automata") { - Nfa left1{ 2}; - left1.initial.insert(0); - left1.final.insert(1); - left1.delta.add(0, 'a', 1); - Nfa left2{ 2}; - left2.initial.insert(0); - left2.final.insert(1); - left2.delta.add(0, 'b', 1); - Nfa right_side{ 3}; - right_side.initial.insert(0); - right_side.delta.add(0, 'a', 1); - right_side.delta.add(1, 'b', 2); - right_side.final.insert(2); - - Nfa noodle{ 4 }; - noodle.initial.insert(0); - noodle.final.insert(3); - noodle.delta.add(0, 'a', 1); - noodle.delta.add(1, 'c', 2); // The automatically chosen epsilon symbol (one larger than 'b'). - noodle.delta.add(2, 'b', 3); - auto result{ seg_nfa::noodlify_for_equation({ left1, left2 }, right_side) }; - REQUIRE(result.size() == 1); - //CHECK(are_equivalent(result[0], noodle)); - } - - SECTION("Single noodle") { - Nfa left{ 10}; - left.initial.insert(0); - left.final.insert(9); - left.delta.add(0, 108, 1); - left.delta.add(1, 111, 2); - left.delta.add(2, 99, 3); - left.delta.add(3, 97, 4); - left.delta.add(4, 108, 5); - left.delta.add(5, 104, 6); - left.delta.add(6, 111, 7); - left.delta.add(7, 115, 8); - left.delta.add(8, 116, 9); - - Nfa right_side{ 1 }; - right_side.initial.insert(0); - right_side.final.insert(0); - right_side.delta.add(0, 44, 0); - right_side.delta.add(0, 47, 0); - right_side.delta.add(0, 58, 0); - right_side.delta.add(0, 85, 0); - right_side.delta.add(0, 90, 0); - right_side.delta.add(0, 97, 0); - right_side.delta.add(0, 99, 0); - right_side.delta.add(0, 104, 0); - right_side.delta.add(0, 108, 0); - right_side.delta.add(0, 111, 0); - right_side.delta.add(0, 115, 0); - right_side.delta.add(0, 116, 0); - right_side.delta.add(0, 117, 0); - right_side.delta.add(0, 122, 0); - - auto result{ seg_nfa::noodlify_for_equation({ left }, right_side) }; - REQUIRE(result.size() == 1); - REQUIRE(result[0].size() == 1); - CHECK(are_equivalent(*result[0][0], left)); - } - - SECTION("Larger automata with separate noodles") { - Nfa left1{ 3}; - left1.initial.insert(0); - left1.final.insert({1, 2}); - left1.delta.add(0, 'a', 1); - left1.delta.add(0, 'b', 2); - Nfa left2{ 2}; - left2.initial.insert(0); - left2.final.insert(1); - left2.delta.add(0, 'a', 1); - Nfa left3{ 2}; - left3.initial.insert(0); - left3.final.insert(1); - left3.delta.add(0, 'b', 1); - - Nfa noodle1_segment1{ 2 }; - noodle1_segment1.initial.insert(0); - noodle1_segment1.final.insert(1); - noodle1_segment1.delta.add(0, 'a', 1); - - Nfa noodle1_segment2{ 2 }; - noodle1_segment2.initial.insert(0); - noodle1_segment2.final.insert(1); - noodle1_segment2.delta.add(0, 'a', 1); - - Nfa noodle1_segment3{ 2 }; - noodle1_segment3.initial.insert(0); - noodle1_segment3.final.insert(1); - noodle1_segment3.delta.add(0, 'b', 1); - - std::vector> noodle1_segments{ std::make_shared(noodle1_segment1), - std::make_shared(noodle1_segment2), std::make_shared(noodle1_segment3) }; - - SECTION("Full intersection") { - Nfa right_side{ 7 }; - right_side.initial.insert(0); - right_side.delta.add(0, 'a', 1); - right_side.delta.add(1, 'a', 2); - right_side.delta.add(2, 'b', 3); - right_side.delta.add(0, 'b', 4); - right_side.delta.add(4, 'a', 5); - right_side.delta.add(5, 'b', 6); - right_side.final.insert({3, 6}); - - Nfa noodle2_segment1{ 2 }; - noodle2_segment1.initial.insert(0); - noodle2_segment1.final.insert(1); - noodle2_segment1.delta.add(0, 'b', 1); - - Nfa noodle2_segment2{ 2 }; - noodle2_segment2.initial.insert(0); - noodle2_segment2.final.insert(1); - noodle2_segment2.delta.add(0, 'a', 1); - - Nfa noodle2_segment3{ 2 }; - noodle2_segment3.initial.insert(0); - noodle2_segment3.final.insert(1); - noodle2_segment3.delta.add(0, 'b', 1); - - std::vector> noodle2_segments{ std::make_shared(noodle2_segment1), - std::make_shared(noodle2_segment2), std::make_shared(noodle2_segment3) }; - - std::vector expected{ noodle1_segments, noodle2_segments }; - - auto result{ seg_nfa::noodlify_for_equation({ left1, left2, left3 }, right_side) }; - REQUIRE(result.size() == 2); - - CHECK(are_equivalent(*result[0][0], *expected[0][0])); - CHECK(are_equivalent(*result[0][1], *expected[0][1])); - CHECK(are_equivalent(*result[0][2], *expected[0][2])); - - CHECK(are_equivalent(*result[1][0], *expected[1][0])); - CHECK(are_equivalent(*result[1][1], *expected[1][1])); - CHECK(are_equivalent(*result[1][2], *expected[1][2])); - } - - SECTION("Partial intersection") { - Nfa right_side{ 7 }; - right_side.initial.insert(0); - right_side.delta.add(0, 'a', 1); - right_side.delta.add(1, 'a', 2); - right_side.delta.add(2, 'b', 3); - right_side.delta.add(0, 'b', 4); - right_side.delta.add(4, 'a', 5); - right_side.delta.add(5, 'b', 6); - right_side.final.insert(3); - - auto result{ seg_nfa::noodlify_for_equation({ left1, left2, left3 }, right_side) }; - REQUIRE(result.size() == 1); - CHECK(are_equivalent(*result[0][0], *noodle1_segments[0])); - CHECK(are_equivalent(*result[0][1], *noodle1_segments[1])); - CHECK(are_equivalent(*result[0][2], *noodle1_segments[2])); - } - } -} - - -TEST_CASE("mata::nfa::SegNfa::noodlify_for_equation() both sides") { - SECTION("Empty input") { - CHECK(seg_nfa::noodlify_for_equation(std::vector>{}, std::vector>{}).empty()); - } - - SECTION("Simple automata") { - Nfa x, y, z, w; - x = create_from_regex("a*"); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - w = create_from_regex("(a|b)*"); - - auto res = std::vector>>({ - {{x, {0, 0} }, {x, {0, 1} }, {y, {1, 1} }}, - {{x, {0, 0} }, {y, {1, 0} }, {y, {1, 1} }} } ); - std::vector noodles = seg_nfa::noodlify_for_equation( - std::vector>{std::make_shared(x), std::make_shared(y) }, - std::vector>{std::make_shared(z), std::make_shared(w)}); - for(size_t i = 0; i < noodles.size(); i++) { - for(size_t j = 0; j < noodles[i].size(); j++) { - CHECK(noodles[i][j].second == res[i][j].second); - CHECK(are_equivalent(*noodles[i][j].first.get(), res[i][j].first, nullptr)); - auto used_symbols{ noodles[i][j].first->delta.get_used_symbols() }; - CHECK(used_symbols.find(EPSILON) == used_symbols.end()); - } - } - } - - SECTION("Simple automata -- epsilon result") { - Nfa x, y, z, w, astar; - x = create_from_regex("a+"); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - w = create_from_regex("(a|b)+"); - astar = create_from_regex("a*"); - - auto res = std::vector>>({ - {{x, {0, 1} }, {z, {1, 1} }}, - {{x, {0, 0} }, {w, {1, 1} }}, - {{x, {0, 0} }, {x, {0, 1} }, {z, {1, 1} }}, - {{x, {0, 0} }, {z, {1, 0} }, {w, {1, 1} }}, - } ); - std::vector noodles = seg_nfa::noodlify_for_equation( - std::vector>{std::make_shared(x), std::make_shared(y) }, - std::vector>{std::make_shared(z), std::make_shared(w)}); - for(size_t i = 0; i < noodles.size(); i++) { - for(size_t j = 0; j < noodles[i].size(); j++) { - CHECK(noodles[i][j].second == res[i][j].second); - CHECK(are_equivalent(*noodles[i][j].first.get(), res[i][j].first, nullptr)); - auto used_symbols{ noodles[i][j].first->delta.get_used_symbols() }; - CHECK(used_symbols.find(EPSILON) == used_symbols.end()); - } - } - } - - SECTION("Simple automata -- epsilon input") { - Nfa x, y, z, w; - x = create_from_regex(""); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - w = create_from_regex("(a|b)*"); - - auto res = std::vector>>({} ); - std::vector noodles = seg_nfa::noodlify_for_equation( - std::vector>{std::make_shared(x) }, - std::vector>{std::make_shared(y), std::make_shared(z), std::make_shared(w)}); - CHECK(noodles.size() == 1); - for(size_t i = 0; i < noodles.size(); i++) { - for(size_t j = 0; j < noodles[i].size(); j++) { - CHECK(noodles[i][j].second == res[i][j].second); - CHECK(are_equivalent(*noodles[i][j].first.get(), res[i][j].first, nullptr)); - auto used_symbols{ noodles[i][j].first->delta.get_used_symbols() }; - CHECK(used_symbols.find(EPSILON) == used_symbols.end()); - } - } - } - - SECTION("Simple automata -- epsilon input 2") { - Nfa x, y, z, w; - x = create_from_regex(""); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - w = create_from_regex("(a|b)*"); - - auto res = std::vector>>({ - {{y, {1, 1} }}, - {{y, {1, 0} }, {y, {1, 1} }}, - } ); - std::vector noodles = seg_nfa::noodlify_for_equation( - std::vector>{std::make_shared(x), std::make_shared(y) }, - std::vector>{std::make_shared(z), std::make_shared(w)}); - CHECK(noodles.size() == 2); - for(size_t i = 0; i < noodles.size(); i++) { - for(size_t j = 0; j < noodles[i].size(); j++) { - CHECK(noodles[i][j].second == res[i][j].second); - CHECK(are_equivalent(*noodles[i][j].first.get(), res[i][j].first, nullptr)); - auto used_symbols{ noodles[i][j].first->delta.get_used_symbols() }; - CHECK(used_symbols.find(EPSILON) == used_symbols.end()); - } - } - } - - SECTION("Simple automata -- regex 1") { - Nfa x, y, z, u; - x = create_from_regex("a"); - y = create_from_regex("ab*"); - z = create_from_regex("ab*"); - u = create_from_regex("a*"); - - auto res = std::vector>>({ - {{x, {0, 0} }, {x, {1, 1} }}, - } ); - std::vector noodles = seg_nfa::noodlify_for_equation( - std::vector>{std::make_shared(x), std::make_shared(y) }, - std::vector>{std::make_shared(z), std::make_shared(u)}); - CHECK(noodles.size() == 1); - for(size_t i = 0; i < noodles.size(); i++) { - for(size_t j = 0; j < noodles[i].size(); j++) { - CHECK(noodles[i][j].second == res[i][j].second); - CHECK(are_equivalent(*noodles[i][j].first.get(), res[i][j].first, nullptr)); - auto used_symbols{ noodles[i][j].first->delta.get_used_symbols() }; - CHECK(used_symbols.find(EPSILON) == used_symbols.end()); - } - } - } - - SECTION("Bug from noodler") { - Nfa l1 = mata::nfa::builder::parse_from_mata(std::string(R"V0G0N( -@NFA-explicit -%Alphabet-auto -%Initial q0 -%Final q2 -q0 0 q1 -q0 1 q1 -q0 37 q1 -q0 38 q1 -q0 43 q1 -q1 0 q2 -q1 1 q2 -q1 37 q2 -q1 38 q2 -q1 43 q2 -)V0G0N")); - Nfa l2 = mata::nfa::builder::parse_from_mata(std::string(R"V0G0N( -@NFA-explicit -%Alphabet-auto -%Initial q0 -%Final q2 -q0 0 q1 -q0 1 q1 -q0 37 q1 -q0 38 q1 -q0 43 q1 -q1 0 q2 -q1 1 q2 -q1 37 q2 -q1 38 q2 -q1 43 q2 -)V0G0N")); - Nfa r = mata::nfa::builder::parse_from_mata(std::string(R"V0G0N( -@NFA-explicit -%Alphabet-auto -%Initial q0 -%Final q4 -q0 0 q1 -q1 0 q2 -q2 0 q3 -q3 0 q4 -)V0G0N")); - std::vector noodles = seg_nfa::noodlify_for_equation( - std::vector>{std::make_shared(l1), std::make_shared(l2) }, - std::vector>{std::make_shared(r)}, - false, - {{"reduce", "forward"}}); - CHECK(noodles.size() == 1); - mata::Word word{0,0}; - CHECK(noodles[0][0].first->get_word().value() == word); - CHECK(noodles[0][1].first->get_word().value() == word); - } -} - -TEST_CASE("mata::nfa::SegNfa::noodlify_for_equation() for profiling", "[.profiling][noodlify]") { - Nfa left1{ 3}; - left1.initial.insert(0); - left1.final.insert({1, 2}); - left1.delta.add(0, 'a', 1); - left1.delta.add(0, 'b', 2); - Nfa left2{ 2}; - left2.initial.insert(0); - left2.final.insert(1); - left2.delta.add(0, 'a', 1); - Nfa left3{ 2}; - left3.initial.insert(0); - left3.final.insert(1); - left3.delta.add(0, 'b', 1); - - Nfa right_side{ 7 }; - right_side.initial.insert(0); - right_side.delta.add(0, 'a', 1); - right_side.delta.add(1, 'a', 2); - right_side.delta.add(2, 'b', 3); - right_side.delta.add(0, 'b', 4); - right_side.delta.add(4, 'a', 5); - right_side.delta.add(5, 'b', 6); - right_side.final.insert({3, 6}); - - std::vector left_side{ &left1, &left2, &left3 }; - for (size_t i{}; i < 10000; ++i) { - seg_nfa::noodlify_for_equation(left_side, right_side); - } -} - -TEST_CASE("mata::nfa::SegNfa::noodlify_for_transducer()") { - Nfa x, y, z, w; - Nft t; - - SECTION("Simple - 1 input, 1 output") { - x = create_from_regex("(a|b)*"); - y = create_from_regex("(a|b)*"); - t = Nft::with_levels(2, 1, {}, {0}, {0}); - t.add_transition(0, {'a', 'a'}, 0); - t.add_transition(0, {'b', 'b'}, 0); - Nft copy = t; - auto noodles = seg_nfa::noodlify_for_transducer(std::make_shared(t), {std::make_shared(x)}, {std::make_shared(y)}); - REQUIRE(noodles.size() == 1); - REQUIRE(noodles[0].size() == 1); - auto segment = noodles[0][0]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 0); - } - - SECTION("Simple - 1 input, 2 output") { - x = create_from_regex("(a|b)*"); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - t = Nft::with_levels(2, 1, {}, {0}, {0}); - t.add_transition(0, {'a', 'a'}, 0); - t.add_transition(0, {'b', 'b'}, 0); - Nft copy = t; - auto noodles = seg_nfa::noodlify_for_transducer(std::make_shared(t), {std::make_shared(x)}, {std::make_shared(y), std::make_shared(z)}); - REQUIRE(noodles.size() == 1); - REQUIRE(noodles[0].size() == 2); - auto segment = noodles[0][0]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 0); - segment = noodles[0][1]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(z, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 1); - } - - SECTION("Simple - 2 input, 1 output") { - x = create_from_regex("(a|b)*"); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - t = Nft::with_levels(2, 1, {}, {0}, {0}); - t.add_transition(0, {'a', 'a'}, 0); - t.add_transition(0, {'b', 'b'}, 0); - Nft copy = t; - auto noodles = seg_nfa::noodlify_for_transducer(std::make_shared(t), {std::make_shared(x), std::make_shared(z)}, {std::make_shared(y)}); - REQUIRE(noodles.size() == 1); - REQUIRE(noodles[0].size() == 2); - auto segment = noodles[0][0]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 0); - segment = noodles[0][1]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(z, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 1); - CHECK(segment.output_index == 0); - } - - SECTION("Simple - 2 input, 2 output") { - x = create_from_regex("(a|b)*"); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - w = create_from_regex("(a|b)*"); - t = Nft::with_levels(2, 1, {}, {0}, {0}); - t.add_transition(0, {'a', 'a'}, 0); - t.add_transition(0, {'b', 'b'}, 0); - Nft copy = t; - auto noodles = seg_nfa::noodlify_for_transducer(std::make_shared(t), {std::make_shared(x), std::make_shared(z)}, {std::make_shared(y), std::make_shared(w)}); - REQUIRE(noodles.size() == 2); - - REQUIRE(noodles[0].size() == 3); - auto segment = noodles[0][0]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 0); - segment = noodles[0][1]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - if (segment.input_index == 0) { - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(w, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 1); - } else { - CHECK(mata::nfa::are_equivalent(z, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 1); - CHECK(segment.output_index == 0); - } - segment = noodles[1][2]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(z, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(w, *segment.output_aut)); - CHECK(segment.input_index == 1); - CHECK(segment.output_index == 1); - - REQUIRE(noodles[1].size() == 3); - segment = noodles[1][0]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 0); - segment = noodles[1][1]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - if (segment.input_index == 0) { - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(w, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 1); - } else { - CHECK(mata::nfa::are_equivalent(z, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(y, *segment.output_aut)); - CHECK(segment.input_index == 1); - CHECK(segment.output_index == 0); - } - segment = noodles[1][2]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(z, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(w, *segment.output_aut)); - CHECK(segment.input_index == 1); - CHECK(segment.output_index == 1); - } - - SECTION("More complex - 2 input, 1 output") { - x = create_from_regex("(a|b)*"); - y = create_from_regex("(a|b)*"); - z = create_from_regex("(a|b)*"); - w = create_from_regex("a*"); - t = Nft::with_levels(2, 1, {}, {0}, {0}); - t.add_transition(0, {'a', 'a'}, 0); - t.add_transition(0, {'b', 'a'}, 0); - Nft copy = t; - auto noodles = seg_nfa::noodlify_for_transducer(std::make_shared(t), {std::make_shared(x), std::make_shared(z)}, {std::make_shared(y)}); - REQUIRE(noodles.size() == 1); - REQUIRE(noodles[0].size() == 2); - auto segment = noodles[0][0]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(x, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(w, *segment.output_aut)); - CHECK(segment.input_index == 0); - CHECK(segment.output_index == 0); - segment = noodles[0][1]; - CHECK(mata::nft::are_equivalent(*segment.transducer, copy)); - CHECK(mata::nfa::are_equivalent(z, *segment.input_aut)); - CHECK(mata::nfa::are_equivalent(w, *segment.output_aut)); - CHECK(segment.input_index == 1); - CHECK(segment.output_index == 0); - } - - SECTION("More complex - 2 input, 1 output, no noodles") { - x = create_from_regex("(a|b)*"); - y = create_from_regex("b+"); - z = create_from_regex("(a|b)*"); - t = Nft::with_levels(2, 1, {}, {0}, {0}); - t.add_transition(0, {'a', 'a'}, 0); - t.add_transition(0, {'b', 'a'}, 0); - auto noodles = seg_nfa::noodlify_for_transducer(std::make_shared(t), {std::make_shared(x), std::make_shared(z)}, {std::make_shared(y)}); - REQUIRE(noodles.size() == 0); - } -}