Skip to content

Commit

Permalink
more docs wip
Browse files Browse the repository at this point in the history
  • Loading branch information
thorstink committed Aug 20, 2023
1 parent 1b9c2ab commit 0d5103a
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 262 deletions.
15 changes: 6 additions & 9 deletions Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -1353,7 +1353,7 @@ HTML_EXTRA_FILES =
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.

HTML_COLORSTYLE = AUTO_LIGHT
HTML_COLORSTYLE = LIGHT

# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
Expand Down Expand Up @@ -1653,7 +1653,7 @@ DISABLE_INDEX = NO
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.

GENERATE_TREEVIEW = NO
GENERATE_TREEVIEW = YES

# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
Expand Down Expand Up @@ -2736,10 +2736,7 @@ GENERATE_LEGEND = YES
DOT_CLEANUP = YES
IMAGE_PATH += docs

# USE_MDFILE_AS_MAINPAGE = README.md
INPUT += README.md \
docs/symmetri_nets.md \
docs/symmetri_lib.md

DISABLE_INDEX = NO
GENERATE_TREEVIEW = YES
USE_MDFILE_AS_MAINPAGE = docs/main.md
INPUT += docs/symmetri_nets.md \
docs/symmetri_lib.md \
docs/main.md
3 changes: 3 additions & 0 deletions docs/main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Symmetri, Petri Nets in C++

Welcome to the documentation for the Symmetri library.
42 changes: 40 additions & 2 deletions docs/symmetri_lib.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
# Symmetri library

blabla

## Build

### Vanilla CMake:

Symmetri can be build as stand-alone CMake-project:

```bash
git clone --recurse-submodules https://github.com/thorstink/Symmetri.git
mkdir build
cd build
cmake ..
make
```

### In a Colcon-workspace
It can also be build as part of a [colcon-workspace](https://colcon.readthedocs.io/en/released/user/what-is-a-workspace.html):

```bash
mkdir ws/src
cd ws/src
git clone --recurse-submodules https://github.com/thorstink/Symmetri.git
cd .. # back to ws-directory
colcon build
```

## Test

Symmetri has a set of tests. There are three build configurations:

- No sanitizers
- [ASAN](https://github.com/google/sanitizers/wiki/AddressSanitizer) + [UBSAN](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
- [TSAN](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual)

Each sanitizer can be enabled by setting either the 'ASAN_BUILD'- or 'TSAN_BUILD'-flag to true. They can not both be true at the same time.

The sanitizers generally increase compiltation time quite significantly, hence they are off by default. In the CI-pipeline however they are turned on for testing.

```bash
# from the build directory...
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_EXAMPLES=ON -DBUILD_TESTING=ON -DASAN_BUILD=OFF -DTSAN_BUILD=OFF ..
make
make test
```

## Architecture
2 changes: 1 addition & 1 deletion docs/symmetri_nets.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Transition T<sub>1</sub> is enabled | Conflict: either T<sub>1</sub
:-------------------------:|:-------------------------:|:-------------------------:
![](img/simple.svg) | ![](img/conflict.svg) | ![](img/parallel.svg)

There are many variations in Petri nets. In the most basic form there are transitions that produce tokens that are indistinguishable (e.g. if a place holds two tokens you can not distinguish one token from the other) and firing is atomic (meaning that tokens are consumed and produced instantaneous at the moment of firing). All transitions are also equal in the sense that if two transitions are active, meaning their input places have the required amount of tokens, simultaneously the order of firing is not determined. In a special case the transitions can be simultaneously active and share input places, this is called conflict. In Symmetri Petri Nets are used to encode the process- or business-logic of a (software) program. In many realistic applications the aforementioned properties are, subtly put, not ideal for modelling. In this text I will explain what kind of ‘Petri net modifications’ are applied to make Petri nets more applicable to the use-case of Symmetri: modelling business logic. I call the resulting cocktail of Petri net semantics Symmetri nets.
There are many variations in Petri nets. In the most basic form there are transitions that produce tokens that are indistinguishable (e.g. if a place holds two tokens you can not distinguish one token from the other) and firing is atomic (meaning that tokens are consumed and produced instantaneous at the moment of firing). All transitions are also equal in the sense that if two transitions are simultaneously active, meaning their input places have the required amount of tokens, the order of firing is not determined. In a special case the transitions can be simultaneously active and share input places, this is called conflict. In Symmetri Petri Nets are used to encode the process- or business-logic of a (software) program. In many realistic applications the aforementioned properties are, subtly put, not ideal for modelling. In this text I will explain what kind of ‘Petri net modifications’ are applied to make Petri nets more applicable to the use-case of Symmetri: modelling business logic. I call the resulting cocktail of Petri net semantics Symmetri nets.

## Transitions as callbacks

Expand Down
2 changes: 2 additions & 0 deletions symmetri/include/symmetri/callback.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

/** @file callback.h */

#include <memory>

#include "symmetri/types.h"
Expand Down
3 changes: 3 additions & 0 deletions symmetri/include/symmetri/parsers.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#pragma once

/** @file parsers.h */

#include <set>
#include <tuple>

Expand Down
2 changes: 2 additions & 0 deletions symmetri/include/symmetri/symmetri.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

/** @file symmetri.h */

#include <set>
#include <unordered_map>

Expand Down
2 changes: 2 additions & 0 deletions symmetri/include/symmetri/tasks.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

/** @file tasks.h */

#include <atomic>
#include <functional>
#include <memory>
Expand Down
3 changes: 3 additions & 0 deletions symmetri/include/symmetri/types.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#pragma once

/** @file types.h */

#include <chrono>
#include <string>
#include <unordered_map>
Expand Down
3 changes: 3 additions & 0 deletions symmetri/include/symmetri/utilities.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#pragma once

/** @file utilities.hpp */

#include <algorithm>

#include "symmetri/types.h"
Expand Down
21 changes: 7 additions & 14 deletions symmetri/model_utilities.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ bool canFire(const SmallVector &pre, const std::vector<size_t> &tokens) {
});
}

