Skip to content

Commit f5cdc7d

Browse files
committed
minimization fixed
1 parent f70842e commit f5cdc7d

File tree

9 files changed

+187
-104
lines changed

9 files changed

+187
-104
lines changed

include/maxplus/base/fsm/fsm.h

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -691,8 +691,8 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
691691
using EquivalenceMap = std::map<const Abstract::State*,
692692
std::shared_ptr<Abstract::SetOfStateRefs>>;
693693

694-
// minimize the automaton based on edge labels only.
695-
std::shared_ptr<FiniteStateMachine<StateLabelType, EdgeLabelType>> minimizeEdgeLabels() {
694+
// minimize the automaton based on edge and state labels.
695+
std::shared_ptr<FiniteStateMachine<StateLabelType, EdgeLabelType>> minimizeEdgeLabels(bool ignoreStateLabels = false) {
696696
// partition refinement algorithm
697697

698698
// generate a vector of equivalence classes
@@ -709,8 +709,58 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
709709
eqMap[sp] = initialClass;
710710
}
711711

712-
// partition refinement
712+
// partition on state labels
713+
713714
bool changed = false;
715+
if (! ignoreStateLabels) {
716+
do {
717+
changed = false;
718+
719+
std::list<std::shared_ptr<Abstract::SetOfStateRefs>> newEqClasses;
720+
721+
// for every potential equivalence class
722+
for (const auto &ic : eqClasses) {
723+
std::shared_ptr<Abstract::SetOfStateRefs> _class = ic;
724+
725+
auto i = _class->begin();
726+
727+
// pick arbitrary state from class
728+
auto s1 = dynamic_cast<const State<StateLabelType, EdgeLabelType> *>(*i);
729+
730+
std::shared_ptr<Abstract::SetOfStateRefs> equivSet =
731+
std::make_shared<Abstract::SetOfStateRefs>();
732+
std::shared_ptr<Abstract::SetOfStateRefs> remainingSet =
733+
std::make_shared<Abstract::SetOfStateRefs>();
734+
equivSet->insert(s1);
735+
736+
// check whether all other states have the same label.
737+
while (++i != _class->end()) {
738+
auto s2 = dynamic_cast<const State<StateLabelType, EdgeLabelType> *>(*i);
739+
if (s1->getLabel() == s2->getLabel()) {
740+
equivSet->insert(s2);
741+
} else {
742+
remainingSet->insert(s2);
743+
}
744+
}
745+
// if not, split the class
746+
if (equivSet->size() == _class->size()) {
747+
newEqClasses.push_back(equivSet);
748+
this->mapStates(eqMap, equivSet);
749+
} else {
750+
newEqClasses.push_back(equivSet);
751+
this->mapStates(eqMap, equivSet);
752+
newEqClasses.push_back(remainingSet);
753+
this->mapStates(eqMap, remainingSet);
754+
changed = true;
755+
}
756+
}
757+
auto tempEqClasses = eqClasses;
758+
eqClasses = newEqClasses;
759+
} while (changed);
760+
}
761+
762+
// partition refinement on transitions
763+
changed = false;
714764
do {
715765
changed = false;
716766

@@ -818,8 +868,8 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
818868
std::shared_ptr<Abstract::SetOfStateRefs> ns1 = s1->nextStatesOfEdgeLabel(l);
819869
std::shared_ptr<Abstract::SetOfStateRefs> ns2 = s2->nextStatesOfEdgeLabel(l);
820870
// collect classes of states in ns1 and ns2
821-
std::set<SetOfStates<StateLabelType, EdgeLabelType> *> cs1;
822-
std::set<SetOfStates<StateLabelType, EdgeLabelType> *> cs2;
871+
std::set<std::shared_ptr<Abstract::SetOfStateRefs>> cs1;
872+
std::set<std::shared_ptr<Abstract::SetOfStateRefs>> cs2;
823873
for (auto j : *ns1) {
824874
auto s = j;
825875
cs1.insert(m[s]);
@@ -846,38 +896,12 @@ class FiniteStateMachine : public Abstract::FiniteStateMachine {
846896
};
847897
} // namespace Labeled
848898

849-
// Edge Labelled Scenario Automaton
850-
using ELSState = ::FSM::Labeled::State<CId, CString>;
851-
using ELSEdge = ::FSM::Labeled::Edge<CId, CString>;
852-
using ELSSetOfStates = ::FSM::Labeled::SetOfStates<CId, CString>;
853-
using ELSSetOfEdges = ::FSM::Abstract::SetOfEdges;
854-
using ELSSetOfStateRefs = ::FSM::Abstract::SetOfStateRefs;
855-
using ELSSetOfEdgeRefs = ::FSM::Abstract::SetOfEdgeRefs;
856-
857-
class EdgeLabeledScenarioFSM : public ::FSM::Labeled::FiniteStateMachine<CId, CString> {
858-
public:
859-
EdgeLabeledScenarioFSM() = default;
860-
~EdgeLabeledScenarioFSM() override = default;
861-
862-
EdgeLabeledScenarioFSM(const EdgeLabeledScenarioFSM &) = delete;
863-
EdgeLabeledScenarioFSM &operator=(const EdgeLabeledScenarioFSM &other) = delete;
864-
EdgeLabeledScenarioFSM(EdgeLabeledScenarioFSM &&) = delete;
865-
EdgeLabeledScenarioFSM &operator=(EdgeLabeledScenarioFSM &&) = delete;
866-
867-
virtual void removeDanglingStates();
868-
};
869899

870900
namespace StateStringLabeled {
871901

872902
// make an FSM class with unlabeled edges, based on the labeled one with some dummy char labels
873903
//
874904

875-
// class SetOfEdges : public Labeled::SetOfEdges<CString, char> {};
876-
// class SetOfEdgeRefs : public Labeled::SetOfEdgeRefs<CString, char> {};
877-
878-
// class SetOfStates : public Labeled::SetOfStates<CString, char> {};
879-
// class SetOfStateRefs : public Labeled::SetOfStateRefs<CString, char> {};
880-
881905
class State : public Labeled::State<CString, char> {
882906
public:
883907
explicit State(const CString &withLabel) : Labeled::State<CString, char>(withLabel) {}

include/maxplus/graph/mpautomaton.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ class MaxPlusAutomatonWithRewards
224224
// compute the maximum cycle ratio of delay over progress
225225
CDouble calculateMCR();
226226
// compute the maximum cycle ratio of delay over progress and also return a critical cycle
227-
CDouble calculateMCRAndCycle(MPARCycle **cycle);
227+
CDouble calculateMCRAndCycle(std::shared_ptr<std::vector<const MPAREdge*>> *cycle);
228+
228229
};
229230

230231
} // namespace MaxPlus

src/base/fsm/fsm.cc

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -46,64 +46,6 @@ namespace FSM {
4646

4747
CId FSM::Abstract::WithUniqueID::nextID = 0;
4848

49-
/*removes states of the fsm with no outgoing edges.*/
50-
void EdgeLabeledScenarioFSM::removeDanglingStates() {
51-
52-
// temporary sets of states and edges
53-
ELSSetOfEdgeRefs edgesToBeRemoved;
54-
ELSSetOfStateRefs statesToBeRemoved;
55-
56-
ELSSetOfStates &elsStates = this->getStates();
57-
ELSSetOfEdges &elsEdges = this->getEdges();
58-
59-
/*go through all edges and find all edges that end in
60-
dangling states. Also store dangling states.*/
61-
for(const auto& it: elsEdges){
62-
auto& e = *(it.second);
63-
const auto& s = dynamic_cast<const ELSState&>(e.getDestination());
64-
const auto &oEdges = dynamic_cast<const ELSSetOfEdgeRefs &>(s.getOutgoingEdges());
65-
if (oEdges.empty()) {
66-
edgesToBeRemoved.insert(&e);
67-
statesToBeRemoved.insert(&s);
68-
}
69-
}
70-
71-
while (!edgesToBeRemoved.empty()) {
72-
73-
// remove dangling states
74-
for (const auto &s : statesToBeRemoved) {
75-
this->removeState(dynamic_cast<const ELSState&>(*s));
76-
}
77-
78-
// remove edges ending in dangling states
79-
// remove edges ending in dangling states from the outgoing edges of their source states
80-
for (const auto & e : edgesToBeRemoved) {
81-
this->removeEdge(dynamic_cast<const ELSEdge&>(*e));
82-
auto s = e->getSource();
83-
s.removeOutgoingEdge(*e);
84-
}
85-
86-
// empty the temporary sets
87-
edgesToBeRemoved.clear();
88-
statesToBeRemoved.clear();
89-
90-
elsStates = this->getStates();
91-
elsEdges = this->getEdges();
92-
93-
/*go through all edges and find all edges that end in
94-
dangling states. Also store dangling states.*/
95-
for (const auto & it : elsEdges) {
96-
const auto& elsEdge = *(it.second);
97-
auto e = dynamic_cast<const ELSEdge&>(elsEdge);
98-
auto s = dynamic_cast<const ELSState&>(e.getDestination());
99-
const auto &oEdges = (s.getOutgoingEdges());
100-
if (oEdges.empty()) {
101-
edgesToBeRemoved.insert(&e);
102-
statesToBeRemoved.insert(&s);
103-
}
104-
}
105-
}
106-
}
10749

10850
namespace StateStringLabeled {
10951

src/graph/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
1+
target_sources(maxplus PRIVATE
2+
mpautomaton.cc
3+
)

src/graph/mpautomaton.cc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "base/basic_types.h"
2+
#include "base/analysis/mcm/mcm.h"
3+
#include "base/analysis/mcm/mcmyto.h"
4+
#include "graph/mpautomaton.h"
5+
#include <memory>
6+
7+
using namespace MaxPlus;
8+
using namespace Graphs;
9+
10+
11+
// compute the maximum cycle ratio of delay over progress
12+
CDouble MaxPlusAutomatonWithRewards::calculateMCR(){
13+
14+
MCMgraph g;
15+
CId nId = 0;
16+
17+
std::map<const ::FSM::Abstract::State*, MCMnode*> nodeMap;
18+
19+
for (auto s: this->getStates()) {
20+
auto n = g.addNode(nId++);
21+
nodeMap[&(*(s.second))] = n;
22+
}
23+
24+
CId eId = 0;
25+
for (auto s: this->getStates()) {
26+
for(auto e: (s.second)->getOutgoingEdges()) {
27+
auto mpae = dynamic_cast<MPAREdge*>(e);
28+
g.addEdge(eId++, *nodeMap[&(mpae->getSource())], *nodeMap[&(mpae->getDestination())], static_cast<CDouble>(mpae->getLabel().delay), mpae->getLabel().reward);
29+
}
30+
}
31+
32+
std::shared_ptr<std::vector<const MCMedge*>> cycle;
33+
CDouble mcr = maxCycleRatioAndCriticalCycleYoungTarjanOrlin(g, &cycle);
34+
35+
return mcr;
36+
}
37+
38+
CDouble MaxPlusAutomatonWithRewards::calculateMCRAndCycle(std::shared_ptr<std::vector<const MPAREdge*>> *cycle) {
39+
40+
MCMgraph g;
41+
42+
CId nId = 0;
43+
std::map<const ::FSM::Abstract::State*, MCMnode*> nodeMap;
44+
45+
for (auto s: this->getStates()) {
46+
auto n = g.addNode(nId++);
47+
nodeMap[&(*(s.second))] = n;
48+
}
49+
50+
CId eId = 0;
51+
std::map<const MCMedge*, const MPAREdge*> edgeMap;
52+
53+
for (auto s: this->getStates()) {
54+
for(auto e: (s.second)->getOutgoingEdges()) {
55+
auto mpae = dynamic_cast<MPAREdge*>(e);
56+
g.addEdge(eId++, *nodeMap[&(mpae->getSource())], *nodeMap[&(mpae->getDestination())], static_cast<CDouble>(mpae->getLabel().delay), mpae->getLabel().reward);
57+
}
58+
}
59+
60+
std::shared_ptr<std::vector<const MCMedge*>> mcmCycle;
61+
CDouble mcr = maxCycleRatioAndCriticalCycleYoungTarjanOrlin(g, &mcmCycle);
62+
if (cycle != nullptr) {
63+
*cycle = std::make_shared<std::vector<const MPAREdge*>>();
64+
for (auto e: *mcmCycle) {
65+
(*cycle)->push_back(edgeMap[e]);
66+
}
67+
}
68+
69+
return mcr;
70+
}

src/testbench/graph/mpautomatontest.cc

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "graph/mpautomaton.h"
99
#include "testing.h"
1010

11+
#define ASSERT_EPSILON 0.001
12+
1113
using namespace MaxPlus;
1214

1315
MPAutomatonTest::MPAutomatonTest() = default;
@@ -19,6 +21,7 @@ void MPAutomatonTest::TearDown() {}
1921
void MPAutomatonTest::Run() {
2022
testCreateFSM();
2123
testDeterminizeFSM();
24+
testMinimizeFSM();
2225
}
2326

2427
void MPAutomatonTest::testCreateFSM() {
@@ -56,12 +59,10 @@ void MPAutomatonTest::testDeterminizeFSM() {
5659
std::cout << "Running test: DeterminizeFSM" << std::endl;
5760

5861
MaxPlusAutomatonWithRewards mpa;
59-
// One FSM state, three tokens:
60-
CId fsm_s0 = 0;
6162

62-
MPARState *s1 = mpa.addState(makeMPAStateLabel(fsm_s0, 0));
63-
MPARState *s2 = mpa.addState(makeMPAStateLabel(fsm_s0, 1));
64-
MPARState *s3 = mpa.addState(makeMPAStateLabel(fsm_s0, 2));
63+
MPARState *s1 = mpa.addState(makeMPAStateLabel(0, 0));
64+
MPARState *s2 = mpa.addState(makeMPAStateLabel(0, 1));
65+
MPARState *s3 = mpa.addState(makeMPAStateLabel(0, 2));
6566

6667
// s1 -- (3,A,1) -> s2
6768
mpa.addEdge(*s1, makeRewardEdgeLabel(MPTime(3.0), CString("A"), 1.0), *s2);
@@ -88,10 +89,37 @@ void MPAutomatonTest::testDeterminizeFSM() {
8889
ASSERT_THROW(mpaDeterminized->getStates().size() == 2);
8990
ASSERT_THROW(mpaDeterminized->getEdges().size()==3);
9091

91-
auto mpaMin = mpa.minimizeEdgeLabels();
92+
CDouble mcr = mpa.calculateMCR();
93+
std::shared_ptr<std::vector<const MPAREdge*>> cycle;
94+
CDouble mcr1 = mpaDeterminized->calculateMCRAndCycle(&cycle);
95+
96+
ASSERT_APPROX_EQUAL(mcr, mcr1, ASSERT_EPSILON);
97+
ASSERT_EQUAL(cycle->size(), 2);
98+
99+
}
100+
101+
void MPAutomatonTest::testMinimizeFSM() {
102+
103+
std::cout << "Running test: MinimizeFSM" << std::endl;
104+
105+
FSM::Labeled::FiniteStateMachine<int,int> fsa;
106+
107+
auto s0 = fsa.addState(3);
108+
auto s1 = fsa.addState(5);
109+
auto s2 = fsa.addState(5);
110+
111+
fsa.addEdge(*s0, 2, *s1);
112+
fsa.addEdge(*s1, 2, *s2);
113+
fsa.addEdge(*s2, 2, *s2);
114+
115+
fsa.setInitialState(*s0);
116+
117+
std::shared_ptr<FSM::Labeled::FiniteStateMachine<int,int>> fsaMin = std::dynamic_pointer_cast<FSM::Labeled::FiniteStateMachine<int,int>>(fsa.minimizeEdgeLabels());
118+
119+
std::cout << "Nr states: " << fsaMin->getStates().size() << std::endl;
120+
std::cout << "Nr edges: " << fsaMin->getEdges().size() << std::endl;
92121

93-
// TODO: implement MCR on MaxPlusAutomatonWithRewards and MCM on MaxPlusAutomaton
94-
// CDouble mcr = mpaDeterminized->calculateMCR();
95-
// std::cout << "MCR: " << mcr << std::endl;
122+
ASSERT_EQUAL(fsaMin->getStates().size(), 2);
123+
ASSERT_EQUAL(fsaMin->getEdges().size(), 2);
96124

97125
}

src/testbench/graph/mpautomatontest.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ class MPAutomatonTest : public ::testing::Test {
1818
void Run() override;
1919
void testCreateFSM();
2020
void testDeterminizeFSM();
21+
void testMinimizeFSM();
2122
};

src/testbench/graph/testing.cc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,5 @@ int main() {
77
MPAutomatonTest T1;
88
T1.Run();
99

10-
// TODO(MG): add tests for MCM algorithms
11-
std::cout << "TODO: add tests for MCM algorithms." << std::endl;
12-
13-
1410
return 0;
1511
}

src/testbench/graph/testing.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,25 @@ namespace testing {
3636
} \
3737
}
3838

39+
#define ASSERT_APPROX_EQUAL( x, y, eps ) \
40+
{ \
41+
if( abs(( x ) - ( y )) > eps ) \
42+
{ \
43+
throw std::runtime_error( std::string( "Asserted approximate equality violated." ) \
44+
+ std::string( "\nIn:" ) \
45+
+ std::string( __FILE__ ) \
46+
+ std::string( ":" ) \
47+
+ std::to_string( __LINE__ ) \
48+
+ std::string( " in " ) \
49+
+ std::string( __FUNCTION__ ) \
50+
+ std::string( ": " ) \
51+
+ std::to_string( ( x ) ) \
52+
+ std::string( " != " ) \
53+
+ std::to_string( ( y ) ) \
54+
); \
55+
} \
56+
}
57+
3958
#define ASSERT_EQUAL_NOPRINT( x, y ) \
4059
{ \
4160
if( ( x ) != ( y ) ) \

0 commit comments

Comments
 (0)