From 8d7ee33c2fae2e0e0a1426591b51881b1d7999e7 Mon Sep 17 00:00:00 2001 From: Quentin Quadrat Date: Tue, 30 Apr 2024 12:17:33 +0200 Subject: [PATCH] WIP adding unit tets for Julia interface --- src/julia/Julia.cpp | 20 ++-- src/julia/Julia.hpp | 12 ++- src/julia/TimedPetriNetEditor.jl | 18 +++- src/julia/tests/test_petri.jl | 170 +++++++++++++++++++++++++++++++ tests/HowardTests.cpp | 1 + 5 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 src/julia/tests/test_petri.jl diff --git a/src/julia/Julia.cpp b/src/julia/Julia.cpp index c20c599..eb8675d 100644 --- a/src/julia/Julia.cpp +++ b/src/julia/Julia.cpp @@ -386,10 +386,10 @@ bool petri_is_event_graph(int64_t const pn, bool* res) std::vector erroneous_arcs; std::string error; - if (isEventGraph(*g_petri_nets[size_t(pn)], error, erroneous_arcs)) - return true; - std::cerr << error << std::endl; - return false; + *res = isEventGraph(*g_petri_nets[size_t(pn)], error, erroneous_arcs); + if (!error.empty()) + std::cerr << error << std::endl; + return true; } //------------------------------------------------------------------------------ @@ -458,16 +458,13 @@ bool petri_to_sys_lin(int64_t const pn, CSparseMatrix_t* pD, CSparseMatrix_t* pA } //------------------------------------------------------------------------------ -bool petri_dater_equation(int64_t const pn, bool use_caption) +bool petri_dater_equation(int64_t const pn, bool use_caption, bool maxplus_notation) { CHECK_VALID_PETRI_HANDLE(pn, false); CHECK_IS_EVENT_GRAPH(pn, false); std::cout - << tpne::showDaterEquation(*g_petri_nets[size_t(pn)], "", use_caption, true).str() - << std::endl - << tpne::showDaterEquation(*g_petri_nets[size_t(pn)], "", use_caption, false).str() - << std::endl; + << tpne::showDaterEquation(*g_petri_nets[size_t(pn)], "", use_caption, maxplus_notation).str(); return true; } @@ -478,10 +475,7 @@ bool petri_counter_equation(int64_t const pn, bool use_caption, bool minplus_not CHECK_IS_EVENT_GRAPH(pn, false); std::cout - << tpne::showCounterEquation(*g_petri_nets[size_t(pn)], "", use_caption, true).str() - << std::endl - << tpne::showCounterEquation(*g_petri_nets[size_t(pn)], "", use_caption, false).str() - << std::endl; + << tpne::showCounterEquation(*g_petri_nets[size_t(pn)], "", use_caption, minplus_notation).str(); return true; } diff --git a/src/julia/Julia.hpp b/src/julia/Julia.hpp index 029cd0e..defd228 100644 --- a/src/julia/Julia.hpp +++ b/src/julia/Julia.hpp @@ -285,16 +285,24 @@ extern "C" int64_t petri_to_canonical(int64_t const pn); // **************************************************************************** //! \brief Display the event graph into its dater equation. //! \param[in] pn: the handle of the petri net created by create_petri_net(). +//! \param[in] use_caption: display node captions instead of node keys. +//! \param[in] maxplus_notation: display (max,+) notation instead of classical +//! algebra. //! \return false if the Petri net handle is invalid or return true. // **************************************************************************** -extern "C" bool petri_dater_equation(int64_t const pn); +extern "C" bool petri_dater_equation(int64_t const pn, bool use_caption, + bool maxplus_notation); // **************************************************************************** //! \brief Display the event graph into its counter equation. //! \param[in] pn: the handle of the petri net created by create_petri_net(). +//! \param[in] use_caption: display node captions instead of node keys. +//! \param[in] minplus_notation: display (min,+) notation instead of classical +//! algebra. //! \return false if the Petri net handle is invalid or return true. // **************************************************************************** -extern "C" bool petri_counter_equation(int64_t const pn); +extern "C" bool petri_counter_equation(int64_t const pn, bool use_caption, + bool minplus_notation); // **************************************************************************** //! \brief Show the critical cycle in the graph event. diff --git a/src/julia/TimedPetriNetEditor.jl b/src/julia/TimedPetriNetEditor.jl index 6784717..e794222 100644 --- a/src/julia/TimedPetriNetEditor.jl +++ b/src/julia/TimedPetriNetEditor.jl @@ -859,7 +859,7 @@ function to_graph(pn::PetriNet) end """ - to_graph + to_syslin If and only if the Petri net is an event graph then generate the implicit dynamic linear Max-Plus system. State space representation: @@ -884,12 +884,20 @@ function to_syslin(pn::PetriNet) ) end -function dater(pn::PetriNet) - ccall((:petri_dater_equation, libtpne), Bool, (Clonglong,), pn.handle) || error("Invalid Petri net handle") +""" + show_dater_equation +""" +function show_dater_equation(pn::PetriNet, use_caption, maxplus_notation) + ccall((:petri_dater_equation, libtpne), Bool, (Clonglong,Bool,Bool,), + pn.handle, use_caption, maxplus_notation) || error("Invalid Petri net handle") end -function counter(pn::PetriNet) - ccall((:petri_counter_equation, libtpne), Bool, (Clonglong,), pn.handle) || error("Invalid Petri net handle") +""" + show_counter_equation +""" +function show_counter_equation(pn::PetriNet, use_caption, minplus_notation) + ccall((:petri_counter_equation, libtpne), Bool, (Clonglong,Bool,Bool,), + pn.handle, use_caption, minplus_notation) || error("Invalid Petri net handle") end #end # TimedPetriNetEditor module diff --git a/src/julia/tests/test_petri.jl b/src/julia/tests/test_petri.jl new file mode 100644 index 0000000..5b4e03c --- /dev/null +++ b/src/julia/tests/test_petri.jl @@ -0,0 +1,170 @@ +using SparseArrays, MaxPlus + +# Note: soon will included in MaxPlus.jl +include("TimedPetriNetEditor.jl") + +# Create an empty Petri net and return its handle. You can create several nets. +pn = petri_net() +@assert pn.handle == 0 + +# Or create new Petri net by loading it. +pn1 = petri_net("../../data/examples/TrafficLights.json") +@assert pn1.handle == 1 + +# Duplicate the net +pn2 = petri_net(pn) +@assert pn2.handle == 2 + +# Failed loading file. FIXME shall not store pn3 internally +pn3 = petri_net("doesnotexist.json") +pn3 = petri_net(pn1) +@assert pn3.handle == 4 # FIXME shall be 3 + +# Has no places and no transitions? Return true in this case. +is_empty(pn) +@assert ans == true +is_empty(pn1) +@assert ans == false +is_empty(pn2) +@assert ans == true +is_empty(pn3) +@assert ans == false + +# Clear the Petri net (remove all nodes and arcs) +clear!(pn3) +@assert is_empty(pn3) == true + +# Create places. X-Y coordinate (3.15, 4.15) and 5 tokens for Place 0. +# Return its identifier. +p0 = add_place!(pn, 100.0, 100.0, 5) +@assert ans == 0 +p1 = add_place!(pn, 200.0, 200.0, 0) +@assert ans == 1 +p2 = add_place!(pn, Place(210.0, 210.0, 10)) +@assert ans == 2 + +# Get the place content +p3 = place(pn, p2) +@assert p3.x == 210.0 +@assert p3.y == 210.0 +@assert p3.tokens == 10 + +# Set/Get the number of tokens +tokens(pn, p0) +@assert ans == 5 +tokens!(pn, p0, 2) +@assert ans == true +tokens(pn, p0) +@assert ans == 2 + +# Create transitions. X-Y coordinate (1.0, 2.0) for Transition 0. +# Return its identifier. +t0 = add_transition!(pn, 150.0, 150.0) +@assert ans == 0 +t1 = add_transition!(pn, 250.0, 250.0) +@assert ans == 1 +t2 = add_transition!(pn, Transition(200.0, 240.0)) +@assert ans == 2 + +# Get the transition content +t3 = transition(pn, t2) +@assert t3.x == 200.0 +@assert t3.y == 240.0 + +# Remove nodes. Be careful the handle of the latest inserted node is invalidated +remove_place!(pn, p1) +@assert ans == true +remove_transition!(pn, t0) +@assert ans == true + +# Get the number of nodes +count_transitions(pn) +@assert ans == 2 +count_places(pn) +@assert ans == 2 + +# Get the list of places +places(pn) +@assert size(ans) == (2,) + +# Get the list of transitions +transitions(pn) +@assert size(ans) == (2,) + +# TODO missing API for arcs :( + +# You can save the Petri net to JSON file +# Note: you cannot save empty net +add_place!(pn3, 100.0, 100.0, 5) +is_empty(pn3) +@assert ans == false +save(pn3, "/tmp/petri.json") +@assert ans == true +clear!(pn3) +@assert ans == true +is_empty(pn3) +@assert ans == true +# Load the same file back (old net is deleted) +load!(pn3, "/tmp/petri.json") +@assert ans == true +is_empty(pn2) +@assert ans == true +save(pn2, "/tmp/dummy.json") +@assert ans == true +load!(pn3, "/tmp/dummy.json") +@assert ans == true +is_empty(pn3) +@assert ans == true + +# Or create one +pn4 = load("../../data/examples/Howard2.json") +@assert pn4.handle == 5 # FIXME should be ideally 4 + +# Number of nodes and arcs +count_places(pn4) +@assert ans == 5 +count_transitions(pn4) +@assert ans == 4 +# TODO count_arcs(pn4) +# @assert ans == 4 + +# Get the list of marks (number of tokens for each place P0, P1 .. Pn) +tokens(pn4) +@assert ans == [2; 0; 0; 0; 0] + +# Check if Petri net is an event graph +is_event_graph(pn4) +@assert ans == true +is_event_graph(pn1) +@assert ans == false + +# Modify the number of tokens for each place +tokens!(pn4, [0; 1; 2; 3; 4]) +@assert ans == true + +# If the Petri net is an event graph, you can return the canonical form +pn5 = canonic(pn4) +@assert pn5.handle == 6 # FIXME should be ideally 5 + +# Show the counter and dater form +show_dater_equation(pn5, false, false) +@assert ans == true +show_counter_equation(pn5, false, false) +@assert ans == true + +# If the Petri net is an event graph, you can generate the graph the (max,+) +# adjacency sparse matrices (that could be used with SimpleGraphs.jl). +N,T = to_graph(pn4) + +# Sparse to full (max,+) matrices +full(N) +full(T) + +# If the Petri net is an event graph, you can generate the implicit dynamic +# linear (max,+) system. +S = to_syslin(pn5) +show(S.D) +show(S.A) +show(S.B) +show(S.C) +show(S.x0) diff --git a/tests/HowardTests.cpp b/tests/HowardTests.cpp index fa1f858..81dfe80 100644 --- a/tests/HowardTests.cpp +++ b/tests/HowardTests.cpp @@ -166,6 +166,7 @@ TEST(TestHoward, TestPetriNetSemiSimple) ASSERT_STREQ(loadFromFile(net,"../data/examples/Howard2.json").c_str(), ""); ASSERT_EQ(net.type(), TypeOfNet::TimedEventGraph); ASSERT_EQ(net.isEmpty(), false); + ASSERT_EQ(isEventGraph(net), true); res = findCriticalCycle(net); ASSERT_EQ(res.success, true); ASSERT_EQ(res.cycles, 1u);