Passed
Push — master ( e49ba9...b59a28 )
by Alessandro
06:09
created

toString()   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
package it.cnr.istc.pst.platinum.ai.deliberative.solver;
2
3
import java.util.ArrayList;
4
import java.util.Collections;
5
import java.util.HashMap;
6
import java.util.HashSet;
7
import java.util.List;
8
import java.util.Map;
9
import java.util.Set;
10
import java.util.concurrent.atomic.AtomicInteger;
11
12
import it.cnr.istc.pst.platinum.ai.framework.domain.component.Decision;
13
import it.cnr.istc.pst.platinum.ai.framework.domain.component.DomainComponent;
14
import it.cnr.istc.pst.platinum.ai.framework.domain.component.DomainComponentType;
15
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.flaw.Flaw;
16
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.flaw.FlawType;
17
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.plan.Plan;
18
19
/**
20
 * 
21
 * @author anacleto
22
 *
23
 */
24
public class SearchSpaceNode implements Comparable<SearchSpaceNode> {
25
	
26
	private static final AtomicInteger ID_COUNTER = new AtomicInteger(0);
27
	
28
	private int id;												// node unique ID
29
	private List<Operator> operators;							// node generation trace
30
	
31
	private Map<DomainComponent, List<DecisionVariable>> plan;	// partial plan
32
 	private Map<DomainComponent, List<Flaw>> agenda; 			// flaws associated to the resulting partial plan
33
 	
34
 	// consolidated information about a partial plan 
35
 	private Map<DomainComponent, Double[]> makespan;				// consolidated makespan of SVs
36
 	private Map<DomainComponent, Double> cost;					// consolidated planning cost of SVs
37
 	
38
 	// heuristic information about a partial plan 
39
 	private Map<DomainComponent, Double[]> heuristicMakespan;		// estimated makespan of SVs
40
 	private Map<DomainComponent, Double[]> heuristicCost;			// estimated planning cost of SVs
41
 	
42
	private Object domainSpecificMetric;						// node domain specific metric
43
	
44
	/**
45
	 * 
46
	 */
47
	protected SearchSpaceNode() {
48
		// set node's id
49
		this.id = ID_COUNTER.getAndIncrement();
50
		// set operators
51
		this.operators = new ArrayList<>();
52
		// set agenda
53
		this.agenda = new HashMap<>();
54
		
55
		// set additional metric
56
		this.domainSpecificMetric = null;
57
		
58
		this.makespan = new HashMap<>();
59
		this.cost = new HashMap<>();
60
		this.heuristicMakespan = new HashMap<>();
61
		this.heuristicCost = new HashMap<>();
62
	}
63
	
64
	/**
65
	 * Create a child node generated by means of the specified operator
66
	 * 
67
	 * @param parent
68
	 * @param op
69
	 */
70
	protected SearchSpaceNode(SearchSpaceNode parent, Operator op) {
71
		this();
72
		// set operators
73
		this.operators = new ArrayList<>(parent.getOperators());
74
		// add generator
75
		this.operators.add(op);
76
		// set agenda
77
		this.agenda = new HashMap<>();
78
		
79
		// set additional metric
80
		this.domainSpecificMetric = null;
81
		
82
		// set cost
83
		this.cost = new HashMap<>(parent.getCost());
84
		// update cost according to the operator
85
		if (!this.cost.containsKey(op.getFlaw().getComponent())) {
86
			// set cost
87
			this.cost.put(op.getFlaw().getComponent(), op.getCost());
88
		}
89
		else {
90
			// update cost
91
			this.cost.put(op.getFlaw().getComponent(),
92
					this.cost.get(op.getFlaw().getComponent()) + op.getCost());
93
		}
94
		
95
		
96
		// set makespan 
97
		this.makespan = new HashMap<>();
98
		this.heuristicCost = new HashMap<>();
99
		this.heuristicMakespan = new HashMap<>();
100
	}
101
	
102
	
103
	/**
104
	 * 
105
	 * @return
106
	 */
107
	public int getId() {
108
		return id;
109
	}
110
	
111
	/**
112
	 * 
113
	 * @param metric
114
	 */
115
	public void setDomainSpecificMetric(Object metric) {
116
		this.domainSpecificMetric = metric;
117
	}
118
	
119
	/**
120
	 * 
121
	 * @return
122
	 */
123
	public Object getDomainSpecificMetric() {
124
		return domainSpecificMetric;
125
	}
126
	
127
	/**
128
	 * 
129
	 * @param flaw
130
	 * @return
131
	 */
132
	public void addCheckedFlaw(Flaw flaw) {
133
		if (!this.agenda.containsKey(flaw.getComponent())) {
134
			this.agenda.put(flaw.getComponent(), new ArrayList<>());
135
		}
136
		
137
		// add the flaw
138
		this.agenda.get(flaw.getComponent()).add(flaw);
139
	}
140
	
141
	/**
142
	 * 
143
	 * @return
144
	 */
145
	public Set<Flaw> getAgenda() {
146
		Set<Flaw> list = new HashSet<>();
147
		for (DomainComponent comp : this.agenda.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
148
			list.addAll(this.agenda.get(comp));
149
		}
150
		
151
		return list;
152
	}
153
	
154
	/**
155
	 * 
156
	 * @return
157
	 */
158
	public Set<Flaw> getFlaws(FlawType type) {
159
		Set<Flaw> list = new HashSet<>();
160
		for (DomainComponent comp : this.agenda.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
161
			for (Flaw flaw : this.agenda.get(comp)) {
162
				// check flaw type
163
				if (flaw.getType().equals(type)) {
164
					// add the flaw
165
					list.add(flaw);
166
				}
167
			}
168
		}
169
		
170
		return list;
171
	}
172
	
173
	/**
174
	 * 
175
	 * @return
176
	 */
177
	public int getNumberOfFlaws() {
178
		int number = 0;
179
		for (List<Flaw> list : this.agenda.values()) {
180
			number += list.size();
181
		}
182
		return number;
183
	}
184
	
185
	
186
	/**
187
	 * 
188
	 * @param comp
189
	 * @return
190
	 */
191
	public Set<Flaw> getFlaws(DomainComponent comp) {
192
		return new HashSet<>(this.agenda.get(comp));
193
	}
194
	
195
	/**
196
	 * 
197
	 * @return
198
	 */
199
	public int getDepth() {
200
		return this.operators.size();
201
	}
202
		
203
	/**
204
	 * 
205
	 * @return
206
	 */
207
	public Map<DomainComponent, Double[]> getHeuristicCost() {
208
		return new HashMap<>(this.heuristicCost);
209
	}
210
	
211
	/**
212
	 * 
213
	 * @param hCost
214
	 */
215
	public void setHeuristicCost(Map<DomainComponent, Double[]> hCost) {
216
		this.heuristicCost = hCost;
217
	}
218
	
219
	/**
220
	 * 
221
	 * @return
222
	 */
223
	public Map<DomainComponent, Double[]> getHeuristicMakespan() {
224
		return new HashMap<>(this.heuristicMakespan);
225
	}
226
	
227
	/**
228
	 * 
229
	 * @param makespanHeuristic
230
	 */
231
	public void setHeuristicMakespan(Map<DomainComponent, Double[]> hMakespan) {
232
		this.heuristicMakespan = hMakespan;
233
	}
234
	
235
	/**
236
	 * 
237
	 * @param partialPlan
238
	 */
239
	public void setPartialPlan(Plan partialPlan) 
240
	{
241
		// set the partial plan 
242
		this.plan = new HashMap<>();
243
		// set of components 
244
		Set<DomainComponent> cset = new HashSet<>();
245
		
246
		// check decisions
247
		for (Decision dec : partialPlan.getDecisions()) 
248
		{
249
			// add component entry 
250
			if (!this.plan.containsKey(dec.getComponent())) {
251
				// add entry 
252
				this.plan.put(dec.getComponent(), new ArrayList<>());
253
			}
254
			
255
			// add decision variable
256
			this.plan.get(dec.getComponent()).add(new DecisionVariable(dec));
257
			
258
			// check if primitive component
259
			if (dec.getComponent().getType().equals(DomainComponentType.SV_PRIMITIVE)) {
260
				// add component 
261
				cset.add(dec.getComponent());
262
			}
263
		}
264
		
265
		
266
		
267
		// set makespan 
268
		for (DomainComponent c : cset) 
269
		{
270
			// get component's decisions
271
			if (this.plan.containsKey(c) && !this.plan.get(c).isEmpty()) 
272
			{
273
				// check last decision
274
				DecisionVariable last = this.plan.get(c).get(0);
275
				for (int index = 1; index < this.plan.get(c).size(); index++) {
276
					// get current decision 
277
					DecisionVariable d = this.plan.get(c).get(index);
278
					
279
					// check if last
280
					if (d.getEnd()[0] > last.getEnd()[0]) {
281
						// update last decision
282
						last = d;
283
					}
284
				}
285
				
286
				
287
				
288
				// set component makespan according to end time bounds of the last decision
289
				this.makespan.put(c, new Double[] {
290
						(double) last.getEnd()[0],
291
						(double) last.getEnd()[1]
292
				});
293
			}
294
			else {
295
				// no decision 
296
				this.makespan.put(c, new Double[] {
297
					(double) 0,
298
					(double) 0
299
				});
300
			}
301
		}
302
	}
303
	
304
	/**
305
	 * 
306
	 * @return
307
	 */
308
	public Map<DomainComponent, List<DecisionVariable>> getPartialPlan() {
309
		// set the partial plan 
310
		return new HashMap<>(this.plan);
311
	}
312
	
313
	/**
314
	 * 
315
	 * @return
316
	 */
317
	public Map<DomainComponent, Double> getCost() {
318
		// get cost by components
319
		return new HashMap<>(this.cost);
320
	}
321
	
322
	/**
323
	 * 
324
	 * @return
325
	 */
326
	public double getPlanCost() {
327
		double cost = 0;
0 ignored issues
show
Comprehensibility introduced by
The variable costshadows a field with the same name declared in line 36. Consider renaming it.
Loading history...
328
		for (DomainComponent c : this.cost.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
329
			cost += this.cost.get(c);
330
		}
331
		
332
		return cost;
333
	}
334
	
335
	/**
336
	 * 
337
	 * @return
338
	 */
339
	public double[] getPlanHeuristicCost() {
340
		// set cost
341
		double[] cost = new double[] {
0 ignored issues
show
Comprehensibility introduced by
The variable costshadows a field with the same name declared in line 36. Consider renaming it.
Loading history...
342
				0, 0
343
		};
344
		
345
		// set optimistic and pessimistic costs
346
		for (DomainComponent c : this.heuristicCost.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
347
			cost[0] += this.heuristicCost.get(c)[0];
348
			cost[1] += this.heuristicCost.get(c)[1];
349
		}
350
		
351
		// get cost
352
		return cost;
353
	}
354
	
355
	/**
356
	 * Return the minimum and maximum makespan of the timelines of a plan
357
	 * 
358
	 * @return
359
	 */
360 View Code Duplication
	public double[] getPlanMakespan() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
361
		
362
		// set the makespan
363
		double[] mk = new double[] {
364
				Double.MIN_VALUE + 1,  
365
				Double.MAX_VALUE - 1
366
		};
367
		
368
		// set update flag
369
		boolean update = false;
370
		// compute makespan bounds
371
		for (DomainComponent c : this.makespan.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
372
			// check primitive components only
373
			if (c.getType().equals(DomainComponentType.SV_PRIMITIVE)) {
374
				// update min and max
375
				mk[0] = Math.max(mk[0], this.makespan.get(c)[0]);
376
				// update max
377
				mk[1] = Math.min(mk[1], this.makespan.get(c)[1]);
378
				// change update flag
379
				update = true;
380
			}
381
		}
382
		
383
		// check update flag
384
		if (!update) {
385
			
386
			// set default data 
387
			mk = new double[] {
388
					0, 
389
					Double.MAX_VALUE - 1
390
			};
391
		}
392
		
393
		// get plan makespan 
394
		return mk;
395
	}
