Commit 95660fda authored by Gijs van Cuyck's avatar Gijs van Cuyck

added starting files from joshuas project

parent 5151768d
Pipeline #15114 failed with stages
in 46 seconds
......@@ -17,43 +17,13 @@ build
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
#general JetBrains nonsense
.idea/
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
......@@ -66,17 +36,9 @@ out/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
cmake_minimum_required(VERSION 3.8)
project(complete_ads)
set(CMAKE_CXX_STANDARD 17)
set(SOURCE_FILES main.cpp src/main_test.cpp)
add_subdirectory("lib")
add_subdirectory("src")
\ No newline at end of file
file(GLOB_RECURSE sources "*.cpp")
file(GLOB_RECURSE headers "*.hpp")
set(libs)
add_library(common ${headers} ${sources})
target_link_libraries(common ${libs})
target_include_directories(common PUBLIC ".")
#pragma once
#include <chrono>
#include <iostream>
struct timer{
using clock = std::chrono::high_resolution_clock;
using time = std::chrono::time_point<clock>;
using seconds = std::chrono::duration<double>;
std::string name;
time begin;
bool active = true;
timer(std::string n)
: name(n)
, begin(clock::now())
{
std::cerr << name << std::endl;
}
void stop(){
if(!active) return;
time end = clock::now();
std::cerr << "* " << from_duration(end - begin) << '\t' << name << std::endl;
active = false;
}
~timer(){
stop();
}
static double from_duration(seconds s){
return s.count();
}
};
// has same signature, but does not log :)
struct silent_timer {
silent_timer(std::string){}
void stop();
};
#pragma once
#include "types.hpp"
#include <map>
#include <string>
#include <vector>
/*
* Everything is indexed by size_t's, so that we can index vectors
* in constant time. Can only represent deterministic machines,
* but partiality still can occur.
*
* Note that graph_size == graph.size() and that input_size equals the size
* of the biggest row in graph. Finally output_size bounds the number of
* outputs. These values are redundant, but nice to have here.
*/
struct mealy {
struct edge {
edge() = default;
edge(state t, output o) : to(t), out(o) {}
state to = state(-1);
output out = output(-1);
};
// state -> input -> (output, state)
std::vector<std::vector<edge>> graph;
size_t graph_size = 0;
size_t input_size = 0;
size_t output_size = 0;
};
inline bool is_complete(const mealy & m){
for(state n = 0; n < m.graph_size; ++n){
if(m.graph[n].size() != m.input_size) return false;
for(auto && e : m.graph[n]) if(e.to == state(-1) || e.out == output(-1)) return false;
}
return true;
}
inline bool defined(mealy const & m, state s, input i) {
if (s >= m.graph.size()) return false;
if (i >= m.graph[s].size()) return false;
if (m.graph[s][i].to == state(-1) || m.graph[s][i].out == output(-1)) return false;
return true;
}
inline mealy::edge apply(mealy const & m, state state, input input){
return m.graph[state][input];
}
template <typename Iterator>
mealy::edge apply(mealy const & m, state state, Iterator b, Iterator e){
mealy::edge ret;
ret.to = state;
while(b != e){
ret = apply(m, ret.to, *b++);
}
return ret;
}
#pragma once
#include <cassert>
#include <list>
#include <numeric>
#include <stdexcept>
#include <type_traits>
#include <vector>
template <typename Iterator, typename Fun>
std::list<std::list<typename Iterator::value_type>>
partition_(Iterator b, Iterator e, Fun&& function, size_t output_size) {
using namespace std;
using T = typename decay<decltype(*b)>::type;
list<T> elements(b, e);
list<list<T>> blocks;
using ref = typename list<list<T>>::iterator;
vector<ref> A(output_size);
auto it = begin(elements);
auto ed = end(elements);
while (it != ed) {
const auto current = it++;
const auto y = function(*current);
if (y >= output_size) throw runtime_error("Output is too big");
auto& ar = A[y];
if (ar == ref{}) {
ar = blocks.insert(blocks.end(), list<T>{});
}
ar->splice(ar->end(), elements, current);
}
return blocks;
}
#include "reachability.hpp"
#include "mealy.hpp"
#include <map>
#include <queue>
#include <vector>
using namespace std;
mealy reachable_submachine(const mealy& in, state start) {
using state_out = state;
state_out max_state = 0;
map<state, state_out> new_state;
vector<bool> visited(in.graph_size, false);
mealy out;
queue<state> work;
work.push(start);
while (!work.empty()) {
state s = work.front();
work.pop();
if (visited[s]) continue;
visited[s] = true;
if (!new_state.count(s)) new_state[s] = max_state++;
state_out s2 = new_state[s];
for (input i = 0; i < in.input_size; ++i) {
const auto ret = apply(in, s, i);
const output o = ret.out;
const state t = ret.to;
if (!new_state.count(t)) new_state[t] = max_state++;
state_out t2 = new_state[t];
if (out.graph.size() < max_state) out.graph.resize(max_state);
if (out.graph[s2].size() < in.input_size) out.graph[s2].resize(in.input_size);
out.graph[s2][i] = mealy::edge(t2, o);
if (!visited[t]) work.push(t);
}
}
out.graph_size = max_state;
out.input_size = in.input_size;
out.output_size = in.output_size;
if(out.graph_size == 0) throw runtime_error("Empty state set");
if(out.input_size == 0) throw runtime_error("Empty input set");
if(out.output_size == 0) throw runtime_error("Empty output set");
if(!is_complete(out)) throw runtime_error("Partial machine");
return out;
}
#pragma once
#include "types.hpp"
struct mealy;
mealy reachable_submachine(const mealy& in, state start);
#include "read_mealy.hpp"
#include "mealy.hpp"
#include <cassert>
#include <cctype>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <string>
using namespace std;
static string easy_substr(string const & s, size_t begin, size_t end){
return s.substr(begin, end - begin);
}
static string trim_copy(string const & str) {
auto it = str.begin();
while(it != str.end() && isspace(*it)){
it++;
}
auto e = str.end();
while(it != e && isspace(*(e-1))) {
e--;
}
return string(it, e);
}
mealy read_mealy_from_txt(std::istream & in, bool check) {
mealy m;
state max_state = 0;
input max_input = 0;
output max_output = 0;
string line;
while (getline(in, line)) {
state from, to;
input i;
output o;
string separator;
stringstream ss(line);
ss >> from >> separator >> i >> separator >> o >> separator >> to;
if (from >= max_state) max_state = from + 1;
if (to >= max_state) max_state = to + 1;
if (i >= max_input) max_input = i + 1;
if (o >= max_output) max_output = o + 1;
if (defined(m, from, i)) throw runtime_error("Nondeterministic machine");
m.graph.resize(max_state);
auto & v = m.graph[from];
v.resize(max_input);
v[i] = mealy::edge(to, o);
assert(defined(m, from, i));
}
m.graph_size = max_state;
m.input_size = max_input;
m.output_size = max_output;
if (m.graph_size == 0) throw runtime_error("Empty state set");
if (m.input_size == 0) throw runtime_error("Empty input set");
if (m.output_size == 0) throw runtime_error("Empty output set");
if (check && !is_complete(m)) throw runtime_error("Partial machine");
return m;
}
mealy read_mealy_from_txt(const std::string & filename, bool check) {
std::ifstream file(filename);
return read_mealy_from_txt(file, check);
}
mealy read_mealy_from_dot(std::istream & in, translation & t, bool check){
mealy m;
std::unordered_map<std::string, state> state_indices;
state max_state = 0;
string line;
while(getline(in, line)){
const auto npos = std::string::npos;
if(line.find("}") != string::npos) break;
// parse states
const auto arrow_pos = line.find("->");
const auto bracket_pos = line.find('[');
if(arrow_pos == npos || bracket_pos == npos) continue;
const auto lh = trim_copy(easy_substr(line, 0, arrow_pos));
const auto rh = trim_copy(easy_substr(line, arrow_pos+2, bracket_pos));
// parse input/output
const auto quote1_pos = line.find('\"', bracket_pos);
const auto slash_pos = line.find('/', quote1_pos);
const auto quote2_pos = line.find('\"', slash_pos);
if(quote1_pos == npos || slash_pos == npos || quote2_pos == npos) continue;
const auto input = trim_copy(easy_substr(line, quote1_pos+1, slash_pos));
const auto output = trim_copy(easy_substr(line, slash_pos+1, quote2_pos));
// make fresh indices, if needed
if(state_indices.count(lh) < 1) state_indices[lh] = max_state++;
if(state_indices.count(rh) < 1) state_indices[rh] = max_state++;
if(t.input_indices.count(input) < 1) t.input_indices[input] = t.max_input++;
if(t.output_indices.count(output) < 1) t.output_indices[output] = t.max_output++;
if(defined(m, state_indices[lh], t.input_indices[input]))
throw runtime_error("Nondeterministic machine");
// add edge
m.graph.resize(max_state);
auto & v = m.graph[state_indices[lh]];
v.resize(t.max_input);
v[t.input_indices[input]] = mealy::edge(state_indices[rh], t.output_indices[output]);
}
m.graph_size = max_state;
m.input_size = t.max_input;
m.output_size = t.max_output;
if(m.graph_size == 0) throw runtime_error("Empty state set");
if(m.input_size == 0) throw runtime_error("Empty input set");
if(m.output_size == 0) throw runtime_error("Empty output set");
if(check && !is_complete(m)) throw runtime_error("Partial machine");
return m;
}
mealy read_mealy_from_dot(const string & filename, translation & t, bool check){
ifstream file(filename);
return read_mealy_from_dot(file, t, check);
}
std::pair<mealy, translation> read_mealy_from_dot(istream & in, bool check){
translation t;
const auto m = read_mealy_from_dot(in, t, check);
return {move(m), move(t)};
}
std::pair<mealy, translation> read_mealy_from_dot(const string & filename, bool check){
translation t;
const auto m = read_mealy_from_dot(filename, t, check);
return {move(m), move(t)};
}
template <typename T>
std::vector<std::string> create_reverse_map_impl(std::unordered_map<std::string, T> const & indices) {
std::vector<std::string> ret(indices.size());
for (auto && p : indices) {
ret[p.second] = p.first;
}
return ret;
}
std::vector<string> create_reverse_map(const std::unordered_map<string, input> & indices) {
return create_reverse_map_impl(indices);
}
#if 0 // Note: input and output are equal types, so this would be a redecl
std::vector<string> create_reverse_map(const std::map<string, output> & indices) {
return create_reverse_map_impl(indices);
}
#endif
translation create_translation_for_mealy(const mealy & m) {
translation t;
t.max_input = m.input_size;
t.max_output = m.output_size;
for (input i = 0; i < t.max_input; ++i) {
t.input_indices[to_string(i)] = i;
}
for (output o = 0; o < t.max_output; ++o) {
t.output_indices[to_string(o)] = o;
}
return t;
}
#pragma once
#include "types.hpp"
#include <iosfwd>
#include <string>
#include <unordered_map>
#include <utility>
struct mealy;
struct translation;
/// \brief reads a mealy machine from plain txt file as provided by A. T. Endo
/// States, inputs and outputs in these files are already integral, so no need for translation
mealy read_mealy_from_txt(std::istream & in, bool check = true);
mealy read_mealy_from_txt(std::string const & filename, bool check = true);
/// \brief reads a mealy machine from dot files as generated by learnlib
/// Here we need a translation, which is extended during parsing
mealy read_mealy_from_dot(std::istream & in, translation & t, bool check = true);
mealy read_mealy_from_dot(std::string const & filename, translation & t, bool check = true);
/// \brief reads a mealy machine from dot files as generated by learnlib
/// Here the translation starts out empty and is returned in the end
std::pair<mealy, translation> read_mealy_from_dot(std::istream & in, bool check = true);
std::pair<mealy, translation> read_mealy_from_dot(std::string const & filename, bool check = true);
/// \brief For non-integral formats we use a translation to integers
struct translation {
std::unordered_map<std::string, input> input_indices;
input max_input = 0;
std::unordered_map<std::string, output> output_indices;
output max_output = 0;
};
/// \brief inverts the input_indices and output_indices maps
std::vector<std::string> create_reverse_map(std::unordered_map<std::string, input> const & indices);
std::vector<std::string> create_reverse_map(std::unordered_map<std::string, output> const & indices);
/// \brief defines trivial translation (the string represent integers directly)
translation create_translation_for_mealy(mealy const & m);
#include "splitting_tree.hpp"
#include "partition.hpp"
#include <algorithm>
#include <cassert>
#include <functional>
#include <numeric>
#include <queue>
#include <random>
#include <utility>
using namespace std;
splitting_tree::splitting_tree(size_t N, size_t d) : states(N), depth(d) {
iota(begin(states), end(states), 0);
}
result create_splitting_tree(const mealy & g, options opt, uint_fast32_t random_seed) {
const auto N = g.graph_size;
const auto P = g.input_size;
const auto Q = g.output_size;
result ret(N);
auto & root = ret.root;
auto & succession = ret.successor_cache;
// We'll use a queue to keep track of leaves we have to investigate;
// In some cases we cannot split, and have to wait for other parts of the
// tree. We keep track of how many times we did no work. If this is too
// much, there is no complete splitting tree.
queue<reference_wrapper<splitting_tree>> work;
size_t days_without_progress = 0;
// List of inputs, will be shuffled in case of randomizations
vector<input> all_inputs(P);
iota(begin(all_inputs), end(all_inputs), 0);
mt19937 generator(random_seed);
size_t current_order = 0;
bool split_in_current_order = false;
// Some lambda functions capturing some state, makes the code a bit easier :)
const auto add_push_new_block = [&work](list<list<state>> const & new_blocks, splitting_tree& boom) {
boom.children.assign(new_blocks.size(), splitting_tree(0, boom.depth + 1));
size_t i = 0;
for (auto && b : new_blocks) {
boom.children[i++].states.assign(begin(b), end(b));
}
for (auto && c : boom.children) {
work.push(c);
}
assert(boom.states.size() == accumulate(begin(boom.children), end(boom.children), 0ul,
[](size_t l, const splitting_tree & r) {
return l + r.states.size();
}));
};
const auto is_valid = [N, opt, &g](list<list<state>> const & blocks, input symbol) {
for (auto && block : blocks) {
const auto new_blocks = partition_(begin(block), end(block), [symbol, &g](state state) {
return apply(g, state, symbol).to;
}, N);
for (auto && new_block : new_blocks) {
if (new_block.size() != 1) return false;
}
}
return true;
};
const auto update_succession = [N, &succession](state s, state t, size_t depth) {
if (succession.size() < depth + 1)
succession.resize(depth + 1, vector<state>(N, state(-1)));
succession[depth][s] = t;
};
// We'll start with the root, obviously
work.push(root);
while (!work.empty()) {
splitting_tree & boom = work.front();
work.pop();
const size_t depth = boom.depth;
if (boom.states.size() == 1) continue;
if (opt.randomized) shuffle(begin(all_inputs), end(all_inputs), generator);
if (!opt.assert_minimal_order || current_order == 0) {
// First try to split on output
for (input symbol : all_inputs) {
const auto new_blocks = partition_(
begin(boom.states),
end(boom.states), [symbol, depth, &g, &update_succession](state state) {
const auto r = apply(g, state, symbol);
update_succession(state, r.to, depth);
return r.out;
}, Q);
// no split -> continue with other input symbols
if (new_blocks.size() == 1) continue;
// not a valid split -> continue
if (opt.check_validity && !is_valid(new_blocks, symbol)) continue;
// a succesful split, update partition and add the children
boom.separator = {symbol};
add_push_new_block(new_blocks, boom);