gch::small_vector<uint8_t, 32> possibleTransitions(
gch::small_vector<size_t, 32> possibleTransitions(
const std::vector<size_t> &tokens,
const std::vector<SmallVector> &p_to_ts_n,
const std::vector<int8_t> &priorities) {
gch::small_vector<uint8_t, 32> possible_transition_list_n;
const std::vector<SmallVector> &p_to_ts_n) {
gch::small_vector<size_t, 32> possible_transition_list_n;
for (const size_t place : tokens) {
for (size_t t : p_to_ts_n[place]) {
if (std::find(possible_transition_list_n.begin(),
Expand All @@ -29,16 +28,10 @@ gch::small_vector<uint8_t, 32> possibleTransitions(
}
}

// sort transition list according to priority
std::sort(possible_transition_list_n.begin(),
possible_transition_list_n.end(),
[&](size_t a, size_t b) { return priorities[a] > priorities[b]; });

return possible_transition_list_n;
}

Reducer createReducerForTransition(size_t t_i, const Eventlog &ev,
State result) {
Reducer createReducerForCallback(size_t t_i, const Eventlog &ev, State result) {
return [=](Petri &model) {
// if it is in the active transition set it means it is finished and we
// should process it.
Expand All @@ -56,7 +49,7 @@ Reducer createReducerForTransition(size_t t_i, const Eventlog &ev,
};
}

Reducer fireTransition(
Reducer scheduleCallback(
size_t t_i, const std::string &transition, const Callback &task,
const std::string &case_id,
const std::shared_ptr<moodycamel::BlockingConcurrentQueue<Reducer>>
Expand All @@ -67,9 +60,9 @@ Reducer fireTransition(
{case_id, transition, State::Started, start_time});
});

auto [ev, res] = fire(task);
auto [ev, res] = ::fire(task);
ev.push_back({case_id, transition, res, Clock::now()});
return createReducerForTransition(t_i, ev, res);
return createReducerForCallback(t_i, ev, res);
}

} // namespace symmetri
149 changes: 67 additions & 82 deletions symmetri/petri.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,75 +112,53 @@ std::vector<size_t> Petri::toTokens(const Marking &marking) const noexcept {
return tokens;
}

std::vector<Transition> Petri::getFireableTransitions() const {
auto possible_transition_list_n =
possibleTransitions(tokens, net.p_to_ts_n, net.priority);
std::vector<Transition> fireable_transitions;
fireable_transitions.reserve(possible_transition_list_n.size());
for (const size_t t_idx : possible_transition_list_n) {
const auto &pre = net.input_n[t_idx];
if (canFire(pre, tokens)) {
fireable_transitions.push_back(net.transition[t_idx]);
}
void Petri::fireSynchronous(const size_t t) {
const auto timestamp = Clock::now();
const auto &task = net.store[t];
const auto &transition = net.transition[t];
const auto &lookup_t = net.output_n[t];
event_log.push_back({case_id, transition, State::Started, timestamp});
auto result = std::get<symmetri::State>(::fire(task));
event_log.push_back({case_id, transition, result, Clock::now()});
if (result == State::Completed) {
tokens.insert(tokens.begin(), lookup_t.begin(), lookup_t.end());
}
return fireable_transitions;
}

bool Petri::Fire(
const size_t t,
const std::shared_ptr<moodycamel::BlockingConcurrentQueue<Reducer>>
&reducers,
const std::string &case_id) {
auto timestamp = Clock::now();
// deduct the marking
for (const size_t place : net.input_n[t]) {
void Petri::fireAsynchronous(const size_t t) {
const auto timestamp = Clock::now();
const auto &task = net.store[t];
const auto &transition = net.transition[t];
active_transitions.push_back(t);
event_log.push_back({case_id, transition, State::Scheduled, timestamp});
pool->push([=] {
reducer_queue->enqueue(
scheduleCallback(t, transition, task, case_id, reducer_queue));
});
}

void Petri::deductMarking(const SmallVector &inputs) {
for (const size_t place : inputs) {
// erase one by one. using std::remove_if would remove all tokens at
// a particular place.
tokens.erase(std::find(tokens.begin(), tokens.end(), place));
}

const auto &task = net.store[t];

// if the transition is direct, we short-circuit the
// marking mutation and do it immediately.
const auto &transition = net.transition[t];
const auto &lookup_t = net.output_n[t];
if (isSynchronous(task)) {
tokens.insert(tokens.begin(), lookup_t.begin(), lookup_t.end());
event_log.push_back({case_id, transition, State::Started, timestamp});
event_log.push_back({case_id, transition,
std::get<symmetri::State>(::fire(task)), timestamp});
return true;
} else {
active_transitions.push_back(t);
event_log.push_back({case_id, transition, State::Scheduled, timestamp});
pool->push([=] {
reducers->enqueue(fireTransition(t, transition, task, case_id, reducers));
});
return false;
}
}

bool Petri::fire(
const Transition &t,
const std::shared_ptr<moodycamel::BlockingConcurrentQueue<Reducer>>
&reducers,
const std::string &case_id) {
void Petri::tryFire(const Transition &t) {
auto it = std::find(net.transition.begin(), net.transition.end(), t);
return it != net.transition.end() &&
canFire(net.input_n[std::distance(net.transition.begin(), it)],
tokens) &&
!Fire(std::distance(net.transition.begin(), it), reducers, case_id);
const auto t_idx = std::distance(net.transition.begin(), it);
if (canFire(net.input_n[t_idx], tokens)) {
deductMarking(net.input_n[t_idx]);
isSynchronous(net.store[t_idx]) ? fireSynchronous(t_idx)
: fireAsynchronous(t_idx);
}
}

void Petri::fireTransitions(
const std::shared_ptr<moodycamel::BlockingConcurrentQueue<Reducer>>
&reducers,
bool run_all, const std::string &case_id) {
void Petri::fireTransitions() {
// find possible transitions
auto possible_transition_list_n =
possibleTransitions(tokens, net.p_to_ts_n, net.priority);
auto filter_transitions = [&](const auto &tokens) {
auto possible_transition_list_n = possibleTransitions(tokens, net.p_to_ts_n);
auto remove_inactive_transitions_predicate = [&](const auto &tokens) {
possible_transition_list_n.erase(
std::remove_if(
possible_transition_list_n.begin(),
Expand All @@ -189,22 +167,41 @@ void Petri::fireTransitions(
possible_transition_list_n.end());
};

// remove transitions that are not fireable;
filter_transitions(tokens);

if (possible_transition_list_n.empty()) {
return;
}
// remove transitions that are not active;
remove_inactive_transitions_predicate(tokens);

// sort transition list according to priority
std::sort(
possible_transition_list_n.begin(), possible_transition_list_n.end(),
[&](size_t a, size_t b) { return net.priority[a] > net.priority[b]; });

while (!possible_transition_list_n.empty()) {
const auto t_idx = possible_transition_list_n.front();
deductMarking(net.input_n[t_idx]);
if (isSynchronous(net.store[t_idx])) {
fireSynchronous(t_idx);
// add the output places-connected transitions as new possible
// transitions:
for (const auto &p : net.output_n[t_idx]) {
for (const auto t : net.p_to_ts_n[p]) {
if (canFire(net.input_n[t], tokens)) {
possible_transition_list_n.push_back(t);
}
}
}

do {
// if Fire returns true, update the possible transition list
if (Fire(possible_transition_list_n.front(), reducers, case_id)) {
possible_transition_list_n =
possibleTransitions(tokens, net.p_to_ts_n, net.priority);
// sort again
std::sort(possible_transition_list_n.begin(),
possible_transition_list_n.end(), [&](size_t a, size_t b) {
return net.priority[a] > net.priority[b];
});
} else {
fireAsynchronous(t_idx);
}
// remove transitions that are not fireable;
filter_transitions(tokens);
} while (run_all && !possible_transition_list_n.empty());
// remove transitions that are not active anymore because of the marking
// mutation;
remove_inactive_transitions_predicate(tokens);
}

return;
}
Expand All @@ -229,16 +226,4 @@ Eventlog Petri::getLog() const {
return eventlog;
}

std::vector<Transition> Petri::getActiveTransitions() const {
std::vector<Transition> transition_list;
if (active_transitions.size() > 0) {
transition_list.reserve(active_transitions.size());
std::transform(active_transitions.cbegin(), active_transitions.cend(),
std::back_inserter(transition_list), [&](auto place_index) {
return net.transition[place_index];
});
}
return transition_list;
}

} // namespace symmetri
Loading

0 comments on commit 0d5103a

Please sign in to comment.