396
	
397
	/**
398
	 * 
399
	 * @return
400
	 */
401 View Code Duplication
	public double[] getPlanHeuristicMakespan() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
402
		
403
		// set heuristic makespan
404
		double[] mk = new double[] {
405
				Double.MAX_VALUE - 1,
406
				Double.MIN_VALUE + 1 
407
		};
408
		
409
		// set update flag
410
		boolean update = false;
411
		// check heuristic makespan bounds of components 
412
		for (DomainComponent c : this.heuristicMakespan.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
413
			// consider primitive components only 
414
			if (c.getType().equals(DomainComponentType.SV_PRIMITIVE)) {
415
				// update min and max
416
				mk[0] = Math.min(mk[0], this.heuristicMakespan.get(c)[0]);
417
				// update max
418
				mk[1] = Math.max(mk[1], this.heuristicMakespan.get(c)[1]);
419
				// change update flag
420
				update = true;
421
			}
422
		}
423
		
424
		// check update flag
425
		if (!update) {
426
			// set default data 
427
			mk = new double[] {
428
					0, 
429
					Double.MAX_VALUE - 1
430
			};
431
		}
432
		
433
		// get plan makespan 
434
		return mk;
435
	}
436
	
437
	/**
438
	 * Compute the estimated makespan as the sum of the consolidated and heuristic makespan
439
	 * 
440
	 * @param component
441
	 * @return
442
	 */
