Total Complexity | 68 |
Total Lines | 597 |
Duplicated Lines | 17.09 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like it.cnr.istc.pst.platinum.ai.framework.parameter.csp.solver.choco.v4.ChocoSolver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | package it.cnr.istc.pst.platinum.ai.framework.parameter.csp.solver.choco.v4; |
||
37 | public class ChocoSolver extends ParameterSolver |
||
38 | { |
||
39 | private boolean clean; // clean flag |
||
40 | private Model model; // CSP model |
||
41 | |||
42 | private Map<Parameter<?>, IntVar> variables; // variables of the model |
||
43 | private Map<ParameterConstraint, Constraint> constraints; // constraints of the model |
||
44 | |||
45 | /** |
||
46 | * |
||
47 | */ |
||
48 | public ChocoSolver() { |
||
49 | super(); |
||
50 | // setup data structures |
||
51 | this.variables = new HashMap<>(); |
||
52 | this.constraints = new HashMap<>(); |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * |
||
57 | */ |
||
58 | @PostConstruct |
||
59 | private void init() { |
||
60 | // build the model |
||
61 | this.build(); |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * |
||
66 | */ |
||
67 | @Override |
||
68 | public boolean isConsistent() |
||
69 | { |
||
70 | // check if clean |
||
71 | if (!this.clean) { |
||
72 | // rebuild the model |
||
73 | this.build(); |
||
74 | } |
||
75 | |||
76 | // consistency flag |
||
77 | boolean consistent = true; |
||
78 | // try to find a solution |
||
79 | this.model.getSolver().solve(); |
||
80 | // check feasibility |
||
81 | switch (this.model.getSolver().isFeasible()) |
||
82 | { |
||
83 | // not feasible |
||
84 | case FALSE : |
||
85 | case UNDEFINED : { |
||
86 | // no feasible solutions exist |
||
87 | consistent = false; |
||
88 | this.model.getSolver().reset(); |
||
89 | } |
||
90 | break; |
||
91 | |||
92 | // feasible or undefined |
||
93 | case TRUE : { |
||
94 | // a feasible solution exists at least |
||
95 | consistent = true; |
||
96 | this.model.getSolver().reset(); |
||
97 | } |
||
98 | break; |
||
99 | } |
||
100 | |||
101 | // get consistency flag |
||
102 | return consistent; |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * |
||
107 | */ |
||
108 | @Override |
||
109 | public void computeSolution() |
||
110 | { |
||
111 | // check clean flag |
||
112 | if (!this.clean) { |
||
113 | // build the model |
||
114 | this.build(); |
||
115 | } |
||
116 | |||
117 | // compute values for each parameter |
||
118 | for (Parameter<?> param : this.variables.keySet()) { |
||
119 | this.computeValues(param); |
||
120 | } |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * |
||
125 | */ |
||
126 | @Override |
||
127 | public void computeValues(Parameter<?> param) |
||
128 | { |
||
129 | // get variable |
||
130 | if (!this.variables.containsKey(param)) { |
||
131 | throw new RuntimeException("Parameter not found in the CSP\n- " + param); |
||
|
|||
132 | } |
||
133 | |||
134 | // check clean flag |
||
135 | if (!this.clean) { |
||
136 | // build the model |
||
137 | this.build(); |
||
138 | } |
||
139 | |||
140 | // check parameter type |
||
141 | switch (param.getType()) |
||
142 | { |
||
143 | // enumeration parameter |
||
144 | case ENUMERATION_PARAMETER_TYPE : |
||
145 | { |
||
146 | // get numeric parameter |
||
147 | EnumerationParameter ep = (EnumerationParameter) param; |
||
148 | // compute allowed values |
||
149 | Set<Integer> vals = new HashSet<>(); |
||
150 | // check possible solutions |
||
151 | while (this.model.getSolver().solve()) |
||
152 | { |
||
153 | // get variable |
||
154 | IntVar var = this.variables.get(ep); |
||
155 | DisposableValueIterator it = var.getValueIterator(true); |
||
156 | while (it.hasNext()) { |
||
157 | // add value |
||
158 | vals.add(it.next()); |
||
159 | } |
||
160 | // dispose iterator |
||
161 | it.dispose(); |
||
162 | } |
||
163 | |||
164 | // set allowed values |
||
165 | int[] values = new int[vals.size()]; |
||
166 | int index = 0; |
||
167 | for (int v : vals) { |
||
168 | // get val |
||
169 | values[index] = v; |
||
170 | index++; |
||
171 | } |
||
172 | |||
173 | // set values |
||
174 | ep.setValues(values); |
||
175 | |||
176 | // reset solver |
||
177 | this.model.getSolver().reset(); |
||
178 | } |
||
179 | break; |
||
180 | |||
181 | // numeric parameter |
||
182 | case NUMERIC_PARAMETER_TYPE : |
||
183 | { |
||
184 | // get numeric parameter |
||
185 | NumericParameter np = (NumericParameter) param; |
||
186 | int lb = Integer.MIN_VALUE; |
||
187 | int ub = Integer.MAX_VALUE; |
||
188 | // check all solutions |
||
189 | while (this.model.getSolver().solve()) { |
||
190 | // check variable bounds |
||
191 | IntVar var = this.variables.get(np); |
||
192 | lb = Math.max(lb, var.getLB()); |
||
193 | ub = Math.min(ub, var.getUB()); |
||
194 | } |
||
195 | |||
196 | // set bounds |
||
197 | np.setLowerBound(lb); |
||
198 | np.setUpperBound(ub); |
||
199 | // reset solver |
||
200 | this.model.getSolver().reset(); |
||
201 | } |
||
202 | break; |
||
203 | } |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * |
||
208 | */ |
||
209 | @Override |
||
210 | public void update(ParameterNotification info) |
||
211 | { |
||
212 | // check notification type |
||
213 | switch (info.getType()) |
||
214 | { |
||
215 | case ADD_PARAM : |
||
216 | { |
||
217 | // get notification |
||
218 | AddParameterNotification notif = (AddParameterNotification) info; |
||
219 | // create CSP variable |
||
220 | IntVar variable = this.doCreateCSPVariable(notif.getParameter()); |
||
221 | // add variable to the model |
||
222 | this.variables.put(notif.getParameter(), variable); |
||
223 | } |
||
224 | break; |
||
225 | |||
226 | case ADD_CONSTRAINT : |
||
227 | { |
||
228 | // get notification |
||
229 | AddConstraintParameterNotification notif = (AddConstraintParameterNotification) info; |
||
230 | // create CSP constraint |
||
231 | Constraint cons = this.doCreateCSPConstraint(notif.getParameterConstraint()); |
||
232 | // check model status to post constraint |
||
233 | if (this.clean) { |
||
234 | cons.post(); |
||
235 | } |
||
236 | // add constraint |
||
237 | this.constraints.put(notif.getParameterConstraint(), cons); |
||
238 | } |
||
239 | break; |
||
240 | |||
241 | case DEL_CONSTRAINT : |
||
242 | { |
||
243 | // get notification |
||
244 | DelConstraintParameterNotification notif = (DelConstraintParameterNotification) info; |
||
245 | // get constraint |
||
246 | ParameterConstraint cons = notif.getParameterConstraint(); |
||
247 | // remove constraint |
||
248 | this.constraints.remove(cons); |
||
249 | // set the clean flag to false |
||
250 | this.clean = false; |
||
251 | } |
||
252 | break; |
||
253 | |||
254 | case DEL_PARAM : |
||
255 | { |
||
256 | // get notification |
||
257 | DelParameterNotification notif = (DelParameterNotification) info; |
||
258 | // get parameter to remove |
||
259 | Parameter<?> param = notif.getParameter(); |
||
260 | // remove variable from the model |
||
261 | this.variables.remove(param); |
||
262 | |||
263 | // remove related constraints |
||
264 | List<ParameterConstraint> toRemove = this.doFindParameterRelatedConstraints(param); |
||
265 | // remove constraints from the model |
||
266 | for (ParameterConstraint cons : toRemove) { |
||
267 | this.constraints.remove(cons); |
||
268 | } |
||
269 | |||
270 | // set the clan flag to false |
||
271 | this.clean = false; |
||
272 | } |
||
273 | break; |
||
274 | |||
275 | default: { |
||
276 | // unknown notification |
||
277 | throw new RuntimeException("Unknown parameter notification type - " + info.getType()); |
||
278 | } |
||
279 | } |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * |
||
284 | */ |
||
285 | @Override |
||
286 | public String toString() { |
||
287 | return this.model.toString(); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * |
||
292 | */ |
||
293 | private void build() |
||
294 | { |
||
295 | // setup new model |
||
296 | this.model = new Model("Chocho CSP model"); |
||
297 | this.model.getSolver().setDFS(); |
||
298 | |||
299 | // update variables associated to parameters |
||
300 | for (Parameter<?> param : this.variables.keySet()) { |
||
301 | // create parameter |
||
302 | IntVar var = this.doCreateCSPVariable(param); |
||
303 | this.variables.put(param, var); |
||
304 | } |
||
305 | |||
306 | |||
307 | // update constraints between variables |
||
308 | for (ParameterConstraint constraint : this.constraints.keySet()) { |
||
309 | // create constraint |
||
310 | Constraint cons = this.doCreateCSPConstraint(constraint); |
||
311 | this.constraints.put(constraint, cons); |
||
312 | } |
||
313 | |||
314 | // post constraints |
||
315 | for (ParameterConstraint constraint : this.constraints.keySet()) { |
||
316 | // post CSP constraint |
||
317 | this.constraints.get(constraint).post(); |
||
318 | } |
||
319 | |||
320 | // set clean flag |
||
321 | this.clean = true; |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * |
||
326 | * @param param |
||
327 | * @return |
||
328 | */ |
||
329 | private IntVar doCreateCSPVariable(Parameter<?> param) |
||
330 | { |
||
331 | // CSP variable |
||
332 | IntVar var; |
||
333 | // check parameter type |
||
334 | switch (param.getType()) |
||
335 | { |
||
336 | // enumeration parameter |
||
337 | case ENUMERATION_PARAMETER_TYPE : |
||
338 | { |
||
339 | // get enumeration parameter |
||
340 | EnumerationParameter p = (EnumerationParameter) param; |
||
341 | // get value indexes |
||
342 | int[] values = p.getValueIndexes(); |
||
343 | // create variable |
||
344 | var = this.model.intVar(p.getLabel(), values); |
||
345 | } |
||
346 | break; |
||
347 | |||
348 | // numeric parameter |
||
349 | case NUMERIC_PARAMETER_TYPE : |
||
350 | { |
||
351 | // get numeric parameter |
||
352 | NumericParameter p = (NumericParameter) param; |
||
353 | // create variable |
||
354 | var = this.model.intVar(p.getLabel(), p.getLowerBound(), p.getUpperBound()); |
||
355 | } |
||
356 | break; |
||
357 | |||
358 | // unknown parameter |
||
359 | default : { |
||
360 | throw new RuntimeException("Unknown parameter type - " + param.getType()); |
||
361 | } |
||
362 | } |
||
363 | |||
364 | // get CSP variable |
||
365 | return var; |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * |
||
370 | * @param constraint |
||
371 | * @return |
||
372 | */ |
||
373 | private Constraint doCreateCSPConstraint(ParameterConstraint constraint) |
||
374 | { |
||
375 | Constraint cons; |
||
376 | // check constraint type |
||
377 | switch (constraint.getType()) |
||
378 | { |
||
379 | // bind parameter constraint |
||
380 | case BIND : |
||
381 | { |
||
382 | // get bind constraint |
||
383 | BindParameterConstraint bind = (BindParameterConstraint) constraint; |
||
384 | cons = this.doCreateBindCSPConstraint(bind); |
||
385 | } |
||
386 | break; |
||
387 | |||
388 | case EXCLUDE : |
||
389 | { |
||
390 | // get exclude constraint |
||
391 | ExcludeParameterConstraint ex = (ExcludeParameterConstraint) constraint; |
||
392 | cons = this.doCreateExcludeCSPConstraint(ex); |
||
393 | } |
||
394 | break; |
||
395 | |||
396 | // equal parameter constraint |
||
397 | case EQUAL : |
||
398 | { |
||
399 | // get equal constraint |
||
400 | EqualParameterConstraint eq = (EqualParameterConstraint) constraint; |
||
401 | cons = this.doCreateEqualCSPConstraint(eq); |
||
402 | } |
||
403 | break; |
||
404 | |||
405 | // not equal parameter constraint |
||
406 | case NOT_EQUAL : |
||
407 | { |
||
408 | // get not equal constraint |
||
409 | NotEqualParameterConstraint neq = (NotEqualParameterConstraint) constraint; |
||
410 | cons = this.doCreateNotEqualCSPConstraint(neq); |
||
411 | } |
||
412 | break; |
||
413 | |||
414 | default : { |
||
415 | throw new RuntimeException("Unknown parameter constraint type - " + constraint.getType()); |
||
416 | } |
||
417 | } |
||
418 | |||
419 | // get CSP constraint |
||
420 | return cons; |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * |
||
425 | * @param bind |
||
426 | * @return |
||
427 | */ |
||
428 | View Code Duplication | private Constraint doCreateBindCSPConstraint(BindParameterConstraint bind) |
|
429 | { |
||
430 | // CSP constraint |
||
431 | Constraint cons; |
||
432 | // get reference parameter |
||
433 | Parameter<?> param = bind.getReference(); |
||
434 | // check if parameter variable exists |
||
435 | if (!this.variables.containsKey(param)) { |
||
436 | throw new RuntimeException("Unknown parameter variable - " + param); |
||
437 | } |
||
438 | |||
439 | // check parameter type |
||
440 | switch(param.getType()) |
||
441 | { |
||
442 | // binding to enumeration parameter |
||
443 | case ENUMERATION_PARAMETER_TYPE : |
||
444 | { |
||
445 | // get parameter |
||
446 | EnumerationParameter p = (EnumerationParameter) param; |
||
447 | // get variable |
||
448 | IntVar var = this.variables.get(p); |
||
449 | // get binding value |
||
450 | String value = (String) bind.getValue(); |
||
451 | // get related index |
||
452 | int index = p.getDomain().getIndex(value); |
||
453 | // create constraint |
||
454 | cons = this.model.arithm(var, "=", index); |
||
455 | } |
||
456 | break; |
||
457 | |||
458 | // binding numeric variable |
||
459 | case NUMERIC_PARAMETER_TYPE : |
||
460 | { |
||
461 | // get parameter |
||
462 | NumericParameter p = (NumericParameter) param; |
||
463 | // get variable |
||
464 | IntVar var = this.variables.get(p); |
||
465 | // get binding value |
||
466 | int value = Integer.parseInt(bind.getValue().toString()); |
||
467 | // create constraint |
||
468 | cons = this.model.arithm(var, "=", value); |
||
469 | } |
||
470 | break; |
||
471 | |||
472 | default : { |
||
473 | throw new RuntimeException("Unknown parameter type - " + param.getType()); |
||
474 | } |
||
475 | } |
||
476 | |||
477 | // get create CSP constraint |
||
478 | return cons; |
||
479 | } |
||
480 | |||
481 | /** |
||
482 | * |
||
483 | * @param bind |
||
484 | * @return |
||
485 | */ |
||
486 | View Code Duplication | private Constraint doCreateExcludeCSPConstraint(ExcludeParameterConstraint bind) |
|
487 | { |
||
488 | // CSP constraint |
||
489 | Constraint cons; |
||
490 | // get reference parameter |
||
491 | Parameter<?> param = bind.getReference(); |
||
492 | // check if parameter variable exists |
||
493 | if (!this.variables.containsKey(param)) { |
||
494 | throw new RuntimeException("Unknown parameter variable - " + param); |
||
495 | } |
||
496 | |||
497 | // check parameter type |
||
498 | switch(param.getType()) |
||
499 | { |
||
500 | // binding to enumeration parameter |
||
501 | case ENUMERATION_PARAMETER_TYPE : |
||
502 | { |
||
503 | // get parameter |
||
504 | EnumerationParameter p = (EnumerationParameter) param; |
||
505 | // get variable |
||
506 | IntVar var = this.variables.get(p); |
||
507 | // get binding value |
||
508 | String value = (String) bind.getValue(); |
||
509 | // get related index |
||
510 | int index = p.getDomain().getIndex(value); |
||
511 | // create constraint |
||
512 | cons = this.model.arithm(var, "!=", index); |
||
513 | } |
||
514 | break; |
||
515 | |||
516 | // binding numeric variable |
||
517 | case NUMERIC_PARAMETER_TYPE : |
||
518 | { |
||
519 | // get parameter |
||
520 | NumericParameter p = (NumericParameter) param; |
||
521 | // get variable |
||
522 | IntVar var = this.variables.get(p); |
||
523 | // get binding value |
||
524 | int value = (int) bind.getValue(); |
||
525 | // create constraint |
||
526 | cons = this.model.arithm(var, "!=", value); |
||
527 | } |
||
528 | break; |
||
529 | |||
530 | default : { |
||
531 | throw new RuntimeException("Unknown parameter type - " + param.getType()); |
||
532 | } |
||
533 | } |
||
534 | |||
535 | // get create CSP constraint |
||
536 | return cons; |
||
537 | } |
||
538 | |||
539 | /** |
||
540 | * |
||
541 | * @param constraint |
||
542 | * @return |
||
543 | */ |
||
544 | private Constraint doCreateEqualCSPConstraint(EqualParameterConstraint constraint) |
||
545 | { |
||
546 | // check if parameter variables exist |
||
547 | if (!this.variables.containsKey(constraint.getReference()) || |
||
548 | !this.variables.containsKey(constraint.getTarget())) |
||
549 | { |
||
550 | throw new RuntimeException("Unknownw parameter variables\n- reference= " + constraint.getReference() + "\n- target= " + constraint.getTarget() + "\n"); |
||
551 | } |
||
552 | |||
553 | // get variables |
||
554 | IntVar ref = this.variables.get(constraint.getReference()); |
||
555 | IntVar tar = this.variables.get(constraint.getTarget()); |
||
556 | |||
557 | // create constraint |
||
558 | Constraint cons = this.model.allEqual(ref, tar); |
||
559 | return cons; |
||
560 | } |
||
561 | |||
562 | /** |
||
563 | * |
||
564 | * @param constraint |
||
565 | * @return |
||
566 | */ |
||
567 | private Constraint doCreateNotEqualCSPConstraint(NotEqualParameterConstraint constraint) |
||
568 | { |
||
569 | // check if parameter variables exist |
||
570 | if (!this.variables.containsKey(constraint.getReference()) || |
||
571 | !this.variables.containsKey(constraint.getTarget())) |
||
572 | { |
||
573 | throw new RuntimeException("Unknownw parameter variables\n- reference= " + constraint.getReference() + "\n- target= " + constraint.getTarget() + "\n"); |
||
574 | } |
||
575 | |||
576 | // get variables |
||
577 | IntVar ref = this.variables.get(constraint.getReference()); |
||
578 | IntVar tar = this.variables.get(constraint.getTarget()); |
||
579 | |||
580 | // create constraint |
||
581 | Constraint cons = this.model.allDifferent(ref, tar); |
||
582 | return cons; |
||
583 | } |
||
584 | |||
585 | /** |
||
586 | * |
||
587 | * @param param |
||
588 | * @return |
||
589 | */ |
||
590 | private List<ParameterConstraint> doFindParameterRelatedConstraints(Parameter<?> param) |
||
591 | { |
||
592 | // list of parameter related constraints |
||
593 | List<ParameterConstraint> list = new ArrayList<>(); |
||
594 | // check constraints |
||
595 | for (ParameterConstraint cons : this.constraints.keySet()) |
||
596 | { |
||
597 | // check constraint type |
||
598 | switch (cons.getType()) |
||
599 | { |
||
600 | // bind constraint |
||
601 | case BIND : |
||
602 | case EXCLUDE : |
||
603 | { |
||
604 | // check reference |
||
605 | if (cons.getReference().equals(param)) { |
||
606 | // remove constraint |
||
607 | list.add(cons); |
||
608 | } |
||
609 | } |
||
610 | break; |
||
611 | |||
612 | // binary constraint |
||
613 | case EQUAL : |
||
614 | case NOT_EQUAL : |
||
615 | { |
||
616 | // get binary constraint |
||
617 | BinaryParameterConstraint binary = (BinaryParameterConstraint) cons; |
||
618 | if (binary.getReference().equals(param) || binary.getTarget().equals(param)) { |
||
619 | // add constraint |
||
620 | list.add(cons); |
||
621 | } |
||
622 | } |
||
623 | break; |
||
624 | |||
625 | default : { |
||
626 | // unknown |
||
627 | throw new RuntimeException("Unknownw parameter constraint type - " + cons.getType()); |
||
628 | } |
||
629 | } |
||
630 | } |
||
631 | |||
632 | // get list |
||
633 | return list; |
||
634 | } |
||
636 |