From c9efb1f29ee19f45f22a1634897b2dce076c85f5 Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Tue, 18 Feb 2025 19:21:25 +0000
Subject: [PATCH 01/12] Quick add: states of CA
---
reactics-bdd/mc.cc | 6 +++---
reactics-bdd/symrs.cc | 40 ++++++++++++++++++++++++++++++++++++++++
reactics-bdd/symrs.hh | 2 ++
3 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/reactics-bdd/mc.cc b/reactics-bdd/mc.cc
index 88f5e36..d1c7b4c 100644
--- a/reactics-bdd/mc.cc
+++ b/reactics-bdd/mc.cc
@@ -200,13 +200,13 @@ void ModelChecker::printReachWithSucc(void)
t = unproc.PickOneMinterm(*pv);
if (opts->backend_mode)
{
- cout << "G " << srs->decodedRctSysStateToStr(t) << endl;
+ cout << "G " << srs->decodedRctSysStateWithCtxAutToStr(t) << endl;
}
else
{
- cout << "Successors of " << srs->decodedRctSysStateToStr(t) << ":" << endl;
+ cout << "Successors of " << srs->decodedRctSysStateWithCtxAutToStr(t) << ":" << endl;
}
- srs->printDecodedRctSysStates(getSucc(t));
+ srs->printDecodedRctSysWithCtxAutStates(getSucc(t));
unproc -= t;
}
diff --git a/reactics-bdd/symrs.cc b/reactics-bdd/symrs.cc
index 98726e3..d99b38a 100644
--- a/reactics-bdd/symrs.cc
+++ b/reactics-bdd/symrs.cc
@@ -567,6 +567,21 @@ std::string SymRS::decodedRctSysStateToStr(const BDD &state)
return s;
}
+std::string SymRS::decodedRctSysStateWithCtxAutToStr(const BDD &state)
+{
+ std::string s = "( ";
+ s += decodedRctSysStateToStr(state) + " ";
+ for (const auto &aut_state : rs->ctx_aut->states_ids) {
+ const auto state_id = rs->ctx_aut->getStateID(aut_state);
+ if (!(encCtxAutState(state_id) * state).IsZero()) {
+ s += aut_state + " ";
+ break;
+ }
+ }
+ s += ")";
+ return s;
+}
+
void SymRS::printDecodedRctSysStates(const BDD &states)
{
BDD unproc = states;
@@ -591,6 +606,31 @@ void SymRS::printDecodedRctSysStates(const BDD &states)
}
}
+void SymRS::printDecodedRctSysWithCtxAutStates(const BDD &states)
+{
+ BDD unproc = states;
+
+ while (!unproc.IsZero()) {
+ BDD t = unproc.PickOneMinterm(*pv);
+ if (opts->backend_mode)
+ {
+ cout << "s " << decodedRctSysStateWithCtxAutToStr(t) << endl;
+ }
+ else
+ {
+ cout << decodedRctSysStateWithCtxAutToStr(t) << endl;
+ }
+
+ if (opts->verbose > 9) {
+ BDD_PRINT(t);
+ cout << endl;
+ }
+
+ unproc -= t;
+ }
+}
+
+
DecompReactions SymRS::getProductionConditions(Process proc_id)
{
DecompReactions dr;
diff --git a/reactics-bdd/symrs.hh b/reactics-bdd/symrs.hh
index 669910e..bcb5999 100644
--- a/reactics-bdd/symrs.hh
+++ b/reactics-bdd/symrs.hh
@@ -319,7 +319,9 @@ class SymRS
BDD compContext(const BDD &context) const;
std::string decodedRctSysStateToStr(const BDD &state);
+ std::string decodedRctSysStateWithCtxAutToStr(const BDD &state);
void printDecodedRctSysStates(const BDD &states);
+ void printDecodedRctSysWithCtxAutStates(const BDD &states);
DecompReactions getProductionConditions(Process proc_id);
--
2.53.0
From 063a703a8af51b38ceee9a9bbf01df0b893ae20a Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Tue, 18 Feb 2025 20:43:20 +0000
Subject: [PATCH 02/12] Removed _config.yml
---
_config.yml | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 _config.yml
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 2f7efbe..0000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
--
2.53.0
From 00cd7181478ecfae1d7ebeba4e0b49a74731a80c Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Tue, 18 Feb 2025 20:44:50 +0000
Subject: [PATCH 03/12] Revert "Removed _config.yml"
This reverts commit 063a703a8af51b38ceee9a9bbf01df0b893ae20a.
---
_config.yml | 1 +
1 file changed, 1 insertion(+)
create mode 100644 _config.yml
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..2f7efbe
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-minimal
\ No newline at end of file
--
2.53.0
From a86432988313bca845f0dc53b07aeca11717002e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Pi=C4=85tkowski?=
Date: Wed, 2 Apr 2025 21:56:25 +0200
Subject: [PATCH 04/12] Translation into ISPL (for benchmarks with MCMAS)
---
reactics-bdd/Makefile | 2 +-
reactics-bdd/ctx_aut.hh | 1 +
reactics-bdd/export.cc | 810 ++++++++++++++++++++++++++++++++++++
reactics-bdd/export.hh | 55 +++
reactics-bdd/formrsctlk.hh | 3 +
reactics-bdd/reactics.cc | 20 +-
reactics-bdd/reactics.hh | 1 +
reactics-bdd/rs.hh | 1 +
reactics-bdd/stateconstr.hh | 3 +
9 files changed, 891 insertions(+), 5 deletions(-)
create mode 100644 reactics-bdd/export.cc
create mode 100644 reactics-bdd/export.hh
diff --git a/reactics-bdd/Makefile b/reactics-bdd/Makefile
index 4235476..0a6c37e 100644
--- a/reactics-bdd/Makefile
+++ b/reactics-bdd/Makefile
@@ -11,7 +11,7 @@ CXXFLAGS = -std=c++14 -O3 -DPUBLIC_RELEASE -DNDEBUG #-g
CXXFLAGS = -std=c++14 -O3 -DPUBLIC_RELEASE #-g
LDLIBS = $(CUDD_INCLUDE)
-OBJ = rs.o ctx_aut.o symrs.o mc.o rsin_driver.o rsin_parser.o rsin_parser.lex.o formrsctlk.o stateconstr.o
+OBJ = rs.o ctx_aut.o symrs.o mc.o rsin_driver.o rsin_parser.o rsin_parser.lex.o formrsctlk.o stateconstr.o export.o
all: reactics
diff --git a/reactics-bdd/ctx_aut.hh b/reactics-bdd/ctx_aut.hh
index 164d4c7..d446459 100644
--- a/reactics-bdd/ctx_aut.hh
+++ b/reactics-bdd/ctx_aut.hh
@@ -27,6 +27,7 @@ class StateConstr;
class CtxAut
{
friend class SymRS;
+ friend class RSExporter;
public:
CtxAut(Options *opts, RctSys *parent_rctsys);
diff --git a/reactics-bdd/export.cc b/reactics-bdd/export.cc
new file mode 100644
index 0000000..6682cec
--- /dev/null
+++ b/reactics-bdd/export.cc
@@ -0,0 +1,810 @@
+
+#include "export.hh"
+
+
+RSExporter::RSExporter(RctSys *rs, rsin_driver *drv, std::ostream & outStream):
+ rs(rs), drv(drv), output(outStream) {
+
+ // Store all possible inputs (reactants and inhibitors) and outputs (products) for each process
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const auto & rr : rs->proc_reactions[proc]) {
+ for (const auto & e : rr.rctt)
+ procInputs[proc].insert(e);
+
+ for (const auto & e : rr.inhib)
+ procInputs[proc].insert(e);
+
+ for (const auto & e : rr.prod)
+ procOutputs[proc].insert(e);
+ }
+ }
+
+ // Add possible inputs from context automaton
+
+ for (const auto & trans : rs->ctx_aut->transitions) {
+ for (const auto & procCtx : trans.ctx) {
+ for (const auto & e : procCtx.second) {
+ procInputs[procCtx.first].insert(e);
+ }
+ }
+ }
+}
+
+
+void RSExporter::exportToISPL() {
+ exportEnvironment();
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc)
+ exportAgent(proc);
+
+ parseFormulas();
+ exportEvaluation();
+ exportInitStates();
+ exportFormulae();
+}
+
+
+void RSExporter::exportEnvironment() {
+ output << "Agent Environment\n\n";
+
+ exportEnvironmentVars();
+ exportEnvironmentActions();
+ exportEnvironmentProtocol();
+ exportEnvironmentEvolution();
+
+ output << "\nend Agent\n" << endl;
+}
+
+
+void RSExporter::exportEnvironmentVars() {
+ output << "Obsvars:\n"
+ << indent << "mode: {init, clear, select_active_agents, activate_agents,\n"
+ << indent << indent << "distribute_local, add_context, distribute_global, produce,\n";
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const Entity & e : procOutputs[proc]) {
+ output << indent << indent << "collect_" << rs->processes_ids[proc]
+ << "_" << rs->entities_ids[e] << ",\n";
+ }
+ }
+
+ output << indent << indent << "finalize\n" << indent << "};\n\n";
+
+ output << indent << "-- Boolean variables denoting entities available for each agent --\n\n";
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const Entity & e : procInputs[proc]) {
+ output << indent << rs->processes_ids[proc]
+ << "_" << rs->entities_ids[e] << ": 0..1;\n";
+ }
+
+ output << "\n";
+ }
+
+ output << indent << "-- Boolean variables denoting which agents are active --\n\n";
+
+ for (const string & procName : rs->processes_ids)
+ output << indent << "Active_" << procName << ": 0..1;\n";
+
+ output << "end Obsvars\n\n"
+ << "Vars:\n\n";
+
+ output << indent << "-- Context automaton details --\n\n";
+
+ output << indent << "state: {";
+
+ for (size_t i=0; ictx_aut->statesCount(); ++i) {
+ string stName = rs->ctx_aut->getStateName(i);
+
+ // Skip the dummy state used for closure
+ if (stName == "T")
+ continue;
+
+ if (i) output << ", ";
+
+ output << stName;
+ }
+
+ output << "};\n";
+
+ // Add dummy transition leading to the initial state (no agent is active)
+ output << indent << "transition: {"
+ << "\n" << indent << indent << "init_0";
+
+ int trId {1};
+
+ for (const CtxAutTransition & trans : rs->ctx_aut->transitions) {
+ string dstName = rs->ctx_aut->getStateName(trans.dst_state);
+
+ if (dstName == "T")
+ continue;
+
+ output << ",";
+ output << "\n" << indent << indent << rs->ctx_aut->getStateName(trans.src_state)
+ << "_" << dstName << "_" << trId;
+ ++trId;
+ }
+
+ output << indent << "\n};\n\n";
+
+ output << indent << "-- Boolean variables denoting existence of entities in the environment.\n\n";
+
+ for (const string & entityName : rs->entities_ids)
+ output << indent << entityName << " : 0..1;\n";
+
+ output << "end Vars" << endl;
+}
+
+
+void RSExporter::exportEnvironmentActions() {
+ output << "\nActions = {\n"
+ << indent << "init, clear, select_active_agents, activate_agents,\n"
+ << indent << "distribute_local, add_context, distribute_global, produce,\n";
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const Entity & e : procOutputs[proc]) {
+ output << indent << "collect_" << rs->processes_ids[proc]
+ << "_" << rs->entities_ids[e] << ",\n";
+ }
+ }
+
+ output << indent << "finalize, sleep\n};\n";
+}
+
+
+void RSExporter::exportEnvironmentProtocol() {
+ output << "\nProtocol:\n"
+ << indent << "mode=clear : {clear};\n"
+ << indent << "mode=select_active_agents : {select_active_agents};\n"
+ << indent << "mode=activate_agents : {activate_agents};\n\n";
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const Entity & e : procOutputs[proc]) {
+ output << indent << "mode=collect_" << rs->processes_ids[proc]
+ << "_" << rs->entities_ids[e]
+ << ": {collect_" << rs->processes_ids[proc]
+ << "_" << rs->entities_ids[e] << "};\n";
+ }
+
+ output << "\n";
+ }
+
+ output << indent << "mode=distribute_local : {distribute_local};\n"
+ << indent << "mode=add_context : {add_context};\n"
+ << indent << "mode=distribute_global : {distribute_global};\n"
+ << indent << "mode=produce : {produce};\n"
+ << indent << "mode=finalize : {finalize};\n\n"
+ << indent << "Other : {sleep};\n"
+ << "end Protocol\n";
+}
+
+
+void RSExporter::exportEnvironmentEvolution() {
+ output << "\nEvolution:\n\n";
+
+ //--------------------------------------------------------------------------------
+ // Reset the environment state after the previous computation step.
+ // (reset agent activity status, availability of entities, etc.)
+ //--------------------------------------------------------------------------------
+
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- Reset the environment state after the previous computation step.\n"
+ << indent <<"-- (reset agent activity status, availability of entities, etc.)\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n"
+ << indent;
+
+ for (const string & procName : rs->processes_ids)
+ output << "Active_" << procName << "=0 and ";
+
+ output << "\n" << indent << indent;
+
+ for (const string & entityName : rs->entities_ids)
+ output << entityName << "=0 and ";
+
+ output << "\n" << indent << indent << "mode=select_active_agents\n"
+ << indent << indent << indent << "if mode=clear;\n\n";
+
+ //--------------------------------------------------------------------------------
+ // Active agents are selected based on the current context automaton transition.
+ //--------------------------------------------------------------------------------
+
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- Active agents are selected based on the current context automaton transition.\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n"
+ << indent;
+
+ // For the dummy initial transition leading to the initial state no agent is active
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc)
+ output << "Active_" << rs->getProcessName(proc) << "=0 and ";
+
+ output << "mode=activate_agents\n"
+ << indent << indent << "if mode=select_active_agents and transition=init_0;\n\n" << indent;
+
+ unsigned transNo = 1;
+
+ for (const CtxAutTransition & trans : rs->ctx_aut->transitions) {
+ vector isActive(rs->getNumberOfProcesses(), false);
+
+ string dstName = rs->ctx_aut->getStateName(trans.dst_state);
+
+ if (dstName == "T")
+ continue;
+
+ for (const auto & entry : trans.ctx)
+ isActive[entry.first] = true;
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc)
+ output << "Active_" << rs->getProcessName(proc) << "="
+ << (isActive[proc] ? "1" : "0")
+ << " and ";
+
+ output << "mode=activate_agents\n"
+ << indent << indent << "if mode=select_active_agents and transition="
+ << rs->ctx_aut->getStateName(trans.src_state) << "_"
+ << rs->ctx_aut->getStateName(trans.dst_state) << "_"
+ << to_string(transNo) << ";\n\n" << indent;
+
+ ++transNo;
+ }
+
+ //--------------------------------------------------------------------------------
+ // Conduct a series of queries to active agents regarding products
+ // that may have been produced in the last step when they were active.
+ //--------------------------------------------------------------------------------
+
+ output << "--------------------------------------------------------------------------------\n"
+ << indent << "-- Conduct a series of queries to active agents regarding products\n"
+ << indent << "-- that may have been produced in the last step when they were active.\n"
+ << indent << "--------------------------------------------------------------------------------\n\n";
+
+ vector> procProducts;
+
+ for (Process proc=0; proc < rs->getNumberOfProcesses(); ++proc)
+ for (const Entity & e : procOutputs[proc])
+ procProducts.push_back(make_pair(proc, e));
+
+ output << indent << "mode=collect_"
+ << rs->getProcessName(procProducts[0].first) << "_"
+ << rs->getEntityName(procProducts[0].second) << "\n"
+ << indent << indent << "if mode=activate_agents;\n\n";
+
+ for (unsigned pairIdx=1; pairIdx < procProducts.size(); ++pairIdx) {
+ output << indent << rs->getEntityName(procProducts[pairIdx-1].second)
+ << "=1 and mode=collect_"
+ << rs->getProcessName(procProducts[pairIdx].first) << "_"
+ << rs->getEntityName(procProducts[pairIdx].second) << "\n"
+ << indent << indent << "if mode=collect_"
+ << rs->getProcessName(procProducts[pairIdx-1].first) << "_"
+ << rs->getEntityName(procProducts[pairIdx-1].second)
+ << " and " << rs->getProcessName(procProducts[pairIdx-1].first)
+ << ".Action=produce_"
+ << rs->getEntityName(procProducts[pairIdx-1].second)
+ << ";\n";
+
+ output << indent << "mode=collect_"
+ << rs->getProcessName(procProducts[pairIdx].first) << "_"
+ << rs->getEntityName(procProducts[pairIdx].second) << "\n"
+ << indent << indent << "if mode=collect_"
+ << rs->getProcessName(procProducts[pairIdx-1].first) << "_"
+ << rs->getEntityName(procProducts[pairIdx-1].second)
+ << " and (" << rs->getProcessName(procProducts[pairIdx-1].first)
+ << ".Action=not_produce_"
+ << rs->getEntityName(procProducts[pairIdx-1].second)
+ << " or " << rs->getProcessName(procProducts[pairIdx-1].first)
+ << ".Action=sleep);\n\n";
+ }
+
+ output << indent << rs->getEntityName(procProducts.rbegin()->second)
+ << "=1 and mode=distribute_local\n"
+ << indent << indent << "if mode=collect_"
+ << rs->getProcessName(procProducts.rbegin()->first) << "_"
+ << rs->getEntityName(procProducts.rbegin()->second)
+ << " and " << rs->getProcessName(procProducts.rbegin()->first)
+ << ".Action=produce_"
+ << rs->getEntityName(procProducts.rbegin()->second)
+ << ";\n";
+
+ output << indent << "mode=distribute_local\n"
+ << indent << indent << "if mode=collect_"
+ << rs->getProcessName(procProducts.rbegin()->first) << "_"
+ << rs->getEntityName(procProducts.rbegin()->second)
+ << " and (" << rs->getProcessName(procProducts.rbegin()->first)
+ << ".Action=not_produce_"
+ << rs->getEntityName(procProducts.rbegin()->second)
+ << " or " << rs->getProcessName(procProducts.rbegin()->first)
+ << ".Action=sleep);\n\n";
+
+ //--------------------------------------------------------------------------------
+ // The entities available in the environment are distributed between the agents.
+ //--------------------------------------------------------------------------------
+
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- The entities available in the environment are distributed between the agents. \n"
+ << indent <<"--------------------------------------------------------------------------------\n\n"
+ << indent;
+
+ bool first = true;
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const Entity & e : procInputs[proc]) {
+ if (!first)
+ output << " and ";
+ else
+ first = false;
+
+ output << rs->getProcessName(proc) << "_" << rs->getEntityName(e) << "=" << rs->getEntityName(e);
+ }
+
+ output << "\n" << indent;
+ }
+
+ output << "and mode=add_context\n" << indent << indent
+ << "if mode=distribute_local;\n\n";
+
+ //--------------------------------------------------------------------------------
+ // A set of additional entities, if any, is provided for each active agent
+ // following the context automaton transition description.
+ //--------------------------------------------------------------------------------
+
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- A set of additional entities, if any, is provided for each active agent\n"
+ << indent <<"-- following the context automaton transition description.\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n"
+ << indent;
+
+ transNo = 1;
+ vector> noCtxTrans;
+
+ for (const CtxAutTransition & trans : rs->ctx_aut->transitions) {
+ if (rs->ctx_aut->getStateName(trans.dst_state) == "T")
+ continue;
+
+ string ctxStr {""};
+ string separator {""};
+ first = true;
+
+ for (const auto & procCtx : trans.ctx) {
+ for (auto ent : procCtx.second) {
+ if (first) {
+ first = false;
+ separator = "";
+ }
+ else
+ separator = "and ";
+
+ ctxStr += separator + rs->getProcessName(procCtx.first) + "_" + rs->getEntityName(ent) + "=1 ";
+ }
+ }
+
+ if (ctxStr.length() > 0)
+ output << indent << ctxStr
+ << "and mode=distribute_global\n" << indent << indent
+ << "if mode=add_context and transition="
+ << rs->ctx_aut->getStateName(trans.src_state) << "_" << rs->ctx_aut->getStateName(trans.dst_state) << "_" << transNo
+ << ";\n\n";
+ else
+ noCtxTrans.push_back({trans.src_state, trans.dst_state, transNo});
+
+ ++transNo;
+ }
+
+ //--------------------------------------------------------------------------------
+ // Mode change for transitions having no additional entities in the context
+ //--------------------------------------------------------------------------------
+
+ if (noCtxTrans.size() > 0) {
+ output << indent << "mode=distribute_global if mode=add_context and\n"
+ << indent << indent << " (transition=init_0";
+
+ for (const auto & trans : noCtxTrans) {
+ output << " or transition=" << rs->ctx_aut->getStateName(get<0>(trans))
+ << "_" << rs->ctx_aut->getStateName(get<1>(trans))
+ << "_" << get<2>(trans);
+ }
+
+ output << ");\n\n";
+ }
+
+ //--------------------------------------------------------------------------------
+ // Notify active agents to execute all possible reactions.
+ //--------------------------------------------------------------------------------
+
+ output << indent << "--------------------------------------------------------------------------------\n"
+ << indent <<"-- Notify active agents to execute all possible reactions.\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n"
+ << indent;
+
+ output << "mode=produce if mode=distribute_global;\n\n";
+
+ output << indent << "mode=finalize\n"
+ << indent << indent << "if mode=produce;\n\n";
+
+ //--------------------------------------------------------------------------------
+ // Select next computation step (choose context automaton transition).
+ //--------------------------------------------------------------------------------
+
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- Select next computation step (choose context automaton transition).\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n"
+ << indent;
+
+ transNo = 1;
+
+ for (const CtxAutTransition & trans : rs->ctx_aut->transitions) {
+ if (rs->ctx_aut->getStateName(trans.dst_state) == "T")
+ continue;
+
+ output << indent << "mode=clear and state="
+ << rs->ctx_aut->getStateName(trans.dst_state)
+ << " and transition="
+ << rs->ctx_aut->getStateName(trans.src_state) << "_"
+ << rs->ctx_aut->getStateName(trans.dst_state) << "_"
+ << transNo << "\n" << indent << indent;
+
+ output << "\n" << indent << indent << "if mode=finalize and state="
+ << rs->ctx_aut->getStateName(trans.src_state);
+
+ if (trans.state_constr)
+ output << " and " << stateConstrToStr(trans.state_constr);
+
+ output << ";\n\n";
+
+ ++transNo;
+ }
+
+ output << "end Evolution\n";
+}
+
+
+void RSExporter::exportEnvironmentInitState() {
+ output << indent << "Environment.mode=clear\n\n";
+
+ for (const string & procName : rs->processes_ids)
+ output << indent << indent << "and Environment.Active_" << procName << "=0\n";
+
+ output << "\n";
+
+ for (const string & entName : rs->entities_ids)
+ output << indent << indent << "and Environment." << entName << "=0\n";
+
+ output << "\n";
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ for (const Entity & e : procInputs[proc])
+ output << indent << indent << "and Environment." << rs->getProcessName(proc) << "_"
+ << rs->entities_ids[e] << "=0\n";
+
+ output << "\n";
+ }
+
+ State initState = rs->ctx_aut->getInitState();
+ output << indent << indent
+ << "and Environment.state="
+ << rs->ctx_aut->getStateName(initState) << "\n"
+ << indent << indent << "and Environment.transition=init_0";
+}
+
+
+void RSExporter::exportAgent(const Process & proc) {
+ output << "\nAgent " << rs->processes_ids[proc] << "\n\n";
+
+ exportAgentVars(proc);
+ exportAgentProtocol(proc);
+ exportAgentEvolution(proc);
+
+ output << "end Agent\n" << endl;
+}
+
+
+void RSExporter::exportAgentVars(const Process & proc) {
+ output << "Vars:\n"
+ << indent << "isActive: 0..1;\n";
+
+ for (const Entity & e : procInputs[proc]) {
+ output << indent << rs->entities_ids[e] << "_in: 0..1;\n";
+ }
+
+ for (const Entity & e : procOutputs[proc]) {
+ output << indent << rs->entities_ids[e] << "_out: 0..1;\n";
+ }
+
+ output << "end Vars\n\n";
+
+ output << "Actions = {\n";
+
+ for (const Entity & e : procOutputs[proc]) {
+ output << indent << "produce_" << rs->entities_ids[e]
+ << ", not_produce_" << rs->entities_ids[e] << ",\n";
+ }
+
+ output << indent << "sleep\n};\n\n";
+}
+
+
+void RSExporter::exportAgentProtocol(const Process & proc) {
+ string procName = rs->processes_ids[proc];
+
+ output << "Protocol:\n";
+
+ for (const Entity & e : procOutputs[proc]) {
+ string entityName = rs->entities_ids[e];
+
+ output << indent << entityName << "_out=1 and (Environment.mode=collect_" << procName << "_" << entityName
+ << " and isActive=1): {produce_" << entityName << "};\n"
+ << indent << entityName << "_out=0 and (Environment.mode=collect_" << procName << "_" << entityName
+ << " and isActive=1): {not_produce_" << entityName << "};\n\n";
+ }
+
+ output << indent << "Other: {sleep};\n"
+ << "end Protocol\n\n";
+}
+
+
+void RSExporter::exportAgentEvolution(const Process & proc) {
+ string procName = rs->processes_ids[proc];
+
+ output << "Evolution:\n\n";
+
+ //--------------------------------------------------------------------------------
+ // Set the agent's activity status.
+ //--------------------------------------------------------------------------------
+
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- Set the agent's activity status.\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n";
+
+ output << indent << "isActive=Environment.Active_" << procName << " ";
+
+ for (const Entity & e : procInputs[proc])
+ output << "and " << rs->entities_ids[e] << "_in=0 ";
+
+ output << "\n" << indent << indent << "if Environment.Action=activate_agents;\n\n";
+
+ bool first {true};
+
+ //--------------------------------------------------------------------------------
+ // Synchronise with the environment state.
+ //--------------------------------------------------------------------------------
+
+ output << indent << "--------------------------------------------------------------------------------\n"
+ << indent <<"-- Synchronise with the environment state.\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n";
+
+ for (const Entity & e : procInputs[proc]) {
+ string entityName = rs->entities_ids[e];
+
+ if (first) {
+ first = false;
+ output << indent;
+ }
+ else {
+ output << "and ";
+ }
+
+ output << entityName << "_in=Environment." << procName << "_" << entityName << " ";
+ }
+
+ output << "\n" << indent << indent << "if isActive=1 and Environment.Action=distribute_global;\n\n";
+
+ exportAgentReactions(proc);
+
+ output << "end Evolution\n\n";
+}
+
+
+void RSExporter::exportAgentReactions(const Process & proc) {
+ output << indent <<"--------------------------------------------------------------------------------\n"
+ << indent <<"-- Agent's reactions.\n"
+ << indent <<"--------------------------------------------------------------------------------\n\n";
+
+ // For each entity produced accumulate all the ways it may be produced
+
+ map sources;
+
+ for (const auto & reaction : rs->proc_reactions[proc]) {
+ string rctCond = reactionCondToStr(reaction);
+
+ for (const Entity & e : reaction.prod) {
+ string rctProduct = rs->getEntityName(e);
+
+ if (sources.count(rctProduct))
+ sources[rctProduct] = appendCondition(sources[rctProduct], rctCond);
+ else
+ sources[rctProduct] = rctCond;
+ }
+ }
+
+ // Output a single formula combining all the agent's reactions
+
+ bool moreReactions {false};
+
+ for (const auto & entry : sources) {
+ if (moreReactions)
+ output << indent << indent << "and\n";
+ else
+ moreReactions = true;
+
+ output << indent << entry.first << "_out = " << entry.second << "\n";
+ }
+
+ output << indent << indent << "if isActive=1 and Environment.Action=produce;\n\n";
+}
+
+
+void RSExporter::exportAgentInitState(const Process & proc) {
+ string procName = rs->processes_ids[proc];
+
+ output << "\n\n" << indent << indent << "and " << procName << ".isActive=0";
+
+ for (const Entity & e : procInputs[proc]) {
+ output << "\n" << indent << indent << "and " << procName << "." << rs->entities_ids[e] << "_in=0";
+ }
+
+ for (const Entity & e : procOutputs[proc]) {
+ output << "\n" << indent << indent << "and " << procName << "." << rs->entities_ids[e] << "_out=0";
+ }
+}
+
+
+void RSExporter::parseFormulas() {
+ for (const auto & prop : drv->properties) {
+ formulas.push_back(formulaToStr(prop.second));
+ }
+}
+
+
+void RSExporter::exportEvaluation() {
+ output << "\nEvaluation\n";
+
+ for (const auto & a : atoms)
+ output << indent << a.second << " if " << a.first << ";\n";
+
+ output << "end Evaluation" << endl;
+}
+
+
+void RSExporter::exportInitStates() {
+ output << "\nInitStates\n";
+
+ exportEnvironmentInitState();
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc)
+ exportAgentInitState(proc);
+
+ output << ";\n"
+ << "end InitStates" << endl;
+}
+
+
+void RSExporter::exportFormulae() {
+ output << "\nFormulae\n";
+
+ for (const string & fStr : formulas)
+ output << indent << fStr << ";\n";
+
+ output << "end Formulae" << endl;
+}
+
+
+/* State constraints are required to be in conjunctive normal form. */
+string RSExporter::stateConstrToStr(const StateConstr * guard) {
+ string expr;
+
+ switch (guard->oper) {
+ case STC_PV:
+ return guard->proc_name + "_" + guard->entity_name + "=1";
+
+ case STC_TF:
+ return guard->tf ? "true" : "false";
+
+ case STC_AND:
+ return "(" + stateConstrToStr(guard->arg[0]) + " and " + stateConstrToStr(guard->arg[1]) + ")";
+
+ case STC_OR:
+ return "(" + stateConstrToStr(guard->arg[0]) + " or " + stateConstrToStr(guard->arg[1]) + ")";
+
+ case STC_NOT:
+ expr = stateConstrToStr(guard->arg[0]);
+ return expr.substr(0, expr.length()-1) + "0";
+
+ default:
+ return "??";
+ assert(0);
+ }
+}
+
+
+string RSExporter::reactionCondToStr(const Reaction & reaction) {
+ string cond;
+
+ bool moreRctts {false};
+
+ for (const Entity & e : reaction.rctt) {
+ if (moreRctts)
+ cond += "*";
+ else
+ moreRctts = true;
+
+ cond += rs->getEntityName(e) + "_in";
+ }
+
+ for (const Entity & e : reaction.inhib)
+ cond += "*(1-" + rs->getEntityName(e) + "_in)";
+
+ return cond;
+}
+
+
+string RSExporter::appendCondition(string & oldCond, string & newCond) {
+ return "(" + oldCond + ") + " + newCond + " - (" + oldCond + ") * " + newCond;
+}
+
+
+string RSExporter::formulaToStr(const FormRSCTLK * form) {
+ string varStr;
+ string labelStr;
+
+ switch (form->oper) {
+
+ case RSCTLK_PV: // propositional variable
+ varStr = form->proc_name + "." + form->entity_name + "_out=1";
+
+ if (atoms.find(varStr) == atoms.end()) {
+ labelStr = "atomic_" + to_string(atoms.size());
+ atoms[varStr] = labelStr;
+ }
+ else {
+ labelStr = atoms[varStr];
+ }
+
+ return labelStr;
+
+ case RSCTLK_AND:
+ return "(" + formulaToStr(form->arg[0]) + " and " + formulaToStr(form->arg[1]) + ")";
+
+ case RSCTLK_OR:
+ return "(" + formulaToStr(form->arg[0]) + " or " + formulaToStr(form->arg[1]) + ")";
+
+ case RSCTLK_XOR:
+ return "((" + formulaToStr(form->arg[0]) + " or " + formulaToStr(form->arg[1]) + ") and !(" +
+ formulaToStr(form->arg[0]) + " and " + formulaToStr(form->arg[1]) + "))";
+
+ case RSCTLK_NOT:
+ return "!(" + formulaToStr(form->arg[0]) +")";
+
+ case RSCTLK_IMPL:
+ return "(" + formulaToStr(form->arg[0]) + " -> " + formulaToStr(form->arg[1]) + ")";
+
+ case RSCTLK_EG: // Existential...
+ return "EG(" + formulaToStr(form->arg[0]) + ")";
+
+ case RSCTLK_EU:
+ return "E(" + formulaToStr(form->arg[0]) + " U " + formulaToStr(form->arg[1]) + ")";
+
+ case RSCTLK_EX:
+ return "EX(" + formulaToStr(form->arg[0]) + ")";
+
+ case RSCTLK_EF:
+ return "EF(" + formulaToStr(form->arg[0]) + ")";
+
+ case RSCTLK_AG: // Universal...
+ return "AG(" + formulaToStr(form->arg[0]) + ")";
+
+ case RSCTLK_AU:
+ return "A(" + formulaToStr(form->arg[0]) + " U " + formulaToStr(form->arg[1]) + ")";
+
+ case RSCTLK_AX:
+ return "AX(" + formulaToStr(form->arg[0]) + ")";
+
+ case RSCTLK_AF:
+ return "AF(" + formulaToStr(form->arg[0]) + ")";
+
+ case RSCTLK_UK: // Epistemic operators
+ return "K(" + form->getSingleAgent() + ", " + formulaToStr(form->arg[0]) + ")";
+
+ default:
+ assert(0);
+ return "??";
+ }
+}
diff --git a/reactics-bdd/export.hh b/reactics-bdd/export.hh
new file mode 100644
index 0000000..b8b0714
--- /dev/null
+++ b/reactics-bdd/export.hh
@@ -0,0 +1,55 @@
+
+#ifndef EXPORT_HH
+#define EXPORT_HH
+
+#include
+#include "types.hh"
+#include "mc.hh"
+#include "rs.hh"
+#include "rsin_driver.hh"
+
+
+class RSExporter {
+public:
+ RSExporter(RctSys *rs, rsin_driver *drv, std::ostream & outStream = std::cout);
+
+ void exportToISPL();
+private:
+ RctSys *rs;
+ rsin_driver *drv;
+ string indent {" "};
+ std::ostream &output;
+
+ void exportEnvironment();
+ void exportAgent(const Process & proc);
+
+ void exportEnvironmentVars();
+ void exportEnvironmentActions();
+ void exportEnvironmentProtocol();
+ void exportEnvironmentEvolution();
+ void exportEnvironmentInitState();
+
+ void exportAgentVars(const Process & proc);
+ void exportAgentProtocol(const Process & proc);
+ void exportAgentEvolution(const Process & proc);
+ void exportAgentReactions(const Process & proc);
+ void exportAgentInitState(const Process & proc);
+
+ void parseFormulas();
+ void exportEvaluation();
+ void exportInitStates();
+ void exportFormulae();
+
+ std::string stateConstrToStr(const StateConstr * guard);
+ std::string reactionCondToStr(const Reaction & reaction);
+ std::string appendCondition(std::string & oldCond, std::string & newCond);
+ std::string formulaToStr(const FormRSCTLK * form);
+
+ EntitiesForProc procInputs;
+ EntitiesForProc procOutputs;
+
+ std::map atoms;
+ std::vector formulas;
+};
+
+#endif
\ No newline at end of file
diff --git a/reactics-bdd/formrsctlk.hh b/reactics-bdd/formrsctlk.hh
index 2eae014..b9091fd 100644
--- a/reactics-bdd/formrsctlk.hh
+++ b/reactics-bdd/formrsctlk.hh
@@ -62,9 +62,12 @@ using std::endl;
typedef std::set Agents_f;
class StateConstr;
+class RSExporter;
class FormRSCTLK
{
+ friend class RSExporter;
+
Oper oper;
FormRSCTLK *arg[2];
std::string entity_name;
diff --git a/reactics-bdd/reactics.cc b/reactics-bdd/reactics.cc
index 6b1a3bd..fddd010 100644
--- a/reactics-bdd/reactics.cc
+++ b/reactics-bdd/reactics.cc
@@ -16,6 +16,7 @@ int main(int argc, char **argv)
bool rstl_model_checking = false;
bool reach_states = false;
bool reach_states_succ = false;
+ bool export_to_ispl = false;
bool bmc = true;
bool benchmarking = false;
bool dump_help_message = false;
@@ -31,7 +32,7 @@ int main(int argc, char **argv)
int c;
int option_index = 0;
- while ((c = getopt_long(argc, argv, "c:bBmpPrsStTvxzXh", long_options,
+ while ((c = getopt_long(argc, argv, "c:bBmpPrsStTvxzXhe", long_options,
&option_index)) != -1) {
switch (c) {
case 0:
@@ -52,6 +53,10 @@ int main(int argc, char **argv)
break;
+ case 'e':
+ export_to_ispl = true;
+ break;
+
//case 'b':
// printf("-b with %s\n", optarg);
// break;
@@ -136,8 +141,8 @@ int main(int argc, char **argv)
}
if (!(reach_states || reach_states_succ || rstl_model_checking
- || show_reactions || print_parsed_sys)) {
- FERROR("No task specified: -c, -P, -r, or -s needs to be used");
+ || show_reactions || print_parsed_sys || export_to_ispl)) {
+ FERROR("No task specified: -c, -P, -r, -s, or -e needs to be used");
}
if (opts->verbose > 0) {
@@ -171,7 +176,7 @@ int main(int argc, char **argv)
rs.printSystem();
}
- if (reach_states || reach_states_succ || rstl_model_checking) {
+ if (reach_states || reach_states_succ || rstl_model_checking || export_to_ispl) {
SymRS srs(&rs, opts);
ModelChecker mc(&srs, opts);
@@ -184,6 +189,11 @@ int main(int argc, char **argv)
mc.printReachWithSucc();
}
+ if (export_to_ispl) {
+ RSExporter exp(&rs, &driver);
+ exp.exportToISPL();
+ }
+
if (rstl_model_checking) {
if (bmc) {
cout << "Using BDD-based Bounded Model Checking" << endl;
@@ -268,6 +278,8 @@ void print_help(std::string path_str)
<< " Benchmarking options:" << endl
<< " -m -- measure and display time and memory usage" << endl
<< " -B -- display an easy to parse summary (enables -m)" << endl
+ << " -e -- exports to ISPL (MCMAS input format)" << endl
+
<< endl;
}
diff --git a/reactics-bdd/reactics.hh b/reactics-bdd/reactics.hh
index c1e0d7c..59a1455 100644
--- a/reactics-bdd/reactics.hh
+++ b/reactics-bdd/reactics.hh
@@ -18,6 +18,7 @@
#include "rsin_driver.hh"
#include "options.hh"
#include "memtime.hh"
+#include "export.hh"
#define VERSION "2.0"
//#define AUTHOR "Artur Meski "
diff --git a/reactics-bdd/rs.hh b/reactics-bdd/rs.hh
index 45dd971..27ace9b 100644
--- a/reactics-bdd/rs.hh
+++ b/reactics-bdd/rs.hh
@@ -28,6 +28,7 @@ class RctSys
{
friend class SymRS;
friend class SymRSstate;
+ friend class RSExporter;
public:
RctSys(void);
diff --git a/reactics-bdd/stateconstr.hh b/reactics-bdd/stateconstr.hh
index 31a038b..c6542ad 100644
--- a/reactics-bdd/stateconstr.hh
+++ b/reactics-bdd/stateconstr.hh
@@ -23,9 +23,12 @@
#define STC_IS_VALID(a) (STC_COND_1ARG(a) || STC_COND_2ARG(a) || (a) == STC_PV || (a) == STC_TF)
class SymRS;
+class RSExporter;
class StateConstr
{
+ friend class RSExporter;
+
Oper oper;
StateConstr *arg[2];
std::string entity_name;
--
2.53.0
From f86316cdbd4f3b6dee490400d46c45c14afc2def Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Tue, 29 Apr 2025 13:13:50 +0100
Subject: [PATCH 05/12] Context Automaton states in transitions (#2)
---
reactics-bdd/mc.cc | 6 +++---
reactics-bdd/symrs.cc | 40 ++++++++++++++++++++++++++++++++++++++++
reactics-bdd/symrs.hh | 2 ++
3 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/reactics-bdd/mc.cc b/reactics-bdd/mc.cc
index 88f5e36..d1c7b4c 100644
--- a/reactics-bdd/mc.cc
+++ b/reactics-bdd/mc.cc
@@ -200,13 +200,13 @@ void ModelChecker::printReachWithSucc(void)
t = unproc.PickOneMinterm(*pv);
if (opts->backend_mode)
{
- cout << "G " << srs->decodedRctSysStateToStr(t) << endl;
+ cout << "G " << srs->decodedRctSysStateWithCtxAutToStr(t) << endl;
}
else
{
- cout << "Successors of " << srs->decodedRctSysStateToStr(t) << ":" << endl;
+ cout << "Successors of " << srs->decodedRctSysStateWithCtxAutToStr(t) << ":" << endl;
}
- srs->printDecodedRctSysStates(getSucc(t));
+ srs->printDecodedRctSysWithCtxAutStates(getSucc(t));
unproc -= t;
}
diff --git a/reactics-bdd/symrs.cc b/reactics-bdd/symrs.cc
index 98726e3..d99b38a 100644
--- a/reactics-bdd/symrs.cc
+++ b/reactics-bdd/symrs.cc
@@ -567,6 +567,21 @@ std::string SymRS::decodedRctSysStateToStr(const BDD &state)
return s;
}
+std::string SymRS::decodedRctSysStateWithCtxAutToStr(const BDD &state)
+{
+ std::string s = "( ";
+ s += decodedRctSysStateToStr(state) + " ";
+ for (const auto &aut_state : rs->ctx_aut->states_ids) {
+ const auto state_id = rs->ctx_aut->getStateID(aut_state);
+ if (!(encCtxAutState(state_id) * state).IsZero()) {
+ s += aut_state + " ";
+ break;
+ }
+ }
+ s += ")";
+ return s;
+}
+
void SymRS::printDecodedRctSysStates(const BDD &states)
{
BDD unproc = states;
@@ -591,6 +606,31 @@ void SymRS::printDecodedRctSysStates(const BDD &states)
}
}
+void SymRS::printDecodedRctSysWithCtxAutStates(const BDD &states)
+{
+ BDD unproc = states;
+
+ while (!unproc.IsZero()) {
+ BDD t = unproc.PickOneMinterm(*pv);
+ if (opts->backend_mode)
+ {
+ cout << "s " << decodedRctSysStateWithCtxAutToStr(t) << endl;
+ }
+ else
+ {
+ cout << decodedRctSysStateWithCtxAutToStr(t) << endl;
+ }
+
+ if (opts->verbose > 9) {
+ BDD_PRINT(t);
+ cout << endl;
+ }
+
+ unproc -= t;
+ }
+}
+
+
DecompReactions SymRS::getProductionConditions(Process proc_id)
{
DecompReactions dr;
diff --git a/reactics-bdd/symrs.hh b/reactics-bdd/symrs.hh
index 669910e..bcb5999 100644
--- a/reactics-bdd/symrs.hh
+++ b/reactics-bdd/symrs.hh
@@ -319,7 +319,9 @@ class SymRS
BDD compContext(const BDD &context) const;
std::string decodedRctSysStateToStr(const BDD &state);
+ std::string decodedRctSysStateWithCtxAutToStr(const BDD &state);
void printDecodedRctSysStates(const BDD &states);
+ void printDecodedRctSysWithCtxAutStates(const BDD &states);
DecompReactions getProductionConditions(Process proc_id);
--
2.53.0
From 092fd94b56b3ce000fe39d6517effca7db08c8b4 Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Mon, 5 May 2025 14:26:40 +0100
Subject: [PATCH 06/12] README update: website
---
README.md | 88 +------------------------------------------------------
1 file changed, 1 insertion(+), 87 deletions(-)
diff --git a/README.md b/README.md
index 044b129..ee1bf24 100644
--- a/README.md
+++ b/README.md
@@ -6,91 +6,5 @@ The toolkit consists of two separate modules implementing:
* Methods implemented using binary decision diagrams (BDD) for storing and manipulating the state space of the verified system.
* Methods translating the verification problems into satisfiability modulo theories (SMT).
-# Installation
-
-## Installing everything manually
-
-(to be added)
-
-## Examples
-
-The `examples` directory contains sample input files.
-
-### Multi-agent reaction systems (rsCTLK verification)
-
-To quickly test the BDD module you can perform verification of the TGC controller consiting of three trains:
-
-```
-$ ./reactics bdd -c f1 examples/bdd/tgc.rs
-```
-
-The above command tests the formula labelled `f1` in the input file.
-
-### Reachability
-
-To test the SMT module you can perform reachability verification of the scalable chain system:
-
-Running the benchmark without any arguments tells us what parameters are available:
-
-```
-$ ./reactics smt examples/smt/scalable_chain.py
-
- ------------------------------------------------
- -- ReactICS -- Reaction Systems Model Checker --
- ------------------------------------------------
-
-arguments:
-```
-
-We may execute the benchmark for `chainLen=2`, `maxConc=3`, and `formulaNumber=1` using the following command:
-
-```
-$ ./reactics smt examples/smt/chain_reaction.py 2 3 1
-```
-
-### rsLTL verification
-
-To test the SMT module for rsLTL verification the scalable chain system benchmark may be used.
-
-
-
-```
-$ ./reactics smt examples/smt/scalable_chain.py 2 5 1
-```
-
-
-
-### Reaction synthesis
-
-To test the reaction synthesis approach on a mutual exclution protocol modelling three processes, run the following command (three processes, parametric verification, result optimised with OptSMT):
-
-```
-$ ./reactics smt examples/smt/mutex_param.py 3 p -o
-```
-
-To check the available parameters for the benchmark, we run it with `-h`:
-
-```
-$ ./reactics smt examples/smt/mutex_param.py -h
-
- ------------------------------------------------
- -- ReactICS -- Reaction Systems Model Checker --
- ------------------------------------------------
-
-usage: mutex_param.py [-h] [-v] [-o] scaling {p,np-p,np-np}
-
-positional arguments:
- scaling scaling parameter value
- {p,np-p,np-np} Selects the mode: p - parameter synthesis (parametric
- implementation), np-p - non-parametric with parametric
- implementation (with the parameters substituted), np-np -
- non-parametric with non-parametric implementation
- (parameters substituted)
-
-optional arguments:
- -h, --help show this help message and exit
- -v, --verbose turn verbosity on
- -o, --optimise minimise the parametric computation result
-```
-
+See: https://reactics.org
--
2.53.0
From 3237c2eae5e0e5a195f8e3d6a21e80de6eb68a21 Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Mon, 5 May 2025 14:27:07 +0100
Subject: [PATCH 07/12] Clean-up
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ee1bf24..d9f809e 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,8 @@
Reaction Systems Verification Toolkit
The toolkit consists of two separate modules implementing:
-* Methods implemented using binary decision diagrams (BDD) for storing and manipulating the state space of the verified system.
+
+* Methods using binary decision diagrams (BDD) for storing and manipulating the state space of the verified system.
* Methods translating the verification problems into satisfiability modulo theories (SMT).
See: https://reactics.org
--
2.53.0
From fafe8dbe07e346907fa38da36f9ebf9486ca91f2 Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Thu, 8 May 2025 19:41:27 +0100
Subject: [PATCH 08/12] Optimisations are now enabled by default
---
reactics-bdd/options.hh | 6 +++---
reactics-bdd/reactics.cc | 23 +++++++++++------------
2 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/reactics-bdd/options.hh b/reactics-bdd/options.hh
index 2c05c49..4428e9f 100644
--- a/reactics-bdd/options.hh
+++ b/reactics-bdd/options.hh
@@ -24,10 +24,10 @@ class Options
show_progress = false;
measure = false;
- part_tr_rel = false;
+ part_tr_rel = true;
- reorder_reach = false;
- reorder_trans = false;
+ reorder_reach = true;
+ reorder_trans = true;
backend_mode = false;
diff --git a/reactics-bdd/reactics.cc b/reactics-bdd/reactics.cc
index fddd010..c86fec5 100644
--- a/reactics-bdd/reactics.cc
+++ b/reactics-bdd/reactics.cc
@@ -32,7 +32,7 @@ int main(int argc, char **argv)
int c;
int option_index = 0;
- while ((c = getopt_long(argc, argv, "c:bBmpPrsStTvxzXhe", long_options,
+ while ((c = getopt_long(argc, argv, "c:bBmpPrsStTvXheEG", long_options,
&option_index)) != -1) {
switch (c) {
case 0:
@@ -102,13 +102,13 @@ int main(int argc, char **argv)
opts->verbose++;
break;
- case 'x':
- opts->part_tr_rel = true;
+ case 'E':
+ opts->reorder_reach = false;
+ opts->reorder_trans = false;
break;
- case 'z':
- opts->reorder_reach = true;
- opts->reorder_trans = true;
+ case 'G':
+ opts->part_tr_rel = false;
break;
case 'X':
@@ -266,15 +266,14 @@ void print_help(std::string path_str)
<< endl
<< endl << " OTHER:" << endl
<< " -b -- disable bounded model checking (BMC) heuristic" << endl
- << " -x -- use partitioned transition relation" <<
- endl
- << " -z -- use reordering of the BDD variables" << endl
- << " -v -- verbose (use more than once to increase verbosity)" <<
- endl
+ << " -v -- verbose (use more than once to increase verbosity)" << endl
<< " -p -- show progress (where possible)" << endl
- << endl
<< " -X -- backend mode (makes output parsing easier)" << endl
<< endl
+ << " Optimisations:" << endl
+ << " -E -- disable auto-reordering optimisation of BDDs" << endl
+ << " -G -- disable partitioned transition relation optimisation" << endl
+ << endl
<< " Benchmarking options:" << endl
<< " -m -- measure and display time and memory usage" << endl
<< " -B -- display an easy to parse summary (enables -m)" << endl
--
2.53.0
From 90c8e4f4c071cab408cd1118f830596badf713ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Pi=C4=85tkowski?=
Date: Wed, 28 May 2025 16:21:56 +0200
Subject: [PATCH 09/12] Added time and memory measurement for windows. (#4)
---
reactics-bdd/memtime.hh | 43 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/reactics-bdd/memtime.hh b/reactics-bdd/memtime.hh
index 992cbf0..69eac41 100644
--- a/reactics-bdd/memtime.hh
+++ b/reactics-bdd/memtime.hh
@@ -5,7 +5,12 @@
#define __MEMTIME_H__
#include
+#ifdef __WIN32__
+#include
+#include
+#else
#include
+#endif
#if defined(__APPLE__)
#include
#endif
@@ -17,6 +22,25 @@ using namespace std;
typedef long long int64;
+#ifdef __WIN32__
+
+static inline double cpuTime()
+{
+ FILETIME createTime, exitTime, kernelTime, userTime;
+
+ if (!GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime)) {
+ return 0.0;
+ }
+
+ ULARGE_INTEGER u;
+ u.LowPart = userTime.dwLowDateTime;
+ u.HighPart = userTime.dwHighDateTime;
+
+ return (double)u.QuadPart / 10e6;
+}
+
+#else
+
static inline double cpuTime(void)
{
struct rusage ru;
@@ -24,6 +48,8 @@ static inline double cpuTime(void)
return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000;
}
+#endif
+
static inline int memReadStat(void)
{
char name[256];
@@ -47,11 +73,28 @@ static inline int memReadStat(void)
return value;
}
+
+#ifdef __WIN32__
+
+static inline int64 memUsedInt64()
+{
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
+ return (int64) pmc.WorkingSetSize;
+ }
+
+ return 0;
+}
+
+#else
+
static inline int64 memUsedInt64()
{
return (int64)memReadStat() * (int64)getpagesize();
}
+#endif
+
#if defined(__APPLE__)
static inline double memUsed()
--
2.53.0
From 363446821e8896b22be6d8ce0400f7dbb61de0e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Pi=C4=85tkowski?=
Date: Wed, 4 Jun 2025 20:21:51 +0200
Subject: [PATCH 10/12] Released version of GUI (#5)
* The most recent version of ReactICS GUI.
* Small fixes + folder structure update + help file included.
* Added export to XML (for data sharing with GUI)
* Released GUI version.
* Automatic update of the reactants set panel + help content.
---
reactics-bdd/export.cc | 103 +++
reactics-bdd/export.hh | 2 +
reactics-bdd/reactics.cc | 17 +-
.../help/img/ctx-automaton-edge-details.png | Bin 0 -> 7886 bytes
.../help/img/ctx-automaton-panel.png | Bin 0 -> 3741 bytes
.../help/img/ctx-automaton-state.png | Bin 0 -> 5242 bytes
.../img/ctx-automaton-transition-edit.png | Bin 0 -> 12138 bytes
reactics-gui/resources/help/index.html | 321 +++++++
reactics-gui/src/META-INF/MANIFEST.MF | 3 +
.../reactics/ContextAutomatonEditor.java | 693 +++++++++++++++
.../reactics/ContextAutomatonGraph.java | 438 ++++++++++
.../reactics/CumulatedReactionsViewer.java | 13 +
.../mat/martinp/reactics/FormulaEditor.java | 410 +++++++++
.../umk/mat/martinp/reactics/HelpWindow.java | 125 +++
.../mat/martinp/reactics/ProcessEditor.java | 427 ++++++++++
.../martinp/reactics/ReactantSetPanel.java | 49 ++
.../umk/mat/martinp/reactics/ReacticsGUI.java | 791 ++++++++++++++++++
.../mat/martinp/reactics/ReacticsRuntime.java | 198 +++++
.../reactics/ReactionSystemEditor.java | 409 +++++++++
.../martinp/reactics/TransitionEditor.java | 208 +++++
.../reactics/TransitionSystemViewer.java | 367 ++++++++
21 files changed, 4571 insertions(+), 3 deletions(-)
create mode 100644 reactics-gui/resources/help/img/ctx-automaton-edge-details.png
create mode 100644 reactics-gui/resources/help/img/ctx-automaton-panel.png
create mode 100644 reactics-gui/resources/help/img/ctx-automaton-state.png
create mode 100644 reactics-gui/resources/help/img/ctx-automaton-transition-edit.png
create mode 100644 reactics-gui/resources/help/index.html
create mode 100644 reactics-gui/src/META-INF/MANIFEST.MF
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonEditor.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonGraph.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/CumulatedReactionsViewer.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/FormulaEditor.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/HelpWindow.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ProcessEditor.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ReactantSetPanel.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsGUI.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsRuntime.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/ReactionSystemEditor.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionEditor.java
create mode 100644 reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionSystemViewer.java
diff --git a/reactics-bdd/export.cc b/reactics-bdd/export.cc
index 6682cec..b8fa322 100644
--- a/reactics-bdd/export.cc
+++ b/reactics-bdd/export.cc
@@ -808,3 +808,106 @@ string RSExporter::formulaToStr(const FormRSCTLK * form) {
return "??";
}
}
+
+
+void RSExporter::exportToXML() {
+ output << "\n"
+ << indent << "\n";
+
+ for (Process proc=0; procgetNumberOfProcesses(); ++proc) {
+ output << indent << indent << "processes_ids[proc] << "\">\n";
+
+ for (const auto & reaction : rs->proc_reactions[proc]) {
+ output << indent << indent << indent << "\n";
+
+ for (const Entity & e : reaction.rctt) {
+ output << indent << indent << indent << indent
+ << "" << rs->getEntityName(e) << "\n";
+ }
+
+ for (const Entity & e : reaction.inhib) {
+ output << indent << indent << indent << indent
+ << "" << rs->getEntityName(e) << "\n";
+ }
+
+ for (const Entity & e : reaction.prod) {
+ output << indent << indent << indent << indent
+ << "" << rs->getEntityName(e) << "\n";
+ }
+
+ output << indent << indent << indent << "\n";
+ }
+
+ output << indent << indent << "\n";
+ }
+
+ output << indent << "\n";
+
+ output << indent << "\n";
+
+ for (unsigned stId=0; stIdctx_aut->states_ids.size(); ++stId) {
+ if (rs->ctx_aut->states_ids[stId] == "T")
+ continue;
+
+ output << indent << indent << "ctx_aut->states_ids[stId] << "\" x=\"0\" y=\"0\"";
+
+ if (rs->ctx_aut->init_state_defined && stId == rs->ctx_aut->init_state_id)
+ output << " initial=\"true\"";
+
+ output << "/>\n";
+ }
+
+ vector\n";
+
+ output << indent << "\n";
+
+ for (const auto & prop : drv->properties) {
+ string fStr = "";
+
+ for (const char ch : prop.second->toStr()) {
+ if (ch == '<')
+ fStr += "<";
+ else if (ch =='>')
+ fStr += ">";
+ else
+ fStr += ch;
+ }
+
+ output << indent << indent << indent << ""
+ << fStr << "\n";
+
+ }
+
+ output << indent << "\n";
+
+ output << "" << endl;
+}
diff --git a/reactics-bdd/export.hh b/reactics-bdd/export.hh
index b8b0714..bc7d3a7 100644
--- a/reactics-bdd/export.hh
+++ b/reactics-bdd/export.hh
@@ -14,6 +14,8 @@ public:
RSExporter(RctSys *rs, rsin_driver *drv, std::ostream & outStream = std::cout);
void exportToISPL();
+ void exportToXML();
+
private:
RctSys *rs;
rsin_driver *drv;
diff --git a/reactics-bdd/reactics.cc b/reactics-bdd/reactics.cc
index c86fec5..90e1298 100644
--- a/reactics-bdd/reactics.cc
+++ b/reactics-bdd/reactics.cc
@@ -17,6 +17,7 @@ int main(int argc, char **argv)
bool reach_states = false;
bool reach_states_succ = false;
bool export_to_ispl = false;
+ bool export_to_xml = false;
bool bmc = true;
bool benchmarking = false;
bool dump_help_message = false;
@@ -32,7 +33,7 @@ int main(int argc, char **argv)
int c;
int option_index = 0;
- while ((c = getopt_long(argc, argv, "c:bBmpPrsStTvXheEG", long_options,
+ while ((c = getopt_long(argc, argv, "c:bBmpPrsStTvXheyEG", long_options,
&option_index)) != -1) {
switch (c) {
case 0:
@@ -57,6 +58,10 @@ int main(int argc, char **argv)
export_to_ispl = true;
break;
+ case 'y':
+ export_to_xml = true;
+ break;
+
//case 'b':
// printf("-b with %s\n", optarg);
// break;
@@ -141,7 +146,7 @@ int main(int argc, char **argv)
}
if (!(reach_states || reach_states_succ || rstl_model_checking
- || show_reactions || print_parsed_sys || export_to_ispl)) {
+ || show_reactions || print_parsed_sys || export_to_ispl || export_to_xml)) {
FERROR("No task specified: -c, -P, -r, -s, or -e needs to be used");
}
@@ -176,7 +181,7 @@ int main(int argc, char **argv)
rs.printSystem();
}
- if (reach_states || reach_states_succ || rstl_model_checking || export_to_ispl) {
+ if (reach_states || reach_states_succ || rstl_model_checking || export_to_ispl || export_to_xml) {
SymRS srs(&rs, opts);
ModelChecker mc(&srs, opts);
@@ -194,6 +199,11 @@ int main(int argc, char **argv)
exp.exportToISPL();
}
+ if (export_to_xml) {
+ RSExporter exp(&rs, &driver);
+ exp.exportToXML();
+ }
+
if (rstl_model_checking) {
if (bmc) {
cout << "Using BDD-based Bounded Model Checking" << endl;
@@ -269,6 +279,7 @@ void print_help(std::string path_str)
<< " -v -- verbose (use more than once to increase verbosity)" << endl
<< " -p -- show progress (where possible)" << endl
<< " -X -- backend mode (makes output parsing easier)" << endl
+ << " -y -- export to XML (for GUI tool)" << endl
<< endl
<< " Optimisations:" << endl
<< " -E -- disable auto-reordering optimisation of BDDs" << endl
diff --git a/reactics-gui/resources/help/img/ctx-automaton-edge-details.png b/reactics-gui/resources/help/img/ctx-automaton-edge-details.png
new file mode 100644
index 0000000000000000000000000000000000000000..858df8edb0b298a34fba4b26bdca58115c2306e7
GIT binary patch
literal 7886
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
r%dT(q_&jS7f3nkF*
literal 0
HcmV?d00001
diff --git a/reactics-gui/resources/help/img/ctx-automaton-panel.png b/reactics-gui/resources/help/img/ctx-automaton-panel.png
new file mode 100644
index 0000000000000000000000000000000000000000..2fdd1f8ab1fceb30a2f6e0b6810de4cece4d2761
GIT binary patch
literal 3741
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
Xl%7gson
literal 0
HcmV?d00001
diff --git a/reactics-gui/resources/help/img/ctx-automaton-state.png b/reactics-gui/resources/help/img/ctx-automaton-state.png
new file mode 100644
index 0000000000000000000000000000000000000000..fbafe425d14eec1d1ec0ee76b5d23eed0329860f
GIT binary patch
literal 5242
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
literal 0
HcmV?d00001
diff --git a/reactics-gui/resources/help/img/ctx-automaton-transition-edit.png b/reactics-gui/resources/help/img/ctx-automaton-transition-edit.png
new file mode 100644
index 0000000000000000000000000000000000000000..888e93da73a94bb6d5706ab6c23e883203d1eb46
GIT binary patch
literal 12138
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
literal 0
HcmV?d00001
diff --git a/reactics-gui/resources/help/index.html b/reactics-gui/resources/help/index.html
new file mode 100644
index 0000000..d40c9a4
--- /dev/null
+++ b/reactics-gui/resources/help/index.html
@@ -0,0 +1,321 @@
+
+
+
+
+ ReactICS GUI Help
+
+
+
+
+ReactICS - Distributed Reaction Systems Model Checker
+
+
+
+
+ ReactICS is a toolkit that allows for verification of Reaction Systems.
+ The toolkit consists of two separate modules implementing:
+
+ - Methods using binary decision diagrams (BDD)
+ for storing and manipulating the state space of the verified system.
+ - Methods translating the verification problems into satisfiability modulo theories (SMT).
+
+
+
+ ReactICS GUI offers a graphical user interface allowing for editing the reaction system details
+ and model checking using BDD module.
+
+
+ More on ReactICS can be found at The Reactics Webpage.
+
+
+
+
+Table of contents
+
+
+
+
+
+Reaction Systems
+
+
+
+ Reaction Systems are a formalism inspired by the functioning of living cells.
+ They allow for specifying and analysing computational processes in which reactions operate on sets of molecules.
+
+
+
+ The behaviour of a reaction system is determined by the interactions of its reactions, which are based on
+ the mechanisms of facilitation and inhibition.
+ The formal treatment of reaction systems is qualitative and there is no direct representation of the number
+ of molecules involved in reactions.
+
+
+
+ A Distributed Reaction System is a computational model in which multiple autonomous processes (aka. agents)
+ interact locally through rule-based reactions.
+ Each agent operates independently (without any central controller—coordination),
+ processing inputs and producing outputs based on predefined reactions,
+ and their collective behavior leads to complex system dynamics.
+
+
+
+ More on reaction system can be found at The Reaction System Webpage
+
+
+Back to top
+
+
+
+Application functions
+
+
+
+ ReactICS GUI allows to:
+
+ - Edit the reaction system structure (the number of processes/agents, reactions for each process/agent, etc.)
+ - Edit the context automaton structure (adding/removing states and transitions,
+ edit context for active processes/agents, setting guards for transitions, etc.).
+ - Preview of the state space represented as a transition system.
+ - Verification of properties defined by formulas using rsCTLK semantics.
+ - Save/load the reaction system structure to/from XML file.
+ - Export/import the reaction system to/from RSSL file.
+ - Export the reaction system structure and behaviour to ISPL file.
+
+
+
+Back to top
+
+
+
+
+
+Distributed reaction system editor
+
+
+
+
+ The structure of distributed reaction system can be edited using the following buttons from the distributed reaction system control panel:
+
+
+ - New process - Creates a new empty process.
+ - Remove - Removes selected processes.
+ - Copy - Makes copies of selected processes (together with all reactions).
+ - Rename - Renames a selected process. Note that process names should be unique.
+ - Move up - Moves selected process up the list of all processes.
+ - Move down - Moves selected process down the list of all processes.
+
+
+
+
+ The details of a single process can be edited using the following buttons in the process panel:
+
+
+ - Add reaction - Creates a new reaction. The details of the reaction should be specified in dialog shown below.
+ - Edit reaction - Allows to edit the details of a selected reaction using dialog shown below.
+ - Remove reaction - Removes a selected reaction.
+
+
+
+
+
+ Verification of formulas against a system specification is done after selecting desired formulas
+ and clicking Evaluate button.
+ The result of the verification (True/False), as well as time and memory used are presented next to each verified formula.
+
+
+
+ Clicking the Reset button resets the status of the model checking.
+ Note that updating the reaction system specification (the list of processes, details of the reactions or the content automaton structure)
+ the results of model checking, if any, are reset automatically.
+
+
+
+Back to top
+
+
+
+
+
+Context automaton editor
+
+
+
+ To create or edit the context automaton structure a proper edit mode should be set using the edit mode panel.
+
+
+
+
+
+ The type of the newly created object (state or edge) is set by checking the proper toggle button.
+ With no button checked, one can transform the net by moving states around, etc.
+ (see below for more details).
+
+
+
+
+States
+
+
+ A new state is created by left-clicking the edit area (the main part of the component).
+ As it is customary in automata theory, states are numbered separately starting from 1.
+ The types of states are distinguished by shape and colour:
+ the initial state is depicted as a square, all other states are depicted as circles.
+ Moreover, hovering the mouse above a state allows to see its full description containing both
+ its name and its (distinct) number.
+
+
+
+
+
+ The details of a state can be edited by right-clicking the existing
+ state and choosing the right option from the popup menu:
+
+ - Set as initial - Makes the selected state the initial state of the context automaton.
+ - Set label - Allows to set the custom label for the selected state. Note that state labels have to be unique.
+
+
+
+
+
+Edges
+
+
+ A directed edge between two states is created by left-clicking both its endpoints in correct order.
+ Each edge may contain several transitions, each of them defined by a context (additional entities
+ provided for active processes/agents) and a guard (a condition required by the transition to execute).
+ The list of transitions may be edited by right-clicking the existing
+ edge and choosing the Nondeterministic transitions popup menu.
+
+
+
+ Hovering the mouse above an edge allows to see the full list of transitions
+ including context and guards.
+
+
+
+
+
+Removing context automaton elements
+
+
+ Switching the toggle button Delete on allows removing context automaton elements.
+ Each left-clicked element is removed from the automaton grapn.
+ Moreover, the entire context automaton graph may be deleted by clicking the Clear button.
+
+
+
+
+Transforming the context automaton graph
+
+
+ Any state of the automaton may be picked and dragged around the edit area to obtain the desired graph shape.
+ It is also possible to select multiple states and move them together at the same time.
+ After CTRL + left-click on a particular state the entire automaton graph is shifted to be centered
+ on the clicked state.
+ Checking the Lock relative nodes positions checkbox locks the relative positions of the states.
+ In that case, dragging a single state moves the entire automaton graph.
+ Moreover, using mouse scroll the automaton graph view can be zoomed in and out.
+
+
+Back to top
+
+
+
+
+
+Transition system viewer
+
+
+
+ The component offers the visualisation of the transition system representing the state space of the reaction systems.
+ The initial state is depicted as a square, all other states are depicted as circles.
+ There is a directed edge connecting the state Si to the state Sj
+ if Sf is reachable from Si by a single computation step of the reaction system.
+
+
+
+ The component has two separate instances represented by two separate tabs in the application window.
+ The first visualises the complete state space of the reaction system,
+ where each state is represented by sets of entities possessed by each agent and the current context automaton state.
+ The second visualises the compressed state space, where context automaton states are ignored
+ (states are represented only by the sets of entities possessed by each agent).
+
+
+
+ Computation is done by pressing Update transition system button.
+ The reaction system structure should be loaded from the file or edited manually for the computation to be possible.
+
+
+
+ Note that no matter in which component the Update transition system button
+ is pressed, the transition system structure will be updated in both.
+
+
+
+ Hovering the mouse above a state allows to see its full description: entities possessed by each process/agent
+ and the context automaton state (not present in the case of the compressed graph).
+
+
+
+
+
+Transforming the transition system graph
+
+
+ Any state of the transition system may be picked and dragged around the edit area to obtain the desired graph shape.
+ It is also possible to select multiple states and move them together at the same time.
+ After CTRL + left-click on a particular state the entire transition system graph is shifted to be centered
+ on the clicked state.
+ Checking the Lock relative nodes positions checkbox locks the relative positions of the states.
+ In that case, dragging a single state moves the entire transition system graph.
+ Moreover, using mouse scroll the transition system graph view can be zoomed in and out.
+
+
+Back to top
+
+
+
+
+Model checking
+
+
+
+Model checking is handled by the component at the bottom of the application window.
+The properties of the analysed system are expressed as formulas following rsCTLK syntax.
+
+
+The list of formulas may be modified using the following buttons:
+
+ - Add formula - Creates an empty formula. The details of the formula should be specified in dialog shown below.
+ - Edit formula - Allows to edit the details of a selected formula using dialog shown below.
+ - Remove formulas - Removes selected formulas.
+
+
+
+
+
+Verification of formulas against a system specification is done after selecting desired formulas
+and clicking Evaluate button.
+The result of the verification (True/False), as well as time and memory used are presented next to each verified formula.
+
+
+
+Clicking the Reset button resets the status of the model checking.
+Note that updating the reaction system specification (the list of processes, details of the reactions or the content automaton structure)
+the results of model checking, if any, are reset automatically.
+
+
+Back to top
+
+
+
+
+
\ No newline at end of file
diff --git a/reactics-gui/src/META-INF/MANIFEST.MF b/reactics-gui/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b010a96
--- /dev/null
+++ b/reactics-gui/src/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: pl.umk.mat.martinp.reactics.ReacticsGUI
+
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonEditor.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonEditor.java
new file mode 100644
index 0000000..4c1ee59
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonEditor.java
@@ -0,0 +1,693 @@
+package pl.umk.mat.martinp.reactics;
+
+import edu.uci.ics.jung.algorithms.layout.FRLayout;
+import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
+import edu.uci.ics.jung.algorithms.layout.Layout;
+import edu.uci.ics.jung.algorithms.layout.StaticLayout;
+import edu.uci.ics.jung.algorithms.layout.util.Relaxer;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.util.Context;
+import edu.uci.ics.jung.graph.util.Pair;
+import edu.uci.ics.jung.visualization.VisualizationViewer;
+import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
+import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
+import edu.uci.ics.jung.visualization.decorators.EdgeShape;
+import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
+import edu.uci.ics.jung.visualization.picking.PickedState;
+import edu.uci.ics.jung.visualization.renderers.Renderer;
+import org.apache.commons.collections15.Transformer;
+import org.w3c.dom.Document;
+import javax.swing.*;
+import javax.swing.border.EtchedBorder;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.io.PrintWriter;
+import java.util.*;
+
+
+public class ContextAutomatonEditor extends JPanel implements RSObserver {
+ private enum EditorState {STATE, EDGE, DELETE, CLEAR, NONE}
+
+ private static final long serialVersionUID = 1L;
+
+ private ReactionSystem rs;
+
+ //---------------------------------------------------------------
+ // Visual configuration settings
+ //---------------------------------------------------------------
+ private static final Color pickedNodeColor = Color.yellow;
+ //private static final Color pickedEdgeColor = Color.red;
+ private static final Color pickedEdgeColor = Color.black;
+ private static final Color edgeColor = Color.black;
+ private static final Color emptyEdgeColor = Color.lightGray;
+ private static final Font nodeLabelFont = new Font("Helvetica", Font.BOLD, 20);
+ private static final Font edgeLabelFont = new Font("Helvetica", Font.BOLD, 15);
+ private static final Font toolTipFont = new Font("Helvetica", Font.PLAIN, 14);
+ private static final float[] dash = {10.0f, 10.0f};
+ private static final Stroke basicEdgeStroke = new BasicStroke(3.0f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
+ private static final Stroke emptyEdgeStroke = new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, dash, 0.f);
+ private static final Stroke basicEdgeArrow = new BasicStroke(3.0f);
+ private static final Stroke emptyEdgeArrow = new BasicStroke(3.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10.0f, dash, 10.0f);
+
+ private static Dimension initialEditorSize;
+
+ private VisualizationViewer graphViewer;
+ private Layout gLayout;
+ DefaultModalGraphMouse graphMouse;
+ private ContextAutomatonGraph graph;
+
+ // Context menus for transitions, places and edges
+ private JPopupMenu stateContextMenu;
+ private JPopupMenu edgeContextMenu;
+ private TransitionEditor transitionEditor = null;
+
+ private EditorState edtState;
+ private Vector modeButtons;
+
+ private CAState lastPickedNode = null;
+
+
+ public ContextAutomatonEditor(Dimension size) {
+ this.setLayout(new BorderLayout());
+ initialEditorSize = size;
+
+ edtState = EditorState.NONE;
+ graph = new ContextAutomatonGraph();
+
+ createGraphEditor();
+ createModeButtonsPanel();
+ createContextMenus();
+
+ rs = ReactionSystem.getInstance();
+ }
+
+ public boolean isModified() { return graph.isModified(); }
+
+ public void clearModificationStatus() { graph.clearModificationStatus(); }
+
+ public String toRSSLString() {
+ return graph.toRSSLString();
+ }
+
+ public void exportToXML(PrintWriter output) {
+ updateNodesLocations();
+ graph.exportToXML(output);
+ }
+
+ public void loadFromXML(Document input) throws ContextAutomatonStructureError {
+ clear();
+ graph.loadFromXML(input);
+ }
+
+
+ public void recalculateCoordinates() {
+ Layout autoLayout = new FRLayout(graph, graphViewer.getSize());
+ autoLayout.initialize();
+
+ for (CAState state : graph.getVertices()) {
+ Point2D pos = autoLayout.transform(state);
+ state.updateLocation(pos);
+ }
+ }
+
+
+ public void clear() {
+ graph.clear();
+
+ for (EditorModeButton emb : modeButtons)
+ emb.setSelected(false);
+
+ graphViewer.repaint();
+ }
+
+ public void lockNodesPositions(boolean lock) {
+ if (lock)
+ graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
+ else
+ graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
+ }
+
+ public Set getReactantsSet() { return graph.getReactantsSet(); }
+
+ //--------------------------------------------------------------------------
+ // Creates visualization viewer for context automaton graph display/edition
+ //--------------------------------------------------------------------------
+ private void createGraphEditor() {
+ gLayout = new StaticLayout(graph,
+ new Transformer() {
+ public Point2D transform(CAState node) {
+ return node.getLocation();
+ }
+ });
+
+ graphViewer = new VisualizationViewer(gLayout, initialEditorSize) {
+ public JToolTip createToolTip() {
+ JToolTip tooltip = super.createToolTip();
+ tooltip.setFont(toolTipFont);
+ return tooltip;
+ }
+ };
+
+ // Nodes labels
+ graphViewer.getRenderContext().setVertexLabelTransformer(
+ new ToStringLabeller());
+ graphViewer.getRenderer().getVertexLabelRenderer()
+ .setPosition(Renderer.VertexLabel.Position.CNTR);
+ graphViewer.getRenderContext().setVertexFontTransformer(
+ new Transformer() {
+ public Font transform(CAState v) {
+ return nodeLabelFont;
+ }
+ });
+
+ // Nodes colour, background, etc.
+ graphViewer.getRenderContext().setVertexIconTransformer(new Transformer() {
+ public Icon transform(final CAState v) {
+ Color nodeColor = null;
+
+ if(graphViewer.getPickedVertexState().isPicked(v))
+ nodeColor = pickedNodeColor;
+ else
+ nodeColor = v.getFillColor();
+
+ return (v.isInitial() ? new SquareIcon(nodeColor, v.getWidth()) : new CircleIcon(nodeColor, v.getWidth()));
+
+ }});
+
+ // Edge color, shape and weight
+ graphViewer.getRenderContext().setEdgeDrawPaintTransformer(new Transformer() {
+ public Paint transform(CAEdge e) {
+ return edgeColor;
+ }
+ });
+
+ graphViewer.getRenderContext().setEdgeStrokeTransformer(new Transformer() {
+ public Stroke transform(CAEdge e) {
+ return basicEdgeStroke;
+ }
+ });
+
+ graphViewer.getRenderContext().setEdgeArrowStrokeTransformer(new Transformer() {
+ public Stroke transform(CAEdge e) {
+ return basicEdgeArrow;
+ }
+ });
+
+ graphViewer.getRenderContext().setArrowFillPaintTransformer(new Transformer() {
+ public Paint transform(CAEdge caEdge) {
+ if (caEdge.hasTransition())
+ return edgeColor;
+ else
+ return emptyEdgeColor;
+ }
+ });
+
+ graphViewer.getRenderContext().setEdgeShapeTransformer(new ConditionalEdgeShape<>(1.5f));
+
+ // Edge labels
+ graphViewer.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());
+ graphViewer.getRenderContext().getEdgeLabelRenderer().setRotateEdgeLabels(true);
+ graphViewer.getRenderContext().setLabelOffset(20);
+
+ graphViewer.getRenderContext().setEdgeFontTransformer(new Transformer() {
+ public Font transform(CAEdge CAEdge) {
+ return edgeLabelFont;
+ }
+ });
+
+ // For interactive graph editing
+ graphMouse = new DefaultModalGraphMouse();
+ graphViewer.setGraphMouse(graphMouse);
+
+ graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
+
+ graphViewer.setVertexToolTipTransformer(new Transformer() {
+ public String transform(CAState caState) {
+ return caState.getTooltipText();
+ }
+ });
+
+ graphViewer.setEdgeToolTipTransformer(new Transformer() {
+ public String transform(CAEdge caEdge) {
+ return caEdge.getTooltipText();
+ }
+ });
+
+ graphViewer.setBackground(Color.white);
+ graphViewer.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
+ graphViewer.addMouseListener(new EditorMouseListener());
+
+ add(graphViewer,BorderLayout.CENTER);
+ }
+
+ //---------------------------------------------------------------
+ // Create panel with buttons alowing editing mode choice
+ //---------------------------------------------------------------
+ private void createModeButtonsPanel() {
+ modeButtons = new Vector();
+ modeButtons.add(new EditorModeButton("STATE",new CircleIcon(CAState.baseFillColor, 15), EditorState.STATE, 0));
+ modeButtons.add(new EditorModeButton("EDGE", EditorState.EDGE, 1));
+ modeButtons.add(new EditorModeButton("DELETE", EditorState.DELETE, 2));
+ modeButtons.add(new EditorModeButton("CLEAR", EditorState.CLEAR, 3));
+
+ ModeButtonsListener mbListener = new ModeButtonsListener();
+
+ for(int i=0;i pickedVertexState = graphViewer.getPickedVertexState();
+ Set pickedNodes = pickedVertexState.getPicked();
+ Iterator it = pickedNodes.iterator();
+
+ if(!it.hasNext()) {
+ return;
+ }
+
+ CAState editedNode = it.next();
+ pickedVertexState.clear();
+
+ graph.setInitialState(editedNode);
+ }
+ });
+
+ JMenuItem setLabelItem = new JMenuItem("Set label");
+ setLabelItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ PickedState pickedVertexState = graphViewer.getPickedVertexState();
+ Set pickedNodes = pickedVertexState.getPicked();
+ Iterator it = pickedNodes.iterator();
+
+ if(!it.hasNext()) {
+ return;
+ }
+
+ CAState state = it.next();
+ pickedVertexState.clear();
+
+ showStateLabelEditDialog(state);
+ }
+ });
+
+ stateContextMenu.add(stateInitialItem);
+ stateContextMenu.add(setLabelItem);
+ stateContextMenu.setVisible(false);
+
+ edgeContextMenu = new JPopupMenu();
+ JMenuItem edgeLabelEditItem = new JMenuItem("Nondeterministic transitions");
+ edgeLabelEditItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ PickedState pickedEdgeState = graphViewer.getPickedEdgeState();
+ Set pickedEdges = pickedEdgeState.getPicked();
+ Iterator it = pickedEdges.iterator();
+
+ if(!it.hasNext()) {
+ return;
+ }
+
+ CAEdge editedEdge = it.next();
+ pickedEdgeState.clear();
+ showEdgeLabelEditDialog(editedEdge);
+ }
+ });
+
+ edgeContextMenu.add(edgeLabelEditItem);
+ edgeContextMenu.setVisible(false);
+
+ transitionEditor = new TransitionEditor();
+ }
+
+ private void showStateLabelEditDialog(CAState state) {
+ String idRegex = "[a-zA-Z][a-zA-Z_0-9:-]*";
+ boolean idOk = false;
+
+ do {
+ String newLabel = JOptionPane.showInputDialog(this, "State label", state.getLabel());
+
+ if (newLabel == null)
+ return;
+
+ newLabel = newLabel.trim();
+
+ if (!newLabel.matches(idRegex)) {
+ JOptionPane.showMessageDialog(this,
+ "State label should start with a letter and may contain only:
" +
+ "letters, numbers, colons (:), underscores (_) and hyphens (-)",
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ else {
+ idOk = true;
+ state.setLabel(newLabel);
+ }
+ }
+ while (!idOk);
+
+ graph.markAsModified();
+ this.repaint();
+ }
+
+ //---------------------------------------------------------------
+ // Creates a simple dialog window for editing edge labels
+ //---------------------------------------------------------------
+ private void showEdgeLabelEditDialog(CAEdge edge) {
+ Pair endPoints = graph.getEndpoints(edge);
+ transitionEditor.showTransitionEditDialog(this, edge.getTransitions(), endPoints.getFirst().getLabel(), endPoints.getSecond().getLabel());
+ graph.markAsModified();
+ graphViewer.repaint();
+ rs.notifyObservers();
+ }
+
+ /** Updates coordinates stored in each graph node (eg. after interactive graph modification).
+ * Called before each storing net structure to the file. */
+ private void updateNodesLocations() {
+ Collection nodes = graph.getVertices();
+
+ for(CAState n : nodes) {
+ n.updateLocation(gLayout.transform(n));
+ }
+ }
+
+ /** For better vertex placement */
+ private void relax() {
+ Layout layout = graphViewer.getGraphLayout();
+ layout.initialize();
+ layout.setSize(graphViewer.getSize());
+ Relaxer relaxer = graphViewer.getModel().getRelaxer();
+ if (relaxer != null) {
+ relaxer.stop();
+ relaxer.prerelax();
+ relaxer.relax();
+ }
+ }
+
+ public void onRSUpdate() {
+ repaint();
+ }
+
+ //---------------------------------------------------------------------------------------------
+
+
+ static class EditorModeButton extends JToggleButton {
+ private EditorState mode;
+ int seqNum;
+
+ public EditorModeButton(String label, EditorState mode) {
+ super(label);
+
+ this.mode = mode;
+ seqNum = 0;
+ }
+
+ public EditorModeButton(String label, EditorState mode, int seqNum) {
+ super(label);
+
+ this.mode = mode;
+ this.seqNum = seqNum;
+ }
+
+ public EditorModeButton(String label, Icon icon, EditorState mode, int seqNum) {
+ super(label, icon);
+
+ this.mode = mode;
+ this.seqNum = seqNum;
+ }
+
+ public EditorState getMode() {
+ return mode;
+ }
+
+ public int getSeqNum() {
+ return seqNum;
+ }
+ }
+
+
+ //---------------------------------------------------------------------------------------------
+
+
+ class ModeButtonsListener implements ActionListener {
+
+ public void actionPerformed(ActionEvent changeEvent) {
+ EditorModeButton emb = (EditorModeButton) changeEvent.getSource();
+
+ if(emb.isSelected()) {
+ int num = emb.getSeqNum();
+
+ for(int i=0;i pickSupport = null;
+ Layout layout = null;
+ CAState node = null;
+ CAEdge edge = null;
+
+ switch(edtState) {
+ case STATE:
+ try {
+ // Transform screen coordinates to layout coordinates.
+ // Necessary for possible automaton structure viewer scaling.
+ Point2D nodeLocation = graphViewer.getRenderContext().getMultiLayerTransformer().inverseTransform(clickPt);
+ graph.addState(new CAState(nodeLocation));
+ } catch (ContextAutomatonStructureError e) {
+ JOptionPane.showMessageDialog(null,
+ e.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+
+ case EDGE:
+ PickedState pickedVertexState = graphViewer.getPickedVertexState();
+
+ pickSupport = graphViewer.getPickSupport();
+ layout = graphViewer.getGraphLayout();
+ node = pickSupport.getVertex(layout, clickPt.getX(), clickPt.getY());
+
+ if (node != null) {
+ if (lastPickedNode == null) {
+ lastPickedNode = node;
+ } else {
+ graph.addEdge(lastPickedNode, node);
+ lastPickedNode = null;
+ pickedVertexState.pick(node,false);
+ }
+ } else {
+ lastPickedNode = null;
+ }
+
+ break;
+
+ case DELETE:
+ pickSupport = graphViewer.getPickSupport();
+ layout = graphViewer.getGraphLayout();
+ node = pickSupport.getVertex(layout, clickPt.getX(), clickPt.getY());
+ edge = pickSupport.getEdge(layout, clickPt.getX(), clickPt.getY());
+
+ if (node != null) {
+ Collection edges = graph.getIncidentEdges(node);
+
+ for (CAEdge e : edges)
+ rs.removeCAEdge(e);
+
+ graph.removeState(node);
+ }
+ else if (edge != null) {
+ graph.removeEdge(edge);
+ rs.removeCAEdge(edge);
+ }
+
+ break;
+
+ case NONE:
+ default:
+ break;
+ }
+
+ me.consume();
+ }
+
+ private void rightButtonClicked(MouseEvent me) {
+ GraphElementAccessor pickSupport = graphViewer.getPickSupport();
+ Layout layout = graphViewer.getGraphLayout();
+ CAState node = pickSupport.getVertex(layout, me.getX(), me.getY());
+ CAEdge edge = pickSupport.getEdge(layout, me.getX(), me.getY());
+
+ if (node != null) {
+ PickedState pickedVertexState = graphViewer.getPickedVertexState();
+ pickedVertexState.clear();
+ pickedVertexState.pick(node,true);
+ stateContextMenu.show(graphViewer, me.getX(), me.getY());
+ }
+ else if (edge != null) {
+ PickedState pickedEdgeState = graphViewer.getPickedEdgeState();
+ pickedEdgeState.clear();
+ pickedEdgeState.pick(edge, true);
+ edgeContextMenu.show(graphViewer, me.getX(), me.getY());
+ }
+
+ me.consume();
+ }
+
+ };
+
+ //---------------------------------------------------------------------------------------------
+ // For drawing the initial state
+ //---------------------------------------------------------------------------------------------
+
+ static class SquareIcon implements Icon {
+ private Color fillColor;
+ private int width;
+
+ public SquareIcon(Color fColor, int width) {
+ fillColor = fColor;
+ this.width = width;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.setColor(fillColor);
+ g.fillRect(x, y, width, width);
+ g.setColor(Color.black);
+ g.drawRect(x, y, width, width);
+ }
+
+ public int getIconWidth() {
+ return width;
+ }
+
+ public int getIconHeight() {
+ return width;
+ }
+ }
+
+ //---------------------------------------------------------------------------------------------
+ // For drawing all non-initial states
+ //---------------------------------------------------------------------------------------------
+
+ static class CircleIcon implements Icon {
+ private Color fillColor;
+ private int radius;
+
+ public CircleIcon(Color fColor, int radius) {
+ fillColor = fColor;
+ this.radius = radius;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.setColor(fillColor);
+ g.fillOval(x, y, radius, radius);
+ g.setColor(Color.black);
+ g.drawOval(x, y, radius, radius);
+ }
+
+ public int getIconWidth() {
+ return radius;
+ }
+
+ public int getIconHeight() {
+ return radius;
+ }
+
+ }
+
+}
+
+
+class ConditionalEdgeShape implements Transformer, E>, Shape> {
+
+ private final EdgeShape.Loop loopShape;
+ private final Transformer, E>, Shape> defaultShape;
+ private final float loopScale;
+
+ public ConditionalEdgeShape(float loopScale) {
+ this.loopShape = new EdgeShape.Loop<>();
+ this.defaultShape = new EdgeShape.QuadCurve<>(); // or Line<>
+ this.loopScale = loopScale;
+ }
+
+ public Shape transform(Context, E> context) {
+ Graph graph = context.graph;
+ E edge = context.element;
+ V src = graph.getSource(edge);
+ V dst = graph.getDest(edge);
+
+ if (src.equals(dst)) {
+ // Scale the self-loop
+ Shape baseLoop = loopShape.transform(context);
+
+ Rectangle bounds = baseLoop.getBounds();
+ double centerX = bounds.getCenterX();
+ double centerY = bounds.getCenterY();
+
+ AffineTransform at = new AffineTransform();
+ at.translate(centerX, centerY);
+ at.scale(loopScale, loopScale);
+ at.translate(-centerX, -centerY);
+
+ return at.createTransformedShape(baseLoop);
+ } else {
+ return defaultShape.transform(context);
+ }
+ }
+}
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonGraph.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonGraph.java
new file mode 100644
index 0000000..ffd3305
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ContextAutomatonGraph.java
@@ -0,0 +1,438 @@
+package pl.umk.mat.martinp.reactics;
+
+
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.util.Pair;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import java.awt.*;
+import java.awt.geom.Point2D;
+import java.io.PrintWriter;
+import java.util.*;
+
+
+class ContextAutomatonGraph extends DirectedSparseGraph {
+ private static final long serialVersionUID = 1L;
+
+ HashMap states = new HashMap<>();
+ private CAState initialState = null;
+ boolean modified = false;
+
+ public ContextAutomatonGraph() {
+ super();
+ }
+
+ public boolean isModified() { return modified; }
+
+ public void markAsModified() { modified = true; }
+
+ public void clearModificationStatus() { modified = false; }
+
+ String toRSSLString() {
+ StringBuilder rsslString = new StringBuilder("context-automaton {\n");
+
+ rsslString.append(" states { ");
+ Collection allStates = states.values();
+ Iterator it = allStates.iterator();
+ if (it.hasNext()) {
+ rsslString.append(it.next().getLabel());
+ }
+ while (it.hasNext()) {
+ rsslString.append(", ").append(it.next().getLabel());
+ }
+ rsslString.append(" };\n");
+
+ rsslString.append(" init-state { ").append(initialState != null ? initialState.getLabel() : "").append(" };\n");
+ rsslString.append(" transitions {\n");
+
+ for (CAEdge edge : this.getEdges()) {
+ for (Transition tr : edge.getTransitions()) {
+ rsslString.append(" { ").append(tr.context).append(" }: ");
+ rsslString.append(this.getSource(edge).label).append(" -> ").append(this.getDest(edge).label);
+
+ String guard = tr.guard;
+
+ if (guard.length() > 0) {
+ rsslString.append(" : ").append(guard);
+ }
+
+ rsslString.append(";\n");
+ }
+ }
+ rsslString.append(" };\n");
+
+ rsslString.append("};\n");
+
+ return rsslString.toString();
+ }
+
+ public void exportToXML(PrintWriter output) {
+ output.println(" ");
+
+ for (CAState state : states.values()) {
+ Point2D pos = state.getLocation();
+ output.print(" ");
+ }
+
+ for (CAEdge edge : edges.keySet()) {
+ Pair endPts = this.getEndpoints(edge);
+ output.print(" ");
+
+ for (Transition tr : edge.getTransitions()) {
+ output.print(" ");
+ }
+ output.println(" ");
+ }
+
+ output.println(" ");
+ }
+
+ /**
+ * Reads the context automaton structure from the XML file.
+ *
+ * @param input An XML file containing a valid description of a context automaton.
+ * @throws ContextAutomatonStructureError In the case of inputFile structure error, eg. \
+ * more than one context-automaton section, more than one \
+ * initial state, repeating state identifiers, \
+ * edge between non-existing nodes, etc.
+ */
+ public void loadFromXML(Document input) throws ContextAutomatonStructureError {
+ NodeList caSections = input.getElementsByTagName("context-automaton");
+
+ if (caSections.getLength() == 0 || caSections.getLength() > 1) {
+ throw new ContextAutomatonStructureError("An XML file should contain a single context automaton description.");
+ }
+
+ //--------------------------------------------------------------------------------------------------------------
+ // Retrieve the states
+ //--------------------------------------------------------------------------------------------------------------
+
+ HashMap states = new HashMap();
+ NodeList stateList = input.getElementsByTagName("state");
+ boolean hasInitialState = false;
+
+ for (int idx = 0; idx < stateList.getLength(); ++idx) {
+ Node stateNode = stateList.item(idx);
+
+ if (stateNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+
+ Element stateElement = (Element) stateNode;
+
+ int id = Integer.parseInt(stateElement.getAttribute("id"));
+ double x = Double.parseDouble(stateElement.getAttribute("x"));
+ double y = Double.parseDouble(stateElement.getAttribute("y"));
+ String name = stateElement.getAttribute("name");
+ String initial = stateElement.getAttribute("initial");
+
+ if (states.containsKey(name)) {
+ throw new ContextAutomatonStructureError("State name <" + name + "> already used.");
+ }
+
+ CAState newState = new CAState(id, name, new Point2D.Double(x, y));
+ this.addState(newState);
+ states.put(name, newState);
+
+ if (initial.equals("true")) {
+ if (hasInitialState)
+ throw new ContextAutomatonStructureError("There should not be more than one initial state.");
+
+ hasInitialState = true;
+ this.setInitialState(newState);
+ }
+ }
+
+ //--------------------------------------------------------------------------------------------------------------
+ // Retrieve the edges
+ //--------------------------------------------------------------------------------------------------------------
+
+ NodeList edgeList = input.getElementsByTagName("edge");
+
+ for (int idx = 0; idx < edgeList.getLength(); ++idx) {
+ Node edgeNode = edgeList.item(idx);
+
+ if (edgeNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+
+ Element edgeElement = (Element) edgeNode;
+
+ String from = edgeElement.getAttribute("from");
+ String to = edgeElement.getAttribute("to");
+
+ // Retrieve all child nodes describing nondeterministic transitions related to this edge
+ NodeList childNodeList = edgeNode.getChildNodes();
+ Vector transitionList = new Vector();
+
+ for (int cIdx=0; cIdx < childNodeList.getLength(); ++ cIdx) {
+ Node childNode = childNodeList.item(cIdx);
+
+ if (childNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+
+ Element transElement = (Element) childNode;
+ transitionList.add(new Transition(transElement.getAttribute("context"), transElement.getAttribute("guard")));
+ }
+
+ CAState stateFrom = states.get(from);
+ CAState stateTo = states.get(to);
+
+ if (stateFrom == null || stateTo == null)
+ throw new ContextAutomatonStructureError("An edge may be created only between existing nodes.");
+
+ this.addEdge(stateFrom, stateTo, transitionList);
+ }
+ }
+
+ public Set getReactantsSet() {
+ Set reactants = new HashSet();
+
+ for (CAEdge edge : getEdges()) {
+ for (Transition trans : edge.getTransitions()) {
+ int start = trans.context.indexOf("{");
+ int end = trans.context.indexOf("}");
+
+ if (start != -1 && end != -1 && start < end) {
+ reactants.addAll(Arrays.asList(trans.context.substring(start + 1, end).split("\\s*,\\s*")));
+ }
+ }
+ }
+
+ return reactants;
+ }
+
+ public void setInitialState(CAState state) {
+ if (initialState != null)
+ initialState.setInitial(false);
+
+ state.setInitial(true);
+ initialState = state;
+ modified = true;
+ }
+
+ public CAState getInitialState() {
+ return initialState;
+ }
+
+ /**
+ * Clears the entire automaton structure structure
+ */
+ public void clear() {
+ for (CAState st : states.values())
+ removeVertex(st);
+
+ states.clear();
+ CAState.resetIdCounter();
+ modified = true;
+ }
+
+ public boolean addEdge(CAState from, CAState to) {
+ modified = true;
+
+ return this.addEdge(new CAEdge(), from, to);
+ }
+
+ public void addEdge(CAState from, CAState to, Collection transitionList) {
+ CAEdge edge = this.findEdge(from, to);
+
+ if (edge == null) {
+ CAEdge caEdge = ReactionSystem.getInstance().createCAEdge();
+ caEdge.addTransitions(transitionList);
+ this.addEdge(caEdge, from, to);
+ }
+ else
+ edge.addTransitions(transitionList);
+
+ modified = true;
+ }
+
+ public void addState(CAState state) throws ContextAutomatonStructureError {
+ int stateId = state.getId();
+
+ if (states.get(stateId) != null)
+ throw new ContextAutomatonStructureError("State id " + stateId + " already in use.");
+
+ states.put(stateId, state);
+ this.addVertex(state);
+ modified = true;
+ }
+
+ public boolean removeState(CAState state) {
+ if (states.get(state.getId()) != null) {
+ states.remove(state.getId());
+ }
+
+ modified = true;
+
+ // Remove the state with all connected edges
+ return this.removeVertex(state);
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Single node of the context automaton graph
+//-------------------------------------------------------------------------------------------------
+
+class CAState {
+ public final static Color baseFillColor = Color.lightGray;
+ public final static Color selectedFillColor = Color.lightGray;
+ public final static Color initStateFillColor = new Color(102, 178, 255);
+
+ private static final int nodeWidth = 30;
+ private static final int nodeHeight = 30;
+ protected static int nextId = 1;
+
+ /** Unique auto incremented identifier of the node */
+ protected int id;
+
+ /** User defined label of the node displayed as a tool tip and used in RSSL and XML files */
+ protected String label;
+
+ protected Point2D location;
+ private boolean isInitial = false;
+
+ /** A state created from the description stored in a file. */
+ public CAState(int id, String label, Point2D location) {
+ this.id = id;
+ this.label = label;
+ this.location = location;
+
+ // To avoid duplicate node identifiers
+ if (id >= nextId)
+ nextId = id + 1;
+ }
+
+ /** A state created by clicking in graphical component. */
+ public CAState(Point2D location) {
+ this.id = nextId;
+ this.label = "q" + id;
+ this.location = location;
+ ++nextId;
+ }
+
+ public Point2D getLocation() { return location; }
+ public void updateLocation(Point2D location) { this.location = location; }
+
+ public boolean isInitial() { return isInitial; }
+ public void setInitial(boolean status) { isInitial = status; }
+
+ public int getWidth() { return nodeWidth; }
+ public int getHeight() { return nodeHeight; }
+
+ public String getLabel() { return label; }
+ public void setLabel(String newLabel) { label = newLabel; }
+
+ public int getId() { return id; }
+
+ static void resetIdCounter() { nextId = 1; }
+
+ public String toString() { return Integer.toString(id); }
+
+ public String getTooltipText() {
+ return ""+
+ "State " + id + "
" + getLabel() + //"
"+
+ "";
+ }
+
+ Color getFillColor() {
+ return isInitial ? initStateFillColor : baseFillColor;
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Single edge of the context automaton graph.
+// Each edge can represent a number of single transitions between the same pair of automaton states.
+//-------------------------------------------------------------------------------------------------
+
+class CAEdge {
+ protected static int nextId = 0;
+
+ protected int id;
+
+ // The list of all nondeterministic transitions represented by this edge
+ private Vector transitions = new Vector();
+
+ public boolean hasTransition() { return transitions.size() > 0; }
+
+
+ public CAEdge() {
+ this.id = nextId++;
+ }
+
+// public CAEdge(Collection transitionList) {
+// this.transitions.addAll(transitionList);
+// }
+
+ Vector getTransitions() {
+ return transitions;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String toString() {
+ // Update this if you need any label displayed together with graph edge
+ return "";
+ }
+
+ public String getTooltipText() {
+ StringBuilder tooTip = new StringBuilder("
Context : Guard
");
+
+ for (Transition tr : transitions) {
+ tooTip.append(tr.context).append(" : ").append(tr.guard).append("
");
+ }
+
+ tooTip.append("");
+
+ return tooTip.toString();
+ }
+
+ public void addTransitions(Collection transitionList) {
+ transitions.addAll(transitionList);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Single transition. To be included in a multiedge.
+//-------------------------------------------------------------------------------------------------
+
+class Transition {
+ String context;
+ String guard;
+
+ public Transition(String ctx, String grd) {
+ context = ctx;
+ guard = grd;
+ }
+}
+
+
+class ContextAutomatonStructureError extends FileStructureError {
+ public ContextAutomatonStructureError(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/CumulatedReactionsViewer.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/CumulatedReactionsViewer.java
new file mode 100644
index 0000000..0b32f30
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/CumulatedReactionsViewer.java
@@ -0,0 +1,13 @@
+package pl.umk.mat.martinp.reactics;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class CumulatedReactionsViewer extends JPanel {
+
+ public CumulatedReactionsViewer() {
+ this.setLayout(new BorderLayout());
+ this.add(new JLabel("Cumulate Reactions Viewer", SwingConstants.CENTER), BorderLayout.CENTER);
+ }
+
+}
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/FormulaEditor.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/FormulaEditor.java
new file mode 100644
index 0000000..9ce60e6
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/FormulaEditor.java
@@ -0,0 +1,410 @@
+package pl.umk.mat.martinp.reactics;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.*;
+import java.awt.*;
+import java.io.*;
+import java.util.Collection;
+import java.util.Vector;
+
+
+public class FormulaEditor extends JPanel implements RSObserver {
+ private FormulaTableModel ftModel;
+ private JTable formTable;
+ private ReactionSystem rs;
+ private Vector formulaList;
+
+ boolean modified = false;
+
+ static final Color TableHeaderBackgroundColor = Color.gray;
+ static final Color TableHeaderFontColor = Color.white;
+ static final Color BaseFormulaBackgroundColor = Color.white;
+ static final Color TrueFormulaBackgroundColor = new Color(138, 242, 183);
+ static final Color FalseFormulaBackgroundColor = new Color(240, 141, 141);
+ static final Color InvalidFormulaBackgroundColor = new Color(124, 124, 124);
+ static final Border emptyBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
+ static final Border selectedBorder = BorderFactory.createLineBorder(Color.RED, 2);
+
+
+ public FormulaEditor(ReactionSystem rs) {
+ this.rs = rs;
+ formulaList = rs.formulas;
+
+ // Create the table for displaying formulas. Prevent default selection modes.
+ ftModel = new FormulaTableModel();
+ formTable = new JTable(ftModel, new FormulaColumnModel());
+ formTable.setCellSelectionEnabled(false);
+ formTable.setRowSelectionAllowed(true);
+
+ Font headerFont = formTable.getTableHeader().getFont();
+ Font cellFont = formTable.getFont();
+ JTableHeader formHeader = formTable.getTableHeader();
+ formTable.setFont(new Font("Monospaced", Font.PLAIN, cellFont.getSize() + 2));
+ formHeader.setFont(new Font(headerFont.getFamily(), Font.BOLD, headerFont.getSize() + 2));
+ formHeader.setBackground(TableHeaderBackgroundColor);
+ formHeader.setForeground(TableHeaderFontColor);
+ formHeader.setReorderingAllowed(false);
+ formHeader.setResizingAllowed(true);
+ formHeader.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
+
+ FormulaTableCellRenderer renderer = new FormulaTableCellRenderer();
+ formTable.setDefaultRenderer(Object.class, renderer);
+ formTable.createDefaultColumnsFromModel();
+
+ setLayout(new BorderLayout());
+ add(new JScrollPane(formTable), BorderLayout.CENTER);
+ }
+
+ public boolean isModified() { return modified; }
+
+ public void clearModificationStatus() { modified = false; }
+
+ public String toRSSLString() {
+ StringBuilder rsslString = new StringBuilder();
+
+ for (Formula f: formulaList)
+ rsslString.append(f.toRSSLString()).append("\n");
+
+ return rsslString.toString();
+ }
+
+ public void exportToXML(PrintWriter output) {
+ output.println(" ");
+
+ for (Formula f : formulaList)
+ output.println(" " + f.toXMLString());
+
+ output.println(" ");
+ }
+
+ public void loadFromXML(Document input) {
+ formulaList.clear();
+
+ NodeList formulaTags = input.getElementsByTagName("formula");
+
+ for (int idx = 0; idx < formulaTags.getLength(); ++idx) {
+ Node formulaNode = formulaTags.item(idx);
+
+ if (formulaNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+
+ Element formulaElement = (Element) formulaNode;
+
+ String label = formulaElement.getAttribute("label");
+ String formula = formulaElement.getTextContent();
+ formulaList.add(new Formula(label, formula));
+ }
+
+ ftModel.fireTableDataChanged();
+ }
+
+ private boolean showFormulaEditDialog(JFrame parent, Formula ff) {
+ final int Select = 0;
+ final int Approve = 1;
+
+ JTextField labelInput = new JTextField(30);
+ JTextField formulaInput = new JTextField(30);
+
+ String labelStr = "";
+ String formulaStr = "";
+
+ labelStr = ff.label;
+ formulaStr = ff.formula;
+
+ labelInput.setToolTipText("A unique label of the formula");
+ formulaInput.setToolTipText("Formula to be evaluated");
+
+ JPanel myPanel = new JPanel(new GridBagLayout());
+ GridBagConstraints cs = new GridBagConstraints();
+
+ cs.insets = new Insets(5, 5, 5, 5);
+ cs.anchor = GridBagConstraints.WEST;
+ cs.gridx = 0;
+ cs.gridy = 0;
+ myPanel.add(new JLabel("Label"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 0;
+ myPanel.add(labelInput, cs);
+
+ cs.gridx = 0;
+ cs.gridy = 1;
+ myPanel.add(new JLabel("Formula"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 1;
+ myPanel.add(formulaInput, cs);
+
+ int opStatus = Select;
+
+ do {
+ labelInput.setText(labelStr);
+ formulaInput.setText(formulaStr);
+
+ int result = JOptionPane.showConfirmDialog(parent, myPanel,
+ "Formula details", JOptionPane.OK_CANCEL_OPTION);
+
+ if (result != JOptionPane.OK_OPTION) {
+ return false;
+ }
+
+ labelStr = labelInput.getText();
+ formulaStr = formulaInput.getText();
+
+ ff.label = labelStr;
+ ff.formula = formulaStr;
+
+ opStatus = Approve;
+ }
+ while(opStatus == Select);
+
+ return true;
+ }
+
+ public void addFormula(JFrame parent) {
+ Formula ff = new Formula();
+
+ if (showFormulaEditDialog(parent, ff)) {
+ formulaList.add(ff);
+ modified = true;
+ ftModel.fireTableDataChanged();
+ }
+ }
+
+ // If more than one formula is selected throw an exception only the first can be edited
+ public void editFormula(JFrame parent) {
+ int[] selected = formTable.getSelectedRows();
+
+ if (selected.length != 1) {
+ JOptionPane.showMessageDialog(parent, "Select a single formula to edit.", "Select formula", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ Formula fEdt = formulaList.get(formTable.convertRowIndexToModel(selected[0]));
+
+ if (fEdt == null)
+ return;
+
+ showFormulaEditDialog(parent, fEdt);
+ fEdt.status = Formula.FormulaStatus.None;
+ modified = true;
+ ftModel.fireTableDataChanged();
+ }
+
+ public void removeFormulas() {
+ int[] viewRows = formTable.getSelectedRows();
+
+ // Remove formulas starting from the last to avoid indexes updates
+ for (int vIdx = viewRows.length - 1; vIdx>=0; --vIdx) {
+ int mr = formTable.convertRowIndexToModel(viewRows[vIdx]);
+ formulaList.remove(mr);
+ }
+
+ modified = true;
+ ftModel.fireTableDataChanged();
+ }
+
+ public Collection getSelectedFormulas() {
+ Vector selected = new Vector();
+ int[] viewRows = formTable.getSelectedRows();
+
+ for (int vr : viewRows) {
+ int mr = formTable.convertRowIndexToModel(vr);
+ selected.add(formulaList.get(mr));
+ }
+
+ return selected;
+ }
+
+ // Needed to repaint JTable. Called after content modification by an external object.
+ public void refreshStatus() {
+ ftModel.fireTableDataChanged();
+ }
+
+ public void resetFormulasStatus() {
+ for (Formula ff : formulaList) {
+ ff.status = Formula.FormulaStatus.None;
+ ff.mcTime = ff.totalTime = ff.memory = "-";
+ }
+
+ ftModel.fireTableDataChanged();
+ }
+
+ public void onRSUpdate() {
+ resetFormulasStatus();
+ refreshStatus();
+ }
+
+ static class FormulaColumnModel extends DefaultTableColumnModel {
+ int colno = 0;
+
+ public void addColumn(TableColumn tc) {
+ switch (colno) {
+ case 0 -> {
+ tc.setMinWidth(60);
+ tc.setMaxWidth(Integer.MAX_VALUE / 10);
+ tc.setPreferredWidth(100);
+ }
+ case 1 -> {
+ tc.setMinWidth(100);
+ tc.setMaxWidth(Integer.MAX_VALUE);
+ }
+ case 2, 3, 4, 5 -> {
+ tc.setMinWidth(50);
+ tc.setPreferredWidth(50);
+ tc.setMaxWidth(Integer.MAX_VALUE / 10);
+ }
+ }
+
+ super.addColumn(tc);
+ ++colno;
+ }
+ }
+
+
+ class FormulaTableModel extends AbstractTableModel implements TableModelListener {
+ private final String[] columnNames = {"Label", "Formula", "Status", "MC Time (s)", "Total Time (s)", "Memory (MB)"};
+
+ public String getColumnName(int colIdx) {
+ return columnNames[colIdx];
+ }
+
+ public int getRowCount() {
+ return formulaList.size();
+ }
+
+ public int getColumnCount() {
+ return columnNames.length;
+ }
+
+ public Object getValueAt(int row, int column) {
+ Formula ff = formulaList.get(row);
+
+ return switch (column) {
+ case 0 -> ff.label;
+ case 1 -> ff.formula;
+ case 2 -> ff.status;
+ case 3 -> ff.mcTime;
+ case 4 -> ff.totalTime;
+ case 5 -> ff.memory;
+ default -> "";
+ };
+ }
+
+ public void setValueAt(Object value, int row, int column) {
+// fireTableDataChanged();
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) { return false; }
+
+ public Class getColumnClass(int column) {
+ return getValueAt(0, column).getClass();
+ }
+
+ public void tableChanged(TableModelEvent tableModelEvent) { }
+ }
+
+
+ // To properly display the content of the table containing formulas
+ class FormulaTableCellRenderer extends JLabel implements TableCellRenderer {
+
+ public FormulaTableCellRenderer() {
+ // Allows background to be visible
+ setOpaque(true);
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ String statusStr = "";
+ Formula ff = formulaList.get(row);
+
+ setForeground(Color.black);
+
+ if (isSelected) {
+ setBorder(selectedBorder);
+ }
+ else {
+ setBorder(emptyBorder);
+ }
+
+ // Set cell background and formula evaluation status
+ if (column == 2) {
+ switch (ff.status) {
+ case None -> {
+ statusStr = "?";
+ setBackground(BaseFormulaBackgroundColor);
+ }
+ case True -> {
+ statusStr = "True";
+ setBackground(TrueFormulaBackgroundColor);
+ }
+ case False -> {
+ statusStr = "False";
+ setBackground(FalseFormulaBackgroundColor);
+ }
+ case Invalid -> {
+ statusStr = "Invalid";
+ setBackground(InvalidFormulaBackgroundColor);
+ }
+ }
+
+ setText(statusStr);
+ }
+ else {
+ setText((String) value);
+ setBackground(BaseFormulaBackgroundColor);
+ }
+
+ if (column > 2) {
+ setHorizontalAlignment(SwingConstants.RIGHT);
+ } else {
+ setHorizontalAlignment(SwingConstants.LEFT);
+ }
+
+ return this;
+ }
+ }
+}
+
+
+class Formula {
+ public enum FormulaStatus {None, True, False, Invalid};
+
+ String label;
+ String formula;
+ FormulaStatus status;
+ String mcTime;
+ String totalTime;
+ String memory;
+
+ public Formula() {
+ this("", "");
+ }
+
+ public Formula(String label, String formula) {
+ this.label = label;
+ this.formula = formula;
+ this.status = FormulaStatus.None;
+ this.mcTime = this.totalTime = this.memory = "-";
+ }
+
+ public String toRSSLString() {
+ return "rsctlk-property { " + label + " : " + formula + " };";
+ }
+
+ public String toXMLString() {
+ return "" +
+ formula.replaceAll("<", "<").replaceAll(">", ">") +
+ "";
+ }
+}
+
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/HelpWindow.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/HelpWindow.java
new file mode 100644
index 0000000..9e970cf
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/HelpWindow.java
@@ -0,0 +1,125 @@
+package pl.umk.mat.martinp.reactics;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+
+public class HelpWindow extends JFrame {
+ private static final long serialVersionUID = 327019113533268901L;
+ private static final String startDocument = "index.html";
+ private static final String docPath = "/help/";
+
+ private static HelpWindow instance = null;
+ private static JTextPane helpPane = new JTextPane();;
+
+ public static synchronized HelpWindow getInstance() {
+ if (instance == null)
+ instance = new HelpWindow();
+
+ return instance;
+ }
+
+ private HelpWindow() {
+ super("ReactICS GUI Help");
+
+ getContentPane().setLayout(new BorderLayout());
+
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout());
+ EtchedBorder panelBorder = new EtchedBorder(EtchedBorder.LOWERED);
+ mainPanel.setBorder(panelBorder);
+
+ helpPane.setEditable(false);
+ helpPane.addHyperlinkListener(new HyperlinkListener() {
+ public void hyperlinkUpdate(HyperlinkEvent hle) {
+ if (hle.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ String target = hle.getDescription();
+
+ if (target.startsWith("#")) {
+ helpPane.scrollToReference(target.substring(1));
+ }
+ }
+ }
+ });
+
+ try {
+ URL helpUrl = this.getClass().getResource(docPath + startDocument);
+ helpPane.setPage(helpUrl);
+
+ helpPane.revalidate();
+ helpPane.repaint();
+ }
+ catch (Exception e) {
+ System.out.println("Exception: " + e.getMessage());
+ JOptionPane.showMessageDialog(null, "Can not load help content",
+ "Help", JOptionPane.ERROR_MESSAGE);
+ }
+
+ JScrollPane helpScroller = new JScrollPane();
+ JViewport helpVp = new JViewport();
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ helpVp.setPreferredSize(new Dimension(screenSize.width/2, screenSize.height-200));
+ helpVp.add(helpPane);
+ helpScroller.setViewport(helpVp);
+ mainPanel.add(helpScroller, BorderLayout.CENTER);
+ getContentPane().add(mainPanel, BorderLayout.CENTER);
+
+ pack();
+ }
+
+ public void show(Component parent) {
+ setLocationRelativeTo(parent);
+ setVisible(true);
+ }
+
+}
+
+
+class AboutWindow extends JFrame {
+ private static AboutWindow instance = null;
+
+ public static synchronized AboutWindow getInstance() {
+ if (instance == null)
+ instance = new AboutWindow();
+
+ return instance;
+ }
+
+ private AboutWindow() {
+ super("About ...");
+
+ getContentPane().setLayout(new BorderLayout());
+
+ JPanel infoPanel = new JPanel();
+ infoPanel.add(new JLabel("" + ReacticsGUI.ApplicationName + "
"
+ + "version " + ReacticsGUI.ApplicationVersion + " (" + ReacticsGUI.ApplicationReleaseDate + ")
"
+ + "Model Checker for Reaction Systems
"
+ + "ReactICS Research Team (CC BY 4.0)
"
+ + ""));
+ getContentPane().add(infoPanel, BorderLayout.CENTER);
+
+ JButton closeButton = new JButton("Close");
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ setVisible(false);
+ }
+ });
+ JPanel ctrlPanel = new JPanel();
+ ctrlPanel.add(closeButton);
+ ctrlPanel.setAlignmentX(SwingConstants.RIGHT);
+ getContentPane().add(ctrlPanel, BorderLayout.SOUTH);
+
+ setSize(new Dimension(400, 250));
+ setResizable(false);
+ this.setAlwaysOnTop(true);
+ this.setUndecorated(true);
+ }
+
+ public void show(Component parent) {
+ setLocationRelativeTo(parent);
+ setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ProcessEditor.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ProcessEditor.java
new file mode 100644
index 0000000..8ace501
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ProcessEditor.java
@@ -0,0 +1,427 @@
+package pl.umk.mat.martinp.reactics;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.table.AbstractTableModel;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.PrintWriter;
+import java.util.*;
+
+
+//-------------------------------------------------------------------------------------------------
+// Graphical component for editing a single process (list of reactions)
+//-------------------------------------------------------------------------------------------------
+
+class ProcessEditor extends JPanel {
+ private JTable reactionList;
+ private RSTableModel reactionsModel;
+
+ private RSProcess rsProcess;
+ private boolean modified = false;
+
+ private JCheckBox selectBox;
+ private TitledBorder outBorder;
+ private ReactionSystem rs;
+
+ private final Color borderColor = new Color(24, 104, 92);;
+ private final Color selectedBorderColor = new Color(150, 10, 10);
+ private static final int borderThickness = 4;
+
+ public ProcessEditor(RSProcess rsProc) {
+ rsProcess = rsProc;
+ init();
+ }
+
+ public RSProcess getProcess() { return rsProcess; }
+
+ public boolean isSelected() {
+ return selectBox.isSelected();
+ }
+
+ public boolean isModified() { return modified; }
+
+ public void clearModificationStatus() { modified = false; }
+
+ public String getLabel() {
+ return rsProcess.label;
+ }
+
+ private void init() {
+ // Create border for the component
+ outBorder = BorderFactory.createTitledBorder(rsProcess.label);
+ Font font = outBorder.getTitleFont();
+ Font newFont = new Font (font.getFamily(), Font.BOLD, font.getSize() + 2);
+ outBorder.setTitleFont(new Font (font.getFamily(), Font.BOLD, font.getSize() + 2));
+ outBorder.setTitleColor(borderColor);
+ outBorder.setBorder(new LineBorder(borderColor, borderThickness, true));
+ setBorder(outBorder);
+
+ // Create reactions list component
+ reactionsModel = new RSTableModel();
+ reactionList = new JTable(reactionsModel);
+
+ Font cellFont = reactionList.getFont();
+ Font headerFont = reactionList.getTableHeader().getFont();
+
+ reactionList.setFont(new Font("Monospaced", 0, cellFont.getSize() + 2));
+ reactionList.getTableHeader().setFont(new Font(headerFont.getFamily(), Font.BOLD, headerFont.getSize() + 2));
+ reactionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ JPanel buttonPanel = new JPanel();
+
+ JButton addButton = new JButton("Add reaction");
+ addButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { addReaction(); }
+ });
+ buttonPanel.add(addButton);
+
+ JButton edtButton = new JButton("Edit reaction");
+ edtButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ editReaction();
+ }
+ });
+ buttonPanel.add(edtButton);
+
+ JButton rmButton = new JButton("Remove reaction");
+ rmButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ removeReactions();
+ }
+ });
+ buttonPanel.add(rmButton);
+
+ selectBox = new JCheckBox("Select");
+ selectBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ toggleSelection();
+ }
+ });
+ buttonPanel.add(selectBox);
+
+ add(buttonPanel);
+
+ add(new JScrollPane(reactionList));
+
+ setPreferredSize(new Dimension(-1, 200));
+
+ rs = ReactionSystem.getInstance();
+ }
+
+ private boolean showReactionEditDialog(JComponent parent, Reaction rr) {
+ final int Select = 0;
+ final int Approve = 1;
+
+ JTextField reactantsInput = new JTextField(30);
+ JTextField inhibitorsInput = new JTextField(30);
+ JTextField productsInput = new JTextField(30);
+
+ String reactantsStr = "";
+ String inhibitorsStr = "";
+ String productsStr = "";
+
+ Iterator> it = rr.reactants.iterator();
+ StringBuilder dataStr = new StringBuilder();
+
+ while (it.hasNext())
+ dataStr.append(it.next()).append(" ");
+
+ reactantsStr = dataStr.toString();
+
+ it = rr.inhibitors.iterator();
+ dataStr.setLength(0);
+
+ while (it.hasNext())
+ dataStr.append(it.next()).append(" ");
+
+ inhibitorsStr = dataStr.toString();
+
+ it = rr.products.iterator();
+ dataStr.setLength(0);
+
+ while (it.hasNext())
+ dataStr.append(it.next()).append(" ");
+
+ productsStr = dataStr.toString();
+
+ reactantsInput.setToolTipText("Space separated list of reactants");
+ inhibitorsInput.setToolTipText("Space separated list of inhibitors");
+ productsInput.setToolTipText("Space separated list of products");
+
+ JPanel myPanel = new JPanel(new GridBagLayout());
+ GridBagConstraints cs = new GridBagConstraints();
+
+ cs.insets = new Insets(5, 5, 5, 5);
+ cs.anchor = GridBagConstraints.WEST;
+ cs.gridx = 0;
+ cs.gridy = 0;
+ myPanel.add(new JLabel("Reactants"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 0;
+ myPanel.add(reactantsInput, cs);
+
+ cs.gridx = 0;
+ cs.gridy = 1;
+ myPanel.add(new JLabel("Inhibitors"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 1;
+ myPanel.add(inhibitorsInput, cs);
+
+ cs.gridx = 0;
+ cs.gridy = 2;
+ myPanel.add(new JLabel("Products"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 2;
+ myPanel.add(productsInput, cs);
+
+ int opStatus = Select;
+
+ do {
+ reactantsInput.setText(reactantsStr);
+ inhibitorsInput.setText(inhibitorsStr);
+ productsInput.setText(productsStr);
+
+ int result = JOptionPane.showConfirmDialog(parent, myPanel,
+ "Reaction details", JOptionPane.OK_CANCEL_OPTION);
+
+ if (result != JOptionPane.OK_OPTION) {
+ return false;
+ }
+
+ reactantsStr = reactantsInput.getText();
+ inhibitorsStr = inhibitorsInput.getText();
+ productsStr = productsInput.getText();
+
+ HashSet reactantsSet = new HashSet(Arrays.asList(reactantsStr.trim().split("\\s+")));
+ HashSet inhibitorsSet = new HashSet(Arrays.asList(inhibitorsStr.trim().split("\\s+")));
+ HashSet productsSet = new HashSet(Arrays.asList(productsStr.trim().split("\\s+")));
+
+ // Verify that reactant and inhibitor sets have empty intersection
+ boolean intersection = false;
+
+ for (String entity : inhibitorsSet) {
+ if (reactantsSet.contains(entity)) {
+ intersection = true;
+ break;
+ }
+ }
+
+ if (intersection) {
+ JOptionPane.showMessageDialog(this, "Reactant and inhibitor sets have to be disjoint.", "Error", JOptionPane.ERROR_MESSAGE);
+ continue;
+ }
+
+ rr.reactants.clear();
+ rr.reactants.addAll(reactantsSet);
+ rr.inhibitors.clear();
+ rr.inhibitors.addAll(inhibitorsSet);
+ rr.products.clear();
+ rr.products.addAll(productsSet);
+
+ opStatus = Approve;
+ }
+ while(opStatus == Select);
+
+ return true;
+ }
+
+ public Set getReactantsSet() {
+ HashSet rset = new HashSet();
+
+ for (Reaction rr : rsProcess.reactions)
+ rset.addAll(rr.getReactantsSet());
+
+ return rset;
+ }
+
+ public void exportToXML(PrintWriter output) {
+ output.println(" ");
+
+ for (Reaction rr : rsProcess.reactions)
+ rr.exportToXML(output);
+
+ output.println(" ");
+ }
+
+ public String toRSSLString() {
+ StringBuilder rsslString = new StringBuilder(" " + rsProcess.label + " {\n");
+
+ for (Reaction rr : rsProcess.reactions) {
+ rsslString.append(rr.toRSSLString());
+ }
+
+ rsslString.append(" };\n");
+
+ return rsslString.toString();
+ }
+
+ void rename(String newLabel) {
+ rsProcess.label = newLabel;
+ outBorder.setTitle(rsProcess.label);
+
+ this.repaint();
+ }
+
+ // Add new reaction
+ public void addReaction(Reaction rr) {
+ rsProcess.reactions.add(rr);
+ reactionsModel.fireTableDataChanged();
+ }
+
+ private void addReaction() {
+ Reaction rr = new Reaction();
+ if (!showReactionEditDialog(this, rr))
+ return;
+
+ rsProcess.reactions.add(rr);
+ modified = true;
+ reactionsModel.fireTableDataChanged();
+ rs.notifyObservers();
+ }
+
+ // Edit details of an existing reaction
+ private void editReaction() {
+ int rIdx = reactionList.getSelectedRow();
+
+ if (rIdx == -1) {
+ JOptionPane.showMessageDialog(this, "Select the reaction to edit.", "Select reaction", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ Reaction rr = new Reaction(rsProcess.reactions.get(rIdx));
+ reactionList.clearSelection();
+ if (!showReactionEditDialog(this, rr)) {
+ return;
+ }
+
+ rsProcess.reactions.setElementAt(rr, rIdx);
+ modified = true;
+ reactionsModel.fireTableDataChanged();
+ rs.notifyObservers();
+ }
+
+ // Remove an existing reaction
+ private void removeReactions() {
+ int rIdx = reactionList.getSelectedRow();
+
+ if (rIdx == -1) {
+ JOptionPane.showMessageDialog(this, "Select the reaction to be removed.", "Select reaction", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ rsProcess.reactions.remove(rIdx);
+ reactionList.clearSelection();
+ reactionsModel.fireTableDataChanged();
+ modified = true;
+ this.repaint();
+ rs.notifyObservers();
+ }
+
+ private void toggleSelection() {
+ if (selectBox.isSelected()) {
+ outBorder.setTitleColor(selectedBorderColor);
+ outBorder.setBorder(new LineBorder(selectedBorderColor, borderThickness, true));
+ }
+ else {
+ outBorder.setTitleColor(borderColor);
+ outBorder.setBorder(new LineBorder(borderColor, borderThickness, true));
+ }
+
+ this.repaint();
+ }
+
+ class RSTableModel extends AbstractTableModel {
+
+ private String columnNames[] = {"Reactants", "Inhibitors", "Results"};
+ private Vector[] data;
+
+ public String getColumnName(int colIdx) {
+ return columnNames[colIdx];
+ }
+
+ public int getRowCount() {
+ return rsProcess.reactions.size();
+ }
+
+ public int getColumnCount() {
+ return columnNames.length;
+ }
+
+ public Object getValueAt(int row, int column) {
+ Reaction rr = rsProcess.reactions.get(row);
+
+ return switch (column) {
+ case 0 -> rr.reactants;
+ case 1 -> rr.inhibitors;
+ case 2 -> rr.products;
+ default -> "";
+ };
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return false;
+ }
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Object representing a single reaction
+//-------------------------------------------------------------------------------------------------
+
+class Reaction {
+ LinkedList reactants;
+ LinkedList inhibitors;
+ LinkedList products;
+
+ public Reaction() {
+ reactants = new LinkedList();
+ inhibitors = new LinkedList();
+ products = new LinkedList();
+ }
+
+ public Reaction(Reaction rr) {
+ reactants = new LinkedList(rr.reactants);
+ inhibitors = new LinkedList(rr.inhibitors);
+ products = new LinkedList(rr.products);
+ }
+
+ public Set getReactantsSet() {
+ Set rset = new HashSet();
+ rset.addAll(reactants);
+ rset.addAll(inhibitors);
+ rset.addAll(products);
+
+ return rset;
+ }
+
+ public String toRSSLString() {
+ return " {" + "{" + reactants.toString().replace("[", "").replace("]", "") + "}, " +
+ "{" + inhibitors.toString().replace("[", "").replace("]", "") + "} -> " +
+ "{" + products.toString().replace("[", "").replace("]", "") + "}" +
+ "};\n";
+ }
+
+ public void exportToXML(PrintWriter output) {
+ output.println(" ");
+
+ for (String str : reactants)
+ output.println(" " + str + "");
+
+ for (String str : inhibitors)
+ output.println(" " + str + "");
+
+ for (String str : products)
+ output.println(" " + str + "");
+
+ output.println(" ");
+ }
+}
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ReactantSetPanel.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReactantSetPanel.java
new file mode 100644
index 0000000..14a46e1
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReactantSetPanel.java
@@ -0,0 +1,49 @@
+package pl.umk.mat.martinp.reactics;
+
+import javax.swing.*;
+import javax.swing.border.LineBorder;
+import javax.swing.border.TitledBorder;
+import java.awt.*;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+public class ReactantSetPanel extends JPanel implements RSObserver {
+ private final int borderThickness = 2;
+
+ JTextArea textArea = new JTextArea();
+
+ public ReactantSetPanel() {
+ textArea.setEditable(false);
+ textArea.setLineWrap(true);
+ textArea.setWrapStyleWord(false);
+ Font textFont = textArea.getFont();
+ textArea.setFont(new Font(textFont.getFamily(), Font.BOLD, textFont.getSize()+2));
+
+ this.setPreferredSize(new Dimension(-1, 75));
+
+ this.setLayout(new BorderLayout());
+ this.add(new JScrollPane(textArea), BorderLayout.CENTER);
+
+ TitledBorder outBorder = BorderFactory.createTitledBorder("Reactants");
+ Font borderFont = outBorder.getTitleFont();
+ outBorder.setTitleFont(new Font (borderFont.getFamily(), Font.BOLD, borderFont.getSize()+2));
+ outBorder.setBorder(new LineBorder(Color.black, borderThickness, true));
+ setBorder(outBorder);
+ }
+
+ public void updateReactants(Set reactantsSet) {
+ textArea.setText("");
+// textArea.setText(Arrays.toString(reactantsSet.toArray()));
+ textArea.setText(Arrays.stream(reactantsSet.toArray()).map(String::valueOf).collect(Collectors.joining(" ")));
+ }
+
+ public void clear() {
+ textArea.setText("");
+ }
+
+ public void onRSUpdate() {
+ ReactionSystem rs = ReactionSystem.getInstance();
+ updateReactants(rs.getReactants());
+ }
+}
\ No newline at end of file
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsGUI.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsGUI.java
new file mode 100644
index 0000000..10d126e
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsGUI.java
@@ -0,0 +1,791 @@
+/** Main application window */
+
+package pl.umk.mat.martinp.reactics;
+
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Collection;
+import java.util.TreeSet;
+
+
+public class ReacticsGUI extends JFrame {
+ public final static String ApplicationName = "ReactICS GUI";
+ public final static String ApplicationVersion = "0.1";
+ public final static String ApplicationReleaseDate = "2025";
+
+ private static final String defaultConfigFileName = "reactics.conf";
+
+ private JFrame window;
+ private JTabbedPane modulePane;
+ private ReactantSetPanel reactantSetPanel;
+ private ReactionSystemEditor reactionSystemEditor;
+ private ContextAutomatonEditor contextAutomatonEditor;
+ private TransitionSystemViewer transitionSystemViewer;
+ private TransitionSystemViewer compressedTransitionSystemViewer;
+ private FormulaEditor formulaEditor;
+
+ private FileSelector fileSelector;
+
+ private ReactionSystem reactionSystem;
+ private ReacticsRuntime reactics;
+
+
+ public ReacticsGUI() {
+ super(ApplicationName);
+
+ reactionSystem = ReactionSystem.getInstance();
+
+ try {
+ reactics = ReacticsRuntime.getInstance();
+ }
+ catch (ReacticsRuntimeException rre) {
+ JOptionPane.showMessageDialog(this,
+ rre.getMessage(),
+ "Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ setSize(new Dimension(screenSize.width-200, screenSize.height-200));
+ createMenuBar();
+ createContentPane();
+
+ fileSelector = FileSelector.getInstance();
+
+ try {
+ reactics.loadConfig(defaultConfigFileName);
+ }
+ catch (ConfigReadingError cre) {
+ System.err.println("[ReactICS] Error reading configuration: " + cre.getMessage());
+ JOptionPane.showMessageDialog(this,
+ "Could not read configuration\n" + cre.getMessage(),
+ "Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
+ }
+ catch (InvalidRuntimeException ire) {
+ System.err.println("[ReactICS] ReactICS runtime cannot be executed" + ire.getMessage());
+ JOptionPane.showMessageDialog(this,
+ "Invalid ReactICS runtime patn\n" + ire.getMessage() +
+ "\nYou may proceed with editing reaction system structure," +
+ "however not model checking will be possible.",
+ "Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ window = this;
+ }
+
+ private void createMenuBar() {
+ JMenuBar menuBar = new JMenuBar();
+
+ JMenu fileMenu = new JMenu("File");
+ menuBar.add(fileMenu);
+
+ JMenuItem xmlLoadItem = new JMenuItem("Load from XML file");
+ xmlLoadItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ loadFromXML();
+ }
+ });
+ fileMenu.add(xmlLoadItem);
+
+ JMenuItem xmlsaveItem = new JMenuItem("Save to XML file");
+ xmlsaveItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ saveAsXML();
+ }
+ });
+ fileMenu.add(xmlsaveItem);
+
+ JMenuItem rsslImportItem = new JMenuItem("Import from RSSL file");
+ rsslImportItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ importFromRSSL();
+ }
+ });
+ fileMenu.add(rsslImportItem);
+
+ JMenuItem rsslExportItem = new JMenuItem("Save to RSSL file");
+ rsslExportItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ exportToRSSLFile();
+ }
+ });
+ fileMenu.add(rsslExportItem);
+
+ JMenuItem isplExportItem = new JMenuItem("Export to ISPL file");
+ isplExportItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { exportToISPLFile(); }
+ });
+ fileMenu.add(isplExportItem);
+
+ JMenuItem exitItem = new JMenuItem("Exit");
+ exitItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ System.exit(0);
+ }
+ });
+ fileMenu.addSeparator();
+ fileMenu.add(exitItem);
+
+ JMenu helpMenu = new JMenu("Help");
+ menuBar.add(helpMenu);
+
+ JMenuItem helpItem = new JMenuItem("Help");
+ helpItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { showHelpWindow(); }
+ });
+ helpMenu.add(helpItem);
+
+ JMenuItem aboutItem = new JMenuItem("About");
+ aboutItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { showAboutWindow(); }
+ });
+ helpMenu.add(aboutItem);
+
+ setJMenuBar(menuBar);
+ }
+
+ private void createContentPane() {
+ modulePane = new JTabbedPane();
+ modulePane.setTabPlacement(JTabbedPane.NORTH);
+
+ //-----------------------------------------------------------------------------------------
+ // Reaction System Editor
+ //-----------------------------------------------------------------------------------------
+
+ reactionSystemEditor = new ReactionSystemEditor();
+ modulePane.addTab("Reaction System", reactionSystemEditor);
+ reactionSystem.addObserver(reactionSystemEditor);
+
+
+ //-----------------------------------------------------------------------------------------
+ // Context Automaton Editor
+ //-----------------------------------------------------------------------------------------
+
+ contextAutomatonEditor = new ContextAutomatonEditor(getSize());
+ modulePane.addTab("Context Automaton", contextAutomatonEditor);
+ reactionSystem.addObserver(contextAutomatonEditor);
+
+
+ //-----------------------------------------------------------------------------------------
+ // Transition System Viewer
+ //-----------------------------------------------------------------------------------------
+
+ JPanel transitionSystemPanel = new JPanel(new BorderLayout());
+ transitionSystemViewer = new TransitionSystemViewer(getSize(), false);
+ transitionSystemPanel.add(transitionSystemViewer, BorderLayout.CENTER);
+ reactionSystem.addObserver(transitionSystemViewer);
+
+ JPanel tsCtrlPanel = new JPanel();
+ JButton tsUpdateBtn = new JButton("Update transition system");
+ tsUpdateBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { updateTransitionSystem(); }
+ });
+ tsCtrlPanel.add(tsUpdateBtn);
+
+ JCheckBox tsLockCBox = new JCheckBox("Lock relative nodes positions");
+ tsLockCBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ transitionSystemViewer.lockNodesPositions(tsLockCBox.isSelected());
+ }
+ });
+ tsCtrlPanel.add(tsLockCBox);
+
+ transitionSystemPanel.add(tsCtrlPanel, BorderLayout.NORTH);
+ modulePane.addTab("Transition System", transitionSystemPanel);
+
+ //-----------------------------------------------------------------------------------------
+ // Compressed Transition System Viewer
+ //-----------------------------------------------------------------------------------------
+
+ JPanel compressedTransitionSystemPanel = new JPanel(new BorderLayout());
+ compressedTransitionSystemViewer = new TransitionSystemViewer(getSize(), true);
+ compressedTransitionSystemPanel.add(compressedTransitionSystemViewer, BorderLayout.CENTER);
+ reactionSystem.addObserver(compressedTransitionSystemViewer);
+
+ JPanel ctsCtrlPanel = new JPanel();
+ JButton ctsUpdateBtn = new JButton("Update transition system");
+ ctsUpdateBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { updateTransitionSystem(); }
+ });
+ ctsCtrlPanel.add(ctsUpdateBtn);
+
+ JCheckBox ctsLockCBox = new JCheckBox("Lock relative nodes positions");
+ ctsLockCBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ compressedTransitionSystemViewer.lockNodesPositions(ctsLockCBox.isSelected());
+ }
+ });
+ ctsCtrlPanel.add(ctsLockCBox);
+
+ compressedTransitionSystemPanel.add(ctsCtrlPanel, BorderLayout.NORTH);
+ modulePane.addTab("Compressed Transition System", compressedTransitionSystemPanel);
+
+ //-----------------------------------------------------------------------------------------
+ // Formula Editor
+ //-----------------------------------------------------------------------------------------
+
+ JPanel formulaEditorPanel = new JPanel();
+ formulaEditorPanel.setLayout(new BorderLayout());
+ formulaEditor = new FormulaEditor(reactionSystem);
+ reactionSystem.addObserver(formulaEditor);
+ formulaEditorPanel.add(formulaEditor, BorderLayout.CENTER);
+
+ JPanel formulaCtrlPanel = new JPanel();
+ JButton addButton = new JButton("Add formula");
+ addButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { formulaEditor.addFormula(window); }
+ });
+ formulaCtrlPanel.add(addButton);
+
+ JButton edtButton = new JButton("Edit formula");
+ edtButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ formulaEditor.editFormula(window);
+ }
+ });
+ formulaCtrlPanel.add(edtButton);
+
+ JButton rmButton = new JButton("Remove formulas");
+ rmButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ formulaEditor.removeFormulas();
+ }
+ });
+ formulaCtrlPanel.add(rmButton);
+
+ JButton evalButton = new JButton("Evaluate");
+ evalButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ evaluateFormulas();
+ }
+ });
+ formulaCtrlPanel.add(evalButton);
+
+ JButton resetButton = new JButton("Reset");
+ resetButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ formulaEditor.resetFormulasStatus();
+ }
+ });
+ formulaCtrlPanel.add(resetButton);
+
+ formulaEditorPanel.add(formulaCtrlPanel, BorderLayout.NORTH);
+
+ //-----------------------------------------------------------------------------------------
+ // Split the main window into two parts. The upper one contains details of the reaction
+ // system. The bottom part contains the list of formulas for model checking.
+ //-----------------------------------------------------------------------------------------
+
+ JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, modulePane, formulaEditorPanel);
+ splitPane.setResizeWeight(0.75);
+// splitPane.setOneTouchExpandable(true);
+ splitPane.setDividerSize(8);
+
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(splitPane, BorderLayout.CENTER);
+
+ //-----------------------------------------------------------------------------------------
+ // Update button + Reactant Set Viewer
+ //-----------------------------------------------------------------------------------------
+
+ JPanel topPanel = new JPanel();
+ topPanel.setLayout(new BorderLayout());
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
+
+ JButton updateButton = new JButton("Update
Reaction System");
+ updateButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ updateReactionSystem();
+ }
+ });
+ buttonPanel.add(updateButton);
+ topPanel.add(buttonPanel, BorderLayout.WEST);
+ reactantSetPanel = new ReactantSetPanel();
+ reactionSystem.addObserver(reactantSetPanel);
+ topPanel.add(reactantSetPanel, BorderLayout.CENTER);
+ getContentPane().add(topPanel, BorderLayout.NORTH);
+ }
+
+ private boolean isModified() {
+ return reactionSystemEditor.isModified() || contextAutomatonEditor.isModified();
+ }
+
+ private void clearModificationStatus() {
+ reactionSystemEditor.clearModificationStatus();
+ contextAutomatonEditor.clearModificationStatus();
+ transitionSystemViewer.clearModificationStatus();
+ compressedTransitionSystemViewer.clearModificationStatus();
+ }
+
+ private void updateReactionSystem() {
+ System.out.println("[ReactICS] Update reaction system structure");
+
+ Path rsslFile = reactics.getRSSLFile();
+
+ try {
+ Files.writeString(rsslFile, "");
+
+ PrintWriter rsslStream = new PrintWriter(Files.newBufferedWriter(rsslFile));
+ printRSStructure(rsslStream);
+ rsslStream.close();
+ }
+ catch (IOException ie) {
+ System.err.println("[ReactICS] Could not read transition system structure: " + ie.getMessage());
+
+ JOptionPane.showMessageDialog(this,
+ "Could not read transition system structure\n" + ie.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ reactionSystem.notifyObservers();
+ updateReactantSet();
+ clearModificationStatus();
+ }
+
+ private void updateReactantSet() {
+ TreeSet rset = new TreeSet();
+
+ rset.addAll(reactionSystemEditor.getReactantsSet());
+ rset.addAll(contextAutomatonEditor.getReactantsSet());
+ rset.remove("");
+ reactantSetPanel.updateReactants(rset);
+ }
+
+ private void updateTransitionSystem() {
+ if (isModified()) {
+ updateReactionSystem();
+ }
+ else if (transitionSystemViewer.isComputed()) {
+ // Skip re-computing transition system structure
+ return;
+ }
+
+ try {
+ String tsStructure = reactics.getTransitionSystemStructure();
+ transitionSystemViewer.updateTransitionSystemStructure(tsStructure);
+ compressedTransitionSystemViewer.updateTransitionSystemStructure(tsStructure);
+ }
+ catch (IOException ioe) {
+ System.out.println("[ReactICS] I/O Error: " + ioe.getMessage());
+ JOptionPane.showMessageDialog(this,
+ "Could not write reaction system structure to a temporary file\n" + ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ catch (ReacticsRuntimeException rre) {
+ System.out.println("[ReactICS] Runtime Error: " + rre.getMessage());
+ JOptionPane.showMessageDialog(this,
+ rre.getMessage(),
+ "Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void evaluateFormulas() {
+ Collection formulas = formulaEditor.getSelectedFormulas();
+
+ if (isModified())
+ updateReactionSystem();
+
+ Path rsslFile = reactics.getRSSLFile();
+ long originalSize = -1;
+
+ try {
+ originalSize = Files.size(rsslFile);
+ }
+ catch (IOException ioe) {
+ System.err.println("[ReactICS] Error: " + ioe.getMessage());
+ JOptionPane.showMessageDialog(this, ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ for (Formula ff : formulas) {
+ try {
+ System.out.println("[ReactICS] Evaluate formula: " + ff.label);
+
+ Files.writeString(rsslFile, ff.toRSSLString() + "\n", StandardCharsets.UTF_8, StandardOpenOption.APPEND);
+
+ ReacticsRuntime.EvalResult result = reactics.evaluateFormula(ff.label);
+ ff.status = result.result();
+ ff.mcTime = result.mcTime() + " s";
+ ff.totalTime = result.totalTime() + " s";
+ ff.memory = result.memory() + " MB";
+ formulaEditor.refreshStatus();
+ }
+ catch(IOException ioe) {
+ System.err.println("[ReactICS] Error: " + ioe.getMessage());
+ JOptionPane.showMessageDialog(this, ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ catch(ReacticsRuntimeException rre) {
+ System.err.println("[ReactICS] Runtime error: " + rre.getMessage());
+ JOptionPane.showMessageDialog(this,
+ rre.getMessage(),
+ "Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
+
+ ff.status = Formula.FormulaStatus.Invalid;
+ }
+ finally {
+ try {
+ RandomAccessFile raf = new RandomAccessFile(rsslFile.toFile(), "rw");
+ raf.setLength(originalSize);
+ }
+ catch (IOException ioe) {
+ System.err.println("[ReactICS] Error: " + ioe.getMessage());
+ JOptionPane.showMessageDialog(this, ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+
+ private void saveAsXML() {
+ File outFile = fileSelector.selectOutputFile(this, new File(System.getProperty("user.dir")),
+ new FileNameExtensionFilter("Reaction System (XML)", "xml"));
+
+ if(outFile == null)
+ return;
+
+ System.out.println("[ReactICS] Save to XML file: " + outFile.getName());
+
+ try {
+ PrintWriter xmlOut = new PrintWriter(new FileWriter(outFile));
+ xmlOut.println("");
+ reactionSystemEditor.exportToXML(xmlOut);
+ contextAutomatonEditor.exportToXML(xmlOut);
+ formulaEditor.exportToXML(xmlOut);
+ xmlOut.println("");
+ xmlOut.flush();
+ xmlOut.close();
+ }
+ catch(IOException ioe) {
+ JOptionPane.showMessageDialog(this, ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void loadFromXML() {
+ FileNameExtensionFilter fileTypeFilter = new FileNameExtensionFilter("Reaction System (XML)", "xml");
+ JFileChooser fc = new JFileChooser();
+ fc.addChoosableFileFilter(fileTypeFilter);
+ fc.setAcceptAllFileFilterUsed(true);
+ fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
+
+
+ if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+ File xmlFile = fc.getSelectedFile();
+ loadFromXMLFile(xmlFile);
+ setTitle("ReactICS GUI : " + xmlFile.getName());
+ }
+
+ modulePane.setSelectedIndex(0);
+ clearModificationStatus();
+ }
+
+
+ private void loadFromXMLFile(File xmlFile) {
+ reactionSystemEditor.clear();
+ contextAutomatonEditor.clear();
+ reactantSetPanel.clear();
+ transitionSystemViewer.clear();
+ compressedTransitionSystemViewer.clear();
+
+ try {
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ Document doc = dBuilder.parse(xmlFile);
+ doc.getDocumentElement().normalize();
+ reactionSystemEditor.loadFromXML(doc);
+ contextAutomatonEditor.loadFromXML(doc);
+ formulaEditor.loadFromXML(doc);
+ updateReactionSystem();
+ }
+ catch(IOException ioe) {
+ System.err.println("[ReactICS] Error: " + ioe.getMessage());
+
+ JOptionPane.showMessageDialog(this, "Error reading file: " + xmlFile.getName() + "\n" + ioe.getMessage(),
+ "Input error", JOptionPane.ERROR_MESSAGE);
+
+ reactionSystemEditor.clear();
+ contextAutomatonEditor.clear();
+ reactantSetPanel.clear();
+ }
+ catch(ParserConfigurationException pce) {
+ System.err.println("[ReactICS] Parse Configuration Error: " + pce.getMessage());
+
+ JOptionPane.showMessageDialog(this, pce.getMessage(),
+ "Parse Configuration Error", JOptionPane.ERROR_MESSAGE);
+
+ reactionSystemEditor.clear();
+ contextAutomatonEditor.clear();
+ reactantSetPanel.clear();
+ }
+ catch(SAXException se) {
+ System.err.println("[ReactICS] SAXException: " + se.getMessage());
+
+ JOptionPane.showMessageDialog(this, se.getMessage(),
+ "Parse error", JOptionPane.ERROR_MESSAGE);
+
+ reactionSystemEditor.clear();
+ contextAutomatonEditor.clear();
+ reactantSetPanel.clear();
+ }
+ catch(FileStructureError err) {
+ System.err.println("[ReactICS]: File Structure Error: " + err.getMessage());
+
+ JOptionPane.showMessageDialog(this, err.getMessage(),
+ "XML file structure error", JOptionPane.ERROR_MESSAGE);
+
+ reactionSystemEditor.clear();
+ contextAutomatonEditor.clear();
+ reactantSetPanel.clear();
+ }
+ catch(Exception e) {
+ System.out.println("[ReactICS] Error: " + e.getMessage());
+
+ JOptionPane.showMessageDialog(this, "Unexpected error reading file: " + xmlFile.getName() +
+ " " + e.getMessage(),
+ "Input error", JOptionPane.ERROR_MESSAGE);
+
+ reactionSystemEditor.clear();
+ contextAutomatonEditor.clear();
+ reactantSetPanel.clear();
+ }
+ }
+
+
+ private void importFromRSSL() {
+ FileNameExtensionFilter fileTypeFilter = new FileNameExtensionFilter("Reaction System Specification Language (rs)", "rs");
+ JFileChooser fc = new JFileChooser();
+ fc.addChoosableFileFilter(fileTypeFilter);
+ fc.setAcceptAllFileFilterUsed(true);
+ fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
+
+
+ if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+ File rsslFile = fc.getSelectedFile();
+
+ try {
+ File xmlFile = reactics.convertToXMLFile(rsslFile);
+ loadFromXMLFile(xmlFile);
+ } catch (IOException e) {
+ System.err.println("IOException: " + e.getMessage());
+ e.printStackTrace();
+ } catch (ReacticsRuntimeException e) {
+ System.err.println("ReacticsRuntimeException: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ setTitle("ReactICS GUI : " + rsslFile.getName());
+ contextAutomatonEditor.recalculateCoordinates();
+ }
+
+ modulePane.setSelectedIndex(0);
+ clearModificationStatus();
+ }
+
+
+ private void exportToRSSLFile() {
+ File outFile = fileSelector.selectOutputFile(this, new File(System.getProperty("user.dir")),
+ new FileNameExtensionFilter("Reaction System Specification Language (RSSL)", "rs"));
+
+ if(outFile == null)
+ return;
+
+ System.out.println("[ReactICS] Export to RSSL file: " + outFile.getName());
+
+ try {
+ PrintWriter rsslOut = new PrintWriter(new FileWriter(outFile));
+ printRSStructure(rsslOut);
+ rsslOut.close();
+ }
+ catch(IOException ioe) {
+ System.err.println("[ReactICS] Error: " + ioe.getMessage());
+ JOptionPane.showMessageDialog(this, ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+
+ private void exportToISPLFile() {
+ updateReactionSystem();
+
+ if (!reactionSystem.isplTranslationAllowed()) {
+ JOptionPane.showMessageDialog(this,
+ "Context automaton contains guards for some transitions.\n" +
+ "Current version of ReactICS does not support translation to ISPL format in this case.\n",
+ "Translation is not possible", JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+
+ File outFile = fileSelector.selectOutputFile(this, new File(System.getProperty("user.dir")),
+ new FileNameExtensionFilter("Interpreted System Programming Language (ISPL)", "ispl"));
+
+ if (outFile == null)
+ return;
+
+ System.out.println("[ReactICS] Export to ISPL file: " + outFile.getAbsolutePath());
+
+ try {
+ if (!outFile.exists() && !outFile.createNewFile()) {
+ System.err.println("[ReactICS] Error: The file "+ outFile.getName() + " could not be created.");
+ JOptionPane.showMessageDialog(this,
+ "The file " + outFile.getName() + " could not be created.",
+ "Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!outFile.canWrite()){
+ System.err.println("[ReactICS] Error: Cannot write to the file " + outFile.getName());
+ JOptionPane.showMessageDialog(this,
+ "Cannot write to the file " + outFile.getName(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ reactics.exportToISPLFile(outFile);
+ }
+ catch(IOException ioe) {
+ System.err.println("[ReactICS] Error: " + ioe.getMessage());
+ JOptionPane.showMessageDialog(this, ioe.getMessage(),
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ catch(ReacticsRuntimeException rre) {
+ System.err.println("[ReactICS] Runtime error: " + rre.getMessage());
+ JOptionPane.showMessageDialog(this,
+ rre.getMessage(),
+ "ReactICS Runtime Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void printRSStructure(PrintWriter outStream) {
+ outStream.println("options { use-context-automaton; make-progressive; };\n");
+ outStream.println(reactionSystemEditor.toRSSLString());
+ outStream.println(contextAutomatonEditor.toRSSLString());
+ }
+
+ private void showAboutWindow() {
+ AboutWindow awin = AboutWindow.getInstance();
+ awin.show(this);
+ }
+
+ private void showHelpWindow() {
+ HelpWindow hwin = HelpWindow.getInstance();
+ hwin.show(this);
+ }
+
+ public static void main(String args[]) {
+ ReacticsGUI wnd = new ReacticsGUI();
+ wnd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ wnd.setLocationRelativeTo(null);
+ wnd.setVisible(true);
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Class responsible for selecting a file to load / save the reaction system structure
+//-------------------------------------------------------------------------------------------------
+
+class FileSelector {
+ private enum Status {SELECT, APPROVE, CANCEL}
+
+ private static FileSelector instance = null;
+
+ public static synchronized FileSelector getInstance() {
+ if (instance == null)
+ instance = new FileSelector();
+
+ return instance;
+ }
+
+ private FileSelector() { }
+
+ public File selecInputFile(Component parent, File rootDir, FileNameExtensionFilter filter) {
+ Status opStatus = Status.SELECT;
+ JFileChooser fc = new JFileChooser();
+ fc.addChoosableFileFilter(filter);
+ fc.setAcceptAllFileFilterUsed(true);
+ fc.setCurrentDirectory(rootDir);
+ File outFile = null;
+
+ do {
+ if (fc.showOpenDialog(parent) != JFileChooser.APPROVE_OPTION) {
+ opStatus = Status.CANCEL;
+ break;
+ }
+
+ outFile = fc.getSelectedFile();
+ opStatus = Status.APPROVE;
+
+ if(outFile.exists()) {
+ if (JOptionPane.showConfirmDialog(parent,
+ "The file " + outFile.getName() + " already exists.\n" +
+ "Do you wish to overwrite it?", "Warning",
+ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.NO_OPTION) {
+ opStatus = Status.SELECT;
+ }
+ }
+ } while(opStatus == Status.SELECT);
+
+ if(opStatus != Status.APPROVE)
+ outFile = null;
+
+ return outFile;
+ }
+
+ public File selectOutputFile(Component parent, File rootDir, FileNameExtensionFilter filter) {
+ Status opStatus = Status.SELECT;
+ JFileChooser fc = new JFileChooser();
+ fc.addChoosableFileFilter(filter);
+ fc.setAcceptAllFileFilterUsed(true);
+ fc.setCurrentDirectory(rootDir);
+ File outFile = null;
+
+ do {
+ if (fc.showSaveDialog(parent) != JFileChooser.APPROVE_OPTION) {
+ opStatus = Status.CANCEL;
+ break;
+ }
+
+ outFile = fc.getSelectedFile();
+ opStatus = Status.APPROVE;
+
+ if(outFile.exists()) {
+ if (JOptionPane.showConfirmDialog(parent,
+ "The file " + outFile.getName() + " already exists.\n" +
+ "Do you wish to overwrite it?", "Warning",
+ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.NO_OPTION) {
+ opStatus = Status.SELECT;
+ }
+ }
+ } while(opStatus == Status.SELECT);
+
+ if(opStatus != Status.APPROVE)
+ outFile = null;
+
+ return outFile;
+ }
+}
+
+
+/**
+ * General exception class for any error related to input XML file
+ */
+class FileStructureError extends Exception {
+ public FileStructureError(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsRuntime.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsRuntime.java
new file mode 100644
index 0000000..d8c73ea
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReacticsRuntime.java
@@ -0,0 +1,198 @@
+package pl.umk.mat.martinp.reactics;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+
+public class ReacticsRuntime {
+ private static ReacticsRuntime instance = null;
+
+ private String rctPath = "./";
+ private String rctRuntime = "reactics";
+ private Path rsslFile;
+
+ public static synchronized ReacticsRuntime getInstance() throws ReacticsRuntimeException {
+ if (instance == null)
+ instance = new ReacticsRuntime();
+
+ return instance;
+ }
+
+ private ReacticsRuntime() throws ReacticsRuntimeException {
+ try {
+ rsslFile = Files.createTempFile("reaction_system_", ".rs");
+ rsslFile.toFile().deleteOnExit();
+ System.out.println("[ReactICS] Created temporary file: " + rsslFile.toAbsolutePath());
+ }
+ catch (IOException e) {
+ System.err.println("[ReactICS] Problem creating temporary file: + e.getMessage()");
+ throw new ReacticsRuntimeException("Problem creating temporary file:\n" + e.getMessage());
+ }
+ }
+
+ public Path getRSSLFile() {
+ return rsslFile;
+ }
+
+ public void loadConfig(String configFileName) throws ConfigReadingError, InvalidRuntimeException {
+ System.out.println("[ReactICS] Loading configuration from: " + configFileName);
+
+ try {
+ BufferedReader inputStream = new BufferedReader(new FileReader(configFileName));
+ String line;
+
+ while ((line = inputStream.readLine()) != null) {
+ if (line.startsWith("runtime")) {
+ line = line.replaceAll("\\s", "");
+ rctRuntime = line.substring(line.indexOf(":") + 1);
+ }
+ else if (line.startsWith("path")) {
+ line = line.replaceAll("\\s", "");
+ rctPath = line.substring(line.indexOf(":") + 1);
+ }
+ }
+ }
+ catch (IOException e) {
+ throw new ConfigReadingError(e.getMessage());
+ }
+
+ File rctRntFile = new File(rctPath + rctRuntime);
+
+ if (! (rctRntFile.exists() && rctRntFile.canExecute()) ) {
+ throw new InvalidRuntimeException(rctPath + rctRuntime + " cannot be executed");
+ }
+
+ System.out.println("[ReactICS] Using runtime: " + rctPath + rctRuntime);
+ }
+
+ public EvalResult evaluateFormula(String fLabel) throws IOException, ReacticsRuntimeException {
+ // Drop "-B" option if verification time should not be measured
+ Process proc = Runtime.getRuntime().exec(rctPath + rctRuntime + " -B -c " + fLabel + " " + rsslFile.toAbsolutePath(),
+ null, new File(rctPath));
+
+ BufferedReader formResultStream = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ String line;
+ Formula.FormulaStatus result = Formula.FormulaStatus.None;
+ String mcTime = "";
+ String totalTime = "";
+ String memory = "";
+
+ while ((line = formResultStream.readLine()) != null) {
+ if (line.contains("holds")) {
+ result = Formula.FormulaStatus.True;
+ }
+ else if (line.contains("not hold")) {
+ result = Formula.FormulaStatus.False;
+ }
+ else if (line.startsWith("STAT")) {
+ String[] tokens = line.split(";");
+ mcTime = tokens[2];
+ totalTime = tokens[5];
+ memory = tokens[4];
+ }
+
+ //TODO: Handle incorrect formulas
+ }
+
+ StringBuilder errMsg = new StringBuilder();
+ BufferedReader procErrStr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
+ while ((line = procErrStr.readLine()) != null) {
+ errMsg.append(line).append("\n");
+ }
+
+ if (!errMsg.isEmpty())
+ throw new ReacticsRuntimeException(errMsg.toString());
+
+ return new EvalResult(result, mcTime, totalTime, memory);
+ }
+
+ /** Complete Transition System. Context automaton state included. */
+ public String getTransitionSystemStructure() throws IOException, ReacticsRuntimeException {
+ Process proc = Runtime.getRuntime().exec(rctPath + rctRuntime + " -t -X " + rsslFile.toAbsolutePath(),
+ null, new File(rctPath));
+
+ String line;
+ StringBuilder tsString = new StringBuilder();
+ BufferedReader procOutStr = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ while ((line = procOutStr.readLine()) != null) {
+ tsString.append(line).append("\n");
+ }
+
+ StringBuilder errMsg = new StringBuilder();
+ BufferedReader procErrStr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
+ while ((line = procErrStr.readLine()) != null) {
+ errMsg.append(line).append("\n");
+ }
+
+ if (!errMsg.isEmpty())
+ throw new ReacticsRuntimeException(errMsg.toString());
+
+ return tsString.toString();
+ }
+
+ public void exportToISPLFile(File outFile) throws IOException, ReacticsRuntimeException {
+ Process proc = Runtime.getRuntime().exec(rctPath + rctRuntime + " -e " + rsslFile.toAbsolutePath() + " > " + outFile.getAbsolutePath(),
+ null, new File(rctPath));
+
+ String line;
+ BufferedReader procOutStr = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ PrintWriter isplOutStr = new PrintWriter(new FileWriter(outFile));
+ while ((line = procOutStr.readLine()) != null) {
+ isplOutStr.println(line);
+ }
+ isplOutStr.close();
+
+ StringBuilder errMsg = new StringBuilder();
+ BufferedReader procErrStr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
+ while ((line = procErrStr.readLine()) != null) {
+ errMsg.append(line).append("\n");
+ }
+
+ if (!errMsg.isEmpty())
+ throw new ReacticsRuntimeException(errMsg.toString());
+ }
+
+
+ public File convertToXMLFile(File rsslInputFile) throws IOException, ReacticsRuntimeException {
+ File xmlFile = Files.createTempFile("_" + rsslInputFile.getName(), ".xml").toFile();
+ xmlFile.deleteOnExit();
+
+ ProcessBuilder procBuilder = new ProcessBuilder(rctPath + rctRuntime, "-y", rsslInputFile.getAbsolutePath());
+ procBuilder.redirectOutput(new File(xmlFile.getAbsolutePath()));
+ Process proc = procBuilder.start();
+
+ String line;
+ StringBuilder errMsg = new StringBuilder();
+ BufferedReader procErrStr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
+ while ((line = procErrStr.readLine()) != null) {
+ errMsg.append(line).append("\n");
+ }
+
+ if (!errMsg.isEmpty())
+ throw new ReacticsRuntimeException(errMsg.toString());
+
+ return xmlFile;
+ }
+
+
+ record EvalResult(Formula.FormulaStatus result, String mcTime, String totalTime, String memory) {}
+}
+
+class ConfigReadingError extends Exception {
+ public ConfigReadingError(String message) {
+ super(message);
+ }
+}
+
+class InvalidRuntimeException extends Exception {
+ public InvalidRuntimeException(String description) {
+ super(description);
+ }
+}
+
+class ReacticsRuntimeException extends Exception {
+ public ReacticsRuntimeException(String description) {
+ super(description);
+ }
+}
\ No newline at end of file
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/ReactionSystemEditor.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReactionSystemEditor.java
new file mode 100644
index 0000000..51a86c9
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/ReactionSystemEditor.java
@@ -0,0 +1,409 @@
+/** A component for editing the reaction system details */
+
+package pl.umk.mat.martinp.reactics;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Vector;
+
+
+//-------------------------------------------------------------------------------------------------
+/** A graphical component allowing edition and display of a distributed reaction system.
+ * A distributed reaction system contains a number of processes.
+ * Each process contains a list of reactions.
+ */
+public class ReactionSystemEditor extends JPanel implements RSObserver {
+ ReactionSystem rs;
+ private Vector procEditors = new Vector();
+ private JPanel processPanel = new JPanel();
+
+
+ public ReactionSystemEditor() {
+ rs = ReactionSystem.getInstance();
+
+ processPanel.setLayout(new BoxLayout(processPanel, BoxLayout.Y_AXIS));
+
+ final Dimension buttonSize = new Dimension(150, 25);
+
+ JButton addButton = new JButton("New process");
+ addButton.setMaximumSize(buttonSize);
+ addButton.setPreferredSize(buttonSize);
+ addButton.setMaximumSize(buttonSize);
+ addButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ createProcess();
+ }
+ });
+
+ JButton rmButton = new JButton("Remove");
+ rmButton.setMaximumSize(buttonSize);
+ rmButton.setPreferredSize(buttonSize);
+ rmButton.setMaximumSize(buttonSize);
+ rmButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ removeProcesses();
+ }
+ });
+
+ JButton copyButton = new JButton("Copy");
+ copyButton.setMaximumSize(buttonSize);
+ copyButton.setPreferredSize(buttonSize);
+ copyButton.setMaximumSize(buttonSize);
+ copyButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ copyProcesses();
+ }
+ });
+
+ JButton renameButton = new JButton("Rename");
+ renameButton.setMaximumSize(buttonSize);
+ renameButton.setPreferredSize(buttonSize);
+ renameButton.setMaximumSize(buttonSize);
+ renameButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ renameProcess();
+ }
+ });
+
+ JButton upButton = new JButton("Move up");
+ upButton.setMaximumSize(buttonSize);
+ upButton.setPreferredSize(buttonSize);
+ upButton.setMaximumSize(buttonSize);
+ upButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ moveSelectedUp();
+ }
+ });
+
+ JButton downButton = new JButton("Move down");
+ downButton.setMaximumSize(buttonSize);
+ downButton.setPreferredSize(buttonSize);
+ downButton.setMaximumSize(buttonSize);
+ downButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ moveSelectedDown();
+ }
+ });
+
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
+
+ buttonPanel.add(addButton);
+ buttonPanel.add(rmButton);
+ buttonPanel.add(copyButton);
+ buttonPanel.add(renameButton);
+ buttonPanel.add(upButton);
+ buttonPanel.add(downButton);
+
+ this.setLayout(new BorderLayout());
+ this.add(new JScrollPane(processPanel), BorderLayout.CENTER);
+ this.add(buttonPanel, BorderLayout.WEST);
+
+ createProcess();
+ }
+
+ public boolean isModified() {
+ boolean procModified = false;
+
+ for (ProcessEditor pe : procEditors)
+ procModified |= pe.isModified();
+
+ return rs.modified | procModified;
+ }
+
+ public void clearModificationStatus() {
+ for (ProcessEditor pe : procEditors)
+ pe.clearModificationStatus();
+
+ rs.modified = false;
+ }
+
+ public Set getReactantsSet() {
+ HashSet rset = new HashSet();
+
+ for (ProcessEditor pe : procEditors)
+ rset.addAll(pe.getReactantsSet());
+
+ return rset;
+ }
+
+ public void exportToXML(PrintWriter output) {
+ output.println(" ");
+
+ for (ProcessEditor process : procEditors)
+ process.exportToXML(output);
+
+ output.println(" ");
+ }
+
+ public void loadFromXML(Document input) throws ReactionSystemStructureError {
+ NodeList rsSections = input.getElementsByTagName("reaction-system");
+
+ if (rsSections.getLength() == 0 || rsSections.getLength() > 1) {
+ throw new ReactionSystemStructureError("An XML file should contain a single reaction system description.");
+ }
+
+ NodeList processList = rsSections.item(0).getChildNodes();
+
+ for (int idx=0; idx toRemove = new Vector();
+ for (ProcessEditor edt : procEditors) {
+ if (edt.isSelected()) {
+ toRemove.add(edt);
+ }
+ }
+
+ if (toRemove.size() == procEditors.size()) {
+ JOptionPane.showMessageDialog(this, "There should be at least a single process in the system.",
+ "Warning", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ for (ProcessEditor edt : toRemove) {
+ if (rs.removeProcess(edt.getProcess())) {
+ processPanel.remove(edt);
+ procEditors.remove(edt);
+ }
+ else {
+ JOptionPane.showMessageDialog(this, "Process " + edt.getLabel() +
+ " appears in a formula or a context automaton guard.\n" +
+ "Update them before removing the process.",
+ "Warning", JOptionPane.WARNING_MESSAGE);
+ }
+ }
+
+ rs.modified = true;
+ revalidate();
+ }
+
+ private void copyProcesses() {
+ Vector toBeCopied = new Vector();
+
+ for (ProcessEditor edt : procEditors) {
+ if (edt.isSelected()) {
+ toBeCopied.add(edt);
+ }
+ }
+
+ for (ProcessEditor edt : toBeCopied) {
+ ProcessEditor newEdt = new ProcessEditor(rs.copyProcess(edt.getProcess()));
+ processPanel.add(newEdt);
+ procEditors.add(newEdt);
+ }
+
+ rs.modified = true;
+ revalidate();
+ }
+
+ private void renameProcess() {
+ ProcessEditor toRename = null;
+ boolean fail = false;
+
+ for (ProcessEditor edt : procEditors) {
+ if (edt.isSelected()) {
+ if (toRename == null) {
+ toRename = edt;
+ }
+ else {
+ fail = true;
+ break;
+ }
+ }
+ }
+
+ if (fail || toRename == null) {
+ JOptionPane.showMessageDialog(this, "Select a single process to rename.", "Select process", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ String idRegex = "[a-zA-Z][a-zA-Z_0-9:-]*";
+ boolean idOk = false;
+
+ do {
+ String newLabel = JOptionPane.showInputDialog(this, "Process name", toRename.getProcess().label);
+ if (newLabel == null)
+ return;
+
+ newLabel = newLabel.trim();
+
+ if (!newLabel.matches(idRegex)) {
+ JOptionPane.showMessageDialog(this,
+ "Process name should start with a letter and may contain only:
" +
+ "letters, numbers, colons (:), underscores (_) and hyphens (-)",
+ "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ else {
+ idOk = true;
+ rs.renameProcess(toRename.getProcess(), newLabel);
+ toRename.rename(newLabel);
+ rs.notifyObservers();
+ }
+ }
+ while (!idOk);
+
+ }
+
+ private void moveSelectedUp() {
+ Vector toBeMovedIds = new Vector();
+ for (int i = 0; i< procEditors.size(); ++i) {
+ if (procEditors.elementAt(i).isSelected())
+ toBeMovedIds.add(i);
+ }
+
+ for (int pos : toBeMovedIds) {
+ if (pos == 0 || procEditors.elementAt(pos-1).isSelected())
+ continue;
+
+ Collections.swap(procEditors, pos, pos-1);
+ }
+
+ processPanel.removeAll();
+
+ for (ProcessEditor edt : procEditors) {
+ processPanel.add(edt);
+ }
+
+ rs.modified = true;
+ revalidate();
+ }
+
+ private void moveSelectedDown() {
+ Vector toBeMovedIds = new Vector();
+
+ for (int i = procEditors.size()-1; i>=0; --i) {
+ if (procEditors.elementAt(i).isSelected())
+ toBeMovedIds.add(i);
+ }
+
+ for (int pos : toBeMovedIds) {
+ if (pos == procEditors.size()-1 || procEditors.elementAt(pos+1).isSelected())
+ continue;
+
+ Collections.swap(procEditors, pos, pos+1);
+ }
+
+ processPanel.removeAll();
+
+ for (ProcessEditor edt : procEditors) {
+ processPanel.add(edt);
+ }
+
+ rs.modified = true;
+ revalidate();
+ }
+
+ public void onRSUpdate() {
+ repaint();
+ }
+
+}
+
+
+class ReactionSystemStructureError extends FileStructureError {
+ public ReactionSystemStructureError(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionEditor.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionEditor.java
new file mode 100644
index 0000000..b7083af
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionEditor.java
@@ -0,0 +1,208 @@
+package pl.umk.mat.martinp.reactics;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.*;
+
+
+class TransitionEditor extends JDialog {
+ private JTable transitionsList;
+ private TransitionTableModel transitionsModel;
+ private Vector transitions;
+
+ public TransitionEditor() {
+ super();
+
+ // Create reactions list component
+ transitionsModel = new TransitionTableModel();
+ transitionsList = new JTable(transitionsModel);
+
+ Font cellFont = transitionsList.getFont();
+ Font headerFont = transitionsList.getTableHeader().getFont();
+
+ transitionsList.setFont(new Font("Monospaced", 0, cellFont.getSize() + 2));
+ transitionsList.getTableHeader().setFont(new Font(headerFont.getFamily(), Font.BOLD, headerFont.getSize() + 2));
+ transitionsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ getContentPane().setLayout(new BorderLayout());
+
+ JButton addButton = new JButton("Add transition");
+ addButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { addTransition(); }
+ });
+
+ JButton edtButton = new JButton("Edit transition");
+ edtButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { editTransition(); }
+ });
+
+ JButton rmButton = new JButton("Remove transition");
+ rmButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { removeTransitions(); }
+ });
+
+ JButton closeButton = new JButton("Close");
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) { setVisible(false); }
+ });
+
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.add(addButton);
+ buttonPanel.add(edtButton);
+ buttonPanel.add(rmButton);
+ buttonPanel.add(closeButton);
+
+ getContentPane().add(buttonPanel, BorderLayout.NORTH);
+ getContentPane().add(new JScrollPane(transitionsList), BorderLayout.CENTER);
+
+ setPreferredSize(new Dimension(-1, 200));
+ setSize(new Dimension(600, 200));
+ setTitle("Transitions");
+ }
+
+ public void showTransitionEditDialog(Component parent, Collection trans, String from, String to) {
+ setTitle("Transitions for (" + from + " -> " + to + ")");
+ transitions = (Vector) trans;
+ transitionsModel.fireTableDataChanged();
+ setLocationRelativeTo(parent);
+ setVisible(true);
+ }
+
+ private void addTransition() {
+ Transition tr = new Transition("", "");
+
+ if (!showTransitionEditDialog(tr))
+ return;
+
+ transitions.add(tr);
+ transitionsModel.fireTableDataChanged();
+ }
+
+ private void editTransition() {
+ int[] selected = transitionsList.getSelectedRows();
+
+ if (selected.length != 1) {
+ JOptionPane.showMessageDialog(this, "Select a single transition to edit.", "Select transition", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ Transition tr = transitions.get(selected[0]);
+ transitionsList.clearSelection();
+ if (!showTransitionEditDialog(tr)) {
+ return;
+ }
+
+ transitions.setElementAt(tr, selected[0]);
+ transitionsModel.fireTableDataChanged();
+ }
+
+ private void removeTransitions() {
+ int[] selected = transitionsList.getSelectedRows();
+
+ for (int trIdx : selected) {
+ transitions.remove(trIdx);
+ transitionsList.clearSelection();
+ }
+
+ transitionsModel.fireTableDataChanged();
+ }
+
+ private boolean showTransitionEditDialog(Transition tr) {
+ final int Select = 0;
+ final int Approve = 1;
+
+ JTextField contextInput = new JTextField(30);
+ JTextField guardInput = new JTextField(30);
+
+ String contextStr = tr.context;
+ String guardStr = tr.guard;
+
+ contextInput.setToolTipText("Additional entities for processes");
+ guardInput.setToolTipText("Initial conditions for the transition in rsCTL format.");
+
+ JPanel myPanel = new JPanel(new GridBagLayout());
+ GridBagConstraints cs = new GridBagConstraints();
+
+ cs.insets = new Insets(5, 5, 5, 5);
+ cs.anchor = GridBagConstraints.WEST;
+ cs.gridx = 0;
+ cs.gridy = 0;
+ myPanel.add(new JLabel("Context"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 0;
+ myPanel.add(contextInput, cs);
+
+ cs.gridx = 0;
+ cs.gridy = 1;
+ myPanel.add(new JLabel("Guards"), cs);
+
+ cs.gridx = 1;
+ cs.gridy = 1;
+ myPanel.add(guardInput, cs);
+
+ int opStatus = Select;
+
+ do {
+ contextInput.setText(contextStr);
+ guardInput.setText(guardStr);
+
+ int result = JOptionPane.showConfirmDialog(null, myPanel,
+ "Reaction details", JOptionPane.OK_CANCEL_OPTION);
+
+ if (result != JOptionPane.OK_OPTION) {
+ return false;
+ }
+
+ contextStr = contextInput.getText();
+ guardStr = guardInput.getText();
+
+ //TODO: Validate the data entered to the text fields
+ tr.context = contextStr;
+ tr.guard = guardStr;
+
+ opStatus = Approve;
+ }
+ while(opStatus == Select);
+
+ return true;
+ }
+
+
+ class TransitionTableModel extends AbstractTableModel {
+
+ private final String[] columnNames = {"Context", "Guard"};
+
+ public String getColumnName(int colIdx) {
+ return columnNames[colIdx];
+ }
+
+ public int getRowCount() {
+ return transitions.size();
+ }
+
+ public int getColumnCount() {
+ return columnNames.length;
+ }
+
+
+ public Object getValueAt(int row, int column) {
+ Transition tr = transitions.get(row);
+
+ return switch (column) {
+ case 0 -> tr.context;
+ case 1 -> tr.guard;
+ default -> "";
+ };
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return false;
+ }
+ }
+
+}
+
diff --git a/reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionSystemViewer.java b/reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionSystemViewer.java
new file mode 100644
index 0000000..82c8949
--- /dev/null
+++ b/reactics-gui/src/pl/umk/mat/martinp/reactics/TransitionSystemViewer.java
@@ -0,0 +1,367 @@
+package pl.umk.mat.martinp.reactics;
+
+import edu.uci.ics.jung.algorithms.layout.*;
+import edu.uci.ics.jung.algorithms.layout.util.Relaxer;
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.visualization.VisualizationViewer;
+import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
+import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
+import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
+import edu.uci.ics.jung.visualization.renderers.Renderer;
+import org.apache.commons.collections15.Transformer;
+
+import javax.swing.JPanel;
+import javax.swing.JToolTip;
+import javax.swing.Icon;
+import javax.swing.border.EtchedBorder;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class TransitionSystemViewer extends JPanel implements RSObserver {
+
+ //---------------------------------------------------------------
+ // Visual configuration settings
+ //---------------------------------------------------------------
+
+ private static final Color pickedNodeColor = Color.yellow;
+ private static final Color edgeColor = Color.black;
+ private static final Font nodeLabelFont = new Font("Helvetica", Font.BOLD, 20);
+ private static final Font toolTipFont = new Font("Helvetica", Font.PLAIN, 14);
+ private static final Stroke basicEdgeArrow = new BasicStroke(3.0f);
+
+ private static Dimension initialEditorSize;
+
+ private VisualizationViewer graphViewer;
+ private Layout graphLayout;
+ DefaultModalGraphMouse graphMouse;
+
+ //---------------------------------------------------------------
+ // Transition system graph
+ //---------------------------------------------------------------
+
+ private DirectedSparseGraph tsGraph;
+ private boolean isCompressed;
+
+ // Is transition system already computed?
+ // Used to avoid unnecessary computation of the same graph.
+ private boolean computed = false;
+
+ // compressed == true => context automaton state is not taken into account.
+ public TransitionSystemViewer(Dimension size, boolean compressed) {
+ this.setLayout(new BorderLayout());
+ tsGraph = new DirectedSparseGraph();
+ this.isCompressed = compressed;
+ initialEditorSize = size;
+
+ createGraphViever();
+ lockNodesPositions(false);
+ }
+
+ // Is transition system graph already computed?
+ boolean isComputed() { return computed; }
+
+ void clearModificationStatus() { computed = false; }
+
+ private void createGraphViever() {
+ graphLayout = new FRLayout(tsGraph);
+
+ graphViewer = new VisualizationViewer(graphLayout, initialEditorSize) {
+ public JToolTip createToolTip() {
+ JToolTip tooltip = super.createToolTip();
+ tooltip.setFont(toolTipFont);
+ return tooltip;
+ }
+ };
+
+ // Nodes labels
+ graphViewer.getRenderContext().setVertexLabelTransformer(
+ new ToStringLabeller());
+ graphViewer.getRenderer().getVertexLabelRenderer()
+ .setPosition(Renderer.VertexLabel.Position.CNTR);
+ graphViewer.getRenderContext().setVertexFontTransformer(
+ new Transformer() {
+ public Font transform(TSState st) {
+ return nodeLabelFont;
+ }
+ });
+
+ // Nodes colour, background, etc.
+ graphViewer.getRenderContext().setVertexIconTransformer(new Transformer() {
+ public Icon transform(final TSState v) {
+ Color nodeColor = null;
+
+ if(graphViewer.getPickedVertexState().isPicked(v))
+ nodeColor = pickedNodeColor;
+ else
+ nodeColor = v.getFillColor();
+
+ return (v.isInitial() ? new SquareIcon(nodeColor, v.getWidth()) : new CircleIcon(nodeColor, v.getWidth()));
+
+ }});
+
+ graphViewer.getRenderContext().setEdgeShapeTransformer(new ConditionalEdgeShape<>(1.5f));
+
+ // Edges
+ graphViewer.getRenderContext().setEdgeArrowStrokeTransformer(new Transformer() {
+ public Stroke transform(TSTransition tr) {
+ return basicEdgeArrow;
+ }
+ });
+
+ // For interactive graph editing
+ graphMouse = new DefaultModalGraphMouse();
+ graphViewer.setGraphMouse(graphMouse);
+ graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
+
+ graphViewer.setVertexToolTipTransformer(new Transformer() {
+ public String transform(TSState st) {
+ return st.getTooltipText();
+ }
+ });
+
+ graphViewer.setBackground(Color.white);
+ graphViewer.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
+
+ add(graphViewer, BorderLayout.CENTER);
+ }
+
+
+ public void updateTransitionSystemStructure(String tsString) {
+ // Clear graph
+ clear();
+
+ // Load graph structure
+ int nextId = 1;
+
+ HashMap nodes = new HashMap<>();
+ TSState src = null;
+
+ for (String line : tsString.split("\\R")) {
+ char type = line.charAt(0);
+ line = line.substring(2).strip();
+ int end = line.lastIndexOf('}');
+ String automatonState = line.substring(end + 1, line.length() - 1).strip();
+ String stateStr = line.substring(line.indexOf('{'), end + 1);
+ TSState state = nodes.get( isCompressed ? stateStr : line);
+
+ if (state == null) {
+ state = new TSState(nextId++, stateStr, isCompressed ? null : automatonState);
+ nodes.put(isCompressed ? stateStr : line, state);
+ tsGraph.addVertex(state);
+ }
+
+ if (type == 'G') {
+ src = state;
+ }
+ else if (type == 's') {
+ tsGraph.addEdge(new TSTransition(), src, state);
+ }
+ }
+
+ // Find the initial state
+ for (TSState state : tsGraph.getVertices()) {
+ if (tsGraph.getInEdges(state).size() == 0) {
+ state.setAsInitial();
+ break;
+ }
+ }
+
+ relax();
+ graphViewer.repaint();
+ computed = true;
+ }
+
+
+ public void lockNodesPositions(boolean lock) {
+ if (lock)
+ graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
+ else
+ graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
+ }
+
+
+ public void clear() {
+ for (TSState st : new java.util.ArrayList(tsGraph.getVertices())) {
+ tsGraph.removeVertex(st);
+ }
+
+ repaint();
+ }
+
+
+ /** For better vertex placement */
+ private void relax() {
+ graphLayout.initialize();
+ graphLayout.setSize(graphViewer.getSize());
+ Relaxer relaxer = graphViewer.getModel().getRelaxer();
+
+ if (relaxer != null) {
+ relaxer.stop();
+ relaxer.prerelax();
+ relaxer.relax();
+ }
+ }
+
+
+ //---------------------------------------------------------------------------------------------
+ // For drawing the initial state
+ //---------------------------------------------------------------------------------------------
+
+ static class SquareIcon implements Icon {
+ private final Color fillColor;
+ private final int width;
+
+ public SquareIcon(Color fColor, int width) {
+ fillColor = fColor;
+ this.width = width;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.setColor(fillColor);
+ g.fillRect(x, y, width, width);
+ g.setColor(Color.black);
+ g.drawRect(x, y, width, width);
+ }
+
+ public int getIconWidth() {
+ return width;
+ }
+
+ public int getIconHeight() {
+ return width;
+ }
+ }
+
+
+ //---------------------------------------------------------------------------------------------
+ // For drawing all non-initial states
+ //---------------------------------------------------------------------------------------------
+
+ static class CircleIcon implements Icon {
+ private final Color fillColor;
+ private final int radius;
+
+ public CircleIcon(Color fColor, int radius) {
+ fillColor = fColor;
+ this.radius = radius;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ g.setColor(fillColor);
+ g.fillOval(x, y, radius, radius);
+ g.setColor(Color.black);
+ g.drawOval(x, y, radius, radius);
+ }
+
+ public int getIconWidth() {
+ return radius;
+ }
+
+ public int getIconHeight() {
+ return radius;
+ }
+ }
+
+ public void onRSUpdate() {
+ clear();
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Single node of the Transition system graph
+//-------------------------------------------------------------------------------------------------
+
+class TSState {
+
+ public final static Color fillColor = Color.lightGray;
+ public final static Color initialColor = new Color(102, 178, 255);
+
+ private static final int nodeWidth = 30;
+ private static final int nodeHeight = 30;
+
+ private final int id;
+ private final String rsDetails;
+ private final String toolTipText;
+ private final String aState;
+ private boolean initial = false;
+
+ public TSState(int id, String stateStr) {
+ this.id = id;
+ this.rsDetails = formatRSDetails(stateStr.substring(1, stateStr.length() - 1).strip());
+ this.aState = null;
+ this.toolTipText = formatToolTipText();
+ }
+
+ public TSState(int id, String stateStr, String aState) {
+ this.id = id;
+ this.rsDetails = formatRSDetails(stateStr.substring(1, stateStr.length() - 1).strip());
+ this.aState = aState;
+ this.toolTipText = formatToolTipText();
+ }
+
+ public boolean isInitial() {
+ return initial;
+ }
+
+ public void setAsInitial() {
+ initial = true;
+ }
+
+ public String toString() {
+ return String.valueOf(id);
+ }
+
+ public int getWidth() {
+ return nodeWidth;
+ }
+
+ public int getHeight() {
+ return nodeHeight;
+ }
+
+ Color getFillColor() {
+ return initial ? initialColor : fillColor;
+ }
+
+ public String getTooltipText() {
+ return toolTipText;
+ }
+
+ private String formatRSDetails(String rsDetails) {
+ return rsDetails.replaceAll("\\} (proc\\d+=\\{)", "}
$1");
+ }
+
+ private String formatToolTipText() {
+ Pattern pattern = Pattern.compile("\\w+=\\{[^}]*\\}");
+ Matcher matcher = pattern.matcher(rsDetails);
+
+ ArrayList blocks = new ArrayList();
+
+ while (matcher.find()) {
+ blocks.add(matcher.group());
+ }
+
+ if (aState != null)
+ return "" +
+ "CA " + aState+ "
" +
+ String.join("
", blocks) +
+ "";
+ else
+ return "" +
+ String.join("
", blocks) +
+ "";
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// Single edge of the Transition system graph
+//-------------------------------------------------------------------------------------------------
+
+class TSTransition { }
--
2.53.0
From f0019619b37ca541200258a67139fcd0a3f987e2 Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Fri, 10 Apr 2026 18:16:49 +0100
Subject: [PATCH 11/12] Refactor + tests (#6)
- Minor codebase clean-up
- Slight reorganisation of examples + tests so that we don't break stuff by accident
- CI tests
---
.github/workflows/tests.yml | 99 ++++
TESTING.md | 65 +++
examples/bdd/generators/gen_asm.py | 103 ++++
examples/bdd/generators/gen_drs.py | 33 +-
examples/bdd/generators/gen_tgc.py | 151 +++---
.../bdd/generators/old_syntax/gen_abstract.py | 62 +++
examples/bdd/generators/old_syntax/gen_bc.py | 88 +++
.../bdd/generators/old_syntax/gen_mutex.py | 71 +++
examples/bdd/{tgc.rs => tgc.drs} | 0
examples/bdd/tgc4.drs | 70 +++
reactics-bdd/ctx_aut.cc | 7 +-
reactics-bdd/ctx_aut.hh | 3 -
reactics-bdd/drs_benchmark.sh | 2 +-
reactics-bdd/formrsctlk.cc | 49 --
reactics-bdd/formrsctlk.hh | 46 --
reactics-bdd/in/{hsr.rs => hsr.drs} | 0
reactics-bdd/in/{hsr_ca.rs => hsr_ca.drs} | 0
reactics-bdd/in/{hsr_drs.rs => hsr_drs.drs} | 0
.../in/old_syntax/{bc32.rs => bc32.drs} | 0
.../in/old_syntax/{bc8.rs => bc8.drs} | 0
.../in/old_syntax/{coffee.rs => coffee.drs} | 0
.../in/old_syntax/{expr.rs => expr.drs} | 0
.../{simple_I1.rs => simple_I1.drs} | 0
.../in/old_syntax/{test.rs => test.drs} | 0
reactics-bdd/in/old_syntax/{u1.rs => u1.drs} | 0
reactics-bdd/in/old_syntax/{u2.rs => u2.drs} | 0
reactics-bdd/in/scripts/bench_bc.sh | 6 +-
reactics-bdd/in/scripts/bench_mutex.sh | 6 +-
reactics-bdd/in/scripts/bench_pipe.sh | 6 +-
reactics-bdd/in/scripts/benchmark.sh | 6 +-
reactics-bdd/in/scripts/benchmark_abs1.sh | 6 +-
reactics-bdd/in/scripts/benchmark_abs1_PT.sh | 6 +-
reactics-bdd/in/scripts/benchmark_bc.sh | 6 +-
reactics-bdd/in/scripts/benchmark_mutex.sh | 6 +-
reactics-bdd/in/scripts/benchmark_mutex_PT.sh | 6 +-
reactics-bdd/in/scripts/gen_abstract1.py | 47 --
reactics-bdd/in/scripts/gen_asm.py | 93 ----
reactics-bdd/in/scripts/gen_bc.py | 118 -----
reactics-bdd/in/scripts/gen_mutex.py | 76 ---
reactics-bdd/in/scripts/gen_tgc_sc.py | 117 ----
reactics-bdd/in/{trivial.rs => trivial.drs} | 0
reactics-bdd/mc.cc | 12 +-
reactics-bdd/reactics.cc | 17 +-
reactics-bdd/rs.cc | 48 +-
reactics-bdd/rs.hh | 1 +
reactics-bdd/symrs.cc | 108 +---
reactics-bdd/symrs.hh | 13 -
reactics-bdd/{test.rs => test.drs} | 0
reactics-smt/rs/context_automaton.py | 12 +-
.../context_automaton_with_concentrations.py | 9 +-
reactics-smt/rs/extended_context_automaton.py | 2 +-
reactics-smt/rs/reaction_system.py | 7 +-
.../rs/reaction_system_with_concentrations.py | 47 +-
...action_system_with_concentrations_param.py | 18 +-
reactics-smt/rs_testing.py | 2 -
reactics-smt/rssmt.py | 1 -
reactics-smt/smt/__init__.py | 1 -
reactics-smt/smt/smt_checker_rs.py | 2 +-
reactics-smt/smt/smt_checker_rsc.py | 3 -
reactics-smt/smt/smt_checker_rsc_param.py | 3 -
run_tests.sh | 3 +
tests/expected/tgc4_mc.txt | 8 +
tests/expected/tgc4_print.txt | 58 ++
tests/expected/tgc4_states.txt | 80 +++
tests/expected/tgc_mc.txt | 8 +
tests/expected/tgc_print.txt | 47 ++
tests/expected/tgc_reactions.txt | 23 +
tests/expected/tgc_states.txt | 32 ++
tests/expected/trivial_print.txt | 17 +
tests/expected/trivial_reactions.txt | 8 +
tests/expected/trivial_states.txt | 3 +
tests/run_all_tests.sh | 85 +++
tests/run_tests.sh | 69 +++
tests/test_smt.py | 501 ++++++++++++++++++
74 files changed, 1672 insertions(+), 930 deletions(-)
create mode 100644 .github/workflows/tests.yml
create mode 100644 TESTING.md
create mode 100644 examples/bdd/generators/gen_asm.py
create mode 100644 examples/bdd/generators/old_syntax/gen_abstract.py
create mode 100644 examples/bdd/generators/old_syntax/gen_bc.py
create mode 100644 examples/bdd/generators/old_syntax/gen_mutex.py
rename examples/bdd/{tgc.rs => tgc.drs} (100%)
create mode 100644 examples/bdd/tgc4.drs
rename reactics-bdd/in/{hsr.rs => hsr.drs} (100%)
rename reactics-bdd/in/{hsr_ca.rs => hsr_ca.drs} (100%)
rename reactics-bdd/in/{hsr_drs.rs => hsr_drs.drs} (100%)
rename reactics-bdd/in/old_syntax/{bc32.rs => bc32.drs} (100%)
rename reactics-bdd/in/old_syntax/{bc8.rs => bc8.drs} (100%)
rename reactics-bdd/in/old_syntax/{coffee.rs => coffee.drs} (100%)
rename reactics-bdd/in/old_syntax/{expr.rs => expr.drs} (100%)
rename reactics-bdd/in/old_syntax/{simple_I1.rs => simple_I1.drs} (100%)
rename reactics-bdd/in/old_syntax/{test.rs => test.drs} (100%)
rename reactics-bdd/in/old_syntax/{u1.rs => u1.drs} (100%)
rename reactics-bdd/in/old_syntax/{u2.rs => u2.drs} (100%)
delete mode 100755 reactics-bdd/in/scripts/gen_abstract1.py
delete mode 100755 reactics-bdd/in/scripts/gen_asm.py
delete mode 100755 reactics-bdd/in/scripts/gen_bc.py
delete mode 100755 reactics-bdd/in/scripts/gen_mutex.py
delete mode 100755 reactics-bdd/in/scripts/gen_tgc_sc.py
rename reactics-bdd/in/{trivial.rs => trivial.drs} (100%)
rename reactics-bdd/{test.rs => test.drs} (100%)
create mode 100755 run_tests.sh
create mode 100644 tests/expected/tgc4_mc.txt
create mode 100644 tests/expected/tgc4_print.txt
create mode 100644 tests/expected/tgc4_states.txt
create mode 100644 tests/expected/tgc_mc.txt
create mode 100644 tests/expected/tgc_print.txt
create mode 100644 tests/expected/tgc_reactions.txt
create mode 100644 tests/expected/tgc_states.txt
create mode 100644 tests/expected/trivial_print.txt
create mode 100644 tests/expected/trivial_reactions.txt
create mode 100644 tests/expected/trivial_states.txt
create mode 100755 tests/run_all_tests.sh
create mode 100755 tests/run_tests.sh
create mode 100644 tests/test_smt.py
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..6f61a61
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,99 @@
+name: Tests
+
+on:
+ push:
+ branches: [master, refactor]
+ pull_request:
+ branches: [master]
+
+jobs:
+ bdd:
+ name: BDD tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install system dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y g++ make bison flex
+
+ - name: Build CUDD
+ working-directory: reactics-bdd
+ run: ./build_cudd.sh
+
+ - name: Build ReactICS BDD module
+ working-directory: reactics-bdd
+ run: make
+
+ - name: Run BDD tests
+ run: bash tests/run_tests.sh
+
+ smt:
+ name: SMT tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install Python dependencies
+ run: pip install z3-solver pytest
+
+ - name: Run SMT tests
+ run: python3 -m pytest tests/test_smt.py -v
+
+ generators:
+ name: Generator tests
+ runs-on: ubuntu-latest
+ needs: bdd
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install system dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y g++ make bison flex
+
+ - name: Build CUDD
+ working-directory: reactics-bdd
+ run: ./build_cudd.sh
+
+ - name: Build ReactICS BDD module
+ working-directory: reactics-bdd
+ run: make
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Run generator tests
+ run: |
+ REACTICS="$PWD/reactics-bdd/reactics"
+ GENERATORS="$PWD/examples/bdd/generators"
+ failed=0
+ check_gen() {
+ local name="$1" gen_cmd="$2" property="$3" expected="$4"
+ local tmpfile=$(mktemp /tmp/reactics_gen_XXXXXX.drs)
+ eval "$gen_cmd" > "$tmpfile" 2>&1
+ local result=$("$REACTICS" -c "$property" "$tmpfile" 2>&1 | grep -oE "(holds|does not hold)" || true)
+ rm -f "$tmpfile"
+ if [ "$result" = "$expected" ]; then
+ echo " PASS: $name"
+ else
+ echo " FAIL: $name (expected '$expected', got '$result')"
+ failed=1
+ fi
+ }
+ check_gen "tgc(3) f1" "python3 $GENERATORS/gen_tgc.py 3" f1 "holds"
+ check_gen "tgc(3) f3" "python3 $GENERATORS/gen_tgc.py 3" f3 "holds"
+ check_gen "tgc(4) f2" "python3 $GENERATORS/gen_tgc.py 4" f2 "holds"
+ check_gen "asm(3) f1" "python3 $GENERATORS/gen_asm.py 3" f1 "holds"
+ check_gen "asm(3) f2" "python3 $GENERATORS/gen_asm.py 3" f2 "holds"
+ check_gen "drs(2,2,2,a) f0" "python3 $GENERATORS/gen_drs.py 2 2 2 a" f0 "does not hold"
+ check_gen "drs(2,2,2,b) f0" "python3 $GENERATORS/gen_drs.py 2 2 2 b" f0 "holds"
+ exit $failed
diff --git a/TESTING.md b/TESTING.md
new file mode 100644
index 0000000..1c99d4b
--- /dev/null
+++ b/TESTING.md
@@ -0,0 +1,65 @@
+# Testing ReactICS
+
+## Prerequisites
+
+- **BDD module**: compiled (`./reactics setup` or `make` in `reactics-bdd/`)
+- **SMT module**: Python 3 with `z3-solver` and `pytest` installed
+
+```
+pip install z3-solver pytest
+```
+
+## Running all tests
+
+```
+./run_tests.sh
+```
+
+This runs the BDD, SMT, and generator test suites in sequence.
+
+## BDD tests
+
+```
+bash tests/run_tests.sh
+```
+
+10 regression tests that run the compiled `reactics-bdd/reactics` binary
+against example `.drs` files and compare output to saved baselines in
+`tests/expected/`. Covers:
+
+- Parsed system printing (`-P`)
+- Reaction listing (`-r`)
+- Reachable state enumeration (`-s`)
+- RSCTLK model checking (`-c`) for properties f1--f4
+
+Input files: `examples/bdd/tgc.drs`, `examples/bdd/tgc4.drs`,
+`reactics-bdd/in/trivial.drs`.
+
+To update baselines after an intentional output change, delete the
+relevant file in `tests/expected/` and re-run; the test will regenerate it.
+
+## SMT tests
+
+```
+python3 -m pytest tests/test_smt.py -v
+```
+
+23 pytest tests exercising the Python API directly. Organised by layer:
+
+- **Data model** -- ReactionSystem, concentrations, context automata,
+ formula construction (BagDescription, rsLTL)
+- **SmtCheckerRS** -- basic reachability (no concentrations)
+- **SmtCheckerRSC** -- reachability and rsLTL model checking with
+ concentrations (chain reaction, heat shock response)
+- **SmtCheckerRSCParam** -- parametric verification
+- **RSC-to-RS translation** -- verifying the translated system
+
+## Generator tests
+
+7 tests that run each generator (from `examples/bdd/generators/`),
+feed the output into the BDD model checker, and verify the expected
+verification result (holds / does not hold).
+
+Generators using old syntax (`examples/bdd/generators/old_syntax/`)
+are not tested this way as they are not compatible with the current
+parser.
diff --git a/examples/bdd/generators/gen_asm.py b/examples/bdd/generators/gen_asm.py
new file mode 100644
index 0000000..0f2e169
--- /dev/null
+++ b/examples/bdd/generators/gen_asm.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+"""
+Generator for the Assembly-line (ASM) benchmark.
+
+Produces a distributed reaction system modelling an assembly line
+where N sequential processes pass activation tokens. Each process
+cycles through entities a -> y,b -> c -> d, and the final process
+signals 'done'. Uses a context automaton for scheduling.
+"""
+
+import argparse
+
+
+OPTIONS = "options { use-context-automaton; make-progressive; };\n"
+
+PROC_TEMPLATE = """
+ proc{i} {{
+ {{{{a}}, {{s}} -> {{y}}}};
+ {{{{y}}, {{s}} -> {{y}}}};
+ {{{{a}}, {{s}} -> {{b}}}};
+ {{{{b}}, {{s}} -> {{c}}}};
+ {{{{c}}, {{s}} -> {{d}}}};
+ {{{{d,y}}, {{s}} -> {{dy}}}};
+ }};
+"""
+
+FINAL_PROC = """
+ procFinal {
+ {{done}, {s} -> {done}};
+ };
+"""
+
+CA_TEMPLATE = """
+context-automaton {{
+ states {{ init, act }};
+ init-state {{ init }};
+ transitions {{
+{transitions}
+ }};
+}};
+"""
+
+PROPERTY_TEMPLATE = '\nrsctlk-property {{ {name} : {formula} }};\n'
+
+
+def generate(n):
+ out = OPTIONS
+ out += "\nreactions {\n"
+ for i in range(1, n + 1):
+ out += PROC_TEMPLATE.format(i=i)
+ out += FINAL_PROC
+ out += "};\n"
+
+ indent = 8 * " "
+ transitions = ""
+
+ # init -> act: first process gets activated
+ transitions += "{indent}{{ proc1={{a}} }}: init -> act;\n".format(indent=indent)
+
+ # act -> act: process stays idle (dy not produced)
+ for i in range(1, n + 1):
+ transitions += "{indent}{{ proc{i}={{}} }}: act -> act : ~proc{i}.dy;\n".format(
+ indent=indent, i=i)
+
+ # act -> act: next process gets activated when previous produces 'dy'
+ for i in range(2, n + 1):
+ transitions += "{indent}{{ proc{i}={{a}} }}: act -> act : proc{prev}.dy;\n".format(
+ indent=indent, i=i, prev=i - 1)
+
+ # act -> act: final process signals done when all have produced 'dy'
+ all_dy = " AND ".join("proc{}.dy".format(i) for i in range(1, n + 1))
+ transitions += "{indent}{{ procFinal={{done}} }}: act -> act : {guard};\n".format(
+ indent=indent, guard=all_dy)
+
+ out += CA_TEMPLATE.format(transitions=transitions)
+
+ # f1: the assembly line eventually completes
+ out += PROPERTY_TEMPLATE.format(name="f1", formula="EF( procFinal.done )")
+
+ # f2: last process knows its predecessor has produced 'y'
+ f2 = "AG( proc{n}.d IMPLIES K[proc{n}]( proc{prev}.y ) )".format(n=n, prev=n - 1)
+ out += PROPERTY_TEMPLATE.format(name="f2", formula=f2)
+
+ # x1: negation of f1 (sanity check -- should also hold)
+ out += PROPERTY_TEMPLATE.format(name="x1", formula="EF( ~procFinal.done )")
+
+ return out
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("n", type=int, help="number of processes (must be > 1)")
+ args = parser.parse_args()
+
+ if args.n < 2:
+ parser.error("number of processes must be > 1")
+
+ print(generate(args.n))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/bdd/generators/gen_drs.py b/examples/bdd/generators/gen_drs.py
index c076f7a..c1f22a7 100755
--- a/examples/bdd/generators/gen_drs.py
+++ b/examples/bdd/generators/gen_drs.py
@@ -1,6 +1,15 @@
#!/usr/bin/env python3
+"""
+Generator for the DRS (Distributed Reaction Systems) epistemic benchmark.
+
+Produces a distributed reaction system with epistemic properties
+(knowledge operators). The system models signal transduction cascades
+with configurable depth (x), number of components (y), threshold (z),
+and context automaton variant (a or b).
+"""
import sys
+import argparse
import itertools
@@ -272,18 +281,20 @@ class DRSGenerator:
def main():
- if len(sys.argv) < 1 + 4:
- print(f"Usage: {sys.argv[0]} ")
- print("\twhere x,y,z >= 2 and y >= z")
- print("\tAut: a, b")
- sys.exit(1)
+ parser = argparse.ArgumentParser(description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("x", type=int, help="cascade depth (>= 2)")
+ parser.add_argument("y", type=int, help="number of components (>= 2, >= z)")
+ parser.add_argument("z", type=int, help="threshold (>= 2, <= y)")
+ parser.add_argument("aut", choices=["a", "b"], help="automaton variant")
+ args = parser.parse_args()
- g = DRSGenerator(
- x=int(sys.argv[1]),
- y=int(sys.argv[2]),
- z=int(sys.argv[3]),
- aut=sys.argv[4],
- )
+ if args.x < 2 or args.y < 2 or args.z < 2:
+ parser.error("x, y, z must all be >= 2")
+ if args.y < args.z:
+ parser.error("y must be >= z")
+
+ DRSGenerator(x=args.x, y=args.y, z=args.z, aut=args.aut)
if __name__ == "__main__":
diff --git a/examples/bdd/generators/gen_tgc.py b/examples/bdd/generators/gen_tgc.py
index d4bfcf0..32b8654 100755
--- a/examples/bdd/generators/gen_tgc.py
+++ b/examples/bdd/generators/gen_tgc.py
@@ -1,13 +1,20 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+"""
+Generator for the Traffic Guard Controller (TGC) benchmark.
-from sys import argv,exit
-
-OPTIONS_STR = """
-options { use-context-automaton; make-progressive; };
+Produces a distributed reaction system where N processes compete
+for access to a shared resource, governed by a context automaton
+with green/red states. Generates RSCTLK properties for liveness,
+reachability, and mutual exclusion (epistemic).
"""
-PROC_STR = """
- proc{:d} {{
+import argparse
+
+
+OPTIONS = "options { use-context-automaton; make-progressive; };\n"
+
+PROC_TEMPLATE = """
+ proc{i} {{
{{{{out}}, {{}} -> {{approach}}}};
{{{{approach}}, {{req}} -> {{req}}}};
{{{{allowed}}, {{}} -> {{in}}}};
@@ -16,102 +23,94 @@ PROC_STR = """
}};
"""
-CA_STR = """
+CA_TEMPLATE = """
context-automaton {{
states {{ init, green, red }};
init-state {{ init }};
transitions {{
-{:s}
+{transitions}
}};
}};
"""
-PROPERTY_STR = """
-rsctlk-property {{ {:s} : {:s} }};
-"""
+PROPERTY_TEMPLATE = '\nrsctlk-property {{ {name} : {formula} }};\n'
-#################################################################
+def conj(fmt, indices, sep=" AND "):
+ return sep.join(fmt.format(i=i) for i in indices)
-if len(argv) < 1:
- print("Usage: {:s} ".format(argv[0]))
- exit(100)
-n = int(argv[1])
+def generate(n):
+ out = OPTIONS
+ out += "\nreactions {\n"
+ for i in range(n):
+ out += PROC_TEMPLATE.format(i=i)
+ out += "};\n"
-assert n > 1, "number of proc must be > 1"
+ indent = 8 * " "
+ transitions = ""
-out = ""
+ # init -> green: all processes start with 'out'
+ transitions += indent + "{ "
+ transitions += " ".join("proc{:d}={{out}}".format(i) for i in range(n))
+ transitions += " }: init -> green;\n"
-out += OPTIONS_STR
-out += "reactions {\n"
-for i in range(n):
- out += PROC_STR.format(i)
-out += "};\n"
+ # green -> red: a process requests
+ for i in range(n):
+ transitions += "{indent}{{ proc{i}={{allowed}} }}: green -> red : proc{i}.req;\n".format(
+ indent=indent, i=i)
-transitions = ""
+ # green -> green: no requests pending
+ no_req = conj("~proc{i}.req", range(n))
+ for i in range(n):
+ transitions += "{indent}{{ proc{i}={{}} }}: green -> green : {guard};\n".format(
+ indent=indent, i=i, guard=no_req)
-init_trans = 8*" " + "{ "
-for i in range(n):
- init_trans += "proc{:d}={{out}} ".format(i)
-init_trans += "}: init -> green;\n"
-transitions += init_trans
+ # red -> green: a process leaves
+ for i in range(n):
+ transitions += "{indent}{{ proc{i}={{}} }}: red -> green : proc{i}.leave;\n".format(
+ indent=indent, i=i)
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{allowed}} }}: green -> red : proc{:d}.req;\n".format(8*" ", i, i)
+ # red -> red: no process leaves
+ no_leave = conj("~proc{i}.leave", range(n))
+ for i in range(n):
+ transitions += "{indent}{{ proc{i}={{}} }}: red -> red : {guard};\n".format(
+ indent=indent, i=i, guard=no_leave)
-no_req_cond = "~proc0.req"
-for i in range(1, n):
- no_req_cond += " AND ~proc{:d}.req".format(i)
+ out += CA_TEMPLATE.format(transitions=transitions)
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{}} }}: green -> green : {:s};\n".format(8*" ", i, no_req_cond)
+ # f1: each process can eventually enter
+ f1 = conj("EF( EX( proc{i}.in ) )", range(n))
+ out += PROPERTY_TEMPLATE.format(name="f1", formula=f1)
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{}} }}: red -> green : proc{:d}.leave;\n".format(8*" ", i, i)
+ # f2: all processes can simultaneously approach
+ f2 = "EF( " + conj("proc{i}.approach", range(n)) + " )"
+ out += PROPERTY_TEMPLATE.format(name="f2", formula=f2)
-no_leave_cond = "~proc0.leave"
-for i in range(1, n):
- no_leave_cond += " AND ~proc{:d}.leave".format(i)
+ # f3: mutual exclusion (knowledge)
+ others_not_in = conj("~proc{i}.in", range(1, n))
+ f3 = "AG( proc0.in IMPLIES K[proc0]({}) )".format(others_not_in)
+ out += PROPERTY_TEMPLATE.format(name="f3", formula=f3)
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{}} }}: red -> red : {:s};\n".format(8*" ", i, no_leave_cond)
+ # f4: mutual exclusion (common knowledge)
+ all_agents = conj("proc{i}", range(n), sep=",")
+ f4 = "AG( proc0.in IMPLIES C[{}]({}) )".format(all_agents, others_not_in)
+ out += PROPERTY_TEMPLATE.format(name="f4", formula=f4)
-out += CA_STR.format(transitions)
+ return out
-## f1
-#formula = "EF( proc0.in )"
-#out += PROPERTY_STR.format("f1",formula)
-# f1
-formula = "EF( EX( proc0.in ) )"
-for i in range(1, n):
- formula += " AND EF( EX( proc{:d}.in ) )".format(i, i)
-out += PROPERTY_STR.format("f1",formula)
+def main():
+ parser = argparse.ArgumentParser(description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("n", type=int, help="number of processes (must be > 1)")
+ args = parser.parse_args()
-# f2
-formula = "EF( proc0.approach"
-for i in range(1, n):
- formula += " AND proc{:d}.approach".format(i)
-formula += " )"
-out += PROPERTY_STR.format("f2",formula)
+ if args.n < 2:
+ parser.error("number of processes must be > 1")
-# f3
-subf = "~proc1.in"
-for i in range(2, n):
- subf += " AND ~proc{:d}.in".format(i)
-formula = "AG( proc0.in IMPLIES K[proc0]({:s}) )".format(subf)
-out += PROPERTY_STR.format("f3",formula)
+ print(generate(args.n))
-# f4
-subf = "~proc1.in"
-for i in range(2, n):
- subf += " AND ~proc{:d}.in".format(i)
-all_agents = "proc0"
-for i in range(1, n):
- all_agents += ",proc{:d}".format(i)
-formula = "AG( proc0.in IMPLIES C[{:s}]({:s}) )".format(all_agents,subf)
-out += PROPERTY_STR.format("f4",formula)
-
-print(out)
+if __name__ == "__main__":
+ main()
diff --git a/examples/bdd/generators/old_syntax/gen_abstract.py b/examples/bdd/generators/old_syntax/gen_abstract.py
new file mode 100644
index 0000000..f7302ce
--- /dev/null
+++ b/examples/bdd/generators/old_syntax/gen_abstract.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+"""
+Generator for the Abstract Pipeline benchmark.
+
+Produces a reaction system modelling a pipeline of N modules,
+each cycling through entities a -> y,b -> c -> d before passing
+activation to the next module. The final reaction requires all
+modules to have completed (all y_i present).
+
+NOTE: generates old-syntax input (context-entities, initial-contexts,
+rsctl-property) which is not compatible with the current parser.
+"""
+
+import argparse
+
+
+def generate(n, variant):
+ out = "reactions {\n"
+ out += "\t{ {x},{s} -> {a1} };\n"
+
+ for i in range(1, n + 1):
+ out += "\t{{ {{a{i}}},{{s}} -> {{y{i}}} }};\n".format(i=i)
+ out += "\t{{ {{a{i}}},{{s}} -> {{b{i}}} }};\n".format(i=i)
+ out += "\t{{ {{b{i}}},{{s}} -> {{c{i}}} }};\n".format(i=i)
+ out += "\t{{ {{c{i}}},{{s}} -> {{d{i}}} }};\n".format(i=i)
+ out += "\t{{ {{d{i},y{i}}},{{s}} -> {{a{j}}} }};\n".format(i=i, j=i + 1)
+ out += "\t{{ {{y{i}}},{{s}} -> {{y{i}}} }};\n".format(i=i)
+
+ final_reactants = "a" + str(n + 1)
+ for i in range(1, n + 1):
+ final_reactants += ",y" + str(i)
+ out += "\t{{ {{{r}}},{{s}} -> {{r}} }};\n".format(r=final_reactants)
+ out += "}\n"
+
+ if variant == 1:
+ out += "context-entities { s }\n"
+ elif variant == 2:
+ ctx = "s"
+ for i in range(1, n + 1):
+ if i % 2 == 0:
+ ctx += ",a" + str(i)
+ out += "context-entities {{ {} }}\n".format(ctx)
+
+ out += "initial-contexts { {x} }\n"
+ out += "rsctl-property { E[{}]F(r) }\n"
+
+ return out
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("n", type=int, help="number of pipeline modules")
+ parser.add_argument("variant", type=int, choices=[1, 2],
+ help="context variant (1: minimal, 2: extended)")
+ args = parser.parse_args()
+
+ print(generate(args.n, args.variant))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/bdd/generators/old_syntax/gen_bc.py b/examples/bdd/generators/old_syntax/gen_bc.py
new file mode 100644
index 0000000..9941a89
--- /dev/null
+++ b/examples/bdd/generators/old_syntax/gen_bc.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+"""
+Generator for the Binary Counter (BC) benchmark.
+
+Produces a reaction system modelling an N-bit binary counter
+with increment and decrement operations controlled via context
+entities. Generates one of four RSCTL properties.
+
+NOTE: generates old-syntax input (context-entities, initial-contexts,
+rsctl-property) which is not compatible with the current parser.
+"""
+
+import argparse
+
+K = 8 # constant used by property 3
+
+
+def generate(n, prop):
+ out = "reactions {\n"
+
+ # (1) no dec, no inc: bits persist
+ out += "\t# (1) no decrement, no increment\n"
+ for j in range(n):
+ out += "\t{{{{p{j}}},{{dec,inc}} -> {{p{j}}}}};\n".format(j=j)
+
+ # (2) increment operation
+ out += "\n\t# (2) increment operation\n"
+ out += "\t{{inc},{dec,p0} -> {p0}};\n"
+ for j in range(1, n):
+ bits = ",".join("p" + str(k) for k in range(j))
+ out += "\t{{{{inc,{bits}}},{{dec,p{j}}} -> {{p{j}}}}};\n".format(bits=bits, j=j)
+
+ out += "\n\t# the more significant bits remain (inc)\n"
+ for j in range(n):
+ for k in range(j + 1, n):
+ out += "\t{{{{inc,p{k}}},{{dec,p{j}}} -> {{p{k}}}}};\n".format(j=j, k=k)
+
+ # (3) decrement operation
+ out += "\n\t# (3) decrement operation\n"
+ for j in range(n):
+ bits = ",".join("p" + str(k) for k in range(j + 1))
+ out += "\t{{{{dec}},{{inc,{bits}}} -> {{p{j}}}}};\n".format(bits=bits, j=j)
+
+ out += "\n\t# the more significant bits remain (dec)\n"
+ for j in range(n):
+ for k in range(j + 1, n):
+ out += "\t{{{{dec,p{j},p{k}}},{{inc}} -> {{p{k}}}}};\n".format(j=j, k=k)
+
+ out += "}\n\n"
+ out += "context-entities { inc,dec }\n"
+ out += "initial-contexts { {} }\n"
+
+ if prop == 1:
+ all_neg = " AND ".join("~p" + str(i) for i in range(n))
+ all_pos = " AND ".join("p" + str(i) for i in range(n))
+ out += "rsctl-property {{ AG(({}) IMPLIES E[{{inc}},{{dec}}]F({})) }}\n".format(
+ all_neg, all_pos)
+ elif prop == 2:
+ all_pos = " AND ".join("p" + str(i) for i in range(n))
+ all_neg = " AND ".join("~p" + str(i) for i in range(n))
+ out += "rsctl-property {{ AG(({}) IMPLIES A[{{inc}}]X({})) }}\n".format(
+ all_pos, all_neg)
+ elif prop == 3:
+ parts = ["p" + str(i) for i in range(n - K)]
+ parts += ["~p" + str(i) for i in range(n - K, n)]
+ out += "rsctl-property {{ E[{{inc}}]F({}) }}\n".format(" AND ".join(parts))
+ elif prop == 4:
+ out += "rsctl-property {{ AG( p{} IMPLIES EF ~p{} ) }}\n".format(n - 1, n - 1)
+
+ return out
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("n", type=int, help="number of bits")
+ parser.add_argument("property", type=int, choices=[1, 2, 3, 4],
+ help="property to generate (1-4)")
+ args = parser.parse_args()
+
+ if args.property == 3 and args.n < K + 1:
+ parser.error("n must be >= {} for property 3".format(K + 1))
+
+ print(generate(args.n, args.property))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/bdd/generators/old_syntax/gen_mutex.py b/examples/bdd/generators/old_syntax/gen_mutex.py
new file mode 100644
index 0000000..4027446
--- /dev/null
+++ b/examples/bdd/generators/old_syntax/gen_mutex.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+"""
+Generator for the Mutual Exclusion (Mutex) benchmark.
+
+Produces a reaction system modelling N processes competing for
+exclusive access to a critical section, using activation signals,
+request tokens, and a lock. Generates one of three RSCTL properties.
+
+NOTE: generates old-syntax input (context-entities, initial-contexts,
+rsctl-property) which is not compatible with the current parser.
+"""
+
+import argparse
+
+
+def generate(n, formula):
+ out = "reactions {\n"
+ for i in range(n):
+ out += "\t{{{{out{i},act{i}}},{{}} -> {{request{i}}}}};\n".format(i=i)
+ out += "\t{{{{out{i}}},{{act{i}}} -> {{out{i}}}}};\n".format(i=i)
+
+ for j in range(n):
+ if i != j:
+ out += "\t{{{{request{i},act{i},act{j}}},{{}} -> {{request{i}}}}};\n".format(
+ i=i, j=j)
+
+ out += "\t{{{{request{i}}},{{act{i}}} -> {{request{i}}}}};\n".format(i=i)
+
+ other_acts = ",".join("act" + str(j) for j in range(n) if i != j)
+ out += "\t{{{{request{i},act{i}}},{{{others},lock}} -> {{in{i},lock}}}};\n".format(
+ i=i, others=other_acts)
+
+ out += "\t{{{{in{i},act{i}}},{{}} -> {{out{i},done}}}};\n".format(i=i)
+ out += "\t{{{{in{i}}},{{act{i}}} -> {{in{i}}}}};\n".format(i=i)
+
+ out += "\t{{lock},{done} -> {lock}};\n"
+ out += "}\n"
+
+ ctx_ents = ",".join("act" + str(i) for i in range(n))
+ out += "context-entities {{ {} }}\n".format(ctx_ents)
+
+ init_ents = ",".join("out" + str(i) for i in range(n))
+ out += "initial-contexts {{ {{{}}} }}\n".format(init_ents)
+
+ if formula == 1:
+ out += "rsctl-property { A[{act1}]F(in1) }\n"
+ elif formula == 2:
+ out += "rsctl-property { E[{act1}]F(in1) }\n"
+ elif formula == 3:
+ pairs = []
+ for i in range(n):
+ for j in range(i + 1, n):
+ pairs.append("~(in{} AND in{})".format(i, j))
+ out += "rsctl-property {{ AG({}) }}\n".format(" AND ".join(pairs))
+
+ return out
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("n", type=int, help="number of processes")
+ parser.add_argument("formula", type=int, choices=[1, 2, 3],
+ help="formula to generate (1: A[]F, 2: E[]F, 3: AG mutex)")
+ args = parser.parse_args()
+
+ print(generate(args.n, args.formula))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/bdd/tgc.rs b/examples/bdd/tgc.drs
similarity index 100%
rename from examples/bdd/tgc.rs
rename to examples/bdd/tgc.drs
diff --git a/examples/bdd/tgc4.drs b/examples/bdd/tgc4.drs
new file mode 100644
index 0000000..ed9b345
--- /dev/null
+++ b/examples/bdd/tgc4.drs
@@ -0,0 +1,70 @@
+
+options { use-context-automaton; make-progressive; };
+reactions {
+
+ proc0 {
+ {{out}, {} -> {approach}};
+ {{approach}, {req} -> {req}};
+ {{allowed}, {} -> {in}};
+ {{in}, {} -> {out,leave}};
+ {{req}, {in} -> {req}};
+ };
+
+ proc1 {
+ {{out}, {} -> {approach}};
+ {{approach}, {req} -> {req}};
+ {{allowed}, {} -> {in}};
+ {{in}, {} -> {out,leave}};
+ {{req}, {in} -> {req}};
+ };
+
+ proc2 {
+ {{out}, {} -> {approach}};
+ {{approach}, {req} -> {req}};
+ {{allowed}, {} -> {in}};
+ {{in}, {} -> {out,leave}};
+ {{req}, {in} -> {req}};
+ };
+
+ proc3 {
+ {{out}, {} -> {approach}};
+ {{approach}, {req} -> {req}};
+ {{allowed}, {} -> {in}};
+ {{in}, {} -> {out,leave}};
+ {{req}, {in} -> {req}};
+ };
+};
+
+context-automaton {
+ states { init, green, red };
+ init-state { init };
+ transitions {
+ { proc0={out} proc1={out} proc2={out} proc3={out} }: init -> green;
+ { proc0={allowed} }: green -> red : proc0.req;
+ { proc1={allowed} }: green -> red : proc1.req;
+ { proc2={allowed} }: green -> red : proc2.req;
+ { proc3={allowed} }: green -> red : proc3.req;
+ { proc0={} }: green -> green : ~proc0.req AND ~proc1.req AND ~proc2.req AND ~proc3.req;
+ { proc1={} }: green -> green : ~proc0.req AND ~proc1.req AND ~proc2.req AND ~proc3.req;
+ { proc2={} }: green -> green : ~proc0.req AND ~proc1.req AND ~proc2.req AND ~proc3.req;
+ { proc3={} }: green -> green : ~proc0.req AND ~proc1.req AND ~proc2.req AND ~proc3.req;
+ { proc0={} }: red -> green : proc0.leave;
+ { proc1={} }: red -> green : proc1.leave;
+ { proc2={} }: red -> green : proc2.leave;
+ { proc3={} }: red -> green : proc3.leave;
+ { proc0={} }: red -> red : ~proc0.leave AND ~proc1.leave AND ~proc2.leave AND ~proc3.leave;
+ { proc1={} }: red -> red : ~proc0.leave AND ~proc1.leave AND ~proc2.leave AND ~proc3.leave;
+ { proc2={} }: red -> red : ~proc0.leave AND ~proc1.leave AND ~proc2.leave AND ~proc3.leave;
+ { proc3={} }: red -> red : ~proc0.leave AND ~proc1.leave AND ~proc2.leave AND ~proc3.leave;
+
+ };
+};
+
+rsctlk-property { f1 : EF( EX( proc0.in ) ) AND EF( EX( proc1.in ) ) AND EF( EX( proc2.in ) ) AND EF( EX( proc3.in ) ) };
+
+rsctlk-property { f2 : EF( proc0.approach AND proc1.approach AND proc2.approach AND proc3.approach ) };
+
+rsctlk-property { f3 : AG( proc0.in IMPLIES K[proc0](~proc1.in AND ~proc2.in AND ~proc3.in) ) };
+
+rsctlk-property { f4 : AG( proc0.in IMPLIES C[proc0,proc1,proc2,proc3](~proc1.in AND ~proc2.in AND ~proc3.in) ) };
+
diff --git a/reactics-bdd/ctx_aut.cc b/reactics-bdd/ctx_aut.cc
index fb7c9f7..7509c7e 100644
--- a/reactics-bdd/ctx_aut.cc
+++ b/reactics-bdd/ctx_aut.cc
@@ -13,12 +13,7 @@ CtxAut::CtxAut(Options *opts, RctSys *parent_rctsys)
bool CtxAut::hasState(std::string name)
{
- if (states_names.find(name) == states_names.end()) {
- return false;
- }
- else {
- return true;
- }
+ return states_names.find(name) != states_names.end();
}
State CtxAut::getStateID(std::string name)
diff --git a/reactics-bdd/ctx_aut.hh b/reactics-bdd/ctx_aut.hh
index d446459..a91a326 100644
--- a/reactics-bdd/ctx_aut.hh
+++ b/reactics-bdd/ctx_aut.hh
@@ -12,10 +12,7 @@
#include
#include
#include
-// #include "rs.hh"
#include "types.hh"
-// #include "options.hh"
-// #include "stateconstr.hh"
using std::cout;
using std::endl;
diff --git a/reactics-bdd/drs_benchmark.sh b/reactics-bdd/drs_benchmark.sh
index b7ad43d..ef2e2c6 100755
--- a/reactics-bdd/drs_benchmark.sh
+++ b/reactics-bdd/drs_benchmark.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-TMPINPUT="tmp_$RANDOM$RANDOM.rs"
+TMPINPUT="tmp_$RANDOM$RANDOM.drs"
CMD="./reactics -B"
diff --git a/reactics-bdd/formrsctlk.cc b/reactics-bdd/formrsctlk.cc
index 4658e52..d203849 100644
--- a/reactics-bdd/formrsctlk.cc
+++ b/reactics-bdd/formrsctlk.cc
@@ -172,39 +172,6 @@ bool FormRSCTLK::isERSCTLK(void) const
std::string FormRSCTLK::getActionsStr(void) const
{
- // if (actions != nullptr) {
- // std::string r = "[ ";
- // bool firstact = true;
- //
- // for (ActionsVec_f::iterator act = actions->begin(); act != actions->end();
- // ++act) {
- // if (!firstact) {
- // r += ",";
- // }
- // else {
- // firstact = false;
- // }
- //
- // r += "{";
- // bool firstent = true;
- //
- // for (Action_f::iterator ent = act->begin(); ent != act->end(); ++ent) {
- // if (!firstent) {
- // r += ",";
- // }
- // else {
- // firstent = false;
- // }
- //
- // r += *ent;
- // }
- //
- // r += "}";
- // }
- //
- // r += " ]";
- // return r;
- // }
if (boolCtx != nullptr) {
return "< " + boolCtx->toStr() + " >";
}
@@ -222,22 +189,6 @@ void FormRSCTLK::encodeActions(const SymRS *srs)
actions_bdd = new BDD(srs->getBDDfalse());
assert(boolCtx != nullptr);
- // assert(actions != nullptr || boolCtx != nullptr);
- // assert(!(actions != nullptr && boolCtx != nullptr));
-
- // if (actions != nullptr) {
- // for (ActionsVec_f::iterator act = actions->begin(); act != actions->end();
- // ++act) {
- // BDD single_action = srs->getBDDtrue();
- //
- // for (Action_f::iterator ent = act->begin(); ent != act->end(); ++ent) {
- // single_action *= srs->encActStrEntity(*ent);
- // }
- //
- // single_action = srs->compContext(single_action);
- // *actions_bdd += single_action;
- // }
- // }
if (boolCtx != nullptr) {
*actions_bdd = boolCtx->getBDDforContext(srs);
}
diff --git a/reactics-bdd/formrsctlk.hh b/reactics-bdd/formrsctlk.hh
index b9091fd..c9f557f 100644
--- a/reactics-bdd/formrsctlk.hh
+++ b/reactics-bdd/formrsctlk.hh
@@ -9,7 +9,6 @@
#include
#include
#include
-// #include "rs.hh"
#include "symrs.hh"
#include "cudd.hh"
#include "types.hh"
@@ -56,9 +55,6 @@
using std::cout;
using std::endl;
-// typedef std::string Entity_f;
-// typedef std::set Action_f;
-// typedef vector ActionsVec_f;
typedef std::set Agents_f;
class StateConstr;
@@ -74,7 +70,6 @@ class FormRSCTLK
std::string proc_name;
bool tf;
BDD *bdd;
- // ActionsVec_f *actions;
BDD *actions_bdd;
StateConstr *boolCtx;
Agents_f agents;
@@ -93,7 +88,6 @@ class FormRSCTLK
arg[0] = nullptr;
arg[1] = nullptr;
bdd = nullptr;
- // actions = nullptr;
actions_bdd = nullptr;
boolCtx = nullptr;
}
@@ -110,7 +104,6 @@ class FormRSCTLK
arg[0] = nullptr;
arg[1] = nullptr;
bdd = nullptr;
- // actions = nullptr;
actions_bdd = nullptr;
boolCtx = nullptr;
}
@@ -126,28 +119,10 @@ class FormRSCTLK
arg[0] = form1;
arg[1] = form2;
bdd = nullptr;
- // actions = nullptr;
actions_bdd = nullptr;
boolCtx = nullptr;
}
- /**
- * @brief Constructor for two-argument formula with action restrictions.
- */
- // FormRSCTLK(Oper op, ActionsVec_f *acts, FormRSCTLK *form1, FormRSCTLK *form2)
- // {
- // assert(acts != nullptr);
- // assert(RSCTLK_COND_2ARG(op));
- // assert(RSCTLK_COND_ACT(op));
- // oper = op;
- // arg[0] = form1;
- // arg[1] = form2;
- // bdd = nullptr;
- // actions = acts;
- // actions_bdd = nullptr;
- // boolCtx = nullptr;
- // }
-
/**
* @brief Constructor for two-argument formula with Boolean context restrictions.
*/
@@ -160,7 +135,6 @@ class FormRSCTLK
arg[0] = form1;
arg[1] = form2;
bdd = nullptr;
- // actions = nullptr;
actions_bdd = nullptr;
boolCtx = bctx;
}
@@ -176,28 +150,10 @@ class FormRSCTLK
arg[0] = form1;
arg[1] = nullptr;
bdd = nullptr;
- // actions = nullptr;
actions_bdd = nullptr;
boolCtx = nullptr;
}
- /**
- * @brief Constructor for one-argument formula with action restrictions.
- */
- // FormRSCTLK(Oper op, ActionsVec_f *acts, FormRSCTLK *form1)
- // {
- // assert(acts != nullptr);
- // assert(RSCTLK_COND_1ARG(op));
- // assert(RSCTLK_COND_ACT(op));
- // oper = op;
- // arg[0] = form1;
- // arg[1] = nullptr;
- // bdd = nullptr;
- // // actions = acts;
- // actions_bdd = nullptr;
- // boolCtx = nullptr;
- // }
-
/**
* @brief Constructor for one-argument formula with Boolean context restrictions.
*/
@@ -210,7 +166,6 @@ class FormRSCTLK
arg[0] = form1;
arg[1] = nullptr;
bdd = nullptr;
- // actions = nullptr;
actions_bdd = nullptr;
boolCtx = bctx;
}
@@ -234,7 +189,6 @@ class FormRSCTLK
delete arg[0];
delete arg[1];
delete bdd;
- // delete actions;
delete actions_bdd;
delete boolCtx;
}
diff --git a/reactics-bdd/in/hsr.rs b/reactics-bdd/in/hsr.drs
similarity index 100%
rename from reactics-bdd/in/hsr.rs
rename to reactics-bdd/in/hsr.drs
diff --git a/reactics-bdd/in/hsr_ca.rs b/reactics-bdd/in/hsr_ca.drs
similarity index 100%
rename from reactics-bdd/in/hsr_ca.rs
rename to reactics-bdd/in/hsr_ca.drs
diff --git a/reactics-bdd/in/hsr_drs.rs b/reactics-bdd/in/hsr_drs.drs
similarity index 100%
rename from reactics-bdd/in/hsr_drs.rs
rename to reactics-bdd/in/hsr_drs.drs
diff --git a/reactics-bdd/in/old_syntax/bc32.rs b/reactics-bdd/in/old_syntax/bc32.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/bc32.rs
rename to reactics-bdd/in/old_syntax/bc32.drs
diff --git a/reactics-bdd/in/old_syntax/bc8.rs b/reactics-bdd/in/old_syntax/bc8.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/bc8.rs
rename to reactics-bdd/in/old_syntax/bc8.drs
diff --git a/reactics-bdd/in/old_syntax/coffee.rs b/reactics-bdd/in/old_syntax/coffee.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/coffee.rs
rename to reactics-bdd/in/old_syntax/coffee.drs
diff --git a/reactics-bdd/in/old_syntax/expr.rs b/reactics-bdd/in/old_syntax/expr.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/expr.rs
rename to reactics-bdd/in/old_syntax/expr.drs
diff --git a/reactics-bdd/in/old_syntax/simple_I1.rs b/reactics-bdd/in/old_syntax/simple_I1.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/simple_I1.rs
rename to reactics-bdd/in/old_syntax/simple_I1.drs
diff --git a/reactics-bdd/in/old_syntax/test.rs b/reactics-bdd/in/old_syntax/test.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/test.rs
rename to reactics-bdd/in/old_syntax/test.drs
diff --git a/reactics-bdd/in/old_syntax/u1.rs b/reactics-bdd/in/old_syntax/u1.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/u1.rs
rename to reactics-bdd/in/old_syntax/u1.drs
diff --git a/reactics-bdd/in/old_syntax/u2.rs b/reactics-bdd/in/old_syntax/u2.drs
similarity index 100%
rename from reactics-bdd/in/old_syntax/u2.rs
rename to reactics-bdd/in/old_syntax/u2.drs
diff --git a/reactics-bdd/in/scripts/bench_bc.sh b/reactics-bdd/in/scripts/bench_bc.sh
index 5335a77..23cd961 100644
--- a/reactics-bdd/in/scripts/bench_bc.sh
+++ b/reactics-bdd/in/scripts/bench_bc.sh
@@ -42,10 +42,10 @@ for form in $forms; do
echo "$x" > $filename
- ./gen_bc.py $n $form > tmp.rs
+ ./gen_bc.py $n $form > tmp.drs
- echo "EXEC: $tool $options $popt tmp.rs"
- $tool $options $popt tmp.rs >&1 >> $filename
+ echo "EXEC: $tool $options $popt tmp.drs"
+ $tool $options $popt tmp.drs >&1 >> $filename
result="$(tail -1 $filename | grep -E '.*;.*;.*;.*'| sed "s/STAT/$n /")"
if [ "$result" = "" ];then
diff --git a/reactics-bdd/in/scripts/bench_mutex.sh b/reactics-bdd/in/scripts/bench_mutex.sh
index 822d4ab..b074516 100644
--- a/reactics-bdd/in/scripts/bench_mutex.sh
+++ b/reactics-bdd/in/scripts/bench_mutex.sh
@@ -44,11 +44,11 @@ for form in $forms; do
echo "$x" > $filename
- ./gen_mutex.py $n $form > tmp.rs
+ ./gen_mutex.py $n $form > tmp.drs
- echo "EXEC: $tool $options $popt tmp.rs"
+ echo "EXEC: $tool $options $popt tmp.drs"
- $tool $options $popt tmp.rs >&1 >> $filename
+ $tool $options $popt tmp.drs >&1 >> $filename
result="$(tail -1 $filename | grep -E '.*;.*;.*;.*'| sed "s/STAT/$n /")"
if [ "$result" = "" ];then
diff --git a/reactics-bdd/in/scripts/bench_pipe.sh b/reactics-bdd/in/scripts/bench_pipe.sh
index a527942..31f0e8b 100644
--- a/reactics-bdd/in/scripts/bench_pipe.sh
+++ b/reactics-bdd/in/scripts/bench_pipe.sh
@@ -43,11 +43,11 @@ for form in $forms; do
echo "$x" > $filename
- ./gen_abstract1.py $n $form > tmp.rs
+ ./gen_abstract1.py $n $form > tmp.drs
- echo "EXEC: $tool $options $popt tmp.rs"
+ echo "EXEC: $tool $options $popt tmp.drs"
- $tool $options $popt tmp.rs >&1 >> $filename
+ $tool $options $popt tmp.drs >&1 >> $filename
result="$(tail -1 $filename | grep -E '.*;.*;.*;.*'| sed "s/STAT/$n /")"
if [ "$result" = "" ];then
diff --git a/reactics-bdd/in/scripts/benchmark.sh b/reactics-bdd/in/scripts/benchmark.sh
index 61efef1..7798bab 100644
--- a/reactics-bdd/in/scripts/benchmark.sh
+++ b/reactics-bdd/in/scripts/benchmark.sh
@@ -4,8 +4,8 @@ for x in `seq 2 1 24`;do
echo $y $x
filename="results/f${y}_n${x}.out"
echo "$x" > $filename
- ./gen_bc.py $x $y > tmp.rs
- ../main -c -B tmp.rs >> $filename
+ ./gen_bc.py $x $y > tmp.drs
+ ../main -c -B tmp.drs >> $filename
result="$(tail -1 $filename | sed "s/STAT/$x /")"
echo $result >> results/summary_f${y}.out
echo $result
@@ -13,4 +13,4 @@ for x in `seq 2 1 24`;do
done
done
-rm tmp.rs
+rm tmp.drs
diff --git a/reactics-bdd/in/scripts/benchmark_abs1.sh b/reactics-bdd/in/scripts/benchmark_abs1.sh
index 6089667..6b3f85b 100644
--- a/reactics-bdd/in/scripts/benchmark_abs1.sh
+++ b/reactics-bdd/in/scripts/benchmark_abs1.sh
@@ -4,8 +4,8 @@ for x in `seq 1 60`;do
echo $x
filename="results/abs_v${y}_f0_n${x}.out"
echo "$x" > $filename
- ./gen_abstract1.py $x $y > tmp.rs
- ../main -z -c -p -v -B tmp.rs >&1 >> $filename
+ ./gen_abstract1.py $x $y > tmp.drs
+ ../main -z -c -p -v -B tmp.drs >&1 >> $filename
result="$(tail -1 $filename | sed "s/STAT/$x /")"
echo $result >> results/summary_v${y}_abs_f0.out
echo $result
@@ -13,4 +13,4 @@ for x in `seq 1 60`;do
done
done
-rm tmp.rs
+rm tmp.drs
diff --git a/reactics-bdd/in/scripts/benchmark_abs1_PT.sh b/reactics-bdd/in/scripts/benchmark_abs1_PT.sh
index 4f40afe..c69b705 100644
--- a/reactics-bdd/in/scripts/benchmark_abs1_PT.sh
+++ b/reactics-bdd/in/scripts/benchmark_abs1_PT.sh
@@ -4,8 +4,8 @@ for x in `seq 1 60`;do
echo $x
filename="results/abs_v${y}_f0_n${x}_PT.out"
echo "$x" > $filename
- ./gen_abstract1.py $x $y > tmp.rs
- ../main -z -x -c -p -v -B tmp.rs >&1 >> $filename
+ ./gen_abstract1.py $x $y > tmp.drs
+ ../main -z -x -c -p -v -B tmp.drs >&1 >> $filename
result="$(tail -1 $filename | sed "s/STAT/$x /")"
echo $result >> results/summary_v${y}_abs_f0_PT.out
echo $result
@@ -13,4 +13,4 @@ for x in `seq 1 60`;do
done
done
-rm tmp.rs
+rm tmp.drs
diff --git a/reactics-bdd/in/scripts/benchmark_bc.sh b/reactics-bdd/in/scripts/benchmark_bc.sh
index f1c5231..acae1a4 100644
--- a/reactics-bdd/in/scripts/benchmark_bc.sh
+++ b/reactics-bdd/in/scripts/benchmark_bc.sh
@@ -4,8 +4,8 @@ for x in `seq 1 50`;do
echo $x $y
filename="results/bc_f${y}_n${x}.out"
echo "$x" > $filename
- ./gen_bc.py $x $y > tmp.rs
- ../main -z -c -v -B tmp.rs >&1 >> $filename
+ ./gen_bc.py $x $y > tmp.drs
+ ../main -z -c -v -B tmp.drs >&1 >> $filename
result="$(tail -1 $filename | sed "s/STAT/$x /")"
echo $result >> results/summary_bc_f${y}.out
echo $result
@@ -13,4 +13,4 @@ for x in `seq 1 50`;do
done
done
-rm tmp.rs
+rm tmp.drs
diff --git a/reactics-bdd/in/scripts/benchmark_mutex.sh b/reactics-bdd/in/scripts/benchmark_mutex.sh
index e3cca35..2853dc7 100644
--- a/reactics-bdd/in/scripts/benchmark_mutex.sh
+++ b/reactics-bdd/in/scripts/benchmark_mutex.sh
@@ -4,8 +4,8 @@ for x in `seq 2 40`;do
echo $x $y
filename="results/mutex_f${y}_n${x}.out"
echo "$x" > $filename
- ./gen_mutex.py $x $y > tmp.rs
- ../main -z -c -p -v -B tmp.rs >&1 >> $filename
+ ./gen_mutex.py $x $y > tmp.drs
+ ../main -z -c -p -v -B tmp.drs >&1 >> $filename
result="$(tail -1 $filename | sed "s/STAT/$x /")"
echo $result >> results/summary_mutex_f${y}.out
echo $result
@@ -13,4 +13,4 @@ for x in `seq 2 40`;do
done
done
-rm tmp.rs
+rm tmp.drs
diff --git a/reactics-bdd/in/scripts/benchmark_mutex_PT.sh b/reactics-bdd/in/scripts/benchmark_mutex_PT.sh
index 096b672..fba41b3 100644
--- a/reactics-bdd/in/scripts/benchmark_mutex_PT.sh
+++ b/reactics-bdd/in/scripts/benchmark_mutex_PT.sh
@@ -4,8 +4,8 @@ for x in `seq 2 40`;do
echo $x $y
filename="results/mutex_f${y}_n${x}_PT.out"
echo "$x" > $filename
- ./gen_mutex.py $x $y > tmp.rs
- ../main -zcpxvB tmp.rs >&1 >> $filename
+ ./gen_mutex.py $x $y > tmp.drs
+ ../main -zcpxvB tmp.drs >&1 >> $filename
result="$(tail -1 $filename | sed "s/STAT/$x /")"
echo $result >> results/summary_mutex_f${y}_PT.out
echo $result
@@ -13,4 +13,4 @@ for x in `seq 2 40`;do
done
done
-rm tmp.rs
+rm tmp.drs
diff --git a/reactics-bdd/in/scripts/gen_abstract1.py b/reactics-bdd/in/scripts/gen_abstract1.py
deleted file mode 100755
index 8112ae8..0000000
--- a/reactics-bdd/in/scripts/gen_abstract1.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-
-from sys import argv,exit
-
-if len(argv) < 3:
- print "Usage:", argv[0], " "
- exit(100)
-
-n = int(argv[1])
-v = int(argv[2])
-
-if not ( v > 0 and v < 3 ):
- print "unsupported variant"
- exit(100)
-
-print "reactions {"
-s = "\t{ {x},{s} -> {a1} };\n"
-print s
-
-for i in range(1,n+1):
- s = "\t{ {a" + str(i) + "},{s} -> {y" + str(i) + "} };\n"
- s += "\t{ {a" + str(i) + "},{s} -> {b" + str(i) + "} };\n"
- s += "\t{ {b" + str(i) + "},{s} -> {c" + str(i) + "} };\n"
- s += "\t{ {c" + str(i) + "},{s} -> {d" + str(i) + "} };\n"
- s += "\t{ {d" + str(i) + ",y" + str(i) + "},{s} -> {a" + str(i+1) + "} };\n"
- s += "\t{ {y" + str(i) + "},{s} -> {y" + str(i) + "} };\n"
- print s
-
-s = "\t{ {a" + str(n+1)
-for i in range(1,n+1):
- s += ",y" + str(i)
-s += "},{s} -> {r} };\n"
-print s
-print "}"
-
-if v == 1:
- print "context-entities { s }"
-elif v == 2:
- s = "context-entities { s"
- for i in range(1,n+1):
- if i % 2 == 0:
- s += ",a" + str(i)
- s += " }"
- print s
-
-print "initial-contexts { {x} }"
-print "rsctl-property { E[{}]F(r) }"
diff --git a/reactics-bdd/in/scripts/gen_asm.py b/reactics-bdd/in/scripts/gen_asm.py
deleted file mode 100755
index a83a4e6..0000000
--- a/reactics-bdd/in/scripts/gen_asm.py
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/env python
-
-from sys import argv,exit
-
-OPTIONS_STR = """
-options { use-context-automaton; make-progressive; };
-"""
-
-PROC_STR = """
- proc{:d} {{
- {{{{a}}, {{s}} -> {{y}}}};
- {{{{y}}, {{s}} -> {{y}}}};
- {{{{a}}, {{s}} -> {{b}}}};
- {{{{b}}, {{s}} -> {{c}}}};
- {{{{c}}, {{s}} -> {{d}}}};
- {{{{d,y}}, {{s}} -> {{dy}}}};
- }};
-"""
-
-FINAL_PROC = """
- procFinal {
- {{done}, {s} -> {done}};
- };
-"""
-
-CA_STR = """
-context-automaton {{
- states {{ init, act }};
- init-state {{ init }};
- transitions {{
-{:s}
- }};
-}};
-"""
-
-PROPERTY_STR = """
-rsctlk-property {{ {:s} : {:s} }};
-"""
-
-
-#################################################################
-
-if len(argv) < 1:
- print("Usage: {:s} ".format(argv[0]))
- exit(100)
-
-n = int(argv[1])
-
-assert n > 1, "number of proc must be > 1"
-
-out = ""
-
-out += OPTIONS_STR
-out += "reactions {\n"
-for i in range(1, n+1):
- out += PROC_STR.format(i)
-
-out += FINAL_PROC
-out += "};\n"
-
-transitions = ""
-
-init_trans = 8*" " + "{ proc1={a} }: init -> act;\n"
-transitions += init_trans
-
-for i in range(1, n+1):
- transitions += "{:s}{{ proc{:d}={{}} }}: act -> act : ~proc{:d}.dy;\n".format(8*" ", i, i, i)
-
-for i in range(2, n+1):
- transitions += "{:s}{{ proc{:d}={{a}} }}: act -> act : proc{:d}.dy;\n".format(8*" ", i, i-1)
-
-final_cond = "proc1.dy"
-for i in range(2, n+1):
- final_cond += " AND proc{:d}.dy".format(i)
-
-transitions += "{:s}{{ procFinal={{done}} }}: act -> act : {:s};\n".format(8*" ", final_cond)
-
-out += CA_STR.format(transitions)
-
-# f1
-formula = "EF( procFinal.done )"
-out += PROPERTY_STR.format("f1",formula)
-
-# f2
-formula = "AG( proc{:d}.d IMPLIES K[proc{:d}]( proc{:d}.y ) )".format(n, n, n-1)
-out += PROPERTY_STR.format("f2",formula)
-
-# x1
-formula = "EF( ~procFinal.done )"
-out += PROPERTY_STR.format("x1",formula)
-
-print(out)
-
diff --git a/reactics-bdd/in/scripts/gen_bc.py b/reactics-bdd/in/scripts/gen_bc.py
deleted file mode 100755
index 0e543ff..0000000
--- a/reactics-bdd/in/scripts/gen_bc.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/env python2.7
-
-from sys import argv,exit
-
-
-if len(argv) < 3:
- print "Usage:", argv[0], " "
- exit(100)
-
-n = int(argv[1])
-property = int(argv[2])
-
-K = 8
-if property == 3:
- if n < K+1:
- print "too small n"
- exit(100)
-
-if not ( property >= 1 and property < 5 ):
- print "property: 1-4"
- exit(101)
-
-print "reactions {"
-
-# (1) no dec, no inc
-print "\t# (1) no decrement, no increment"
-for j in range(0,n):
- print "\t{{p" + str(j) + "},{dec,inc} -> {p" + str(j) + "}};"
-
-# (2) increment
-s = "\n\t# (2) increment operation\n"
-s += "\t{{inc},{dec,p0} -> {p0}};\n"
-for j in range(1,n):
- s += "\t{{inc,"
- for k in range(0,j):
- s += "p" + str(k)
- if k < j-1:
- s += ","
- s += "},{dec,p" + str(j) + "} -> {p" + str(j) + "}};\n"
-
-print s
-
-print "\t# the more significant bits remain (inc)"
-s = ""
-for j in range(0,n):
- for k in range(j+1,n):
- s += "\t{{inc,p" + str(k) + "},{dec,p" + str(j) + "} -> {p" + str(k) + "}};\n"
-
-print s
-
-print "\t# (3) decrement operation"
-s = ""
-for j in range(0,n):
- s += "\t{{dec},{inc,"
- for k in range(0,j+1):
- s += "p" + str(k)
- if k < j:
- s += ","
- s += "} -> {p" + str(j) + "}};\n"
-print s
-
-print "\t# the more significant bits remain (dec)"
-s = ""
-for j in range(0,n):
- for k in range(j+1,n):
- s += "\t{{dec,p" + str(j) + ",p" + str(k) + "},{inc} -> {p" + str(k) + "}};\n"
-
-s += "}\n"
-print s
-
-print "context-entities { inc,dec }"
-print "initial-contexts { {} }"
-
-if property == 4:
- print "rsctl-property { AG( p" + str(n-1) + " IMPLIES EF ~p" + str(n-1) + " ) }"
-elif property == 1:
- s = "rsctl-property { AG(("
- for i in range(0,n):
- s += "~p" + str(i)
- if i < n-1:
- s += " AND "
- s += ") IMPLIES E[{inc},{dec}]F("
- for i in range(0,n):
- s += "p" + str(i)
- if i < n-1:
- s += " AND "
-
- s += ")) }"
- print s
-elif property == 2:
- s = "rsctl-property { AG(("
- for i in range(0,n):
- s += "p" + str(i)
- if i < n-1:
- s += " AND "
- s += ") IMPLIES A[{inc}]X("
- for i in range(0,n):
- s += "~p" + str(i)
- if i < n-1:
- s += " AND "
-
- s += ")) }"
- print s
-
-elif property == 3:
- s = "rsctl-property { E[{inc}]F("
- for i in range(0,n-K):
- s += "p" + str(i)
- if i < n-1:
- s += " AND "
- for i in range(n-K,n):
- s += "~p" + str(i)
- if i < n-1:
- s += " AND "
- s += ") }"
- print s
-
-
diff --git a/reactics-bdd/in/scripts/gen_mutex.py b/reactics-bdd/in/scripts/gen_mutex.py
deleted file mode 100755
index 95337d5..0000000
--- a/reactics-bdd/in/scripts/gen_mutex.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env python
-
-from sys import argv,exit
-
-
-if len(argv) < 3:
- print "Usage:", argv[0], " "
- exit(100)
-
-n = int(argv[1])
-f = int(argv[2])
-
-if not ( f>0 and f<4 ):
- print "unsupported formula"
- exit(100)
-
-print "reactions {"
-for i in range(0,n):
- s = "\t{{out" + str(i) + ",act" + str(i) + "},{} -> {request" + str(i) + "}};\n"
- s += "\t{{out" + str(i) + "},{act" + str(i) + "} -> {out" + str(i) + "}};\n"
- for j in range(0,n):
- if i != j:
- s += "\t{{request" + str(i) + ",act" + str(i) + ",act" + str(j) + "},{} -> {request" + str(i) + "}};\n"
- #s += "\t{{request" + str(i) + "},{"
- #for j in range(0,n):
- # s += "act" + str(j)
- # if j < n-1:
- # s += ","
- #s += "} -> {request" + str(i) + "}};\n"
- s += "\t{{request" + str(i) + "},{act" + str(i) + "} -> {request" + str(i) + "}};\n"
- s += "\t{{request" + str(i) + ",act" + str(i) + "},{"
- for j in range(0,n):
- if i != j:
- s += "act" + str(j) + ","
- s += "lock} -> {in" + str(i) + ",lock}};\n"
- s += "\t{{in" + str(i) + ",act" + str(i) + "},{} -> {out" + str(i) + ",done}};\n"
- s += "\t{{in" + str(i) + "},{act" + str(i) + "} -> {in" + str(i) + "}};\n"
- print s
-
-print "\t{{lock},{done} -> {lock}};"
-
-print "}"
-
-s = "context-entities {"
-for i in range(0,n):
- s += "act" + str(i)
- if i < n-1:
- s += ","
-s += "}\n"
-print s
-
-s = "initial-contexts { {"
-for i in range(0,n):
- s += "out" + str(i)
- if i < n-1:
- s += ","
-s += "} }\n"
-print s
-
-#print "rsctl-property { A[{act1}]G(A[{act1}]F(in1)) }"
-if f == 1:
- print "rsctl-property { A[{act1}]F(in1) }"
-elif f == 2:
- print "rsctl-property { E[{act1}]F(in1) }"
-elif f == 3:
- s = "rsctl-property { AG("
- for i in range(0,n):
- for j in range(i+1,n):
- s += "~(in" + str(i) + " AND in" + str(j) + ")"
- if i < n-2:
- s += " AND "
- s += ") }"
- print s
-else:
- exit(111)
-
diff --git a/reactics-bdd/in/scripts/gen_tgc_sc.py b/reactics-bdd/in/scripts/gen_tgc_sc.py
deleted file mode 100755
index d4bfcf0..0000000
--- a/reactics-bdd/in/scripts/gen_tgc_sc.py
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/env python
-
-from sys import argv,exit
-
-OPTIONS_STR = """
-options { use-context-automaton; make-progressive; };
-"""
-
-PROC_STR = """
- proc{:d} {{
- {{{{out}}, {{}} -> {{approach}}}};
- {{{{approach}}, {{req}} -> {{req}}}};
- {{{{allowed}}, {{}} -> {{in}}}};
- {{{{in}}, {{}} -> {{out,leave}}}};
- {{{{req}}, {{in}} -> {{req}}}};
- }};
-"""
-
-CA_STR = """
-context-automaton {{
- states {{ init, green, red }};
- init-state {{ init }};
- transitions {{
-{:s}
- }};
-}};
-"""
-
-PROPERTY_STR = """
-rsctlk-property {{ {:s} : {:s} }};
-"""
-
-
-#################################################################
-
-if len(argv) < 1:
- print("Usage: {:s} ".format(argv[0]))
- exit(100)
-
-n = int(argv[1])
-
-assert n > 1, "number of proc must be > 1"
-
-out = ""
-
-out += OPTIONS_STR
-out += "reactions {\n"
-for i in range(n):
- out += PROC_STR.format(i)
-out += "};\n"
-
-transitions = ""
-
-init_trans = 8*" " + "{ "
-for i in range(n):
- init_trans += "proc{:d}={{out}} ".format(i)
-init_trans += "}: init -> green;\n"
-transitions += init_trans
-
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{allowed}} }}: green -> red : proc{:d}.req;\n".format(8*" ", i, i)
-
-no_req_cond = "~proc0.req"
-for i in range(1, n):
- no_req_cond += " AND ~proc{:d}.req".format(i)
-
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{}} }}: green -> green : {:s};\n".format(8*" ", i, no_req_cond)
-
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{}} }}: red -> green : proc{:d}.leave;\n".format(8*" ", i, i)
-
-no_leave_cond = "~proc0.leave"
-for i in range(1, n):
- no_leave_cond += " AND ~proc{:d}.leave".format(i)
-
-for i in range(n):
- transitions += "{:s}{{ proc{:d}={{}} }}: red -> red : {:s};\n".format(8*" ", i, no_leave_cond)
-
-out += CA_STR.format(transitions)
-
-## f1
-#formula = "EF( proc0.in )"
-#out += PROPERTY_STR.format("f1",formula)
-
-# f1
-formula = "EF( EX( proc0.in ) )"
-for i in range(1, n):
- formula += " AND EF( EX( proc{:d}.in ) )".format(i, i)
-out += PROPERTY_STR.format("f1",formula)
-
-# f2
-formula = "EF( proc0.approach"
-for i in range(1, n):
- formula += " AND proc{:d}.approach".format(i)
-formula += " )"
-out += PROPERTY_STR.format("f2",formula)
-
-# f3
-subf = "~proc1.in"
-for i in range(2, n):
- subf += " AND ~proc{:d}.in".format(i)
-formula = "AG( proc0.in IMPLIES K[proc0]({:s}) )".format(subf)
-out += PROPERTY_STR.format("f3",formula)
-
-# f4
-subf = "~proc1.in"
-for i in range(2, n):
- subf += " AND ~proc{:d}.in".format(i)
-all_agents = "proc0"
-for i in range(1, n):
- all_agents += ",proc{:d}".format(i)
-formula = "AG( proc0.in IMPLIES C[{:s}]({:s}) )".format(all_agents,subf)
-out += PROPERTY_STR.format("f4",formula)
-
-print(out)
-
diff --git a/reactics-bdd/in/trivial.rs b/reactics-bdd/in/trivial.drs
similarity index 100%
rename from reactics-bdd/in/trivial.rs
rename to reactics-bdd/in/trivial.drs
diff --git a/reactics-bdd/mc.cc b/reactics-bdd/mc.cc
index d1c7b4c..19fb1c0 100644
--- a/reactics-bdd/mc.cc
+++ b/reactics-bdd/mc.cc
@@ -517,9 +517,9 @@ BDD ModelChecker::getIthOnly(Process proc_id)
/* nothing has been found in the cache */
BDD bdd = BDD_TRUE;
- for (auto i = 0; i < pv_drs_E->size(); ++i) {
+ for (size_t i = 0; i < pv_drs_E->size(); ++i) {
- if (i == proc_id) {
+ if (i == static_cast(proc_id)) {
continue;
}
@@ -591,13 +591,7 @@ bool ModelChecker::checkRSCTLKfull(FormRSCTLK *form)
VERB("Checking the formula");
- //if (*initStates * getStatesRSCTLK(form) != cuddMgr->bddZero())
- if (*initStates * getStatesRSCTLK(form) == *initStates) {
- result = true;
- }
- else {
- result = false;
- }
+ result = (*initStates * getStatesRSCTLK(form) == *initStates);
cleanup();
diff --git a/reactics-bdd/reactics.cc b/reactics-bdd/reactics.cc
index 90e1298..6cd731f 100644
--- a/reactics-bdd/reactics.cc
+++ b/reactics-bdd/reactics.cc
@@ -37,13 +37,13 @@ int main(int argc, char **argv)
&option_index)) != -1) {
switch (c) {
case 0:
- printf("option %s", long_options[option_index].name);
+ cout << "option " << long_options[option_index].name;
if (optarg) {
- printf(" with arg %s", optarg);
+ cout << " with arg " << optarg;
}
- printf("\n");
+ cout << endl;
if (strcmp(long_options[option_index].name, "trace-parsing")) {
driver.trace_parsing = true;
@@ -236,16 +236,7 @@ int main(int argc, char **argv)
delete opts;
- int ret_val;
-
- if (result) {
- ret_val = 0;
- }
- else {
- ret_val = 1;
- }
-
- return ret_val;
+ return result ? 0 : 1;
}
void print_help(std::string path_str)
diff --git a/reactics-bdd/rs.cc b/reactics-bdd/rs.cc
index f04c578..8cfb547 100644
--- a/reactics-bdd/rs.cc
+++ b/reactics-bdd/rs.cc
@@ -14,12 +14,7 @@ RctSys::RctSys(void)
bool RctSys::hasEntity(std::string name)
{
- if (entities_names.find(name) == entities_names.end()) {
- return false;
- }
- else {
- return true;
- }
+ return entities_names.find(name) != entities_names.end();
}
void RctSys::addEntity(std::string name)
@@ -84,21 +79,12 @@ void RctSys::addProcess(std::string processName)
bool RctSys::hasProcess(std::string processName)
{
- if (processes_names.find(processName) == processes_names.end()) {
- return false;
- }
- else {
- return true;
- }
+ return processes_names.find(processName) != processes_names.end();
}
bool RctSys::hasProcess(Process processID)
{
- if (processID >= processes_ids.size()) {
- return false;
- }
-
- return true;
+ return processID < processes_ids.size();
}
Process RctSys::getProcessID(std::string processName)
@@ -120,28 +106,26 @@ std::string RctSys::getProcessName(Process processID)
}
}
-void RctSys::pushReactant(std::string entityName)
+void RctSys::ensureEntity(std::string entityName)
{
if (!hasEntity(entityName)) {
addEntity(entityName);
}
+}
+void RctSys::pushReactant(std::string entityName)
+{
+ ensureEntity(entityName);
tmpReactants.insert(getEntityID(entityName));
}
void RctSys::pushInhibitor(std::string entityName)
{
- if (!hasEntity(entityName)) {
- addEntity(entityName);
- }
-
+ ensureEntity(entityName);
tmpInhibitors.insert(getEntityID(entityName));
}
void RctSys::pushProduct(std::string entityName)
{
- if (!hasEntity(entityName)) {
- addEntity(entityName);
- }
-
+ ensureEntity(entityName);
tmpProducts.insert(getEntityID(entityName));
}
@@ -232,10 +216,7 @@ void RctSys::commitInitState(void)
void RctSys::addActionEntity(std::string entityName)
{
- if (!hasEntity(entityName)) {
- addEntity(entityName);
- }
-
+ ensureEntity(entityName);
actionEntities.insert(getEntityID(entityName));
}
@@ -248,12 +229,7 @@ void RctSys::addActionEntity(Entity entity)
bool RctSys::isActionEntity(Entity entity)
{
- if (actionEntities.count(entity) > 0) {
- return true;
- }
- else {
- return false;
- }
+ return actionEntities.count(entity) > 0;
}
void RctSys::showActionEntities(void)
diff --git a/reactics-bdd/rs.hh b/reactics-bdd/rs.hh
index 27ace9b..68a0f54 100644
--- a/reactics-bdd/rs.hh
+++ b/reactics-bdd/rs.hh
@@ -50,6 +50,7 @@ class RctSys
void addReactionForCurrentProcess(Reaction reaction);
+ void ensureEntity(std::string entityName);
void pushReactant(std::string entityName);
void pushInhibitor(std::string entityName);
void pushProduct(std::string entityName);
diff --git a/reactics-bdd/symrs.cc b/reactics-bdd/symrs.cc
index d99b38a..61fa969 100644
--- a/reactics-bdd/symrs.cc
+++ b/reactics-bdd/symrs.cc
@@ -17,9 +17,6 @@ SymRS::SymRS(RctSys *rs, Options *opts)
totalEntities = rs->getEntitiesSize();
- // TODO: remove
- totalActions = 0;
-
totalRctSysStateVars = getTotalProductVariables();
totalCtxEntities = getTotalCtxEntitiesVariables();
@@ -404,39 +401,19 @@ BDD SymRS::encCtxEntity(Process proc_id, Entity entity) const
bool SymRS::productEntityExists(Process proc_id, Entity entity) const
{
- if (prod_ent_local_idx.count(proc_id) == 0) {
- return false;
- }
- else {
- if (prod_ent_local_idx.at(proc_id).count(entity) == 1) {
- return true;
- }
- }
-
- return false;
+ return prod_ent_local_idx.count(proc_id) != 0 &&
+ prod_ent_local_idx.at(proc_id).count(entity) == 1;
}
bool SymRS::ctxEntityExists(Process proc_id, Entity entity) const
{
- if (ctx_ent_local_idx.count(proc_id) == 0) {
- return false;
- }
- else {
- if (ctx_ent_local_idx.at(proc_id).count(entity) == 1) {
- return true;
- }
- }
-
- return false;
+ return ctx_ent_local_idx.count(proc_id) != 0 &&
+ ctx_ent_local_idx.at(proc_id).count(entity) == 1;
}
bool SymRS::processUsesEntity(Process proc_id, Entity entity_id) const
{
- if (productEntityExists(proc_id, entity_id) || ctxEntityExists(proc_id, entity_id)) {
- return true;
- }
-
- return false;
+ return productEntityExists(proc_id, entity_id) || ctxEntityExists(proc_id, entity_id);
}
BDD SymRS::encEntitiesConj_raw(Process proc_id, const Entities &entities, bool succ)
@@ -511,20 +488,6 @@ BDD SymRS::encContext(const EntitiesForProc &proc_entities)
return r;
}
-BDD SymRS::compState(const BDD &state) const
-{
- assert(0);
- BDD s = state;
-
- for (unsigned int i = 0; i < totalRctSysStateVars; ++i) {
- if (!(*pv)[i] * state != cuddMgr->bddZero()) {
- s *= !(*pv)[i];
- }
- }
-
- return s;
-}
-
BDD SymRS::compContext(const BDD &context) const
{
BDD c = context;
@@ -725,65 +688,6 @@ BDD SymRS::encEnabledness(Process prod_proc_id, Entity entity_id)
return enab;
}
-// BDD SymRS::encEnabledness(Process prod_proc_id, Entity entity_id)
-// {
-// assert(prod_conds.size() > prod_proc_id);
-
-// BDD enab = BDD_FALSE;
-
-// auto production_conditions = prod_conds[prod_proc_id][entity_id];
-
-// VERB_LN(5, "| Produce " << rs->getEntityName(entity_id) << " in " << rs->getProcessName(prod_proc_id) << ":");
-
-// for (const auto &cond : production_conditions) {
-
-// BDD reactants = BDD_TRUE;
-// BDD inhibitors = BDD_TRUE;
-
-// for (const auto &reactant : cond.rctt) {
-// BDD proc_reactants = BDD_FALSE;
-
-// for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) {
-// if (processUsesEntity(proc_id, reactant)) {
-// proc_reactants += encProcEnabled(proc_id) * encEntityCondition(proc_id, reactant);
-
-// VERB_LN(5, "| - if process " << rs->getProcessName(proc_id) << " is enabled and has " << rs->getEntityName(reactant));
-
-// }
-// } // END FOR: prod_id
-
-// reactants *= proc_reactants;
-// } // END FOR: reactant
-
-// // For inhibitors, we take all the processes first and then we iterate over the inhibitors
-// for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) {
-// BDD proc_inhibitors = BDD_TRUE;
-
-// for (const auto &inhibitor : cond.inhib) {
-// if (processUsesEntity(proc_id, inhibitor)) {
-// proc_inhibitors *= !encEntityCondition(proc_id, inhibitor);
-// }
-// }
-
-// if (proc_inhibitors != BDD_TRUE) { // just an optimisation
-// proc_inhibitors += !encProcEnabled(proc_id);
-// inhibitors *= proc_inhibitors;
-// }
-// }
-
-// enab += reactants * inhibitors;
-
-// } // END FOR: cond
-
-// if (opts->reorder_trans) {
-// VERB_L2("Reordering");
-// Cudd_ReduceHeap(cuddMgr->getManager(), CUDD_REORDER_SIFT, 10000);
-// }
-
-// return enab;
-// }
-
-
BDD SymRS::encEntitySameSuccessor(Process proc_id, Entity entity_id)
{
return BDD_IFF(encEntity(proc_id, entity_id), encEntitySucc(proc_id, entity_id));
@@ -810,7 +714,7 @@ void SymRS::encodeTransitions(void)
prod_conds.resize(numberOfProc);
- for (auto proc_id = 0; proc_id < numberOfProc; ++proc_id) {
+ for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) {
prod_conds[proc_id] = getProductionConditions(proc_id);
}
diff --git a/reactics-bdd/symrs.hh b/reactics-bdd/symrs.hh
index bcb5999..4feef3c 100644
--- a/reactics-bdd/symrs.hh
+++ b/reactics-bdd/symrs.hh
@@ -15,7 +15,6 @@
#include "types.hh"
#include "macro.hh"
#include "bdd_macro.hh"
-// #include "rs.hh"
#include "options.hh"
#include "memtime.hh"
@@ -240,16 +239,11 @@ class SymRS
vector *pv_proc_ctx;
BDDvec *pv_proc_ctx_E;
- // TODO: remove
- BDDvec *pv_act;
- BDD *pv_act_E;
-
unsigned int totalEntities;
unsigned int numberOfProc; /*!< The number of DRS processes */
unsigned int totalStateVars;
unsigned int totalRctSysStateVars; /*!< Total number of different entities produced by reactions */
unsigned int totalCtxEntities; /*!< Total number of different (process,context) entities used */
- unsigned int totalActions;
unsigned int totalCtxAutStateVars;
EntitiesForProc usedProducts; /*!< Entities used in products (per process) */
@@ -309,13 +303,6 @@ class SymRS
BDD encContext(const EntitiesForProc &proc_entities);
- /**
- * @brief Complements an encoding of a given state by negating all the variables that are not set to true
- *
- * @return Returns the encoded state
- */
- BDD compState(const BDD &state) const;
-
BDD compContext(const BDD &context) const;
std::string decodedRctSysStateToStr(const BDD &state);
diff --git a/reactics-bdd/test.rs b/reactics-bdd/test.drs
similarity index 100%
rename from reactics-bdd/test.rs
rename to reactics-bdd/test.drs
diff --git a/reactics-smt/rs/context_automaton.py b/reactics-smt/rs/context_automaton.py
index 6d0a084..3b29e68 100644
--- a/reactics-smt/rs/context_automaton.py
+++ b/reactics-smt/rs/context_automaton.py
@@ -55,10 +55,7 @@ class ContextAutomaton(object):
return self._states[self._init_state]
def is_state(self, name):
- if name in self._states:
- return True
- else:
- return False
+ return name in self._states
def get_state_id(self, name):
try:
@@ -78,10 +75,7 @@ class ContextAutomaton(object):
print(state)
def is_valid_rs_set(self, elements):
- if set(elements).issubset(self._reaction_system.background_set):
- return True
- else:
- return False
+ return set(elements).issubset(self._reaction_system.background_set)
def is_valid_context(self, context):
return self.is_valid_rs_set(context)
@@ -95,7 +89,7 @@ class ContextAutomaton(object):
return new_set
def add_transition(self, src, context_set, dst):
- if not type(context_set) is set and not type(context_set) is list:
+ if not isinstance(context_set, (set, list)):
print("Contexts set must be of type set or list")
if not self.is_valid_context(context_set):
diff --git a/reactics-smt/rs/context_automaton_with_concentrations.py b/reactics-smt/rs/context_automaton_with_concentrations.py
index 14b0271..f702df1 100644
--- a/reactics-smt/rs/context_automaton_with_concentrations.py
+++ b/reactics-smt/rs/context_automaton_with_concentrations.py
@@ -9,12 +9,9 @@ class ContextAutomatonWithConcentrations(ContextAutomaton):
super(ContextAutomatonWithConcentrations, self).__init__(reaction_system)
def is_valid_context(self, context):
- if set([e for e, lvl in context]).issubset(
+ return set(e for e, lvl in context).issubset(
self._reaction_system.background_set
- ):
- return True
- else:
- return False
+ )
def context2str(self, ctx):
if len(ctx) == 0:
@@ -26,7 +23,7 @@ class ContextAutomatonWithConcentrations(ContextAutomaton):
return s
def add_transition(self, src, context_set, dst):
- if not type(context_set) is set and not type(context_set) is list:
+ if not isinstance(context_set, (set, list)):
print("Contexts set must be of type set or list")
if not self.is_valid_context(context_set):
diff --git a/reactics-smt/rs/extended_context_automaton.py b/reactics-smt/rs/extended_context_automaton.py
index bae9ce2..502cb77 100644
--- a/reactics-smt/rs/extended_context_automaton.py
+++ b/reactics-smt/rs/extended_context_automaton.py
@@ -70,7 +70,7 @@ class ExtendedContextAutomaton(ContextAutomaton):
ctx_reactants, ctx_inhibitors, ctx_products = ctx_reaction
- if not type(ctx_products) is set and not type(ctx_products) is list:
+ if not isinstance(ctx_products, (set, list)):
print("Contexts set (context products) must be of type set or list")
if not self.is_valid_rs_set(ctx_reactants):
diff --git a/reactics-smt/rs/reaction_system.py b/reactics-smt/rs/reaction_system.py
index ad10a9f..e1d55fc 100644
--- a/reactics-smt/rs/reaction_system.py
+++ b/reactics-smt/rs/reaction_system.py
@@ -43,11 +43,8 @@ class ReactionSystem(object):
self.add_bg_set_entity(e)
def is_in_background_set(self, entity):
- """Checks if the given name is valid wrt the background set="""
- if entity in self.background_set:
- return True
- else:
- return False
+ """Checks if the given name is valid wrt the background set"""
+ return entity in self.background_set
def get_entity_id(self, name):
try:
diff --git a/reactics-smt/rs/reaction_system_with_concentrations.py b/reactics-smt/rs/reaction_system_with_concentrations.py
index 9bdeb28..00967c8 100644
--- a/reactics-smt/rs/reaction_system_with_concentrations.py
+++ b/reactics-smt/rs/reaction_system_with_concentrations.py
@@ -18,9 +18,9 @@ class ReactionSystemWithConcentrations(ReactionSystem):
def add_bg_set_entity(self, e):
name = ""
def_max_conc = -1
- if type(e) is tuple and len(e) == 2:
+ if isinstance(e, tuple) and len(e) == 2:
name, def_max_conc = e
- elif type(e) is str:
+ elif isinstance(e, str):
name = e
print("\nWARNING: no maximal concentration level specified for:", e, "\n")
else:
@@ -46,18 +46,10 @@ class ReactionSystemWithConcentrations(ReactionSystem):
def is_valid_entity_with_concentration(self, e):
"""Sanity check for entities with concentration"""
- if type(e) is tuple:
- if len(e) == 2 and type(e[1]) is int:
- return True
+ if isinstance(e, (tuple, list)) and len(e) == 2 and isinstance(e[1], int):
+ return True
- if type(e) is list:
- if len(e) == 2 and type(e[1]) is int:
- return True
-
- print("FATAL. Invalid entity+concentration: {:s}".format(e))
- exit(1)
-
- return False
+ raise RuntimeError("Invalid entity+concentration: " + repr(e))
def get_state_ids(self, state):
"""Returns entities of the given state without levels"""
@@ -415,33 +407,4 @@ class ReactionSystemWithConcentrations(ReactionSystem):
return rs
-class ReactionSystemWithAutomaton(object):
- def __init__(self, reaction_system, context_automaton):
- self.rs = reaction_system
- self.ca = context_automaton
-
- def show(self, soft=False):
- self.rs.show(soft)
- self.ca.show()
-
- def is_with_concentrations(self):
- if not isinstance(self.rs, ReactionSystemWithConcentrations):
- return False
- if not isinstance(self.ca, ContextAutomatonWithConcentrations):
- return False
- return True
-
- def sanity_check(self):
- pass
-
- def get_ordinary_reaction_system_with_automaton(self):
- if not self.is_with_concentrations():
- raise RuntimeError("Not RS/CA with concentrations")
-
- ors = self.rs.get_reaction_system()
- oca = self.ca.get_automaton_with_flat_contexts(ors)
-
- return ReactionSystemWithAutomaton(ors, oca)
-
-
# EOF
diff --git a/reactics-smt/rs/reaction_system_with_concentrations_param.py b/reactics-smt/rs/reaction_system_with_concentrations_param.py
index ab628dc..1e52492 100644
--- a/reactics-smt/rs/reaction_system_with_concentrations_param.py
+++ b/reactics-smt/rs/reaction_system_with_concentrations_param.py
@@ -34,9 +34,9 @@ class ReactionSystemWithConcentrationsParam(ReactionSystem):
def add_bg_set_entity(self, e):
name = ""
def_max_conc = -1
- if type(e) is tuple and len(e) == 2:
+ if isinstance(e, tuple) and len(e) == 2:
name, def_max_conc = e
- elif type(e) is str:
+ elif isinstance(e, str):
name = e
print("\nWARNING: no maximal concentration level specified for:", e, "\n")
else:
@@ -80,18 +80,10 @@ class ReactionSystemWithConcentrationsParam(ReactionSystem):
def is_valid_entity_with_concentration(self, e):
"""Sanity check for entities with concentration"""
- if type(e) is tuple:
- if len(e) == 2 and type(e[1]) is int:
- return True
+ if isinstance(e, (tuple, list)) and len(e) == 2 and isinstance(e[1], int):
+ return True
- if type(e) is list:
- if len(e) == 2 and type(e[1]) is int:
- return True
-
- print("FATAL. Invalid entity+concentration: {:s}".format(e))
- exit(1)
-
- return False
+ raise RuntimeError("Invalid entity+concentration: " + repr(e))
def is_valid_concentration_for_entity(self, entity, level):
"""Checks the concentration level for entity"""
diff --git a/reactics-smt/rs_testing.py b/reactics-smt/rs_testing.py
index 727dcce..c492227 100644
--- a/reactics-smt/rs_testing.py
+++ b/reactics-smt/rs_testing.py
@@ -1,12 +1,10 @@
from rs import *
from smt import *
-import rs_examples
from logics import *
from rsltl_shortcuts import *
from itertools import chain, combinations
-import sys
import resource
def powerset(iterable,N=None):
diff --git a/reactics-smt/rssmt.py b/reactics-smt/rssmt.py
index 4ebf902..95398ec 100755
--- a/reactics-smt/rssmt.py
+++ b/reactics-smt/rssmt.py
@@ -11,7 +11,6 @@ import argparse
from rs import *
from smt import *
import sys
-import rs_examples
import rs_testing
from colour import *
diff --git a/reactics-smt/smt/__init__.py b/reactics-smt/smt/__init__.py
index dbce176..af9a852 100644
--- a/reactics-smt/smt/__init__.py
+++ b/reactics-smt/smt/__init__.py
@@ -1,4 +1,3 @@
-# from smt.smt_checker import SmtChecker
from smt.smt_checker_rs import SmtCheckerRS
from smt.smt_checker_rsc import SmtCheckerRSC
from smt.smt_checker_rsc_param import SmtCheckerRSCParam
diff --git a/reactics-smt/smt/smt_checker_rs.py b/reactics-smt/smt/smt_checker_rs.py
index 88242a4..57ee035 100644
--- a/reactics-smt/smt/smt_checker_rs.py
+++ b/reactics-smt/smt/smt_checker_rs.py
@@ -267,7 +267,7 @@ class SmtCheckerRS(object):
):
"""Main testing function"""
- if not type(state) is tuple:
+ if not isinstance(state, tuple):
state = (state, [])
if print_time:
diff --git a/reactics-smt/smt/smt_checker_rsc.py b/reactics-smt/smt/smt_checker_rsc.py
index 9b4a795..7934e73 100644
--- a/reactics-smt/smt/smt_checker_rsc.py
+++ b/reactics-smt/smt/smt_checker_rsc.py
@@ -11,9 +11,6 @@ from colour import *
from logics import rsLTL_Encoder
-# def simplify(x):
-# return x
-
class SmtCheckerRSC(object):
def __init__(self, rsca):
diff --git a/reactics-smt/smt/smt_checker_rsc_param.py b/reactics-smt/smt/smt_checker_rsc_param.py
index 2d48fcf..c345025 100644
--- a/reactics-smt/smt/smt_checker_rsc_param.py
+++ b/reactics-smt/smt/smt_checker_rsc_param.py
@@ -14,9 +14,6 @@ from logics import ParamConstr_Encoder
from rs.reaction_system_with_concentrations_param import ParameterObj, is_param
-# def simplify(x):
-# return x
-
def z3_max(a, b):
return If(a > b, a, b)
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..bc65fc7
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Run all ReactICS tests (BDD, SMT, generators)
+exec bash "$(dirname "$0")/tests/run_all_tests.sh" "$@"
diff --git a/tests/expected/tgc4_mc.txt b/tests/expected/tgc4_mc.txt
new file mode 100644
index 0000000..a1ae75c
--- /dev/null
+++ b/tests/expected/tgc4_mc.txt
@@ -0,0 +1,8 @@
+Using BDD-based Bounded Model Checking
+Formula (((EF(E< proc0.allowed >X(proc0.in)) AND EF(E< proc1.allowed >X(proc1.in))) AND EF(E< proc2.allowed >X(proc2.in))) AND EF(E< proc3.allowed >X(proc3.in))) holds
+Using BDD-based Bounded Model Checking
+Formula EF((((proc0.approach AND proc1.approach) AND proc2.approach) AND proc3.approach)) holds
+Using BDD-based Bounded Model Checking
+Formula AG((proc0.in IMPLIES K[proc0](((~proc1.in AND ~proc2.in) AND ~proc3.in)))) holds
+Using BDD-based Bounded Model Checking
+Formula AG((proc0.in IMPLIES C[proc0 proc1 proc2 proc3](((~proc1.in AND ~proc2.in) AND ~proc3.in)))) holds
diff --git a/tests/expected/tgc4_print.txt b/tests/expected/tgc4_print.txt
new file mode 100644
index 0000000..ccbcebb
--- /dev/null
+++ b/tests/expected/tgc4_print.txt
@@ -0,0 +1,58 @@
+# Context entities: out allowed
+# Reactions:
+
+ . proc = "proc0":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc1":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc2":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc3":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+# Context Automaton States:
+ = Init state: init
+ * init
+ * green
+ * red
+ * T
+# Context Automaton Transitions:
+ * [init -> green]: { proc0={ out } proc1={ out } proc2={ out } proc3={ out } }
+ * [green -> red]: { proc0={ allowed } } proc0.req
+ * [green -> red]: { proc1={ allowed } } proc1.req
+ * [green -> red]: { proc2={ allowed } } proc2.req
+ * [green -> red]: { proc3={ allowed } } proc3.req
+ * [green -> green]: { proc0={ } } (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)
+ * [green -> green]: { proc1={ } } (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)
+ * [green -> green]: { proc2={ } } (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)
+ * [green -> green]: { proc3={ } } (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)
+ * [red -> green]: { proc0={ } } proc0.leave
+ * [red -> green]: { proc1={ } } proc1.leave
+ * [red -> green]: { proc2={ } } proc2.leave
+ * [red -> green]: { proc3={ } } proc3.leave
+ * [red -> red]: { proc0={ } } (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)
+ * [red -> red]: { proc1={ } } (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)
+ * [red -> red]: { proc2={ } } (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)
+ * [red -> red]: { proc3={ } } (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)
+ * [green -> T]: { } ~((((((((true OR proc0.req) OR proc1.req) OR proc2.req) OR proc3.req) OR (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)) OR (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)) OR (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req)) OR (((~proc0.req AND ~proc1.req) AND ~proc2.req) AND ~proc3.req))
+ * [red -> T]: { } ~((((((((true OR proc0.leave) OR proc1.leave) OR proc2.leave) OR proc3.leave) OR (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)) OR (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)) OR (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave)) OR (((~proc0.leave AND ~proc1.leave) AND ~proc2.leave) AND ~proc3.leave))
+ * [T -> T]: { }
diff --git a/tests/expected/tgc4_states.txt b/tests/expected/tgc4_states.txt
new file mode 100644
index 0000000..bb515cf
--- /dev/null
+++ b/tests/expected/tgc4_states.txt
@@ -0,0 +1,80 @@
+{ proc0={ req } proc1={ approach } proc2={ req } proc3={ out leave } }
+{ proc0={ req } proc1={ req } proc2={ approach } proc3={ out leave } }
+{ proc0={ req } proc1={ req } proc2={ req } proc3={ out leave } }
+{ proc0={ approach } proc1={ approach } proc2={ req } proc3={ out leave } }
+{ proc0={ approach } proc1={ req } proc2={ req } proc3={ out leave } }
+{ proc0={ approach } proc1={ approach } proc2={ req in } proc3={ approach } }
+{ proc0={ approach } proc1={ req } proc2={ out leave } proc3={ req } }
+{ proc0={ approach } proc1={ req } proc2={ req in } proc3={ approach } }
+{ proc0={ req } proc1={ approach } proc2={ approach } proc3={ out leave } }
+{ proc0={ approach } proc1={ approach } proc2={ approach } proc3={ out leave } }
+{ proc0={ req } proc1={ approach } proc2={ req in } proc3={ req } }
+{ proc0={ approach } proc1={ req } proc2={ approach } proc3={ out leave } }
+{ proc0={ req } proc1={ approach } proc2={ req } proc3={ req in } }
+{ proc0={ approach } proc1={ req } proc2={ req in } proc3={ req } }
+{ proc0={ req } proc1={ approach } proc2={ out leave } proc3={ approach } }
+{ proc0={ req in } proc1={ approach } proc2={ req } proc3={ req } }
+{ proc0={ approach } proc1={ approach } proc2={ approach } proc3={ req in } }
+{ proc0={ req } proc1={ req } proc2={ out leave } proc3={ req } }
+{ proc0={ req } proc1={ approach } proc2={ out leave } proc3={ req } }
+{ proc0={ req } proc1={ req } proc2={ req in } proc3={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ out leave } proc3={ req } }
+{ proc0={ req } proc1={ req } proc2={ req in } proc3={ req } }
+{ proc0={ req } proc1={ approach } proc2={ req in } proc3={ approach } }
+{ proc0={ approach } proc1={ req } proc2={ out leave } proc3={ approach } }
+{ proc0={ req } proc1={ req } proc2={ out leave } proc3={ approach } }
+{ proc0={ } proc1={ } proc2={ } proc3={ } }
+{ proc0={ approach } proc1={ approach } proc2={ out leave } proc3={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ req in } proc3={ req } }
+{ proc0={ req } proc1={ approach } proc2={ approach } proc3={ req in } }
+{ proc0={ req in } proc1={ req } proc2={ approach } proc3={ req } }
+{ proc0={ req in } proc1={ approach } proc2={ approach } proc3={ approach } }
+{ proc0={ req } proc1={ req in } proc2={ req } proc3={ req } }
+{ proc0={ req in } proc1={ req } proc2={ approach } proc3={ approach } }
+{ proc0={ req } proc1={ req } proc2={ req } proc3={ req in } }
+{ proc0={ approach } proc1={ req } proc2={ req } proc3={ req in } }
+{ proc0={ approach } proc1={ approach } proc2={ req } proc3={ req in } }
+{ proc0={ out leave } proc1={ req } proc2={ approach } proc3={ approach } }
+{ proc0={ req in } proc1={ req } proc2={ req } proc3={ req } }
+{ proc0={ out leave } proc1={ req } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ req } proc2={ approach } proc3={ req in } }
+{ proc0={ req in } proc1={ approach } proc2={ approach } proc3={ req } }
+{ proc0={ req } proc1={ approach } proc2={ approach } proc3={ req } }
+{ proc0={ req in } proc1={ approach } proc2={ req } proc3={ approach } }
+{ proc0={ out leave } proc1={ approach } proc2={ req } proc3={ req } }
+{ proc0={ approach } proc1={ req } proc2={ approach } proc3={ req in } }
+{ proc0={ out leave } proc1={ req } proc2={ approach } proc3={ req } }
+{ proc0={ req } proc1={ out leave } proc2={ req } proc3={ req } }
+{ proc0={ req } proc1={ approach } proc2={ req } proc3={ req } }
+{ proc0={ out leave } proc1={ req } proc2={ req } proc3={ req } }
+{ proc0={ approach } proc1={ req in } proc2={ approach } proc3={ approach } }
+{ proc0={ out leave } proc1={ approach } proc2={ approach } proc3={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ approach } proc3={ approach } }
+{ proc0={ approach } proc1={ req } proc2={ req } proc3={ req } }
+{ proc0={ approach } proc1={ out leave } proc2={ approach } proc3={ approach } }
+{ proc0={ approach } proc1={ req } proc2={ approach } proc3={ approach } }
+{ proc0={ approach } proc1={ out leave } proc2={ req } proc3={ req } }
+{ proc0={ req } proc1={ out leave } proc2={ approach } proc3={ req } }
+{ proc0={ approach } proc1={ out leave } proc2={ approach } proc3={ req } }
+{ proc0={ approach } proc1={ req in } proc2={ req } proc3={ req } }
+{ proc0={ out leave } proc1={ approach } proc2={ req } proc3={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ req } proc3={ req } }
+{ proc0={ out leave } proc1={ approach } proc2={ approach } proc3={ req } }
+{ proc0={ req in } proc1={ req } proc2={ req } proc3={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ approach } proc3={ req } }
+{ proc0={ req } proc1={ req } proc2={ approach } proc3={ req } }
+{ proc0={ approach } proc1={ req } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ approach } proc2={ approach } proc3={ approach } }
+{ proc0={ approach } proc1={ req in } proc2={ approach } proc3={ req } }
+{ proc0={ approach } proc1={ approach } proc2={ req } proc3={ approach } }
+{ proc0={ approach } proc1={ req in } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ approach } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ req in } proc2={ approach } proc3={ req } }
+{ proc0={ approach } proc1={ req } proc2={ approach } proc3={ req } }
+{ proc0={ req } proc1={ req in } proc2={ approach } proc3={ approach } }
+{ proc0={ req } proc1={ out leave } proc2={ approach } proc3={ approach } }
+{ proc0={ req } proc1={ req } proc2={ approach } proc3={ approach } }
+{ proc0={ approach } proc1={ out leave } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ out leave } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ req in } proc2={ req } proc3={ approach } }
+{ proc0={ req } proc1={ req } proc2={ req } proc3={ approach } }
diff --git a/tests/expected/tgc_mc.txt b/tests/expected/tgc_mc.txt
new file mode 100644
index 0000000..6c19266
--- /dev/null
+++ b/tests/expected/tgc_mc.txt
@@ -0,0 +1,8 @@
+Using BDD-based Bounded Model Checking
+Formula ((EF(E< proc0.allowed >X(proc0.in)) AND EF(E< proc1.allowed >X(proc1.in))) AND EF(E< proc2.allowed >X(proc2.in))) holds
+Using BDD-based Bounded Model Checking
+Formula EF(((proc0.approach AND proc1.approach) AND proc2.approach)) holds
+Using BDD-based Bounded Model Checking
+Formula AG((proc0.in IMPLIES K[proc0]((~proc1.in AND ~proc2.in)))) holds
+Using BDD-based Bounded Model Checking
+Formula AG((proc0.in IMPLIES C[proc0 proc1 proc2]((~proc1.in AND ~proc2.in)))) holds
diff --git a/tests/expected/tgc_print.txt b/tests/expected/tgc_print.txt
new file mode 100644
index 0000000..0685301
--- /dev/null
+++ b/tests/expected/tgc_print.txt
@@ -0,0 +1,47 @@
+# Context entities: out allowed
+# Reactions:
+
+ . proc = "proc0":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc1":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc2":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+# Context Automaton States:
+ = Init state: init
+ * init
+ * green
+ * red
+ * T
+# Context Automaton Transitions:
+ * [init -> green]: { proc0={ out } proc1={ out } proc2={ out } }
+ * [green -> red]: { proc0={ allowed } } proc0.req
+ * [green -> red]: { proc1={ allowed } } proc1.req
+ * [green -> red]: { proc2={ allowed } } proc2.req
+ * [green -> green]: { proc0={ } } ((~proc0.req AND ~proc1.req) AND ~proc2.req)
+ * [green -> green]: { proc1={ } } ((~proc0.req AND ~proc1.req) AND ~proc2.req)
+ * [green -> green]: { proc2={ } } ((~proc0.req AND ~proc1.req) AND ~proc2.req)
+ * [red -> green]: { proc0={ } } proc0.leave
+ * [red -> green]: { proc1={ } } proc1.leave
+ * [red -> green]: { proc2={ } } proc2.leave
+ * [red -> red]: { proc0={ } } ((~proc0.leave AND ~proc1.leave) AND ~proc2.leave)
+ * [red -> red]: { proc1={ } } ((~proc0.leave AND ~proc1.leave) AND ~proc2.leave)
+ * [red -> red]: { proc2={ } } ((~proc0.leave AND ~proc1.leave) AND ~proc2.leave)
+ * [green -> T]: { } ~((((((true OR proc0.req) OR proc1.req) OR proc2.req) OR ((~proc0.req AND ~proc1.req) AND ~proc2.req)) OR ((~proc0.req AND ~proc1.req) AND ~proc2.req)) OR ((~proc0.req AND ~proc1.req) AND ~proc2.req))
+ * [red -> T]: { } ~((((((true OR proc0.leave) OR proc1.leave) OR proc2.leave) OR ((~proc0.leave AND ~proc1.leave) AND ~proc2.leave)) OR ((~proc0.leave AND ~proc1.leave) AND ~proc2.leave)) OR ((~proc0.leave AND ~proc1.leave) AND ~proc2.leave))
+ * [T -> T]: { }
diff --git a/tests/expected/tgc_reactions.txt b/tests/expected/tgc_reactions.txt
new file mode 100644
index 0000000..6b44f93
--- /dev/null
+++ b/tests/expected/tgc_reactions.txt
@@ -0,0 +1,23 @@
+# Reactions:
+
+ . proc = "proc0":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc1":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
+ . proc = "proc2":
+ * (R={ out },I={ },P={ approach })
+ * (R={ approach },I={ req },P={ req })
+ * (R={ allowed },I={ },P={ in })
+ * (R={ in },I={ },P={ out leave })
+ * (R={ req },I={ in },P={ req })
+
diff --git a/tests/expected/tgc_states.txt b/tests/expected/tgc_states.txt
new file mode 100644
index 0000000..d48f59b
--- /dev/null
+++ b/tests/expected/tgc_states.txt
@@ -0,0 +1,32 @@
+{ proc0={ req in } proc1={ req } proc2={ req } }
+{ proc0={ req in } proc1={ req } proc2={ approach } }
+{ proc0={ req } proc1={ req in } proc2={ approach } }
+{ proc0={ req in } proc1={ approach } proc2={ req } }
+{ proc0={ req in } proc1={ approach } proc2={ approach } }
+{ proc0={ req } proc1={ req in } proc2={ req } }
+{ proc0={ approach } proc1={ req in } proc2={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ req in } }
+{ proc0={ approach } proc1={ req in } proc2={ req } }
+{ proc0={ approach } proc1={ out leave } proc2={ req } }
+{ proc0={ req } proc1={ approach } proc2={ out leave } }
+{ proc0={ req } proc1={ req } proc2={ req in } }
+{ proc0={ approach } proc1={ req } proc2={ req } }
+{ proc0={ approach } proc1={ req } proc2={ req in } }
+{ proc0={ out leave } proc1={ req } proc2={ req } }
+{ proc0={ req } proc1={ approach } proc2={ req in } }
+{ proc0={ req } proc1={ out leave } proc2={ approach } }
+{ proc0={ approach } proc1={ out leave } proc2={ approach } }
+{ proc0={ req } proc1={ req } proc2={ out leave } }
+{ proc0={ req } proc1={ out leave } proc2={ req } }
+{ proc0={ } proc1={ } proc2={ } }
+{ proc0={ req } proc1={ approach } proc2={ req } }
+{ proc0={ out leave } proc1={ req } proc2={ approach } }
+{ proc0={ approach } proc1={ req } proc2={ out leave } }
+{ proc0={ approach } proc1={ approach } proc2={ req } }
+{ proc0={ req } proc1={ req } proc2={ approach } }
+{ proc0={ out leave } proc1={ approach } proc2={ req } }
+{ proc0={ approach } proc1={ req } proc2={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ approach } }
+{ proc0={ approach } proc1={ approach } proc2={ out leave } }
+{ proc0={ out leave } proc1={ approach } proc2={ approach } }
+{ proc0={ req } proc1={ approach } proc2={ approach } }
diff --git a/tests/expected/trivial_print.txt b/tests/expected/trivial_print.txt
new file mode 100644
index 0000000..ad83c3e
--- /dev/null
+++ b/tests/expected/trivial_print.txt
@@ -0,0 +1,17 @@
+# Context entities: e1 e4
+# Reactions:
+
+ . proc = "m":
+ * (R={ e1 e4 },I={ e2 },P={ e1 e2 })
+ * (R={ e2 },I={ e4 },P={ e1 e4 e3 })
+ * (R={ e1 e3 },I={ e2 },P={ e1 e2 })
+ * (R={ e3 },I={ e2 },P={ e1 })
+
+# Context Automaton States:
+ = Init state: s0
+ * s0
+ * s1
+# Context Automaton Transitions:
+ * [s0 -> s1]: { m={ e1 e4 } }
+ * [s1 -> s1]: { m={ } }
+ * [s1 -> s1]: { m={ e4 } }
diff --git a/tests/expected/trivial_reactions.txt b/tests/expected/trivial_reactions.txt
new file mode 100644
index 0000000..0149240
--- /dev/null
+++ b/tests/expected/trivial_reactions.txt
@@ -0,0 +1,8 @@
+# Reactions:
+
+ . proc = "m":
+ * (R={ e1 e4 },I={ e2 },P={ e1 e2 })
+ * (R={ e2 },I={ e4 },P={ e1 e4 e3 })
+ * (R={ e1 e3 },I={ e2 },P={ e1 e2 })
+ * (R={ e3 },I={ e2 },P={ e1 })
+
diff --git a/tests/expected/trivial_states.txt b/tests/expected/trivial_states.txt
new file mode 100644
index 0000000..53dc9d6
--- /dev/null
+++ b/tests/expected/trivial_states.txt
@@ -0,0 +1,3 @@
+{ m={ e1 e4 e3 } }
+{ m={ e1 e2 } }
+{ m={ } }
diff --git a/tests/run_all_tests.sh b/tests/run_all_tests.sh
new file mode 100755
index 0000000..bbfa9d3
--- /dev/null
+++ b/tests/run_all_tests.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+
+# Runs all ReactICS regression tests (BDD + SMT + generators)
+
+set -uo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+REACTICS="$ROOT_DIR/reactics-bdd/reactics"
+GENERATORS="$ROOT_DIR/examples/bdd/generators"
+
+cd "$ROOT_DIR"
+
+failed=0
+
+# ---------------------------------------------------------------
+echo "========== BDD Tests =========="
+echo
+bash "$SCRIPT_DIR/run_tests.sh" || failed=1
+
+# ---------------------------------------------------------------
+echo
+echo "========== SMT Tests (pytest) =========="
+echo
+python3 -m pytest "$SCRIPT_DIR/test_smt.py" -v || failed=1
+
+# ---------------------------------------------------------------
+echo
+echo "========== Generator Tests =========="
+echo
+
+gen_pass=0
+gen_fail=0
+
+check_gen() {
+ local name="$1"
+ local gen_cmd="$2"
+ local property="$3"
+ local expected="$4"
+
+ local tmpfile
+ tmpfile=$(mktemp /tmp/reactics_gen_XXXXXX.drs)
+
+ eval "$gen_cmd" > "$tmpfile" 2>&1
+ local result
+ result=$("$REACTICS" -c "$property" "$tmpfile" 2>&1 | grep -oE "(holds|does not hold)" || true)
+ rm -f "$tmpfile"
+
+ if [[ "$result" == "$expected" ]]; then
+ echo " PASS: $name"
+ ((gen_pass++))
+ else
+ echo " FAIL: $name (expected '$expected', got '$result')"
+ ((gen_fail++))
+ failed=1
+ fi
+}
+
+echo "[gen_tgc]"
+check_gen "tgc(3) f1" "python3 $GENERATORS/gen_tgc.py 3" f1 "holds"
+check_gen "tgc(3) f3" "python3 $GENERATORS/gen_tgc.py 3" f3 "holds"
+check_gen "tgc(4) f2" "python3 $GENERATORS/gen_tgc.py 4" f2 "holds"
+
+echo
+echo "[gen_asm]"
+check_gen "asm(3) f1" "python3 $GENERATORS/gen_asm.py 3" f1 "holds"
+check_gen "asm(3) f2" "python3 $GENERATORS/gen_asm.py 3" f2 "holds"
+
+echo
+echo "[gen_drs]"
+check_gen "drs(2,2,2,a) f0" "python3 $GENERATORS/gen_drs.py 2 2 2 a" f0 "does not hold"
+check_gen "drs(2,2,2,b) f0" "python3 $GENERATORS/gen_drs.py 2 2 2 b" f0 "holds"
+
+echo
+echo "Generator results: $gen_pass passed, $gen_fail failed"
+
+# ---------------------------------------------------------------
+echo
+echo "================================"
+if [[ $failed -eq 0 ]]; then
+ echo "All test suites passed."
+else
+ echo "Some tests FAILED."
+ exit 1
+fi
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
new file mode 100755
index 0000000..b64a847
--- /dev/null
+++ b/tests/run_tests.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+
+# Regression test harness for ReactICS BDD module
+# Compares current output against saved expected output
+
+set -uo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+REACTICS="$ROOT_DIR/reactics-bdd/reactics"
+EXPECTED_DIR="$SCRIPT_DIR/expected"
+
+PASS=0
+FAIL=0
+ERRORS=""
+
+run_test() {
+ local name="$1"
+ local expected_file="$2"
+ shift 2
+ local actual
+ actual=$("$@" 2>&1) || true
+ local expected
+ expected=$(cat "$expected_file")
+
+ if [[ "$actual" == "$expected" ]]; then
+ echo " PASS: $name"
+ ((PASS++))
+ else
+ echo " FAIL: $name"
+ diff <(echo "$expected") <(echo "$actual") | head -20
+ ((FAIL++))
+ ERRORS="$ERRORS\n - $name"
+ fi
+}
+
+echo "Running ReactICS regression tests..."
+echo
+
+# --- tgc.drs tests ---
+echo "[tgc]"
+run_test "tgc print" "$EXPECTED_DIR/tgc_print.txt" "$REACTICS" -P examples/bdd/tgc.drs
+run_test "tgc reactions" "$EXPECTED_DIR/tgc_reactions.txt" "$REACTICS" -r examples/bdd/tgc.drs
+run_test "tgc states" "$EXPECTED_DIR/tgc_states.txt" "$REACTICS" -s examples/bdd/tgc.drs
+run_test "tgc mc (f1-f4)" "$EXPECTED_DIR/tgc_mc.txt" \
+ bash -c 'for f in f1 f2 f3 f4; do '"$REACTICS"' -c $f examples/bdd/tgc.drs 2>&1; done'
+
+echo
+echo "[trivial]"
+run_test "trivial print" "$EXPECTED_DIR/trivial_print.txt" "$REACTICS" -P reactics-bdd/in/trivial.drs
+run_test "trivial reactions" "$EXPECTED_DIR/trivial_reactions.txt" "$REACTICS" -r reactics-bdd/in/trivial.drs
+run_test "trivial states" "$EXPECTED_DIR/trivial_states.txt" "$REACTICS" -s reactics-bdd/in/trivial.drs
+
+echo
+echo "[tgc4]"
+run_test "tgc4 print" "$EXPECTED_DIR/tgc4_print.txt" "$REACTICS" -P examples/bdd/tgc4.drs
+run_test "tgc4 states" "$EXPECTED_DIR/tgc4_states.txt" "$REACTICS" -s examples/bdd/tgc4.drs
+run_test "tgc4 mc (f1-f4)" "$EXPECTED_DIR/tgc4_mc.txt" \
+ bash -c 'for f in f1 f2 f3 f4; do '"$REACTICS"' -c $f examples/bdd/tgc4.drs 2>&1; done'
+
+echo
+echo "================================"
+echo "Results: $PASS passed, $FAIL failed"
+if [[ $FAIL -gt 0 ]]; then
+ echo -e "Failures:$ERRORS"
+ exit 1
+else
+ echo "All tests passed."
+fi
diff --git a/tests/test_smt.py b/tests/test_smt.py
new file mode 100644
index 0000000..17f1b02
--- /dev/null
+++ b/tests/test_smt.py
@@ -0,0 +1,501 @@
+"""
+Regression tests for the ReactICS SMT module.
+
+Tests the Python API directly: reaction system construction,
+context automata, and SMT-based verification (reachability + rsLTL).
+"""
+
+import sys
+import os
+import io
+from contextlib import redirect_stdout
+
+# Add reactics-smt to the path
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "reactics-smt"))
+
+from z3 import sat, unsat
+from rs import (
+ ReactionSystem,
+ ContextAutomaton,
+ ReactionSystemWithConcentrations,
+ ContextAutomatonWithConcentrations,
+ ReactionSystemWithConcentrationsParam,
+ ReactionSystemWithAutomaton,
+)
+from smt import SmtCheckerRS, SmtCheckerRSC, SmtCheckerRSCParam
+from logics import Formula_rsLTL, BagDescription
+from rsltl_shortcuts import ltl_F, bag_entity, param_entity, param_And
+
+
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
+
+def run_silent(fn, *args, **kwargs):
+ """Run a function with stdout suppressed, return its result."""
+ with redirect_stdout(io.StringIO()):
+ return fn(*args, **kwargs)
+
+
+# ---------------------------------------------------------------------------
+# Data model tests
+# ---------------------------------------------------------------------------
+
+class TestReactionSystem:
+ def test_add_entities(self):
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ rs.add_bg_set_entity("b")
+ assert rs.background_set == ["a", "b"]
+ assert rs.get_entity_id("a") == 0
+ assert rs.get_entity_id("b") == 1
+ assert rs.get_entity_name(0) == "a"
+
+ def test_add_reaction(self):
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ rs.add_bg_set_entity("b")
+ rs.add_bg_set_entity("c")
+ rs.add_reaction(["a"], ["b"], ["c"])
+ assert len(rs.reactions) == 1
+ reactants, inhibitors, products = rs.reactions[0]
+ assert reactants == [0]
+ assert inhibitors == [1]
+ assert products == [2]
+
+ def test_reactions_by_product(self):
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ rs.add_bg_set_entity("b")
+ rs.add_reaction(["a"], [], ["b"])
+ rbp = rs.get_reactions_by_product()
+ assert 1 in rbp # entity id for "b"
+ assert len(rbp[1]) == 1
+
+ def test_duplicate_entity_raises(self):
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ try:
+ rs.add_bg_set_entity("a")
+ assert False, "Should have raised"
+ except RuntimeError:
+ pass
+
+
+class TestReactionSystemWithConcentrations:
+ def test_add_entity_with_concentration(self):
+ rs = ReactionSystemWithConcentrations()
+ rs.add_bg_set_entity(("x", 3))
+ rs.add_bg_set_entity(("y", 5))
+ assert rs.background_set == ["x", "y"]
+ assert rs.get_max_concentration_level(0) == 3
+ assert rs.get_max_concentration_level(1) == 5
+
+ def test_add_reaction_with_concentrations(self):
+ rs = ReactionSystemWithConcentrations()
+ rs.add_bg_set_entity(("a", 2))
+ rs.add_bg_set_entity(("b", 1))
+ rs.add_reaction([("a", 1)], [], [("b", 1)])
+ assert len(rs.reactions) == 1
+
+ def test_meta_reactions(self):
+ rs = ReactionSystemWithConcentrations()
+ rs.add_bg_set_entity(("e", 3))
+ rs.add_bg_set_entity(("inc", 1))
+ rs.add_reaction_inc("e", "inc", [("e", 1)], [("e", 3)])
+ e_id = rs.get_entity_id("e")
+ assert e_id in rs.meta_reactions
+ assert rs.meta_reactions[e_id][0][0] == "inc"
+
+
+class TestContextAutomaton:
+ def test_states_and_transitions(self):
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ rs.add_bg_set_entity("b")
+ ca = ContextAutomaton(rs)
+ ca.add_init_state("s0")
+ ca.add_state("s1")
+ assert ca.get_init_state_id() == 0
+ assert ca.get_state_id("s1") == 1
+ ca.add_transition("s0", ["a"], "s1")
+ ca.add_transition("s1", [], "s1")
+ assert len(ca.transitions) == 2
+
+ def test_invalid_transition_raises(self):
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ ca = ContextAutomaton(rs)
+ ca.add_init_state("s0")
+ try:
+ ca.add_transition("s0", ["a"], "nonexistent")
+ assert False, "Should have raised"
+ except RuntimeError:
+ pass
+
+
+class TestContextAutomatonWithConcentrations:
+ def test_transitions_with_concentrations(self):
+ rs = ReactionSystemWithConcentrations()
+ rs.add_bg_set_entity(("a", 2))
+ rs.add_bg_set_entity(("b", 1))
+ ca = ContextAutomatonWithConcentrations(rs)
+ ca.add_init_state("s0")
+ ca.add_state("s1")
+ ca.add_transition("s0", [("a", 2)], "s1")
+ ca.add_transition("s1", [], "s1")
+ assert len(ca.transitions) == 2
+
+
+# ---------------------------------------------------------------------------
+# Formula construction tests
+# ---------------------------------------------------------------------------
+
+class TestFormulas:
+ def test_bag_description(self):
+ b = BagDescription.f_entity("x")
+ assert str(b) == "x"
+ b_gt = b > 0
+ assert ">" in str(b_gt)
+
+ def test_rsltl_formula(self):
+ f = Formula_rsLTL.f_F(
+ BagDescription.f_TRUE(),
+ BagDescription.f_entity("x") > 0,
+ )
+ assert "F" in str(f)
+
+ def test_combined_formula(self):
+ f = Formula_rsLTL.f_G(
+ BagDescription.f_TRUE(),
+ Formula_rsLTL.f_Implies(
+ BagDescription.f_entity("a") == 1,
+ Formula_rsLTL.f_F(
+ BagDescription.f_TRUE(),
+ BagDescription.f_entity("b") > 0,
+ ),
+ ),
+ )
+ assert "G" in str(f)
+ assert "F" in str(f)
+
+
+# ---------------------------------------------------------------------------
+# Verification tests: SmtCheckerRS (basic, no concentrations)
+# ---------------------------------------------------------------------------
+
+def _build_simple_rs():
+ """A minimal RS for testing: a -> b -> c with context automaton."""
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ rs.add_bg_set_entity("b")
+ rs.add_bg_set_entity("c")
+ rs.add_reaction(["a"], [], ["b"])
+ rs.add_reaction(["b"], [], ["c"])
+
+ ca = ContextAutomaton(rs)
+ ca.add_init_state("s0")
+ ca.add_state("s1")
+ ca.add_transition("s0", ["a"], "s1")
+ ca.add_transition("s1", [], "s1")
+
+ return ReactionSystemWithAutomaton(rs, ca)
+
+
+class TestSmtCheckerRS:
+ def test_reachability_sat(self):
+ rsa = _build_simple_rs()
+ checker = SmtCheckerRS(rsa)
+ run_silent(
+ checker.check_reachability,
+ ["c"],
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ )
+ # After finding SAT, solver state should be satisfiable
+ assert checker.solver.check() == sat
+
+ def test_reachability_unreachable(self):
+ """Entity 'a' is never produced, so it shouldn't be reachable as a final state."""
+ rs = ReactionSystem()
+ rs.add_bg_set_entity("a")
+ rs.add_bg_set_entity("b")
+ rs.add_reaction(["a"], [], ["b"])
+
+ ca = ContextAutomaton(rs)
+ ca.add_init_state("s0")
+ ca.add_state("s1")
+ # Context provides 'a' initially, then nothing
+ ca.add_transition("s0", ["a"], "s1")
+ ca.add_transition("s1", [], "s1")
+
+ rsa = ReactionSystemWithAutomaton(rs, ca)
+ checker = SmtCheckerRS(rsa)
+
+ # 'b' is reachable (produced from 'a')
+ run_silent(
+ checker.check_reachability,
+ ["b"],
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ )
+ assert checker.solver.check() == sat
+
+
+# ---------------------------------------------------------------------------
+# Verification tests: SmtCheckerRSC (with concentrations)
+# ---------------------------------------------------------------------------
+
+def _build_chain_rsc(chain_len=2, max_conc=2):
+ """Build a chain reaction system with concentrations."""
+ r = ReactionSystemWithConcentrations()
+ r.add_bg_set_entity(("inc", 1))
+ r.add_bg_set_entity(("dec", 1))
+
+ for i in range(1, chain_len + 1):
+ r.add_bg_set_entity(("e_" + str(i), max_conc))
+
+ for i in range(1, chain_len + 1):
+ ent = "e_" + str(i)
+ r.add_reaction_inc(ent, "inc", [(ent, 1)], [(ent, max_conc)])
+ r.add_reaction_dec(ent, "dec", [(ent, 1)], [])
+ if i < chain_len:
+ r.add_reaction([(ent, max_conc)], [], [("e_" + str(i + 1), 1)])
+
+ r.add_reaction(
+ [("e_" + str(chain_len), max_conc)],
+ [("dec", 1)],
+ [("e_" + str(chain_len), max_conc)],
+ )
+
+ c = ContextAutomatonWithConcentrations(r)
+ c.add_init_state("init")
+ c.add_state("working")
+ c.add_transition("init", [("e_1", 1), ("inc", 1)], "working")
+ c.add_transition("working", [("inc", 1)], "working")
+
+ return ReactionSystemWithAutomaton(r, c)
+
+
+class TestSmtCheckerRSC:
+ def test_chain_reachability_sat(self):
+ """Chain(2,2): e_2=2 should be reachable at level 3."""
+ rc = _build_chain_rsc(2, 2)
+ checker = SmtCheckerRSC(rc)
+ prop = ([("e_2", 2)], [])
+ run_silent(
+ checker.check_reachability,
+ prop,
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ max_level=10,
+ )
+ assert checker.solver.check() == sat
+ assert checker.current_level == 3
+
+ def test_chain_reachability_longer(self):
+ """Chain(3,2): e_3=2 should be reachable at level 5."""
+ rc = _build_chain_rsc(3, 2)
+ checker = SmtCheckerRSC(rc)
+ prop = ([("e_3", 2)], [])
+ run_silent(
+ checker.check_reachability,
+ prop,
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ max_level=10,
+ )
+ assert checker.solver.check() == sat
+ assert checker.current_level == 5
+
+ def test_rsltl_finally(self):
+ """Check rsLTL F formula on chain(2,2)."""
+ rc = _build_chain_rsc(2, 2)
+ checker = SmtCheckerRSC(rc)
+ form = Formula_rsLTL.f_F(
+ BagDescription.f_entity("inc") > 0,
+ BagDescription.f_entity("e_2") >= 2,
+ )
+ run_silent(
+ checker.check_rsltl,
+ formula=form,
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ )
+ assert checker.solver.check() == sat
+ assert checker.current_level == 3
+
+ def test_rsltl_finally_fast(self):
+ """Chain(2,2), F(e_1==2): should be SAT at level 1."""
+ rc = _build_chain_rsc(2, 2)
+ checker = SmtCheckerRSC(rc)
+ form = Formula_rsLTL.f_F(
+ BagDescription.f_entity("inc") > 0,
+ BagDescription.f_entity("e_1") == 2,
+ )
+ run_silent(
+ checker.check_rsltl,
+ formula=form,
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ )
+ assert checker.solver.check() == sat
+ assert checker.current_level == 1
+
+ def test_rsltl_next_until(self):
+ """Chain(2,2), X[TRUE](e_1 > 0 U[inc > 0] e_2 > 0): SAT at level 2."""
+ rc = _build_chain_rsc(2, 2)
+ checker = SmtCheckerRSC(rc)
+ form = Formula_rsLTL.f_X(
+ BagDescription.f_TRUE(),
+ Formula_rsLTL.f_U(
+ BagDescription.f_entity("inc") > 0,
+ BagDescription.f_entity("e_1") > 0,
+ BagDescription.f_entity("e_2") > 0,
+ ),
+ )
+ run_silent(
+ checker.check_rsltl,
+ formula=form,
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ )
+ assert checker.solver.check() == sat
+ assert checker.current_level == 2
+
+
+# ---------------------------------------------------------------------------
+# Verification tests: SmtCheckerRSC on heat shock response
+# ---------------------------------------------------------------------------
+
+def _build_heat_shock():
+ """Heat shock response model."""
+ stress_temp = 42
+ max_temp = 50
+
+ r = ReactionSystemWithConcentrations()
+ for e in [("hsp", 1), ("hsf", 1), ("hsf2", 1), ("hsf3", 1), ("hse", 1),
+ ("mfp", 1), ("prot", 1), ("hsf3:hse", 1), ("hsp:mfp", 1),
+ ("hsp:hsf", 1), ("temp", max_temp), ("heat", 1), ("cool", 1)]:
+ r.add_bg_set_entity(e)
+
+ r.add_reaction([("hsf", 1)], [("hsp", 1)], [("hsf3", 1)])
+ r.add_reaction([("hsf", 1), ("hsp", 1), ("mfp", 1)], [], [("hsf3", 1)])
+ r.add_reaction([("hsf3", 1)], [("hse", 1), ("hsp", 1)], [("hsf", 1)])
+ r.add_reaction([("hsf3", 1), ("hsp", 1), ("mfp", 1)], [("hse", 1)], [("hsf", 1)])
+ r.add_reaction([("hsf3", 1), ("hse", 1)], [("hsp", 1)], [("hsf3:hse", 1)])
+ r.add_reaction([("hsp", 1), ("hsf3", 1), ("mfp", 1), ("hse", 1)], [], [("hsf3:hse", 1)])
+ r.add_reaction([("hse", 1)], [("hsf3", 1)], [("hse", 1)])
+ r.add_reaction([("hsp", 1), ("hsf3", 1), ("hse", 1)], [("mfp", 1)], [("hse", 1)])
+ r.add_reaction([("hsf3:hse", 1)], [("hsp", 1)], [("hsp", 1), ("hsf3:hse", 1)])
+ r.add_reaction([("hsp", 1), ("mfp", 1), ("hsf3:hse", 1)], [], [("hsp", 1), ("hsf3:hse", 1)])
+ r.add_reaction([("hsf", 1), ("hsp", 1)], [("mfp", 1)], [("hsp:hsf", 1)])
+ r.add_reaction([("hsp:hsf", 1), ("temp", stress_temp)], [], [("hsf", 1), ("hsp", 1)])
+ r.add_reaction([("hsp:hsf", 1)], [("temp", stress_temp)], [("hsp:hsf", 1)])
+ r.add_reaction([("hsp", 1), ("hsf3:hse", 1)], [("mfp", 1)], [("hse", 1), ("hsp:hsf", 1)])
+ r.add_reaction([("temp", stress_temp), ("prot", 1)], [], [("mfp", 1), ("prot", 1)])
+ r.add_reaction([("prot", 1)], [("temp", stress_temp)], [("prot", 1)])
+ r.add_reaction([("hsp", 1), ("mfp", 1)], [], [("hsp:mfp", 1)])
+ r.add_reaction([("mfp", 1)], [("hsp", 1)], [("mfp", 1)])
+ r.add_reaction([("hsp:mfp", 1)], [], [("hsp", 1), ("prot", 1)])
+
+ r.add_reaction_inc("temp", "heat", [("temp", 1)], [("temp", max_temp)])
+ r.add_reaction_dec("temp", "cool", [("temp", 1)], [])
+ r.add_permanency("temp", [("heat", 1), ("cool", 1)])
+
+ c = ContextAutomatonWithConcentrations(r)
+ c.add_init_state("0")
+ c.add_state("1")
+ c.add_transition("0", [("hsf", 1), ("prot", 1), ("hse", 1), ("temp", 35)], "1")
+ c.add_transition("1", [("cool", 1)], "1")
+ c.add_transition("1", [("heat", 1)], "1")
+ c.add_transition("1", [], "1")
+
+ return ReactionSystemWithAutomaton(r, c)
+
+
+class TestHeatShockResponse:
+ def test_mfp_reachable(self):
+ """mfp should be reachable at level 9."""
+ rc = _build_heat_shock()
+ checker = SmtCheckerRSC(rc)
+ prop = ([("mfp", 1)], [])
+ run_silent(
+ checker.check_reachability,
+ prop,
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ max_level=40,
+ )
+ assert checker.solver.check() == sat
+ assert checker.current_level == 9
+
+
+# ---------------------------------------------------------------------------
+# Verification tests: SmtCheckerRSCParam (parametric)
+# ---------------------------------------------------------------------------
+
+def _build_parametric_overview():
+ """Parametric example from doc/overview.py."""
+ prs = ReactionSystemWithConcentrationsParam()
+ for ec in [("a", 3), ("b", 2), ("c", 1), ("h", 1)]:
+ prs.add_bg_set_entity(ec)
+
+ lda = prs.get_param("lda")
+ prs.add_reaction([("a", 1)], [("h", 1)], [("b", 2)])
+ prs.add_reaction(lda, [("h", 1)], [("c", 1)])
+
+ ca = ContextAutomatonWithConcentrations(prs)
+ ca.add_init_state("0")
+ ca.add_state("1")
+ ca.add_transition("0", [("a", 3)], "1")
+ ca.add_transition("1", [], "1")
+ ca.add_transition("1", [("h", 1)], "1")
+
+ return ReactionSystemWithAutomaton(prs, ca), lda
+
+
+class TestSmtCheckerRSCParam:
+ def test_parametric_sat(self):
+ """Parametric overview: F[h==0](c>0) should be SAT at level 2."""
+ rc, lda = _build_parametric_overview()
+ pc = param_entity(lda, "a") == 0
+ f = ltl_F(bag_entity("h") == 0, "c")
+
+ checker = SmtCheckerRSCParam(rc, optimise=True)
+ run_silent(
+ checker.check_rsltl,
+ formulae_list=[f],
+ param_constr=pc,
+ )
+ # The parametric checker uses path-based solving;
+ # after SAT the solver should be satisfiable
+ assert checker.solver.check() == sat
+
+
+# ---------------------------------------------------------------------------
+# RS translation tests (RSC -> RS)
+# ---------------------------------------------------------------------------
+
+class TestRSCtoRSTranslation:
+ def test_translated_system_reachability(self):
+ """Chain(2,2) translated to plain RS: e_2#2 should be reachable."""
+ rc = _build_chain_rsc(2, 2)
+ orc = rc.get_ordinary_reaction_system_with_automaton()
+ checker = SmtCheckerRS(orc)
+ run_silent(
+ checker.check_reachability,
+ ["e_2#2"],
+ print_witness=False,
+ print_time=False,
+ print_mem=False,
+ )
+ assert checker.solver.check() == sat
--
2.53.0
From ae419e5e0e0f4e3abe200e8d8c452ebe13996b21 Mon Sep 17 00:00:00 2001
From: Artur Meski
Date: Fri, 10 Apr 2026 18:27:57 +0100
Subject: [PATCH 12/12] Failure handling (CI)
---
.github/workflows/tests.yml | 19 ++++++++++++++++---
tests/run_all_tests.sh | 13 ++++++++++---
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 6f61a61..279edf6 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -72,21 +72,30 @@ jobs:
python-version: "3.12"
- name: Run generator tests
+ shell: bash --noprofile --norc {0}
run: |
+ set -uo pipefail
REACTICS="$PWD/reactics-bdd/reactics"
GENERATORS="$PWD/examples/bdd/generators"
+ passed=0
failed=0
check_gen() {
local name="$1" gen_cmd="$2" property="$3" expected="$4"
local tmpfile=$(mktemp /tmp/reactics_gen_XXXXXX.drs)
- eval "$gen_cmd" > "$tmpfile" 2>&1
+ if ! eval "$gen_cmd" > "$tmpfile" 2>&1; then
+ echo " FAIL: $name (generator crashed)"
+ failed=$((failed + 1))
+ rm -f "$tmpfile"
+ return
+ fi
local result=$("$REACTICS" -c "$property" "$tmpfile" 2>&1 | grep -oE "(holds|does not hold)" || true)
rm -f "$tmpfile"
if [ "$result" = "$expected" ]; then
echo " PASS: $name"
+ passed=$((passed + 1))
else
echo " FAIL: $name (expected '$expected', got '$result')"
- failed=1
+ failed=$((failed + 1))
fi
}
check_gen "tgc(3) f1" "python3 $GENERATORS/gen_tgc.py 3" f1 "holds"
@@ -96,4 +105,8 @@ jobs:
check_gen "asm(3) f2" "python3 $GENERATORS/gen_asm.py 3" f2 "holds"
check_gen "drs(2,2,2,a) f0" "python3 $GENERATORS/gen_drs.py 2 2 2 a" f0 "does not hold"
check_gen "drs(2,2,2,b) f0" "python3 $GENERATORS/gen_drs.py 2 2 2 b" f0 "holds"
- exit $failed
+ echo
+ echo "Generator results: $passed passed, $failed failed"
+ if [ "$failed" -gt 0 ]; then
+ exit 1
+ fi
diff --git a/tests/run_all_tests.sh b/tests/run_all_tests.sh
index bbfa9d3..c326d9d 100755
--- a/tests/run_all_tests.sh
+++ b/tests/run_all_tests.sh
@@ -41,17 +41,24 @@ check_gen() {
local tmpfile
tmpfile=$(mktemp /tmp/reactics_gen_XXXXXX.drs)
- eval "$gen_cmd" > "$tmpfile" 2>&1
+ if ! eval "$gen_cmd" > "$tmpfile" 2>&1; then
+ echo " FAIL: $name (generator crashed)"
+ gen_fail=$((gen_fail + 1))
+ failed=1
+ rm -f "$tmpfile"
+ return
+ fi
+
local result
result=$("$REACTICS" -c "$property" "$tmpfile" 2>&1 | grep -oE "(holds|does not hold)" || true)
rm -f "$tmpfile"
if [[ "$result" == "$expected" ]]; then
echo " PASS: $name"
- ((gen_pass++))
+ gen_pass=$((gen_pass + 1))
else
echo " FAIL: $name (expected '$expected', got '$result')"
- ((gen_fail++))
+ gen_fail=$((gen_fail + 1))
failed=1
fi
}
--
2.53.0