443
	public double[] getEstimatedMakespan(DomainComponent component) {
444
		
445
		// set heuristic makespan
446
		double[] mk = new double[] {
447
				0,
448
				0
449
		};
450
		
451
		// check if component exists
452
		if (this.makespan.containsKey(component)) {
453
			// set consolidated lower bound
454
			mk[0] = this.makespan.get(component)[0];
455
			mk[1] = this.makespan.get(component)[1];
456
			
457
		}
458
		
459
		// add heuristic value
460
		if (this.heuristicMakespan.containsKey(component)) {
461
			// increment
462
			mk[0] += this.heuristicMakespan.get(component)[0];
463
			mk[1] += this.heuristicMakespan.get(component)[1];
464
		}
465
466
		// get makespan
467
		return mk;
468
	}
469
	
470
	/**
471
	 * 
472
	 * @return
473
	 */
474
	public Map<DomainComponent, Double[]> getEstimatedMakespan() {
475
		
476
		// set data
477
		Map<DomainComponent, Double[]> mk = new HashMap<>();
478
		
479
		// check components
480
		for (DomainComponent comp : this.makespan.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
481
			
482
			// put consolidated minimum and maximum makespan
483
			mk.put(comp, new Double[] {
484
					this.makespan.get(comp)[0],
485
					this.makespan.get(comp)[1]
486
			});
487
		}
488
		
489
		// consider heuristic values
490
		for (DomainComponent comp : this.heuristicMakespan.keySet()) {
0 ignored issues
show
Performance introduced by
When you need both the keys and the value of a Map, iterating over entrySet() instead of keySet() is more readable.
Loading history...
491
			
492
			// add heuristic values
493
			double min = mk.containsKey(comp) ? mk.get(comp)[0] : 0;
494
			double max = mk.containsKey(comp) ? mk.get(comp)[1] : 0;
495
			
496
			// add heuristic value
497
			mk.put(comp, new Double[] {
498
				min + this.heuristicMakespan.get(comp)[0],
499
				max + this.heuristicMakespan.get(comp)[1]
500
			});
501
		}
502
		
503
		// get data
504
		return mk;
505
	}
