Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
3d16e09
wip
koniksedy Jun 24, 2025
516e11d
draft draft draft
koniksedy Jun 24, 2025
3a9b951
map_epsilon_path tested
koniksedy Jun 25, 2025
67da98c
map_common trans tested
koniksedy Jun 25, 2025
3d15c66
rest of the algorithm
koniksedy Jun 25, 2025
1e5fec5
it seems that it works
koniksedy Jun 26, 2025
973c6a4
before Noodler test
koniksedy Jun 26, 2025
c09a225
helper functions
koniksedy Jun 26, 2025
4da5c9d
add storage from intersection to composition
koniksedy Jun 27, 2025
195f20a
before Noodler
koniksedy Jun 27, 2025
739501a
throw
koniksedy Jun 27, 2025
1a1a963
no jump
koniksedy Jun 27, 2025
b20501c
repare visit condition
koniksedy Jun 28, 2025
1282c59
ifs
koniksedy Jun 28, 2025
e01c21a
update anonymous helper functions
koniksedy Jul 16, 2025
37d4835
Merge branch 'composition-wip' of github.com:VeriFIT/mata into compos…
koniksedy Jul 16, 2025
5edb4cb
wip
koniksedy Aug 6, 2025
97a6eb8
notes
koniksedy Aug 10, 2025
87f87c4
wip
koniksedy Aug 11, 2025
db2fd2d
Merge branch 'ord_vec' into composition-wip
koniksedy Aug 11, 2025
186fb23
new draft
koniksedy Aug 11, 2025
851cba2
polished draft
koniksedy Aug 11, 2025
5248bcb
put old composition back
koniksedy Aug 12, 2025
2f6a373
new idea
koniksedy Aug 12, 2025
933f07d
wip
koniksedy Aug 13, 2025
d5ea5aa
draft done
koniksedy Aug 14, 2025
6d40856
add comments
koniksedy Aug 14, 2025
ce0051e
Merge branch 'devel' into composition-wip
koniksedy Aug 14, 2025
79edec6
2D storage
koniksedy Aug 14, 2025
7cf387e
nft new add_transition methods
koniksedy Aug 14, 2025
0b359b4
deduplicated
koniksedy Aug 14, 2025
aaa808d
simplify main loop
koniksedy Aug 15, 2025
c70012f
copy_transition lambda function done
koniksedy Aug 15, 2025
a7bcce5
synchronize done
koniksedy Aug 15, 2025
9008edf
model waiting - some halucinations
koniksedy Aug 15, 2025
ed56cd6
much better model_waiting function
koniksedy Aug 16, 2025
d3a8f24
draft done
koniksedy Aug 16, 2025
6e53115
draft done
koniksedy Aug 16, 2025
523283b
draft final version
koniksedy Aug 16, 2025
0a564aa
before testing
koniksedy Aug 21, 2025
11f4d0a
old implementation is working
koniksedy Aug 21, 2025
e98278a
update algorithms to work with JumpMode::NoJump
koniksedy Aug 21, 2025
0c19a66
add semantic of NFA EPSILON transitions
koniksedy Sep 2, 2025
b150e0f
compose only for JumpMode::NoJump with fast EPSILON
koniksedy Sep 2, 2025
ed5e2d8
correct synchronization type table
koniksedy Sep 3, 2025
09aefb6
composition tests in the progress
koniksedy Sep 4, 2025
358ceed
composition tests - sliding levels no epsilon
koniksedy Sep 4, 2025
6a21b50
first tests with EPSILON + first major fixes
koniksedy Sep 5, 2025
6bbc920
wip
koniksedy Sep 5, 2025
9a85e6c
sliding sync with EPSILON + optimizations of the number of transitions
koniksedy Sep 7, 2025
bff6d75
wip
koniksedy Sep 7, 2025
ac21b10
new tests done
koniksedy Sep 11, 2025
9aa81c1
remove old tests
koniksedy Sep 11, 2025
7f53ee0
working of string tests
koniksedy Sep 11, 2025
b26c10a
merge with devel:f049292
koniksedy Sep 13, 2025
591bed3
weaker can_synchronize_in_the_future cindition in copy
koniksedy Sep 14, 2025
f72dcda
synchronization optimized
koniksedy Sep 17, 2025
c49f585
optimize copy
koniksedy Sep 18, 2025
849be97
process_fast_epsilon_transitions optimized
koniksedy Sep 18, 2025
2244881
add wait optimized
koniksedy Sep 18, 2025
734063a
optimize wait - add state
koniksedy Sep 25, 2025
0f4bded
merge with devel
koniksedy Jan 9, 2026
7a11b0f
add jump_mode parameter to the redirection to the general compose
koniksedy Jan 9, 2026
fef7919
dispatch function for composition
koniksedy Jan 9, 2026
163cc2f
noodlify and replace_reluctant with JumpMode::NoJump
koniksedy Jan 9, 2026
7ea30db
add JumpMode::NoJump to calls of apply
koniksedy Jan 9, 2026
6f32caa
add comment
koniksedy Jan 9, 2026
7ad9f67
Update include/mata/nft/nft.hh
koniksedy Jan 14, 2026
a89bddc
fix type in nft.cc
koniksedy Jan 14, 2026
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
2 changes: 1 addition & 1 deletion examples/nft.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ int main() {
Nfa nfa = nfa::builder::create_from_regex("ABCDEFggg");

// Compute the pre-image of an NFA (applies NFA to the image tape, i.e., tape 1)
Nft backward_applied_nft = nft.apply(nfa, 1);
Nft backward_applied_nft = nft.apply(nfa, 1, true, mata::nft::JumpMode::NoJump);
// Extract a pre-image NFA.
Nfa nfa_pre_image{ backward_applied_nft.to_nfa_move() };
// Minimize the result pre-image using hopcroft minimization.
Expand Down
41 changes: 41 additions & 0 deletions include/mata/nft/algorithms.hh
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,47 @@ Nft product(const Nft& lhs, const Nft& rhs, const std::function<bool(State,State
Nft concatenate_eps(const Nft& lhs, const Nft& rhs, const Symbol& epsilon, bool use_epsilon = false,
StateRenaming* lhs_state_renaming = nullptr, StateRenaming* rhs_state_renaming = nullptr);

/**
* @brief Composes two NFTs (lhs || rhs; read as "rhs after lhs").
*
* This function computes the composition of two NFTs, `lhs` and `rhs`, by aligning their synchronization levels.
* Transitions between two synchronization levels are ordered as follows: first the transitions of `lhs`, then
* the transitions of `rhs` followed by next synchronization level (if exists). By default, synchronization
* levels are projected out from the resulting NFT.
*
* Vectors of synchronization levels have to be non-empty and of the same size.
*
* NOTE: If you have only a single synchronization level per NFT and don't use jump transitions, consider using
* no-jump varsion of the composition for better performance.
*
* @param[in] lhs First transducer to compose.
* @param[in] rhs Second transducer to compose.
* @param[in] lhs_sync_levels Ordered vector of synchronization levels of the @p lhs.
* @param[in] rhs_sync_levels Ordered vector of synchronization levels of the @p rhs.
* @param[in] project_out_sync_levels Whether we want to project out the synchronization levels.
* @param[in] jump_mode Specifies if the symbol on a jump transition (a transition with a length greater than 1)
* is interpreted as a sequence repeating the same symbol or as a single instance of the symbol followed by a sequence of @c DONT_CARE.
*
* @return A new NFT after the composition.
*/
Nft compose_general(const Nft& lhs, const Nft& rhs,
const utils::OrdVector<Level>& lhs_sync_levels, const utils::OrdVector<Level>& rhs_sync_levels,
bool project_out_sync_levels = true,
JumpMode jump_mode = JumpMode::RepeatSymbol);

/**
* @brief Composes two NFTs (lhs || rhs; read as "rhs after lhs") with a single synchronization level and no jumps.
*
* @param[in] lhs First transducer to compose.
* @param[in] rhs Second transducer to compose.
* @param[in] lhs_sync_level The synchronization level of the @p lhs.
* @param[in] rhs_sync_level The synchronization level of the @p rhs.
* @param[in] project_out_sync_levels Whether we wont to project out the synchronization levels.
*
* @return A new NFT after the composition.
*/
Nft compose_fast_no_jump(const Nft& lhs, const Nft& rhs, Level lhs_sync_level = 1, Level rhs_sync_level = 0, bool project_out_sync_levels = true);

}

#endif
80 changes: 65 additions & 15 deletions include/mata/nft/nft.hh
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,45 @@ public:
*/
State add_transition(State source, const std::vector<Symbol>& symbols);

/**
* @brief Add a single NFT transition with a length @p length from a source state @p source
* to a newly created target state, creating as many inner states as needed for the transition
* @p length and the jump mode.
*
* @param source The source state where the transition begins. @p source must already exist.
* @param symbol The symbol used for the transition.
* @param length The length of the transition.
* @param jump_mode Specifies the semantic of jump transitions (transitions with a length greater than 1).
*/
State add_transition_with_length(State source, Symbol symbol, size_t length, JumpMode jump_mode = JumpMode::RepeatSymbol);

/**
* @brief Add a single NFT transition from a source state @p source to a target state @p target
* creating as many inner states as needed for the transition length and the jump mode.
*
* @param source The source state where the transition begins. @p source must already exist.
* @param symbol The symbol used for the transition.
* @param target The target state where the transition ends. @p target must already exist.
* @param jump_mode Specifies the semantic of jump transitions (transitions with a length greater than 1).
*/
void add_transition_with_target(const State source, const Symbol symbol, const State target, const JumpMode jump_mode = JumpMode::RepeatSymbol) {
add_transition_with_same_level_targets(source, symbol, { target }, jump_mode);
}

/**
* @brief Add a NFT transition from a source state @p source to a set of target states @p targets
* creating as many inner states as needed for the transition length and the jump mode.
*
* For transitions with a length `l` greater than 1, the common part (before the `l`-th symbol) of the
* is shared for all targets and only at the `l`-th symbol the transition branches to each target.
*
* @param source The source state where the transition begins. @p source must already exist.
* @param symbol The symbol used for the transition.
* @param targets The set of target states where the transition ends. Each target must already exist.
* @param jump_mode Specifies the semantic of jump transitions (transitions with a length greater than 1).
*/
void add_transition_with_same_level_targets(State source, Symbol symbol, const StateSet& targets, JumpMode jump_mode = JumpMode::RepeatSymbol);

/**
* @brief Inserts a word, which is created by interleaving parts from @p word_parts_on_levels, into the NFT
* from a source state @p source to a target state @p target, creating new states along the path of @p word.
Expand Down Expand Up @@ -913,45 +952,56 @@ Nft intersection(const Nft& lhs, const Nft& rhs,
/**
* @brief Composes two NFTs (lhs || rhs; read as "rhs after lhs").
*
* This function computes the composition of two NFTs, `lhs` and `rhs`, by aligning their synchronization levels.
* Transitions between two synchronization levels are ordered as follows: first the transitions of `lhs`, then
* the transitions of `rhs` followed by next synchronization level (if exists). By default, synchronization
* levels are projected out from the resulting NFT.
*
* Vectors of synchronization levels have to be non-empty and of the same size.
*
* @param[in] lhs First transducer to compose.
* @param[in] rhs Second transducer to compose.
* @param[in] lhs_sync_levels Ordered vector of synchronization levels of the @p lhs.
* @param[in] rhs_sync_levels Ordered vector of synchronization levels of the @p rhs.
* @param[in] project_out_sync_levels Whether we want to project out the synchronization levels.
* @param[in] jump_mode Specifies if the symbol on a jump transition (a transition with a length greater than 1)
* is interpreted as a sequence repeating the same symbol or as a single instance of the symbol followed by a sequence of @c DONT_CARE.
* @param[in] composition_mode Mode of composition to use. Default is Auto, which selects the best mode based
* on the presence of jump transitions. General mode supports all NFTs, while FastNoJump
* mode is optimized for NFTs without jump transitions and with only one synchronization level.
*
* @return A new NFT after the composition.
*/
Nft compose(const Nft& lhs, const Nft& rhs,
const utils::OrdVector<Level>& lhs_sync_levels, const utils::OrdVector<Level>& rhs_sync_levels,
bool project_out_sync_levels = true,
JumpMode jump_mode = JumpMode::RepeatSymbol);
JumpMode jump_mode = JumpMode::RepeatSymbol,
CompositionMode composition_mode = CompositionMode::Auto);

/**
* @brief Composes two NFTs (lhs || rhs; read as "rhs after lhs").
*
* This function computes the composition of two NFTs, `lhs` and `rhs`, by aligning their synchronization levels.
* Transitions between two synchronization levels are ordered as follows: first the transitions of `lhs`, then
* the transitions of `rhs` followed by next synchronization level (if exists). By default, synchronization
* levels are projected out from the resulting NFT.
*
* @param[in] lhs First transducer to compose.
* @param[in] rhs Second transducer to compose.
* @param[in] lhs_sync_level The synchronization level of the @p lhs.
* @param[in] rhs_sync_level The synchronization level of the @p rhs.
* @param[in] project_out_sync_levels Whether we wont to project out the synchronization levels.
* @param[in] project_out_sync_levels Whether we want to project out the synchronization levels.
* @param[in] jump_mode Specifies if the symbol on a jump transition (a transition with a length greater than 1)
* is interpreted as a sequence repeating the same symbol or as a single instance of the symbol followed by a sequence of @c DONT_CARE.
* @param[in] composition_mode Mode of composition to use. Default is Auto, which selects the best mode based
* on the presence of jump transitions. General mode supports all NFTs, while FastNoJump
* mode is optimized for NFTs without jump transitions and with only one synchronization level.
*
* @return A new NFT after the composition.
*/
Nft compose(const Nft& lhs, const Nft& rhs, Level lhs_sync_level = 1, Level rhs_sync_level = 0, bool project_out_sync_levels = true, JumpMode jump_mode = JumpMode::RepeatSymbol);
inline Nft compose(const Nft& lhs, const Nft& rhs,
const Level lhs_sync_level = 1, const Level rhs_sync_level = 0,
const bool project_out_sync_levels = true,
const JumpMode jump_mode = JumpMode::RepeatSymbol,
const CompositionMode composition_mode = CompositionMode::Auto)
{
return compose(
lhs, rhs,
utils::OrdVector<Level>{ lhs_sync_level },
utils::OrdVector<Level>{ rhs_sync_level },
project_out_sync_levels,
jump_mode,
composition_mode
);
}

/**
* @brief Concatenate two NFTs.
Expand Down
14 changes: 11 additions & 3 deletions include/mata/nft/types.hh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,14 @@ class Nft; ///< A non-deterministic finite transducer.

enum class JumpMode {
RepeatSymbol, ///< Repeat the symbol on the jump.
AppendDontCares ///< Append a sequence of DONT_CAREs to the symbol on the jump.
AppendDontCares, ///< Append a sequence of DONT_CAREs to the symbol on the jump.
NoJump ///< No jumps are allowed.
};

enum class CompositionMode {
General, ///< General composition mode for arbitrary number or synchronization levels and jump modes.
FastNoJump, ///< Fast composition mode for a single synchronization level and no jumps.
Auto ///< Automatically select the best composition mode based on the parameters.
};

using ProductFinalStateCondition = mata::nfa::ProductFinalStateCondition;
Expand Down Expand Up @@ -99,8 +106,9 @@ public:
*/
size_t num_of_levels{ DEFAULT_NUM_OF_LEVELS };

// explicit Levels(const std::vector<Level>& levels): super{ levels }, num_of_levels{ num_of_levels_of(levels).value_or(DEFAULT_NUM_OF_LEVELS) } {}
// explicit Levels(std::vector<Level>&& levels): super{ std::move(levels) } { num_of_levels = num_of_levels_of(*this).value_or(DEFAULT_NUM_OF_LEVELS); }
// explicit Levels(std::initializer_list<Level> levels): super{ levels }, num_of_levels{ num_of_levels_of(levels).value_or(DEFAULT_NUM_OF_LEVELS) } {}
explicit Levels(const std::vector<Level>& levels): super{ levels }, num_of_levels{ num_of_levels_of(levels).value_or(DEFAULT_NUM_OF_LEVELS) } {}
explicit Levels(std::vector<Level>&& levels): super{ std::move(levels) } { num_of_levels = num_of_levels_of(*this).value_or(DEFAULT_NUM_OF_LEVELS); }
explicit Levels(const size_t num_of_levels, std::vector<Level> levels = {}): super{ std::move(levels) }, num_of_levels{ num_of_levels } {}
Levels() = default;
Levels(size_t num_of_levels, size_t count, Level value = DEFAULT_LEVEL);
Expand Down
8 changes: 4 additions & 4 deletions src/applications/strings/noodlification.cc
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,9 @@ std::vector<seg_nfa::TransducerNoodle> seg_nfa::noodlify_for_transducer(
// we can do the following optimization. Let I1, ..., In be the input automata. Because T is homomorphic,
// we can take the concatenation T(I1).T(I2)...T(In) connected with INPUT_DELIMITER instead of computing
// the composition with concatenated_input_nft.
mata::nft::Nft concatenation{ mata::nft::compose(mata::nft::Nft(*input_automata[0]), *nft, 0, 0, false) };
mata::nft::Nft concatenation{ mata::nft::compose(mata::nft::Nft(*input_automata[0]), *nft, 0, 0, false, mata::nft::JumpMode::NoJump) };
for (size_t i = 1; i < input_automata.size(); ++i) {
mata::nft::Nft composition = mata::nft::compose(mata::nft::Nft(*input_automata[i]), *nft, 0, 0, false);
mata::nft::Nft composition = mata::nft::compose(mata::nft::Nft(*input_automata[i]), *nft, 0, 0, false, mata::nft::JumpMode::NoJump);
concatenation = mata::nft::algorithms::concatenate_eps(concatenation, composition, input_delimiter, true);
}
intersection = std::move(concatenation);
Expand All @@ -480,7 +480,7 @@ std::vector<seg_nfa::TransducerNoodle> seg_nfa::noodlify_for_transducer(
// we intersect input nfa with nft on the input track, but we need to add INPUT_DELIMITER as an "epsilon transition" of nft
add_self_loop_for_every_default_state(intersection, input_delimiter);

intersection = mata::nft::compose(concatenated_input_nft, intersection, 0, 0, false);
intersection = mata::nft::compose(concatenated_input_nft, intersection, 0, 0, false, mata::nft::JumpMode::NoJump);
}
intersection.trim();

Expand All @@ -491,7 +491,7 @@ std::vector<seg_nfa::TransducerNoodle> seg_nfa::noodlify_for_transducer(
// We also need to INPUT_DELIMITER as "epsilon transition" of the output nfa, so that we do not lose it.
add_self_loop_for_every_default_state(concatenated_output_nft, input_delimiter);
add_self_loop_for_every_default_state(intersection, output_delimiter);
intersection = mata::nft::compose(concatenated_output_nft, intersection, 0, 1, false);
intersection = mata::nft::compose(concatenated_output_nft, intersection, 0, 1, false, mata::nft::JumpMode::NoJump);
intersection.trim();

if (intersection.final.empty()) { return {}; }
Expand Down
4 changes: 2 additions & 2 deletions src/applications/strings/replace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ Nft ReluctantReplace::replace_regex(nfa::Nfa aut, const Word& replacement, Alpha
const Nft dft_begin_marker{ begin_marker_nft(begin_marker_nfa(aut, alphabet), begin_marker) };
const Nft nft_reluctant_replace{
reluctant_leftmost_nft(std::move(aut), alphabet, begin_marker, replacement, replace_mode) };
return compose(dft_begin_marker, nft_reluctant_replace);
return compose(dft_begin_marker, nft_reluctant_replace, 1, 0, true, mata::nft::JumpMode::NoJump);
}

Nft ReluctantReplace::replace_literal(const Word& literal, const Word& replacement, Alphabet const* const alphabet,
Expand Down Expand Up @@ -551,7 +551,7 @@ Nft ReluctantReplace::replace_literal(const Word& literal, const Word& replaceme
const Nft nft_literal_replace{
replace_literal_nft(literal, replacement, alphabet, end_marker, replace_mode)
};
return compose(nft_end_marker, nft_literal_replace);
return compose(nft_end_marker, nft_literal_replace, 1, 0, true, mata::nft::JumpMode::NoJump);
}

Nft ReluctantReplace::replace_symbol(
Expand Down
Loading
Loading