Passed
Push — master ( c71db0...37262d )
by Alessandro
05:46 queued 14s
created

doComputeFlawSolutions(Flaw)   A

Complexity

Conditions 3

Size

Total Lines 45
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 15
dl 0
loc 45
rs 9.65
c 0
b 0
f 0
1
package it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.resource.discrete;
2
3
import java.util.ArrayList;
4
import java.util.Collections;
5
import java.util.HashSet;
6
import java.util.List;
7
import java.util.Set;
8
9
import it.cnr.istc.pst.platinum.ai.framework.domain.component.Decision;
10
import it.cnr.istc.pst.platinum.ai.framework.domain.component.DomainComponent;
11
import it.cnr.istc.pst.platinum.ai.framework.domain.component.ex.FlawSolutionApplicationException;
12
import it.cnr.istc.pst.platinum.ai.framework.domain.component.ex.RelationPropagationException;
13
import it.cnr.istc.pst.platinum.ai.framework.domain.component.resource.discrete.DiscreteResource;
14
import it.cnr.istc.pst.platinum.ai.framework.domain.component.resource.discrete.RequirementResourceEvent;
15
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.ex.ConsistencyCheckException;
16
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.flaw.Flaw;
17
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.flaw.FlawSolution;
18
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.relations.Relation;
19
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.relations.RelationType;
20
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.relations.temporal.BeforeRelation;
21
import it.cnr.istc.pst.platinum.ai.framework.microkernel.query.TemporalQueryType;
22
import it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.Resolver;
23
import it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.ResolverType;
24
import it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.ex.UnsolvableFlawException;
25
import it.cnr.istc.pst.platinum.ai.framework.time.ex.TemporalConstraintPropagationException;
26
import it.cnr.istc.pst.platinum.ai.framework.time.lang.TemporalConstraintType;
27
import it.cnr.istc.pst.platinum.ai.framework.time.lang.allen.BeforeIntervalConstraint;
28
import it.cnr.istc.pst.platinum.ai.framework.time.lang.query.IntervalOverlapQuery;
29
import it.cnr.istc.pst.platinum.ai.framework.time.tn.TimePoint;
30
import it.cnr.istc.pst.platinum.ai.framework.utils.properties.FilePropertyReader;
31
32
/**
33
 * 
34
 * @author anacleto
35
 *
36
 */