506
	
507
	/**
508
	 * 
509
	 * @return
510
	 */
511
	public Map<DomainComponent, Double[]> getMakespan() {
512
		return new HashMap<>(this.makespan);
513
	}
514
	
515
	/**
516
	 * Get the flaw that has been solved to generate the node 
517
	 * 
518
	 * @return
519
	 */
520
	public Flaw getGeneratingFlaw() {
521
		// verify whether the node is root
522
		return this.isRootNode() ? null : this.getGenerator().getFlaw();
523
	}
524
	
525
	/**
526
	 * Verify whether the node is root, i.e. not generator operator has been applied.
527
	 * 
528
	 * @return
529
	 */
530
	public boolean isRootNode() {
531
		// check if root node
532
		return this.getGenerator() == null;
533
	}
534
	
535
	/**
536
	 * The method returns the order list of operators that have been applied to generated the node. 
537
	 * 
538
	 * The last operator of the list is the node generator operator (i.e. the last applied operator).
539
	 * 
540
	 * @return
541
	 */
542
	public List<Operator> getOperators() {
543
		// get list of operators
544
		return new ArrayList<>(this.operators);
545
	}
546
	
547
	/**
548
	 * The method returns the node generator operator.
549
	 * 
550
	 * The method returns null for the root node of the search space
551
	 * 
552
	 * @return
553
	 */
