Skip to content

Commit

Permalink
Feature/shutting down (#28)
Browse files Browse the repository at this point in the history
* exit nicely

* getters that are valid during runtime

* wip commenting

* no more optional final marking

* fix missing header

* does nullptr work?

Co-authored-by: Thomas Horstink <thomas@mainblades.com>
  • Loading branch information
thorstink and Thomas Horstink authored Jan 23, 2023
1 parent df06f9c commit 46c7853
Show file tree
Hide file tree
Showing 16 changed files with 305 additions and 139 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if(NOT package_xml_str MATCHES "<version>([0-9]+.[0-9]+.[0-9]+)</version>")
endif()
project(symmetri VERSION ${CMAKE_MATCH_1})

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down
4 changes: 2 additions & 2 deletions examples/hello_world/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ int main(int, char *argv[]) {
// store based on the petri net. You can specifiy a final marking, the amount
// of threads it can use (maximum amount of stuff it can do in parallel) and a
// name so the net is easy to identifiy in a log.
symmetri::Application net({pnml_path_start, pnml_path_passive}, std::nullopt,
store, {}, "CASE_X", pool);
symmetri::Application net({pnml_path_start, pnml_path_passive}, {}, store, {},
"CASE_X", pool);

// We use a simple boolean flag to terminate the threads once the net
// finishes. Without it, these threads would prevent the program from cleanly
Expand Down
2 changes: 1 addition & 1 deletion examples/model_benchmark/model_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ int main(int, char *argv[]) {
if (bolus) {
store.insert({t, []() {}});
} else {
store.insert({t, std::nullopt});
store.insert({t, nullptr});
}
}

Expand Down
4 changes: 2 additions & 2 deletions symmetri/include/symmetri/maxplus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ inline size_t hashf(const T &matrix) {
std::map<std::tuple<size_t, size_t, size_t>, MatrixXd> cache;
MatrixXd system(const MatrixXd &A, const MatrixXd &x0, size_t k = 1) {
auto t = std::tuple{hashf(A), hashf(x0), k};
if (cache.contains(t)) {
if (cache.find(t) != cache.end()) {
return cache[t];
} else {
if (k == 1) {
Expand All @@ -180,7 +180,7 @@ std::tuple<double, double, double> pgc(const MatrixXd &A, const MatrixXd &x0,
std::map<size_t, MatrixXd> stans;
const auto s = x0.size();
for (size_t p = 1; p < r; p++) {
stans.contains(p) ? stans[p] : stans[p] = system(A, x0, p);
stans.find(p) != stans.end() ? stans[p] : stans[p] = system(A, x0, p);
for (size_t q = 1; q < (p - 1); q++) {
// use normal minus operator..
Eigen::VectorXd diff = Eigen::Map<Eigen::VectorXd>(stans[p].data(), s) -
Expand Down
7 changes: 7 additions & 0 deletions symmetri/include/symmetri/pnml_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@

#include "symmetri/types.h"

/**
* @brief Given a set of unique paths to pnml-files, it parses and merges them,
* and returns _one_ StateNet and initial marking.
*
* @param files
* @return std::tuple<symmetri::StateNet, symmetri::NetMarking>
*/
std::tuple<symmetri::StateNet, symmetri::NetMarking> readPetriNets(
const std::set<std::string> &files);
11 changes: 9 additions & 2 deletions symmetri/include/symmetri/polyaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
#include "symmetri/types.h"
namespace symmetri {

/**
* @brief PolyAction is a wrapper around any type that you want to tie to a
* transition. Typically this is an invokable object, such as a function, that
* executes some side-effects. The output of the invokable object can be used to
* communicate success or failure to the petri-net executor.
*
*/
class PolyAction {
public:
template <typename T>
Expand All @@ -12,7 +19,7 @@ class PolyAction {
friend TransitionResult runTransition(const PolyAction &x) {
return x.self_->run_();
}
friend bool directTransition(const PolyAction &x) {
friend bool isDirectTransition(const PolyAction &x) {
return x.self_->simple_();
}

Expand All @@ -29,7 +36,7 @@ class PolyAction {
return runTransition(transition_function_);
}
bool simple_() const override {
return directTransition(transition_function_);
return isDirectTransition(transition_function_);
}
T transition_function_;
};
Expand Down
66 changes: 45 additions & 21 deletions symmetri/include/symmetri/symmetri.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
#include <chrono>
#include <functional>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

#include "symmetri/actions.h"
Expand All @@ -15,52 +13,78 @@

namespace symmetri {

/**
* @brief Calculates a hash given an event log. This hash is only influenced by
* the order of the completions of transitions and if the output of those
* transitions is Completed, or something else.
*
* @param event_log An eventlog, can be both from a terminated or a still active
* net.
* @return size_t The hashed result.
*/
size_t calculateTrace(const Eventlog &event_log) noexcept;

/**
* @brief A convenience function to get a string representation of the
* state-enum.
*
* @param s The TransitionState
* @return std::string The TransitionState as a human readable string.
*/
std::string printState(symmetri::TransitionState s) noexcept;

/**
* @brief A Store is a mapping from Transitions, represented by a string that is
* also used for their identification in the petri-net, to a PolyTask. A
* PolyTask may contain side-effects.
*
*/
using Store = std::map<std::string, PolyAction>;

/**
* @brief Forward decleration for the implementation of the Application class.
* This is used to hide implementation from the end-user and speed up
* compilation times.
*
*/
struct Impl;
struct Application {

/**
* @brief The Application class executes the petri net, by using the
*
*/
class Application {
private:
mutable std::shared_ptr<Impl> impl;
std::function<void(const std::string &t)> p;
std::function<void(const clock_s::time_point &t)> publish;
void createApplication(
const symmetri::StateNet &net, const symmetri::NetMarking &m0,
const std::optional<symmetri::NetMarking> &final_marking,
const Store &store,
const symmetri::NetMarking &final_marking, const Store &store,
const std::vector<std::pair<symmetri::Transition, int8_t>> &priority,
const std::string &case_id, const symmetri::StoppablePool &stp);

public:
Application(
const std::set<std::string> &path_to_petri,
const std::optional<symmetri::NetMarking> &final_marking,
const Store &store,
const symmetri::NetMarking &final_marking, const Store &store,
const std::vector<std::pair<symmetri::Transition, int8_t>> &priority,
const std::string &case_id, const symmetri::StoppablePool &stp);

Application(
const symmetri::StateNet &net, const symmetri::NetMarking &m0,
const std::optional<symmetri::NetMarking> &final_marking,
const Store &store,
const symmetri::NetMarking &final_marking, const Store &store,
const std::vector<std::pair<symmetri::Transition, int8_t>> &priority,
const std::string &case_id, const symmetri::StoppablePool &stp);

inline std::function<void()> registerTransitionCallback(
const std::string &transition) const {
return [transition, this]() { p(transition); };
}

void doMeData(std::function<void()> f) const;

std::tuple<clock_s::time_point, symmetri::Eventlog,
std::vector<symmetri::Place>, std::vector<std::string>>
get() const noexcept;

TransitionResult operator()() const noexcept;
void togglePause();
symmetri::Eventlog getEvenLog() const noexcept;
std::vector<symmetri::Place> getMarking() const noexcept;
std::vector<std::string> getActiveTransitions() const noexcept;
std::function<void()> registerTransitionCallback(
const std::string &transition) const noexcept;
void togglePause() const noexcept;
void exitEarly() const noexcept;
};

} // namespace symmetri
90 changes: 77 additions & 13 deletions symmetri/include/symmetri/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,26 @@ using Place = std::string;
using Transition = std::string;
using clock_s = std::chrono::steady_clock;

enum class TransitionState { Started, Completed, Deadlock, UserExit, Error };
/**
* @brief The difference kinds of results a transition can have.
*
*/
enum class TransitionState {
Started, /// The transition started
Completed, /// The transition completed as expected
Deadlock, /// The transition deadlocked (e.g. it was a petri net)
UserExit, /// The transition or interupted and was stopped
Error /// None of the above
};

/**
* @brief This struct defines a subset of data that we associate with the
* payload of each transition.
*
*/
struct Event {
std::string case_id, transition;
std::string case_id;
std::string transition;
TransitionState state;
clock_s::time_point stamp;
size_t thread_id;
Expand All @@ -27,29 +43,77 @@ using StateNet =
std::pair<std::vector<Place>, std::vector<Place>>>;
using NetMarking = std::unordered_map<Place, uint16_t>;

/**
* @brief Checks if the transition-function can be invoked.
*
* @tparam T The type of the transition.
* @return true The pre- and post-marking-mutation can happen instantly.
* @return false The pre-marking mutation happens instantly and the
* post-marking-mutation should only happen after the transition is invoked.
*/
template <typename T>
bool constexpr isDirectTransition(const T&) {
return !std::is_invocable_v<T>;
}

/**
* @brief Generates a TransitionResult based on what kind of information the
* transition-function returns.
*
* @tparam T The transition-function type.
* @param transition The function to be executed.
* @return TransitionResult Contains information on the result-state and
* possible eventlog of the transition.
*/
template <typename T>
TransitionResult runTransition(const T& x) {
TransitionResult runTransition(const T& transition) {
if constexpr (!std::is_invocable_v<T>) {
return {{}, TransitionState::Completed};
} else if constexpr (std::is_same_v<TransitionState, decltype(x())>) {
return {{}, x()};
} else if constexpr (std::is_same_v<TransitionResult, decltype(x())>) {
return x();
} else if constexpr (std::is_same_v<TransitionState,
decltype(transition())>) {
return {{}, transition()};
} else if constexpr (std::is_same_v<TransitionResult,
decltype(transition())>) {
return transition();
} else {
x();
transition();
return {{}, TransitionState::Completed};
}
}

template <typename T>
bool constexpr directTransition(const T&) {
return !std::is_invocable_v<T>;
}

/**
* @brief Checks if the markings are exactly the same.
*
* @tparam T
* @param m1
* @param m2
* @return true
* @return false
*/
template <typename T>
bool MarkingEquality(const std::vector<T>& m1, const std::vector<T>& m2);

/**
* @brief Checks if marking is at least at least a subset of final_marking.
*
* @tparam T
* @param marking
* @param final_marking
* @return true
* @return false
*/
template <typename T>
bool MarkingReached(const std::vector<T>& marking,
const std::vector<T>& final_marking);

/**
* @brief Checks if two petri-nets have the equal amount of arcs between places
* and transitions of the same name.
*
* @param net1
* @param net2
* @return true
* @return false
*/
bool StateNetEquality(const StateNet& net1, const StateNet& net2);
} // namespace symmetri
6 changes: 4 additions & 2 deletions symmetri/model.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ Model &runTransitions(Model &model,

auto task = model.net.store[T_i];

// if the function is nullopt_t, we short-circuit the
// if the transition is direct, we short-circuit the
// marking mutation and do it immediately.
if (directTransition(task)) {
if (isDirectTransition(task)) {
model.tokens_n.insert(model.tokens_n.begin(),
model.net.output_n[T_i].begin(),
model.net.output_n[T_i].end());
Expand Down Expand Up @@ -162,6 +162,7 @@ Model &runTransitions(Model &model,

std::vector<Place> Model::getMarking() const {
std::vector<Place> marking;
marking.reserve(tokens_n.size());
std::transform(tokens_n.cbegin(), tokens_n.cend(),
std::back_inserter(marking),
[&](auto place_index) { return net.place[place_index]; });
Expand All @@ -170,6 +171,7 @@ std::vector<Place> Model::getMarking() const {

std::vector<Transition> Model::getActiveTransitions() const {
std::vector<Transition> transition_list;
transition_list.reserve(active_transitions_n.size());
std::transform(active_transitions_n.cbegin(), active_transitions_n.cend(),
std::back_inserter(transition_list),
[&](auto place_index) { return net.transition[place_index]; });
Expand Down
8 changes: 4 additions & 4 deletions symmetri/pnml_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@ std::tuple<StateNet, NetMarking> readPetriNets(
->GetText());

for (int i = 0; i < multiplicity; i++) {
if (places.contains(source_id)) {
if (places.find(source_id) != places.end()) {
// if the source is a place, tokens are consumed.
if (state_net.contains(target_id)) {
if (state_net.find(target_id) != state_net.end()) {
state_net.find(target_id)->second.first.push_back(source_id);
} else {
state_net.insert({target_id, {{source_id}, {}}});
}
} else if (transitions.contains(source_id)) {
} else if (transitions.find(source_id) != transitions.end()) {
// if the destination is a place, tokens are produced.
if (state_net.contains(source_id)) {
if (state_net.find(source_id) != state_net.end()) {
state_net.find(source_id)->second.second.push_back(target_id);
} else {
state_net.insert({source_id, {{}, {target_id}}});
Expand Down
Loading

0 comments on commit 46c7853

Please sign in to comment.