37
public class DiscreteResourceSchedulingResolver extends Resolver<DiscreteResource> 
38
{ 
39
	private boolean load;
40
	private double cost;
41
	
42
	/**
43
	 * 
44
	 */
45
	protected DiscreteResourceSchedulingResolver() {
46
		super(ResolverType.DISCRETE_RESOURCE_SCHEDULING_RESOLVER.getLabel(), 
47
				ResolverType.DISCRETE_RESOURCE_SCHEDULING_RESOLVER.getFlawTypes());
48
		
49
		// set load flag
50
		this.load = false;
51
	}
52
	
53
	/**
54
	 * 
55
	 */
56
	private void load( ) {
57
		// get deliberative property file
58
		FilePropertyReader properties = new FilePropertyReader(
59
				FRAMEWORK_HOME + FilePropertyReader.DEFAULT_DELIBERATIVE_PROPERTY);
60
		// read weight
61
		this.cost = Double.parseDouble(properties.getProperty("scheduling-cost"));
62
		// set load flag
63
		this.load = true;
64
	}
65
	
66
	/**
67
	 * 
68
	 */
69
	@Override
70
	protected List<Flaw> doFindFlaws()
71
	{
72
		// load parameter if necessary
73
		if (!this.load) {
74
			this.load();
75
		}
76
		
77
		// list of critical sets found
78
		List<Flaw> CSs = new ArrayList<>();
79
		// list of requirement events
80
		List<RequirementResourceEvent> requirements = this.component.getRequirements();
81
		// compute "pessimistic resource profiles"
82
		for (int index = 0; index < requirements.size() - 1; index++)
83
		{
84
			// get current requirement event
85
			RequirementResourceEvent event = requirements.get(index);
86
			// prepare critical set
87
			CriticalSet cs = new CriticalSet(FLAW_COUNTER.getAndIncrement(), (DiscreteResource) this.component);
88
			// add the current decision 
89
			cs.addRequirementDecision(event);
90
			// find possibly conflicting events
91
			for (int jndex = index + 1; jndex < requirements.size(); jndex++)
92
			{
93
				// get possibly conflicting event
94
				RequirementResourceEvent conflicting = requirements.get(jndex);
95
				// check if events conflict
96
				debug("Checking possibly conflicting resource requirement events:\n"
97
						+ "- component: " + this.component + "\n"
98
						+ "- current critical set : " + cs + "\n"
99
						+ "- possibly conflicting event: " + conflicting + "\n");
100
				
101
				// check if the current critical set can temporally overlap with the conflicting one
102
				if (this.conflict(cs, conflicting))
103
				{
104
					// add the event to the critical set
105
					cs.addRequirementDecision(conflicting);
106
					// conflicting decision found
107
					debug("Conflicting event found:\n"
108
							+ "- component: " + this.component + "\n"
109
							+ "- current critical set : " + cs + "\n"
110
							+ "- possibly conflicting event: " + conflicting + "\n");
111
				}
112
			}
113
			
114
			// check the amount of requirement of the critical set
115
			if (cs.getAmountOfRequirement() > this.component.getMaxCapacity()) {
116
				// add the critical set to the flaws
117
				CSs.add(cs);
118
				// a peak has been found
119
				debug("A discrete resource peak has been found:\n"
120
						+ "- component: " + this.component + "\n"
121
						+ "- critical set: " + cs + "\n"
122
						+ "- amount required: " + cs.getAmountOfRequirement() + "\n");
123
			}
124
		}
125
		
126
		// get the list of critical sets found
127
		return CSs;
128
	}
129
	
130
	/**
131
	 * 
132
	 * @param set
133
	 * @param event
134
	 * @return
135
	 */
136
	private boolean conflict(CriticalSet set, RequirementResourceEvent event) 
137
	{
138
		// conflicting flag
139
		boolean conflict = true;
140
		// get events of the critical set
141
		List<RequirementResourceEvent> events = set.getRequirementEvents();
142
		// check set of events
143
		for (int index = 0; index < events.size() && conflict; index++)
144
		{
145
			// get an event from the set
146
			RequirementResourceEvent e = events.get(index);
147
			// check if current decision and target overlap
148
			IntervalOverlapQuery query = this.tdb.createTemporalQuery(TemporalQueryType.INTERVAL_OVERLAP);
149
			// set intervals
150
			query.setReference(e.getEvent());
151
			query.setTarget(event.getEvent());
152
			// process query
153
			this.tdb.process(query);
154
			// check whether the (flexible) temporal interval can overlap or not
155
			conflict = query.canOverlap();
156
		}
157
		
158
		// get result
159
		return conflict;
160
	}
161
	
162
	/**
163
	 * Å critical set (CS) is not necessary minimal. 
164
	 * 
165
	 * This method samples a critical set in order to find all the minimal critical sets (MCSs) available.
166
	 * 
167
	 * A minimal critical set is a "sub-peak" which can be solved by posting a precedence constraint between 
168
	 * any pair of activities.
169
	 * 
170
	 * @param cs
171
	 * @return
172
	 */
173
	private List<MinimalCriticalSet> sampleMCSs(CriticalSet cs)
174
	{
175
		// list of MCSs that can be extracted from the critical set
176
		List<MinimalCriticalSet> mcss = new ArrayList<>();
177
		// get the events composing the critical set 
178
		List<RequirementResourceEvent> events = cs.getRequirementEvents();
179
		// sort requirements in decreasing order of resource amount needed
180
		Collections.sort(events);
181
		
182
		// sample MCSs from the CS
183
		for (int index = 0; index < events.size() -1; index++)  
184
		{
185
			// get current event
186
			RequirementResourceEvent reference = events.get(index);
187
			// initialize an MCS
188
			MinimalCriticalSet mcs = new MinimalCriticalSet(cs, this.cost);
189
			// add the current event to the MCS
190
			mcs.addEvent(reference);
191
			
192
			// check other samples
193
			for (int jndex = index + 1; jndex < events.size(); jndex++) 
194
			{
195
				// get other event
196
				RequirementResourceEvent other = events.get(jndex);
197
				// check the resulting amount 
198
				double amount = mcs.getTotalAmount() + other.getAmount();
199
				// an MCS is minimal so check the amount of resource required  (minimal condition)
200
				if (amount > this.component.getMaxCapacity()) 
201
				{
202
					// copy current MCS
203
					MinimalCriticalSet copy = new MinimalCriticalSet(mcs, this.cost);
204
					// add sample to the MCS
205
					mcs.addEvent(other);
206
					// add to the list of MCSs
207
					mcss.add(mcs);
208
					debug("Minimal Critical Set sampled:\n"
209
							+ "- component: " + this.component + "\n"
210
							+ "- critical set: " + cs + "\n"
211
							+ "- sampled minimial critical set: " + mcs + "\n");
212
					
213
					// go on with the search by using the copy 
214
					mcs = copy;
215
				}
216
				else {
217
					// simply add the event and go on
218
					mcs.addEvent(other);
219
				}
220
			}
221
		}
222
		
223
		// get sampled MCSs
224
		return mcss;
225
	}
226
	
227
	/**
228
	 * Estimate the preserved values of time point domains after propagation of a precedence constraint "tp1 < tp2".
229
	 * 
230
	 * The method assumes that the precedence constraint is feasible and that the bounds of the time points have been updated 
231
	 * according to precedence constraint (i.e. the underlying temporal must encapsulate additional information coming from 
232
	 * the temporal constraint) 
233
	 * 
234
	 * @param tp1
235
	 * @param tp2
236
	 * @return
237
	 */
238
	private double computePreservedSpaceHeuristicValue(TimePoint tp1, TimePoint tp2)
239
	{
240
		// initialize value
241
		double preserved = 0;
242
		// compute parameters
243
		double A = (tp2.getUpperBound() - tp2.getLowerBound() + 1) * (tp1.getUpperBound() - tp1.getLowerBound() + 1);
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this multiplication to double.
Loading history...
244
		double B = (tp2.getUpperBound() - tp1.getLowerBound() + 1) * (tp2.getUpperBound() - tp1.getLowerBound() + 2);
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this multiplication to double.
Loading history...
245
		double Cmin = Math.max(0, (tp2.getLowerBound() - tp1.getLowerBound()) * (tp2.getLowerBound() - tp1.getLowerBound() + 1));
246
		double Cmax = Math.max(0, (tp2.getUpperBound() - tp1.getUpperBound() * (tp2.getUpperBound() - tp1.getUpperBound() + 1)));
247
248
		// compute preserved space value
249
		preserved = (B - Cmin - Cmax) / (2 * A);
250
		
251
		// get computed heuristic value
252
		return preserved;
253
	}
254
255
	
256
	/**
257
	 * 
258
	 * @param mcs
259
	 * @throws Exception - contains information concerning the unsolvable MCS
260
	 */
261
	private void computeMinimalCriticalSetSolutions(MinimalCriticalSet mcs) 
262
			throws UnsolvableFlawException
263
	{
264
		// list of events
265
		List<RequirementResourceEvent> list = mcs.getEvents();
266
		// for each pair of decisions check the feasibility of a precedence constraint and compute the resulting preserved heuristic value
267
		for (int index = 0; index < list.size() - 1; index++)
268
		{
269
			// get reference decision
270
			Decision reference = list.get(index).getDecision();
271
			for (int jndex = index + 1; jndex < list.size(); jndex++)
272
			{
273
				// get target decision
274
				Decision target = list.get(jndex).getDecision();
275
				// prepare precedence constraint
276
				BeforeIntervalConstraint before = this.tdb.createTemporalConstraint(TemporalConstraintType.BEFORE);
277
278 View Code Duplication
				try
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
279
				{
280
					/*
281
					 *  check feasibility of precedence constraint "reference < target" and compute the resulting preserved heuristic value
282
					 */
283
					
284
					// set reference interval
285
					before.setReference(reference.getToken().getInterval());
286
					// set target interval
287
					before.setTarget(target.getToken().getInterval());
288
					// set bounds
289
					before.setLowerBound(0);
290
					before.setUpperBound(this.tdb.getHorizon());
291
					
292
					// verify constraint feasibility through constraint propagation
293
					this.tdb.propagate(before);
294
					// check temporal consistency
295
					this.tdb.verify();
296
					
297
298
					// compute the preserved space heuristic value resulting after constraint propagation
299
					double preserved = this.computePreservedSpaceHeuristicValue(
300
							reference.getToken().getInterval().getEndTime(), 
301
							target.getToken().getInterval().getStartTime());
302
					
303
					// create and add solution to the MCS
304
					PrecedenceConstraint pc = mcs.addSolution(reference, target, preserved);
305
					// print some debugging information
306
					debug("Feasible solution of MCS found:\n"
307
							+ "- mcs: " + mcs + "\n"
308
							+ "- precedence constraint: " + pc + "\n");
309
				}
310
				catch (TemporalConstraintPropagationException | ConsistencyCheckException ex) {
311
					// warning message
312
					debug("Unfeasible solution found for MCS:\n- mcs: " + mcs + "\n- unfeasible precedence constraint: " + reference + " < " + target + "\n");
313
				}
314
				finally {
315
					// retract propagated constraint
316
					this.tdb.retract(before);
317
					// clear temporal relation
318
					before.clear();
319
				}
320
				
321
				
322 View Code Duplication
				try
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
323
				{
324
					/*
325
					 *  check feasibility of precedence constraint "target < reference" and compute the resulting preserved heuristic value
326
					 */
327
					
328
					// set reference interval
329
					before.setReference(target.getToken().getInterval());
330
					// set target interval
331
					before.setTarget(reference.getToken().getInterval());
332
					// set bounds
333
					before.setLowerBound(0);
334
					before.setUpperBound(this.tdb.getHorizon());
335
					
336
					// verify constraint feasibility through constraint propagation
337
					this.tdb.propagate(before);
338
					// check temporal consistency
339
					this.tdb.verify();
340
					
341
					
342
					// compute the preserved space heuristic value resulting after constraint propagation
343
					double preserved = this.computePreservedSpaceHeuristicValue(
344
							target.getToken().getInterval().getEndTime(), 
345
							reference.getToken().getInterval().getStartTime());
346
					
347
					// create and add solution to the MCS
348
					PrecedenceConstraint pc = mcs.addSolution(target, reference, preserved);
349
					// print some debugging information
350
					debug("Feasible solution of MCS found:\n"
351
							+ "- mcs: " + mcs + "\n"
352
							+ "- precedence constraint: " + pc + "\n");
353
				}
354
				catch (TemporalConstraintPropagationException | ConsistencyCheckException ex) {
355
					// warning message
356
					debug("Unfeasible solution found for MCS:\n- mcs: " + mcs + "\n- unfeasible precedence constraint: " + target + " < " + reference + "\n");
357
				}
358
				finally {
359
					// retract (inverted) precedence constraint
360
					this.tdb.retract(before);
361
					// clear temporal relation
362
					before.clear();
363
				}
364
			}
365
		}
366
		
367
		// check MCS solutions
368
		if (mcs.getSolutions().isEmpty()) {
369
			// unsolvable MCS found
370
			throw new UnsolvableFlawException("Unsolvable MCS found on discrete resource " + this.component.getName() + "\n- mcs: " + mcs + "\n");
371
		}
372
	}
373
	
374
	/**
375
	 * 	Given a set of overlapping activities that exceed the resource capacity (i.e. a peak) this method computes sets of 
376
	 * precedence constraints among activities composing the critical set (CS) in order to solve the peak 
377
	 */
378
	@Override
379
	protected void doComputeFlawSolutions(Flaw flaw) 
380
			throws UnsolvableFlawException 
381
	{
382
		// cast flaw
383
		CriticalSet cs = (CriticalSet) flaw;
384
		
385
		/*
386
		 * A critical set (CS) is not necessary minimal, so sample MCSs from the CS
387
		 */
388
		
389
		// sample the critical set in order to find minimal critical sets
390
		List<MinimalCriticalSet> mcss = this.sampleMCSs(cs);
391
		
392
		/*
393
		 * An MCS can be solved by posting a precedence constraint between any pair of activities. 
394
		 * 
395
		 * Thus, each MCS can have several solutions depending on the number of activities involved.
396
		 * 
397
		 * For each MCS compute the set of feasible solutions and the related preserved heuristic value 
398
		 */
399
		
400
		try
401
		{
402
			
403
			// sort MCSs according to the total requirement
404
			Collections.sort(mcss);	
405
			// get the "best" MCS 
406
			MinimalCriticalSet best = mcss.get(0);
407
			// compute (minimal) critical set solutions 
408
			this.computeMinimalCriticalSetSolutions(best);
409
			
410
			/*
411
			 * Rate MCSs according to the computed preserved heuristic value and select the best one for expansion
412
			 */
413
			
414
			// add computed solutions to the flow
415
			for (PrecedenceConstraint pc : best.getSolutions()) {
416
				// add this precedence constraint as a possible solution of the peak
417
				flaw.addSolution(pc);
418
			}
419
		}
420
		catch (Exception ex) {
421
			// unsolvable MCS found
422
			throw new UnsolvableFlawException("Unsolvable MCS found on discrete resourc e" + this.component.getName() + ":\n" + flaw);
423
		}
424
	}
425
	
426
	/**
427
	 * 
428
	 */
429
	@Override
430
	protected void doApply(FlawSolution solution) 
431
			throws FlawSolutionApplicationException 
432
	{
433
		// get the flaw solution to consider
434
		PrecedenceConstraint pc = (PrecedenceConstraint) solution;
435
		try 
436
		{
437
			// get reference and target decisions
438
			Decision reference = pc.getReference();
439
			Decision target = pc.getTarget();
440
			// create relation
441
			BeforeRelation before = this.component.create(RelationType.BEFORE, reference, target);
442
			// set bounds
443
			before.setBound(new long[] {1, this.tdb.getHorizon()});
444
			// add created relation
445
			solution.addCreatedRelation(before);
446
			debug("Applying flaw solution:\n"
447
					+ "- solution: " + solution + "\n"
448
					+ "- created temporal constraint: " + before + "\n");
449
			
450
			// propagate relations
451
			this.component.activate(before);
452
			// add activated relations to solution
453
			solution.addActivatedRelation(before);
454
			
455
			// check (temporal) consistency
456
			this.tdb.verify();
457
		}
458
		catch (RelationPropagationException | ConsistencyCheckException ex) 
459
		{
460
			// deactivate created relation
461
			for (Relation rel : solution.getActivatedRelations()) {
462
				// get reference
463
				DomainComponent refComp = rel.getReference().getComponent();
464
				refComp.deactivate(rel);
465
			}
466
			
467
			// delete created relations
468
			for (Relation rel : solution.getCreatedRelations()) {
469
				// get reference component
470
				DomainComponent refComp = rel.getReference().getComponent();
471
				// delete relation from component
472
				refComp.delete(rel);
473
			}
474
475
			// not feasible solution
476
			throw new FlawSolutionApplicationException(ex.getMessage());
477
		}
478
	}
479
}
480
481
/**
482
 * 
483
 * @author anacleto
484
 *
485
 */
