1
/* PeTe - Petri Engine exTremE
2
* Copyright (C) 2011 Jonas Finnemann Jensen <jopsen@gmail.com>,
3
* Thomas Søndersø Nielsen <primogens@gmail.com>,
4
* Lars Kærlund Østergaard <larsko@gmail.com>,
6
* This program is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#include "StateConstraints.h"
21
#include <lpsolve/lp_lib.h>
25
/** Don't give lp_solve more than 30s to handle the problem */
26
#define OVER_APPROX_TIMEOUT 30
27
/** Number of M-traps to account for each place */
28
#define OVER_APPROX_TRAP_FACTOR 2
30
namespace PetriEngine{
33
/** Attempts to merge two StateConstraints, return NULL if can't be merged */
34
StateConstraints* StateConstraints::merge(const StateConstraints* c) const{
35
StateConstraints* nc = new StateConstraints(nPlaces, nVars);
36
for(size_t p = 0; p < nPlaces; p++){
37
if(!nc->setPlaceMax(p, pcs[p].max) ||
38
!nc->setPlaceMax(p, c->pcs[p].max) ||
39
!nc->setPlaceMin(p, pcs[p].min) ||
40
!nc->setPlaceMin(p, c->pcs[p].min)){
45
for(size_t v = 0; v < nVars; v++){
46
if(!nc->setVarMax(v, vcs[v].max) ||
47
!nc->setVarMax(v, c->vcs[v].max) ||
48
!nc->setVarMin(v, vcs[v].min) ||
49
!nc->setVarMin(v, c->vcs[v].min)){
57
/** True, if every marking satisfied by subset is also satisfied by this */
58
bool StateConstraints::isSuperSet(const StateConstraints* subset) const{
59
for(size_t p = 0; p < nPlaces; p++){
60
if(pcs[p].max < subset->pcs[p].max)
62
if(pcs[p].min > subset->pcs[p].min)
65
for(size_t v = 0; v < nVars; v++){
66
if(vcs[v].max < subset->vcs[v].max)
68
if(vcs[v].min > subset->vcs[v].min)
74
void StateConstraints::reset(){
75
for(size_t p = 0; p < nPlaces; p++){
76
pcs[p].max = CONSTRAINT_INFTY;
79
for(size_t v = 0; v < nVars; v++){
80
vcs[v].max = CONSTRAINT_INFTY;
85
/** Merge the two sets of StateConstraints such that one from A and one from B is always satisfied, when one in the return value is
86
* @remarks This will take ownership of the provided StateConstraints, and delete them or own them
88
std::vector<StateConstraints*> StateConstraints::mergeAnd(const std::vector<StateConstraints*>& A, const std::vector<StateConstraints*>& B){
89
std::vector<StateConstraints*> retval;
90
typedef std::vector<StateConstraints*>::const_iterator iter;
91
for(iter a = A.begin(); a != A.end(); a++){
92
for(iter b = B.begin(); b != B.end(); b++){
93
StateConstraints* nc = (*a)->merge(*b);
99
for(iter b = B.begin(); b != B.end(); b++)
104
/** Merge the two sets of StateConstraints such that either on from A or B is always satisfied, when one in the return value is
105
* @remarks This will take ownership of the provided StateConstraints, and delete them or own them
107
std::vector<StateConstraints*> StateConstraints::mergeOr(const std::vector<StateConstraints*>& A, const std::vector<StateConstraints*>& B){
108
std::vector<StateConstraints*> retval, excluded;
109
typedef std::vector<StateConstraints*>::const_iterator iter;
110
for(iter a = A.begin(); a != A.end(); a++){
111
bool exclude = false;
112
for(iter b = B.begin(); b != B.end(); b++){
113
exclude |= (*b)->isSuperSet(*a) && !(*b)->isEqual(*a);
117
retval.push_back(*a);
119
excluded.push_back(*a);
121
for(iter b = B.begin(); b != B.end(); b++){
122
bool exclude = false;
123
for(iter a = A.begin(); a != A.end(); a++){
124
exclude |= (*a)->isSuperSet(*b);
128
retval.push_back(*b);
130
excluded.push_back(*b);
132
for(iter e = excluded.begin(); e != excluded.end(); e++)
137
StateConstraints* StateConstraints::requirePlaceMin(const PetriNet& net, int place, MarkVal value){
138
StateConstraints* c = new StateConstraints(net);
139
c->setPlaceMin(place, value);
143
StateConstraints* StateConstraints::requirePlaceMax(const PetriNet& net, int place, MarkVal value){
144
StateConstraints* c = new StateConstraints(net);
145
c->setPlaceMax(place, value);
149
StateConstraints* StateConstraints::requirePlaceEqual(const PetriNet& net, int place, MarkVal value){
150
StateConstraints* c = new StateConstraints(net);
151
c->setPlaceMax(place, value);
152
c->setPlaceMin(place, value);
157
StateConstraints* StateConstraints::requireVarMin(const PetriNet& net, int var, MarkVal value){
158
StateConstraints* c = new StateConstraints(net);
159
c->setVarMin(var, value);
163
StateConstraints* StateConstraints::requireVarMax(const PetriNet& net, int var, MarkVal value){
164
StateConstraints* c = new StateConstraints(net);
165
c->setVarMax(var, value);
169
StateConstraints* StateConstraints::requireVarEqual(const PetriNet& net, int var, MarkVal value){
170
StateConstraints* c = new StateConstraints(net);
171
c->setVarMax(var, value);
172
c->setVarMin(var, value);
176
/** Attempts to solve using lp_solve, returns True if the net cannot satisfy these constraints! */
177
bool StateConstraints::isImpossible(const PetriNet& net,
179
const VarVal*) const{
180
assert(nPlaces == net.numberOfPlaces());
181
assert(nVars == net.numberOfVariables());
183
/*printf("-------\n");
184
for(int p = 0; p < nPlaces; p++)
185
printf("%s: [%i, %i]\n", net.placeNames()[p].c_str(), pcs[p].min, pcs[p].max);
186
printf("-------\n");*/
188
// Create linary problem
190
lp = make_lp(0, net.numberOfTransitions()); // One variable for each entry in the firing vector
192
if(!lp) return false;
195
set_verbose(lp, IMPORTANT);
197
// Set transition names (not strictly needed)
198
for(size_t i = 0; i < net.numberOfTransitions(); i++)
199
set_col_name(lp, i+1, const_cast<char*>(net.transitionNames()[i].c_str()));
202
set_add_rowmode(lp, TRUE);
204
REAL row[net.numberOfTransitions() + 1];
205
for(size_t p = 0; p < nPlaces; p++){
207
memset(row, 0, sizeof(REAL) * net.numberOfTransitions() + 1);
208
for(size_t t = 0; t < net.numberOfTransitions(); t++){
209
int d = net.outArc(t, p) - net.inArc(p, t);
213
if(pcs[p].min == pcs[p].max &&
214
pcs[p].max != CONSTRAINT_INFTY){
215
double target = pcs[p].min - m0[p];
216
add_constraint(lp, row, EQ, target);
218
// There's always a min, even zero is interesting
219
double target = pcs[p].min - m0[p];
220
add_constraint(lp, row, GE, target);
221
if(pcs[p].max != CONSTRAINT_INFTY){
222
double target = pcs[p].max - m0[p];
223
add_constraint(lp, row, LE, target);
228
// Finished adding rows
229
set_add_rowmode(lp, FALSE);
232
memset(row, 0, sizeof(REAL) * net.numberOfTransitions() + 1);
233
for(size_t t = 0; t < net.numberOfTransitions(); t++)
234
row[1+t] = 1; // The sum the components in the firing vector
239
// Minimize the objective
242
// Set variables as integer variables
243
for(size_t i = 0; i < net.numberOfTransitions(); i++)
244
set_int(lp, 1+i, TRUE);
246
// Write it to stdout
247
/*printf("--------------\n");
248
write_LP(lp, stdout);
249
printf("--------------\n");*/
251
//Set timeout, and handle a timeout
252
set_timeout(lp, OVER_APPROX_TIMEOUT);
254
// Attempt to solve the problem
255
int result = solve(lp);
257
// Limit on traps to test
258
size_t traplimit = nPlaces * OVER_APPROX_TRAP_FACTOR;
259
// Try to add a minimal trap constraint
260
while((result == OPTIMAL || result == SUBOPTIMAL) && traplimit-- < 0){
261
memset(row, 0, sizeof(REAL) * net.numberOfTransitions() + 1);
262
// Get the firing vector
263
get_variables(lp, row);
264
// Compute the resulting marking
265
MarkVal rMark[net.numberOfPlaces()];
266
for(size_t p = 0; p < nPlaces; p++){
268
for(size_t t = 0; t < net.numberOfTransitions(); t++)
269
rMark[p] += (net.outArc(t, p) - net.inArc(p, t)) * (int)row[t];
273
BitField trap(minimalTrap(net, m0, rMark));
275
/*printf("Initial marking:\n");
276
for(size_t p = 0; p < nPlaces; p++)
277
printf("\t %s = %i\n", net.placeNames()[p].c_str(), m0[p]);
279
printf("Firing vector:\n");
280
for(size_t t = 0; t < net.numberOfTransitions(); t++)
281
printf("\t %s = %i\n", net.transitionNames()[t].c_str(), (int)row[t]);
282
printf("Result marking:\n");
283
for(size_t p = 0; p < nPlaces; p++)
284
printf("\t %s = %i\n", net.placeNames()[p].c_str(), rMark[p]);
285
printf("Got trap: ");
286
for(size_t p = 0; p < nPlaces; p++){
288
printf("%s, ", net.placeNames()[p].c_str());
292
//Break if there's no trap
293
if(trap.none()) break;
295
// Compute the new equation
296
for(size_t t = 0; t < net.numberOfTransitions(); t++){
298
for(size_t p = 0; p < nPlaces; p++)
300
row[1+t] += net.outArc(t, p) - net.inArc(p, t);
303
// Add a new row with target as greater than equal to 1
304
set_add_rowmode(lp, TRUE);
305
add_constraint(lp, row, GE, 1);
306
set_add_rowmode(lp, FALSE);
308
// Attempt to solve the again
312
// Delete the linear problem
316
// Return true, if it was infeasible
317
return result == INFEASIBLE;
320
BitField StateConstraints::maxTrap(const PetriNet& net,
322
const MarkVal* resultMarking) const{
323
BitField next(places.size());
324
BitField prev(places);
327
for(size_t i = 0; i < places.size(); i++)
328
next.set(i, isInMaxTrap(net, i, places, resultMarking));
330
}while(prev != next);
334
bool StateConstraints::isInMaxTrap(const PetriNet& net,
336
const BitField& places,
337
const MarkVal* resultMarking) const{
338
if(!places.test(place))
342
0 if there is (p_i , t) ∈ F such that x_j = 0
346
if(resultMarking[place] > 0)
349
for(unsigned int t = 0; t < net.numberOfTransitions(); t++){
350
if(net.inArc(place, t) > 0){
352
for(unsigned int j = 0; j < net.numberOfPlaces(); j++){
353
if(net.outArc(t, j) > 0){
354
exclude &= !places.test(j);
364
BitField StateConstraints::minimalTrap(const PetriNet& net,
365
const MarkVal* marking,
366
const MarkVal* resultMarking) const{
367
const size_t nPlaces = net.numberOfPlaces();
368
BitField trap(nPlaces);
370
trap = maxTrap(net, trap, resultMarking);
371
if(!isMarked(trap, marking))
372
return BitField(nPlaces).clear();
374
//Get the exclusion candidates
376
BitField tmp(nPlaces);
378
int exclude = EC.first();
382
tmp = maxTrap(net, tmp, resultMarking);
383
if(isMarked(tmp, marking)){
391
bool StateConstraints::isMarked(const BitField& places, const MarkVal* marking) const{
392
for(size_t p = 0; p < places.size(); p++)
393
if(places.test(p) && marking[p] > 0)
398
bool StateConstraints::isSpecific() const{
399
for(size_t p = 0; p < nPlaces; p++){
400
if(pcs[p].max != pcs[p].min)
403
for(size_t v = 0; v < nVars; v++){
404
if(vcs[v].max != vcs[v].min)
410
int StateConstraints::fireVectorSize(const PetriNet& net,
412
const VarVal*) const{
413
assert(nPlaces == net.numberOfPlaces());
414
assert(nVars == net.numberOfVariables());
416
// Create linary problem
418
lp = make_lp(0, net.numberOfTransitions()); // One variable for each entry in the firing vector
420
if(!lp) return false;
423
set_verbose(lp, IMPORTANT);
425
// Set transition names (not strictly needed)
426
for(size_t i = 0; i < net.numberOfTransitions(); i++)
427
set_col_name(lp, i+1, const_cast<char*>(net.transitionNames()[i].c_str()));
430
set_add_rowmode(lp, TRUE);
432
REAL row[net.numberOfTransitions() + 1];
433
for(size_t p = 0; p < nPlaces; p++){
435
memset(row, 0, sizeof(REAL) * net.numberOfTransitions() + 1);
436
for(size_t t = 0; t < net.numberOfTransitions(); t++){
437
int d = net.outArc(t, p) - net.inArc(p, t);
441
if(pcs[p].min == pcs[p].max &&
442
pcs[p].max != CONSTRAINT_INFTY){
443
double target = pcs[p].min - m0[p];
444
add_constraint(lp, row, EQ, target);
446
// There's always a min, even zero is interesting
447
double target = pcs[p].min - m0[p];
448
add_constraint(lp, row, GE, target);
449
if(pcs[p].max != CONSTRAINT_INFTY){
450
double target = pcs[p].max - m0[p];
451
add_constraint(lp, row, LE, target);
456
// Finished adding rows
457
set_add_rowmode(lp, FALSE);
460
memset(row, 0, sizeof(REAL) * net.numberOfTransitions() + 1);
461
for(size_t t = 0; t < net.numberOfTransitions(); t++)
462
row[1+t] = 1; // The sum the components in the firing vector
467
// Minimize the objective
470
// Set variables as integer variables
471
for(size_t i = 0; i < net.numberOfTransitions(); i++)
472
set_int(lp, 1+i, TRUE);
474
// Attempt to solve the problem
475
int result = solve(lp);
477
// Limit on traps to test
478
size_t traplimit = nPlaces * OVER_APPROX_TRAP_FACTOR;
479
// Try to add a minimal trap constraint
480
while((result == OPTIMAL) && traplimit-- < 0){
481
memset(row, 0, sizeof(REAL) * net.numberOfTransitions() + 1);
482
// Get the firing vector
483
get_variables(lp, row);
484
// Compute the resulting marking
485
MarkVal rMark[net.numberOfPlaces()];
486
for(size_t p = 0; p < nPlaces; p++){
488
for(size_t t = 0; t < net.numberOfTransitions(); t++)
489
rMark[p] += (net.outArc(t, p) - net.inArc(p, t)) * (int)row[t];
493
BitField trap(minimalTrap(net, m0, rMark));
495
//Break if there's no trap
496
if(trap.none()) break;
498
// Compute the new equation
499
for(size_t t = 0; t < net.numberOfTransitions(); t++){
501
for(size_t p = 0; p < nPlaces; p++)
503
row[1+t] += net.outArc(t, p) - net.inArc(p, t);
506
// Add a new row with target as greater than equal to 1
507
set_add_rowmode(lp, TRUE);
508
add_constraint(lp, row, GE, 1);
509
set_add_rowmode(lp, FALSE);
511
// Attempt to solve the again
517
if(result != INFEASIBLE){
518
get_variables(lp, row);
519
for(size_t t = 0; t < net.numberOfTransitions(); t++)
520
retval += (int)row[t];
523
// Delete the linear problem
527
// Return true, if it was infeasible