diff --git a/lib/test_suite.cpp b/lib/test_suite.cpp index b74521f..f33a53f 100644 --- a/lib/test_suite.cpp +++ b/lib/test_suite.cpp @@ -23,7 +23,7 @@ void test(const mealy & specification, const transfer_sequences & prefixes, output.apply(prefix); output.apply(middle); output.apply(suffix); - output.reset(); + if(!output.reset()) return; } } } @@ -51,7 +51,7 @@ void randomized_test(const mealy & specification, const transfer_sequences & pre const auto & prefix = prefixes[prefix_selection(generator)]; current_state = apply(specification, current_state, begin(prefix), end(prefix)).to; - vector middle; + word middle; middle.reserve(min_k + 1); size_t minimal_size = min_k; while (minimal_size || unfair_coin(generator)) { @@ -68,12 +68,77 @@ void randomized_test(const mealy & specification, const transfer_sequences & pre output.apply(prefix); output.apply(middle); output.apply(suffix); - output.reset(); + if(!output.reset()) return; + } +} + +void randomized_test_suffix(const mealy & specification, const transfer_sequences & prefixes, + const separating_family & separating_family, size_t min_k, + size_t rnd_length, const writer & output, uint_fast32_t random_seed) { + vector> all_suffixes; + for (state s = 0; s < separating_family.size(); ++s) { + for (auto const & w : separating_family[s].local_suffixes) { + all_suffixes.emplace_back(s, w); + } + } + + // state -> [(input, state)] + vector>> reverse_machine(specification.graph_size); + for (state s = 0; s < specification.graph_size; ++s) { + for (input i = 0; i < specification.input_size; ++i) { + const auto t = apply(specification, s, i).to; + reverse_machine[t].emplace_back(i, s); + } + } + + std::mt19937 generator(random_seed); + + // https://en.wikipedia.org/wiki/Geometric_distribution we have the random variable Y here + uniform_int_distribution<> unfair_coin(0, rnd_length); + uniform_int_distribution suffix_selection(0, all_suffixes.size() - 1); + uniform_int_distribution input_selection; + + while (true) { + const auto & state_suffix = all_suffixes[suffix_selection(generator)]; + const auto & suffix = state_suffix.second; + state current_state = state_suffix.first; + + word middle; + middle.reserve(min_k + 1); + size_t minimal_size = min_k; + while (minimal_size || unfair_coin(generator)) { + const auto & preds = reverse_machine[current_state]; + if(preds.empty()) { + cerr << "ERROR: no predecessors for this state!\n"; + break; + } + + using params = decltype(input_selection)::param_type; + const auto & input_state + = preds[input_selection(generator, params{0, preds.size() - 1})]; + + current_state = input_state.second; + middle.insert(middle.begin(), input_state.first); + + if (minimal_size) minimal_size--; + } + + const auto & prefix = prefixes[current_state]; + + output.apply(prefix); + output.apply(middle); + output.apply(suffix); + if(!output.reset()) return; } } writer default_writer(std::vector const & inputs) { - static const auto print_word = [&](word w) { for (auto && x : w) cout << inputs[x] << ' '; }; - static const auto reset = [&] { cout << endl; }; + static const auto print_word = [&](word w) { + for (auto && x : w) cout << inputs[x] << ' '; + }; + static const auto reset = [&] { + cout << endl; + return bool(cout); + }; return {print_word, reset}; } diff --git a/lib/test_suite.hpp b/lib/test_suite.hpp index d797b58..768d4c6 100644 --- a/lib/test_suite.hpp +++ b/lib/test_suite.hpp @@ -9,7 +9,7 @@ struct writer { std::function apply; // store a part of a word - std::function reset; // flush + std::function reset; // flush, if flase is returned, testing is stopped }; /// \brief Performs exhaustive tests with \p k_max extra states (harmonized, e.g. HSI / DS) @@ -18,8 +18,12 @@ void test(mealy const & specification, transfer_sequences const & prefixes, /// \brief Performs random non-exhaustive tests for more states (harmonized, e.g. HSI / DS) void randomized_test(mealy const & specification, transfer_sequences const & prefixes, - separating_family const & separating_family, size_t min_k, - size_t rnd_length, writer const & output, uint_fast32_t random_seed); + separating_family const & separating_family, size_t min_k, size_t rnd_length, + writer const & output, uint_fast32_t random_seed); + +void randomized_test_suffix(mealy const & specification, transfer_sequences const & prefixes, + separating_family const & separating_family, size_t min_k, + size_t rnd_length, writer const & output, uint_fast32_t random_seed); /// \brief returns a writer which simply writes everything to cout (via inputs) writer default_writer(const std::vector & inputs); diff --git a/src/main.cpp b/src/main.cpp index 07364fb..ba0705d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ R"(Generate a test suite --no-ds Only use the classical algorithm (hsi) --random-ds Choose randomly between the ds method or hsi method --no-suffix Dont calculate anything smart, just do the random stuff + --suffix-based Only applies in random mode. Chooses suffix first, and not prefix first )"; using time_logger = silent_timer; @@ -47,6 +48,7 @@ int main(int argc, char *argv[]) try { const bool streaming = args.at("all").asBool() || args.at("fixed").asBool(); const bool random_part = args.at("all").asBool() || args.at("random").asBool(); const bool no_suffix = args.at("--no-suffix").asBool(); + const bool suffix_based = args.at("--suffix-based").asBool(); const bool seed_provided = bool(args.at("--seed")); const uint_fast32_t seed = seed_provided ? args.at("--seed").asLong() : 0; @@ -153,7 +155,14 @@ int main(int argc, char *argv[]) try { if(random_part){ time_logger t("outputting all random tests"); const auto k_max_ = streaming ? k_max + 1 : 0; - randomized_test(machine, transfer_sequences, separating_family, k_max_, rnd_length, default_writer(inputs), random_seeds[3]); + + if (suffix_based) { + randomized_test_suffix(machine, transfer_sequences, separating_family, k_max_, + rnd_length, default_writer(inputs), random_seeds[3]); + } else { + randomized_test(machine, transfer_sequences, separating_family, k_max_, rnd_length, + default_writer(inputs), random_seeds[3]); + } } } catch (exception const & e) { diff --git a/src/methods.cpp b/src/methods.cpp index 03463be..be75b37 100644 --- a/src/methods.cpp +++ b/src/methods.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -15,7 +16,7 @@ using namespace std; static const char USAGE[] = -R"(FSM-based test methods + R"(FSM-based test methods Usage: methods (hsi|ads) [options] @@ -25,14 +26,15 @@ R"(FSM-based test methods --version Show version -s , --seed Specify a seed --non-random Iterate inputs in specified order (as occurring in input file) - -k Testing extra states [default: 0] + -k Testing extra states [default: 1] + --print-suite Prints the whole test suite )"; int main(int argc, char * argv[]) { const auto args = docopt::docopt(USAGE, {argv + 1, argv + argc}, true, __DATE__ __TIME__); const string filename = args.at("").asString(); - const size_t k_max = args.at("-k").asLong() + 1; + const size_t k_max = args.at("-k").asLong(); const auto machine = [&] { if (filename.find(".txt") != string::npos) { @@ -61,54 +63,71 @@ int main(int argc, char * argv[]) { if (args.at("hsi").asBool()) { return create_adaptive_distinguishing_sequence(result(machine.graph_size)); } - const auto tree - = create_splitting_tree(machine, args.at("--non-random").asBool() ? lee_yannakakis_style : randomized_lee_yannakakis_style, random_seeds[0]); + const auto tree = create_splitting_tree(machine, args.at("--non-random").asBool() + ? lee_yannakakis_style + : randomized_lee_yannakakis_style, + random_seeds[0]); return create_adaptive_distinguishing_sequence(tree); }); auto pairs_fut = async([&] { - const auto tree - = create_splitting_tree(machine, args.at("--non-random").asBool() ? min_hopcroft_style : randomized_min_hopcroft_style, random_seeds[1]); + const auto tree = create_splitting_tree(machine, args.at("--non-random").asBool() + ? min_hopcroft_style + : randomized_min_hopcroft_style, + random_seeds[1]); return tree.root; }); - auto prefixes_fut - = async([&] { return create_randomized_transfer_sequences(machine, 0, random_seeds[2]); }); - - auto middles_fut = async([&] { - vector all_sequences(1); - for (size_t k = 0; k < k_max; ++k) { - const auto new_sequences = all_seqs(0, machine.input_size, all_sequences); - all_sequences.reserve(all_sequences.size() + new_sequences.size()); - copy(begin(new_sequences), end(new_sequences), back_inserter(all_sequences)); - } - return all_sequences; + auto prefixes_fut = async([&] { + return create_transfer_sequences(args.at("--non-random").asBool() + ? canonical_transfer_sequences + : randomized_transfer_sequences, + machine, 0, random_seeds[2]); }); auto suffixes_fut = async([&] { return create_separating_family(sequence_fut.get(), pairs_fut.get()); }); - const auto prefixes = prefixes_fut.get(); - const auto middles = middles_fut.get(); - const auto suffixes = suffixes_fut.get(); trie test_suite; + word buffer; - const state start = 0; - const word empty = {}; - for (auto && p : prefixes) { - const state s1 = apply(machine, start, begin(p), end(p)).to; - const word w1 = concat(empty, p); - for (auto && m : middles) { - const state s2 = apply(machine, s1, begin(m), end(m)).to; - const word w2 = concat(w1, m); - const auto & suffixes_for_state = suffixes[s2].local_suffixes; - for (auto && s : suffixes_for_state) { - word test = concat(w2, s); - test_suite.insert(test); + const auto suffixes = suffixes_fut.get(); + for(state s = 0; s < suffixes.size(); ++s){ + clog << "suffixes for " << s << endl; + for(auto s : suffixes[s].local_suffixes) { + for(auto x : s){ + clog << x; } + clog << endl; } } + const auto prefixes = prefixes_fut.get(); + for(state s = 0; s < prefixes.size(); ++s) { + clog << "prefix for " << s << endl; + for(auto x : prefixes[s]) { + clog << x; + } + clog << endl; + } + + test(machine, prefixes, suffixes, k_max, + {[&buffer](auto const & w) { buffer.insert(buffer.end(), w.begin(), w.end()); }, + [&buffer, &test_suite]() { + test_suite.insert(buffer); + buffer.clear(); + return true; + }}); + const auto p = total_size(test_suite); cout << p.first << '\t' << p.second << '\t' << p.first + p.second << endl; + + if(args.at("--print-suite").asBool()){ + test_suite.for_each([](const auto & w){ + for(const auto & x : w) { + cout << x; + } + cout << endl; + }); + } }