486
class MinimalCriticalSet implements Comparable<MinimalCriticalSet>
487
{
488
	protected CriticalSet cs;									// the set of overlapping activities
489
	private Set<RequirementResourceEvent> events;				// activities composing the MCS
490
	private List<PrecedenceConstraint> solutions;				// a MCS can be solved by posting a simple precedence constraint
491
	private double cost;
492
	
493
	/**
494
	 * 
495
	 * @param cs
496
	 * @param cost
497
	 */
498
	protected MinimalCriticalSet(CriticalSet cs, double cost) {
499
		this.cs = cs;
500
		this.events = new HashSet<>();
501
		this.solutions = new ArrayList<>();
502
		this.cost = cost;
503
	}
504
	
505
	/**
506
	 * 
507
	 * @param mcs
508
	 */
509
	protected MinimalCriticalSet(MinimalCriticalSet mcs, double cost) {
510
		this.cs = mcs.cs;
511
		this.events = new HashSet<>(mcs.events);
512
		this.solutions = new ArrayList<>(mcs.solutions);
513
		this.cost = cost;
514
	}
515
	
516
	/**
517
	 * Get the total amount of resource required
518
	 * 
519
	 * @return
520
	 */
521
	public double getTotalAmount() {
522
		double amount = 0;
523
		for (RequirementResourceEvent event : this.events) {
524
			amount += event.getAmount();
525
		}
526
		// get the computed total
527
		return amount;
528
	}
529
	
530
	/**
531
	 * 
532
	 * @param sample
533
	 * @return
534
	 */
535
	public boolean contains(RequirementResourceEvent event) {
536
		// check whether the MCS already contains the event 
537
		return this.events.contains(event);
538
	}
539
	
540
	/**
541
	 * 
542
	 * @return
543
	 */
544
	public List<RequirementResourceEvent> getEvents() {
545
		return new ArrayList<>(this.events);
546
	}
547
	
548
	/**
549
	 * 
550
	 * @param sample
551
	 */
552
	public void addEvent(RequirementResourceEvent event) {
553
		this.events.add(event);
554
	}
555
	
556
	/**
557
	 * 
558
	 * @return
559
	 */
560
	public CriticalSet getCriticalSet() {
561
		return cs;
562
	}
563
	
564
	/**
565
	 * 
566
	 * @return
567
	 */
568
	public List<PrecedenceConstraint> getSolutions() {
569
		return new ArrayList<>(this.solutions);
570
	}
571
	
572
	/**
573
	 * The preserved value of a MCS is given by the average preserved values of its solutions
574
	 * 
575
	 * See [Laborie 2005] for further details about the preserved value heuristics 
576
	 * 
577
	 * @return
578
	 */
579
	public double getPreservedValue() 
580
	{
581
		// initialize preserved value
582
		double preserved = 0;
583
		// take into account the preserved values of solutions
584
		for (PrecedenceConstraint pc : this.solutions) {
585
			preserved += pc.getPreservedValue();
586
		}
587
		
588
		// get the average value
589
		preserved = preserved / this.solutions.size();
590
		// get computed value
591
		return preserved;
592
	}
593
	
594
	/**
595
	 * 
596
	 * @param reference
597
	 * @param target
598
	 * @param preserved
599
	 * @return
600
	 */
601
	protected PrecedenceConstraint addSolution(Decision reference, Decision target, double preserved) 
602
	{
603
		// create a precedence constraint
604
		PrecedenceConstraint pc = new PrecedenceConstraint(this.cs, reference, target, this.cost);
605
		// set the value of resulting preserved space
606
		pc.setPreservedSpace(preserved);
607
		// add solution to the original flaw
608
		this.solutions.add(pc);
609
		// get constraint
610
		return pc;
611
	}
612
	
613
	/**
614
	 * 
615
	 */
616
	@Override
617
	public int compareTo(MinimalCriticalSet o) {
0 ignored issues
show
Bug introduced by
When overriding compareTo(), you should always override equals() to provide the same behaviour.

compareTo() and equals() must provide the same behaviour so that if, and only if, compareTo() returns 0, equals() returns true.

Loading history...
618
		
619
		// compare MCSs according to the total amount of resource required
620
		return this.getTotalAmount() > o.getTotalAmount() ? -1 : this.getTotalAmount() < o.getTotalAmount() ? 1 : 0;
621
	}
622
	
623
	/**
624
	 * 
625
	 */
626
	@Override
627
	public String toString() {
628
		return "{ \"requirement\": " + this.getTotalAmount() + ", \"events\": " + this.events + " }";
629
	}
630
}
631