554
	public Operator getGenerator() 
555
	{
556
		// get generator
557
		Operator operator = null;
558
		if (!this.operators.isEmpty()) {
559
			// get last applied operator
560
			operator = this.operators.get(this.operators.size() - 1);
561
		}
562
		
563
		// get generator operator
564
		return operator;
565
	}
566
	
567
	/**
568
	 * Get the list of applied operators from the more recent to the specified one (not included).
569
	 * 
570
	 * The method returns the list of operators that have been applied after the specified one starting with the more recent. The first
571
	 * element of the list is the node generator operator.
572
	 * 
573
	 * @param operator
574
	 * @return
575
	 */
576
	public List<Operator> getOperatorsUpTo(Operator operator) {
577
		// list of operators
578
		List<Operator> list = new ArrayList<>();
579
		if (operator == null) {
580
			// add all operators
581
			list.addAll(this.operators);
582
			// reverse order
583
			Collections.reverse(list);
584
		}
585
		else {
586
			// get index of the operator
587
			int index = this.operators.indexOf(operator);
588
			for (int i = this.operators.size() - 1; i > index; i--) {
589
				// add operator
590
				list.add(this.operators.get(i));
591
			}
592
		}
593
		// get list of operators
594
		return list;
595
	}
596
	
597
	/**
598
	 * Get the list of applied operators starting from the selected operator (not included).
599
	 * 
600
	 * The method returns the orderer list of operators that have been applied after the specified one. The 
601
	 * last operator of the list is the node generator operator.
602
	 * 
603
	 * 
604
	 * @param operator
605
	 * @return
606
	 */
607
	public List<Operator> getOperatorsFrom(Operator operator) {
608
		// list of operators
609
		List<Operator> list = new ArrayList<>();
610
		if (operator == null) {
611
			// add all operators
612
			list.addAll(this.operators);
613
		}
614
		else {
615
			// get index of the operator
616
			for (int index = this.operators.indexOf(operator) + 1; index < this.operators.size(); index++) {
617
				// add operator
618
				list.add(this.operators.get(index));
619
			}
620
		}
621
		// get list of operators
622
		return list;
623
	}
624
	
625
	/**
626
	 * 
627
	 */
628
	@Override
629
	public int hashCode() {
630
		final int prime = 31;
631
		int result = 1;
632
		result = prime * result + id;
633
		return result;
634
	}
635
636
	/**
637
	 * 
638
	 */
639
	@Override
640
	public boolean equals(Object obj) {
641
		if (this == obj)
642
			return true;
643
		if (obj == null)
644
			return false;
645
		if (getClass() != obj.getClass())
646
			return false;
647
		SearchSpaceNode other = (SearchSpaceNode) obj;
648
		if (id != other.id)
649
			return false;
650
		return true;
651
	}
652
	
653
	/**
654
	 * 
655
	 */
656
	@Override
657
	public int compareTo(SearchSpaceNode o) {
658
		// compare nodes by their ID
659
		return this.id < o.id ? -1 : this.id > o.id ? 1 : 0;
660
	}
661
	
662
	/**
663
	 * 
664
	 */
665
	@Override
666
	public String toString() {
667
		// JSON style description of node
668
		return "{ "
669
				+ "\"id\": " + this.id + ", "
670
				+ "\"depth\": " + this.getDepth() + ", "
671
				+ "\"cost\": " + this.getPlanCost() + ", "
672
				+ "\"makespan\": [" + this.getPlanMakespan()[0] + ", " + this.getPlanMakespan()[1] + "], "
673
				+ "\"heuristic-cost\": [" + this.getPlanHeuristicCost()[0] +", " + this.getPlanHeuristicCost()[1] + "], "
674
				+ "\"heuristic-makespan\": [" + this.getPlanHeuristicMakespan()[0] + ", " + this.getPlanHeuristicMakespan()[1] + "] "
675
				+ " }";
676
	}
677
}
678