-
Notifications
You must be signed in to change notification settings - Fork 37
/
Chapter-Planner_configuration.xml
executable file
·2192 lines (1687 loc) · 102 KB
/
Chapter-Planner_configuration.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5.0"
xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd http://www.w3.org/1999/xlink http://www.docbook.org/xml/5.0/xsd/xlink.xsd"
xml:base="../" xml:id="plannerConfiguration" xmlns="http://docbook.org/ns/docbook"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Planner Configuration</title>
<section xml:id="plannerConfigurationOverview">
<title>Overview</title>
<para>Solving a planning problem with Planner consists out of 5 steps:</para>
<orderedlist>
<listitem>
<para><emphasis role="bold">Model your planning problem</emphasis> as a class that implements the interface
<literal>Solution</literal>, for example the class <literal>NQueens</literal>.</para>
</listitem>
<listitem>
<para><emphasis role="bold">Configure a <literal>Solver</literal></emphasis>, for example a First Fit and Tabu
Search solver for any <literal>NQueens</literal> instance.</para>
</listitem>
<listitem>
<para><emphasis role="bold">Load a problem data set</emphasis> from your data layer, for example a 4 Queens
instance. That is the planning problem.</para>
</listitem>
<listitem>
<para><emphasis role="bold">Solve it</emphasis> with <literal>Solver.solve(planningProblem)</literal> which
returns the best solution found.</para>
</listitem>
</orderedlist>
<mediaobject>
<imageobject>
<imagedata fileref="images/Chapter-Planner_configuration/inputOutputOverview.png"/>
</imageobject>
</mediaobject>
</section>
<section xml:id="solverConfiguration">
<title>Solver Configuration</title>
<section xml:id="solverConfigurationByXML">
<title>Solver Configuration by XML</title>
<para>Build a <literal>Solver</literal> instance with the <literal>SolverFactory</literal>. Configure the
<literal>SolverFactory</literal> with a solver configuration XML file, provided as a classpath resource (as
definied by <literal>ClassLoader.getResource()</literal>):</para>
<programlisting language="java"> SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource(
"org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
Solver<NQueens> solver = solverFactory.buildSolver();</programlisting>
<para>In a typical project (following the Maven directory structure), that solverConfig XML file would be located
at
<literal>$PROJECT_DIR/src/main/resources/org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml</literal>.
Alternatively, a <literal>SolverFactory</literal> can be created from a <literal>File</literal>, an
<literal>InputStream</literal> or a <literal>Reader</literal> with methods such as
<literal>SolverFactory.createFromXmlFile()</literal>. However, for portability reasons, a classpath resource is
recommended.</para>
<note>
<para>On some environments (<link linkend="integrationWithOSGi">OSGi</link>, <link
linkend="integrationWithJBossModules">JBoss modules</link>, ...), classpath resources (such as the solver
config, score DRL's and domain classes) in your jars might not be available to the default
<literal>ClassLoader</literal> of the <literal>optaplanner-core</literal> jar. In those cases, provide the
<literal>ClassLoader</literal> of your classes as a parameter:</para>
<programlisting language="java"> SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource(
".../nqueensSolverConfig.xml", getClass().getClassLoader());</programlisting>
</note>
<note>
<para>When using Workbench or Execution Server or to take advantage of Drools's <literal>KieContainer</literal>
features, provide the <literal>KieContainer</literal> as a parameter:</para>
<programlisting language="java"> KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer = kieServices.newKieContainer(
kieServices.newReleaseId("org.nqueens", "nqueens", "1.0.0"));
SolverFactory<NQueens> solverFactory = SolverFactory.createFromKieContainerXmlResource(
kieContainer, ".../nqueensSolverConfig.xml");</programlisting>
<para>And use <link linkend="droolsScoreCalculationKsessionName">a ksessionName in the solver
configuration</link>.</para>
</note>
<para>Both a <literal>Solver</literal> and a <literal>SolverFactory</literal> have a generic type called
<literal>Solution_</literal>, which is the class representing a <link
linkend="planningProblemAndPlanningSolution">planning problem and solution</link>.</para>
<para>A solver configuration XML file looks like this:</para>
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
<solver>
<!-- Define the model -->
<solutionClass>org.optaplanner.examples.nqueens.domain.NQueens</solutionClass>
<entityClass>org.optaplanner.examples.nqueens.domain.Queen</entityClass>
<!-- Define the score function -->
<scoreDirectorFactory>
<scoreDefinitionType>SIMPLE</scoreDefinitionType>
<scoreDrl>org/optaplanner/examples/nqueens/solver/nQueensScoreRules.drl</scoreDrl>
</scoreDirectorFactory>
<!-- Configure the optimization algorithms (optional) -->
<termination>
...
</termination>
<constructionHeuristic>
...
</constructionHeuristic>
<localSearch>
...
</localSearch>
</solver></programlisting>
<para>Notice the three parts in it:</para>
<itemizedlist>
<listitem>
<para>Define the model.</para>
</listitem>
<listitem>
<para>Define the score function.</para>
</listitem>
<listitem>
<para>Optionally configure the optimization algorithm(s).</para>
</listitem>
</itemizedlist>
<para>These various parts of a configuration are explained further in this manual.</para>
<para><emphasis role="bold">Planner makes it relatively easy to switch optimization algorithm(s) just by changing
the configuration.</emphasis> There is even a <link linkend="benchmarker">Benchmarker</link> which allows you to
play out different configurations against each other and report the most appropriate configuration for your use
case.</para>
</section>
<section xml:id="solverConfigurationByJavaAPI">
<title>Solver Configuration by Java API</title>
<para>A solver configuration can also be configured with the <literal>SolverConfig</literal> API. This is
especially useful to change some values dynamically at runtime. For example, to change the running time based on
user input, before building the <literal>Solver</literal>:</para>
<programlisting language="java"> SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource(
"org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
TerminationConfig terminationConfig = new TerminationConfig();
terminationConfig.setMinutesSpentLimit(userInput);
solverFactory.getSolverConfig().setTerminationConfig(terminationConfig);
Solver<NQueens> solver = solverFactory.buildSolver();</programlisting>
<para>Every element in the solver configuration XML is available as a <literal>*Config</literal> class or a
property on a <literal>*Config</literal> class in the package namespace
<literal>org.optaplanner.core.config</literal>. These <literal>*Config</literal> classes are the Java
representation of the XML format. They build the runtime components (of the package namespace
<literal>org.optaplanner.core.impl</literal>) and assemble them into an efficient
<literal>Solver</literal>.</para>
<important>
<para>The <literal>SolverFactory</literal> is only multi-thread safe after its configured. So the
<literal>getSolverConfig()</literal> method is not thread-safe. To configure a <literal>SolverFactory</literal>
dynamically for each user request, build a <literal>SolverFactory</literal> as base during initialization and
clone it with the <literal>cloneSolverFactory()</literal> method for a user request:</para>
<programlisting language="java"> private SolverFactory<NQueens> base;
public void init() {
base = SolverFactory.createFromXmlResource(
"org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
base.getSolverConfig().setTerminationConfig(new TerminationConfig());
}
// Called concurrently from different threads
public void userRequest(..., long userInput)
SolverFactory<NQueens> solverFactory = base.cloneSolverFactory();
solverFactory.getSolverConfig().getTerminationConfig().setMinutesSpentLimit(userInput);
Solver<NQueens> solver = solverFactory.buildSolver();
...
}</programlisting>
</important>
</section>
<section xml:id="annotationsConfiguration">
<title>Annotations Configuration</title>
<section xml:id="automaticScanningForAnnotations">
<title>Automatic Scanning for Annotations</title>
<para>Instead of the declaring the classes that have a <literal>@PlanningSolution</literal> or
<literal>@PlanningEntity</literal> manually:</para>
<programlisting language="xml"><solver>
<!-- Define the model -->
<solutionClass>org.optaplanner.examples.nqueens.domain.NQueens</solutionClass>
<entityClass>org.optaplanner.examples.nqueens.domain.Queen</entityClass>
...
</solver></programlisting>
<para>Planner can find scan the classpath and find them automatically:</para>
<programlisting language="xml"><solver>
<!-- Define the model -->
<scanAnnotatedClasses/>
...
</solver></programlisting>
<para>This comes at a bootstrap performance cost.</para>
<para>If there are multiple models in your classpath or to speed up scanning, specify the packages to
scan:</para>
<programlisting language="xml"><solver>
<!-- Define the model -->
<scanAnnotatedClasses>
<packageInclude>org.optaplanner.examples.cloudbalancing</packageInclude>
</scanAnnotatedClasses>
...
</solver></programlisting>
<para>This finds all solution and entity classes in that package or its subpackages.</para>
<note>
<para>If <literal>scanAnnotatedClasses</literal> is not specified, the <literal>org.reflections</literal>
transitive maven dependency can be excluded.</para>
</note>
</section>
<section xml:id="annotationAlternatives">
<title>Annotation Alternatives</title>
<para>Planner needs to be told which classes in your domain model are planning entities, which properties are
planning variables, etc. There are several ways to deliver this information:</para>
<itemizedlist>
<listitem>
<para>Add class annotations and JavaBean property annotations on the domain model (recommended). The
property annotations must be the getter method, not on the setter method. Such a getter does not need to be
public.</para>
</listitem>
<listitem>
<para>Add class annotations and field annotations on the domain model. Such a field does not need to be
public.</para>
</listitem>
<listitem>
<para>No annotations: externalize the domain configuration in an XML file. This is <link
xlink:href="https://issues.jboss.org/browse/PLANNER-151">not yet supported</link>.</para>
</listitem>
</itemizedlist>
<para>This manual focuses on the first manner, but every features supports all 3 manners, even if it's not
explicitly mentioned.</para>
</section>
</section>
</section>
<section xml:id="modelAPlanningProblem">
<title>Model a Planning Problem</title>
<section xml:id="isThisClassAProblemFactOrPlanningEntity">
<title>Is This Class a Problem Fact or Planning Entity?</title>
<para>Look at a dataset of your planning problem. You will recognize domain classes in there, each of which can be
categorized as one of the following:</para>
<itemizedlist>
<listitem>
<para>A unrelated class: not used by any of the score constraints. From a planning standpoint, this data is
obsolete.</para>
</listitem>
<listitem>
<para>A <emphasis role="bold">problem fact</emphasis> class: used by the score constraints, but does NOT
change during planning (as long as the problem stays the same). For example: <literal>Bed</literal>,
<literal>Room</literal>, <literal>Shift</literal>, <literal>Employee</literal>, <literal>Topic</literal>,
<literal>Period</literal>, ... All the properties of a problem fact class are problem properties.</para>
</listitem>
<listitem>
<para>A <emphasis role="bold">planning entity</emphasis> class: used by the score constraints and changes
during planning. For example: <literal>BedDesignation</literal>, <literal>ShiftAssignment</literal>,
<literal>Exam</literal>, ... The properties that change during planning are planning variables. The other
properties are problem properties.</para>
</listitem>
</itemizedlist>
<para>Ask yourself: <emphasis>What class changes during planning?</emphasis> <emphasis>Which class has variables
that I want the <literal>Solver</literal> to change for me?</emphasis> That class is a planning entity. Most use
cases have only one planning entity class. Most use cases also have only one planning variable per planning entity
class.</para>
<note>
<para>In <link linkend="realTimePlanning">real-time planning</link>, even though the problem itself changes,
problem facts do not really change during planning, instead they change between planning (because the Solver
temporarily stops to apply the problem fact changes).</para>
</note>
<para>A good model can greatly improve the success of your planning implementation. Follow these guidelines to
design a good model:</para>
<itemizedlist>
<listitem>
<para>In a <emphasis>many to one</emphasis> relationship, it is normally the <emphasis>many</emphasis> side
that is the planning entity class. The property referencing the other side is then the planning variable. For
example in employee rostering: the planning entity class is <literal>ShiftAssignment</literal>, not
<literal>Employee</literal>, and the planning variable is <literal>ShiftAssignment.getEmployee()</literal>
because one <literal>Employee</literal> has multiple <literal>ShiftAssignment</literal>s but one
<literal>ShiftAssignment</literal> has only one <literal>Employee</literal>.</para>
</listitem>
<listitem>
<para>A planning entity class should have at least one problem property. A planning entity class with only
planning variables can normally be simplified by converting one of those planning variables into a problem
property. That heavily decreases <link linkend="searchSpaceSize">the search space size</link>. For example in
employee rostering: the <literal>ShiftAssignment</literal>'s <literal>getShift()</literal> is a problem
property and the <literal>getEmployee()</literal> is a planning variable. If both were a planning variable,
solving it would be far less efficient.</para>
<itemizedlist>
<listitem>
<para>A surrogate ID does not suffice as the required minimum of one problem property. It needs to be
understandable by the business. A business key does suffice. This prevents an unassigned entity from being
nameless (unidentifiable by the business).</para>
</listitem>
<listitem>
<para>This way, there is no need to add a hard constraint to assure that two planning entities are
different: they are already different due to their problem properties.</para>
</listitem>
<listitem>
<para>In some cases, multiple planning entities have the same problem property. In such cases, it can be
useful to create an extra problem property to distinguish them. For example in employee rostering:
<literal>ShiftAssignment</literal> has besides the problem property <literal>Shift</literal> also the
problem property <literal>indexInShift</literal>.</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>The number of planning entities is recommended to be fixed during planning. When unsure of which
property should be a planning variable and which should be a problem property, choose it so the number of
planning entities is fixed. For example in employee rostering: if the planning entity class would have been
<literal>EmployeeAssignment</literal> with a problem property <literal>getEmployee()</literal> and a planning
variable <literal>getShift()</literal>, than it is impossible to accurately predict how many
<literal>EmployeeAssignment</literal> instances to make per <literal>Employee</literal>.</para>
</listitem>
</itemizedlist>
<para>For inspiration, take a look at <link linkend="designPatterns">typical design patterns</link> or how the
examples modeled their domain:</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/Chapter-Planner_configuration/entityVariableAndValueExamples.png"/>
</imageobject>
</mediaobject>
<note>
<para>Vehicle routing is special, because it uses a <link linkend="chainedPlanningVariable">chained planning
variable</link>.</para>
</note>
<para><emphasis role="bold">In Planner, all problems facts and planning entities are plain old JavaBeans
(POJOs).</emphasis> Load them from a database, an XML file, a data repository, a REST service, a noSQL cloud, ...
(see <link linkend="integration">integration</link>): it doesn't matter.</para>
</section>
<section xml:id="problemFact">
<title>Problem Fact</title>
<para>A problem fact is any JavaBean (POJO) with getters that does not change during planning. Implementing the
interface <literal>Serializable</literal> is recommended (but not required). For example in n queens, the columns
and rows are problem facts:</para>
<programlisting language="java">public class Column implements Serializable {
private int index;
// ... getters
}</programlisting>
<programlisting language="java">public class Row implements Serializable {
private int index;
// ... getters
}</programlisting>
<para>A problem fact can reference other problem facts of course:</para>
<programlisting language="java">public class Course implements Serializable {
private String code;
private Teacher teacher; // Other problem fact
private int lectureSize;
private int minWorkingDaySize;
private List<Curriculum> curriculumList; // Other problem facts
private int studentSize;
// ... getters
}</programlisting>
<para>A problem fact class does <emphasis>not</emphasis> require any Planner specific code. For example, you can
reuse your domain classes, which might have JPA annotations.</para>
<note>
<para>Generally, better designed domain classes lead to simpler and more efficient score constraints. Therefore,
when dealing with a messy (denormalized) legacy system, it can sometimes be worthwhile to convert the messy
domain model into a Planner specific model first. For example: if your domain model has two
<literal>Teacher</literal> instances for the same teacher that teaches at two different departments, it is
harder to write a correct score constraint that constrains a teacher's spare time on the original model than on
an adjusted model.</para>
<para>Alternatively, you can sometimes also introduce <link linkend="cachedProblemFact"><emphasis>a cached
problem fact</emphasis></link> to enrich the domain model for planning only.</para>
</note>
</section>
<section xml:id="planningEntity">
<title>Planning Entity</title>
<section xml:id="planningEntityAnnotation">
<title>Planning Entity Annotation</title>
<para>A planning entity is a JavaBean (POJO) that changes during solving, for example a <literal>Queen</literal>
that changes to another row. A planning problem has multiple planning entities, for example for a single n
queens problem, each <literal>Queen</literal> is a planning entity. But there is usually only one planning
entity class, for example the <literal>Queen</literal> class.</para>
<para>A planning entity class needs to be annotated with the <literal>@PlanningEntity</literal>
annotation.</para>
<para>Each planning entity class has one or more <emphasis>planning variables</emphasis>. It should also have
one or more <emphasis>defining</emphasis> properties. For example in n queens, a <literal>Queen</literal> is
defined by its <literal>Column</literal> and has a planning variable <literal>Row</literal>. This means that a
Queen's column never changes during solving, while its row does change.</para>
<programlisting language="java">@PlanningEntity
public class Queen {
private Column column;
// Planning variables: changes during planning, between score calculations.
private Row row;
// ... getters and setters
}</programlisting>
<para>A planning entity class can have multiple planning variables. For example, a <literal>Lecture</literal> is
defined by its <literal>Course</literal> and its index in that course (because one course has multiple
lectures). Each <literal>Lecture</literal> needs to be scheduled into a <literal>Period</literal> and a
<literal>Room</literal> so it has two planning variables (period and room). For example: the course Mathematics
has eight lectures per week, of which the first lecture is Monday morning at 08:00 in room 212.</para>
<programlisting language="java">@PlanningEntity
public class Lecture {
private Course course;
private int lectureIndexInCourse;
// Planning variables: changes during planning, between score calculations.
private Period period;
private Room room;
// ...
}</programlisting>
<para>Without <link linkend="automaticScanningForAnnotations">automated scanning</link>, the solver
configuration also needs to declare each planning entity class:</para>
<programlisting language="java"><solver>
...
<entityClass>org.optaplanner.examples.nqueens.domain.Queen</entityClass>
...
</solver></programlisting>
<para>Some uses cases have multiple planning entity classes. For example: route freight and trains into railway
network arcs, where each freight can use multiple trains over its journey and each train can carry multiple
freights per arc. Having multiple planning entity classes directly raises the implementation complexity of your
use case.</para>
<note>
<para><emphasis>Do not create unnecessary planning entity classes.</emphasis> This leads to difficult
<literal>Move</literal> implementations and slower score calculation.</para>
<para>For example, do not create a planning entity class to hold the total free time of a teacher, which needs
to be kept up to date as the <literal>Lecture</literal> planning entities change. Instead, calculate the free
time in the score constraints (or as a <link linkend="shadowVariable">shadow variable</link>) and put the
result per teacher into a logically inserted score object.</para>
<para>If historic data needs to be considered too, then create problem fact to hold the total of the historic
assignments up to, but <emphasis>not including</emphasis>, the planning window (so that it does not change
when a planning entity changes) and let the score constraints take it into account.</para>
</note>
</section>
<section xml:id="planningEntityDifficulty">
<title>Planning Entity Difficulty</title>
<para>Some optimization algorithms work more efficiently if they have an estimation of which planning entities
are more difficult to plan. For example: in bin packing bigger items are harder to fit, in course scheduling
lectures with more students are more difficult to schedule, and in n queens the middle queens are more difficult
to fit on the board.</para>
<note>
<para><emphasis role="bold">Do not try to use planning entity difficulty to implement a business
constraint.</emphasis> It will not affect the score function: if we have infinite solving time, the returned
solution will be the same.</para>
<para>To attain a schedule in which certain entities are scheduled earlier in the schedule, <link
linkend="formalizeTheBusinessConstraints">add a score constraint</link> to change the score function so it
prefers such solutions. Only consider adding planning entity difficulty too if it can make the solver more
efficient.</para>
</note>
<para>To allow the heuristics to take advantage of that domain specific information, set a
<literal>difficultyComparatorClass</literal> to the <literal>@PlanningEntity</literal> annotation:</para>
<programlisting language="java">@PlanningEntity(difficultyComparatorClass = CloudProcessDifficultyComparator.class)
public class CloudProcess {
// ...
}</programlisting>
<programlisting language="java">public class CloudProcessDifficultyComparator implements Comparator<CloudProcess> {
public int compare(CloudProcess a, CloudProcess b) {
return new CompareToBuilder()
.append(a.getRequiredMultiplicand(), b.getRequiredMultiplicand())
.append(a.getId(), b.getId())
.toComparison();
}
}</programlisting>
<para>Alternatively, you can also set a <literal>difficultyWeightFactoryClass</literal> to the
<literal>@PlanningEntity</literal> annotation, so that you have access to the rest of the problem facts from the
<literal>Solution</literal> too:</para>
<programlisting language="java">@PlanningEntity(difficultyWeightFactoryClass = QueenDifficultyWeightFactory.class)
public class Queen {
// ...
}</programlisting>
<para>See <link linkend="sortedSelection">sorted selection</link> for more information.</para>
<important>
<para>Difficulty should be implemented ascending: easy entities are lower, difficult entities are higher. For
example, in bin packing: small item < medium item < big item.</para>
<para>Although most algorithms start with the more difficult entities first, they just reverse the
ordering.</para>
</important>
<para><emphasis>None of the current planning variable states should be used to compare planning entity
difficulty.</emphasis> During Construction Heuristics, those variables are likely to be <literal>null</literal>
anyway. For example, a <literal>Queen</literal>'s <literal>row</literal> variable should not be used.</para>
</section>
</section>
<section xml:id="planningVariable">
<title>Planning Variable</title>
<section xml:id="planningVariableAnnotation">
<title>Planning Variable Annotation</title>
<para>A planning variable is a JavaBean property (so a getter and setter) on a planning entity. It points to a
planning value, which changes during planning. For example, a <literal>Queen</literal>'s <literal>row</literal>
property is a planning variable. Note that even though a <literal>Queen</literal>'s <literal>row</literal>
property changes to another <literal>Row</literal> during planning, no <literal>Row</literal> instance itself is
changed.</para>
<para>A planning variable getter needs to be annotated with the <literal>@PlanningVariable</literal> annotation,
which needs a non-empty <literal>valueRangeProviderRefs</literal> property.</para>
<programlisting language="java">@PlanningEntity
public class Queen {
...
private Row row;
@PlanningVariable(valueRangeProviderRefs = {"rowRange"})
public Row getRow() {
return row;
}
public void setRow(Row row) {
this.row = row;
}
}</programlisting>
<para>The <literal>valueRangeProviderRefs</literal> property defines what are the possible planning values for
this planning variable. It references one or more <literal>@ValueRangeProvider</literal>
<literal>id</literal>'s.</para>
<note>
<para>A @PlanningVariable annotation needs to be on a member in a class with a @PlanningEntity annotation. It
is ignored on parent classes or subclasses without that annotation.</para>
</note>
<para><link linkend="annotationAlternatives">Annotating the field</link> instead of the property works
too:</para>
<programlisting language="java">@PlanningEntity
public class Queen {
...
@PlanningVariable(valueRangeProviderRefs = {"rowRange"})
private Row row;
}</programlisting>
</section>
<section xml:id="nullablePlanningVariable">
<title>Nullable Planning Variable</title>
<para>By default, an initialized planning variable cannot be <literal>null</literal>, so an initialized solution
will never use <literal>null</literal> for any of its planning variables. In an over-constrained use case, this
can be counterproductive. For example: in task assignment with too many tasks for the workforce, we would rather
leave low priority tasks unassigned instead of assigning them to an overloaded worker.</para>
<para>To allow an initialized planning variable to be <literal>null</literal>, set <literal>nullable</literal>
to <literal>true</literal>:</para>
<programlisting language="java"> @PlanningVariable(..., nullable = true)
public Worker getWorker() {
return worker;
}</programlisting>
<important>
<para>Planner will automatically add the value <literal>null</literal> to the value range. There is no need to
add <literal>null</literal> in a collection used by a <literal>ValueRangeProvider</literal>.</para>
</important>
<note>
<para>Using a nullable planning variable implies that your score calculation is responsible for punishing (or
even rewarding) variables with a null value.</para>
</note>
<para><link linkend="repeatedPlanning">Repeated planning</link> (especially <link
linkend="realTimePlanning">real-time planning</link>) does not mix well with a nullable planning variable. Every
time the Solver starts or a problem fact change is made, the <link linkend="constructionHeuristics">Construction
Heuristics</link> will try to initialize all the <literal>null</literal> variables again, which can be a huge
waste of time. One way to deal with this, is to change when a planning entity should be reinitialized with an
<literal>reinitializeVariableEntityFilter</literal>:</para>
<programlisting language="java"> @PlanningVariable(..., nullable = true, reinitializeVariableEntityFilter = ReinitializeTaskFilter.class)
public Worker getWorker() {
return worker;
}</programlisting>
</section>
<section xml:id="whenIsAPlanningVariableInitialized">
<title>When is a Planning Variable Considered Initialized?</title>
<para>A planning variable is considered initialized if its value is not <literal>null</literal> or if the
variable is <literal>nullable</literal>. So a nullable variable is always considered initialized, even when a
custom <literal>reinitializeVariableEntityFilter</literal> triggers a reinitialization during construction
heuristics.</para>
<para>A planning entity is initialized if all of its planning variables are initialized.</para>
<para>A <literal>Solution</literal> is initialized if all of its planning entities are initialized.</para>
</section>
</section>
<section xml:id="planningValueAndPlanningValueRange">
<title>Planning Value and Planning Value Range</title>
<section xml:id="planningValue">
<title>Planning Value</title>
<para>A planning value is a possible value for a planning variable. Usually, a planning value is a problem fact,
but it can also be any object, for example a <literal>double</literal>. It can even be another planning entity
or even a interface implemented by both a planning entity and a problem fact.</para>
<para>A planning value range is the set of possible planning values for a planning variable. This set can be a
countable (for example row <literal>1</literal>, <literal>2</literal>, <literal>3</literal> or
<literal>4</literal>) or uncountable (for example any <literal>double</literal> between <literal>0.0</literal>
and <literal>1.0</literal>).</para>
</section>
<section xml:id="planningValueRangeProvider">
<title>Planning Value Range Provider</title>
<section xml:id="planningValueRangeProviderOverview">
<title>Overview</title>
<para>The value range of a planning variable is defined with the <literal>@ValueRangeProvider</literal>
annotation. A <literal>@ValueRangeProvider</literal> annotation always has a property <literal>id</literal>,
which is referenced by the <literal>@PlanningVariable</literal>'s property
<literal>valueRangeProviderRefs</literal>.</para>
<para>This annotation can be located on 2 types of methods:</para>
<itemizedlist>
<listitem>
<para>On the Solution: All planning entities share the same value range.</para>
</listitem>
<listitem>
<para>On the planning entity: The value range differs per planning entity. This is less common.</para>
</listitem>
</itemizedlist>
<note>
<para>A @ValueRangeProvider annotation needs to be on a member in a class with a @PlanningSolution or a
@PlanningEntity annotation. It is ignored on parent classes or subclasses without those annotations.</para>
</note>
<para>The return type of that method can be 2 types:</para>
<itemizedlist>
<listitem>
<para><literal>Collection</literal>: The value range is defined by a <literal>Collection</literal>
(usually a <literal>List</literal>) of its possible values.</para>
</listitem>
<listitem>
<para><literal>ValueRange</literal>: The value range is defined by its bounds. This is less common.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="valueRangeProviderOnSolution">
<title><literal>ValueRangeProvider</literal> on the <literal>Solution</literal></title>
<para>All instances of the same planning entity class share the same set of possible planning values for that
planning variable. This is the most common way to configure a value range.</para>
<para>The <literal>Solution</literal> implementation has method that returns a <literal>Collection</literal>
(or a <literal>ValueRange</literal>). Any value from that <literal>Collection</literal> is a possible planning
value for this planning variable.</para>
<programlisting language="java"> @PlanningVariable(valueRangeProviderRefs = {"rowRange"})
public Row getRow() {
return row;
}</programlisting>
<programlisting language="java">@PlanningSolution
public class NQueens {
...
@ValueRangeProvider(id = "rowRange")
public List<Row> getRowList() {
return rowList;
}
}</programlisting>
<important>
<para>That <literal>Collection</literal> (or <literal>ValueRange</literal>) must not contain the value
<literal>null</literal>, not even for a <link linkend="nullablePlanningVariable">nullable planning
variable</link>.</para>
</important>
<para><link linkend="annotationAlternatives">Annotating the field</link> instead of the property works
too:</para>
<programlisting language="java">@PlanningSolution
public class NQueens {
...
@ValueRangeProvider(id = "rowRange")
private List<Row> rowList;
}</programlisting>
</section>
<section xml:id="valueRangeProviderOnPlanningEntity">
<title><literal>ValueRangeProvider</literal> on the Planning Entity</title>
<para>Each planning entity has its own value range (a set of possible planning values) for the planning
variable. For example, if a teacher can <emphasis role="bold">never</emphasis> teach in a room that does not
belong to his department, lectures of that teacher can limit their room value range to the rooms of his
department.</para>
<programlisting language="java"> @PlanningVariable(valueRangeProviderRefs = {"departmentRoomRange"})
public Room getRoom() {
return room;
}
@ValueRangeProvider(id = "departmentRoomRange")
public List<Room> getPossibleRoomList() {
return getCourse().getTeacher().getDepartment().getRoomList();
}</programlisting>
<para>Never use this to enforce a soft constraint (or even a hard constraint when the problem might not have a
feasible solution). For example: <emphasis>Unless there is no other way</emphasis>, a teacher can not teach in
a room that does not belong to his department. In this case, the teacher should <emphasis>not</emphasis> be
limited in his room value range (because sometimes there is no other way).</para>
<note>
<para>By limiting the value range specifically of one planning entity, you are effectively creating a
<emphasis>built-in hard constraint</emphasis>. This can have the benefit of severely lowering the number of
possible solutions; however, it can also away the freedom of the optimization algorithms to temporarily
break that constraint in order to escape from a local optimum.</para>
</note>
<para>A planning entity should <emphasis>not</emphasis> use other planning entities to determinate its value
range. That would only try to make the planning entity solve the planning problem itself and interfere with
the optimization algorithms.</para>
<para>Every entity has its own <literal>List</literal> instance, unless multiple entities have the same value
range. For example, if teacher A and B belong to the same department, they use the same
<literal>List<Room></literal> instance. Furthermore, each <literal>List</literal> contains a subset of
the same set of planning value instances. For example, if department A and B can both use room X, then their
<literal>List<Room></literal> instances contain the same <literal>Room</literal> instance.</para>
<note>
<para>A <literal>ValueRangeProvider</literal> on the planning entity consumes more memory than
<literal>ValueRangeProvider</literal> on the Solution and disables certain automatic performance
optimizations.</para>
</note>
<warning>
<para>A <literal>ValueRangeProvider</literal> on the planning entity is not currently compatible with a
<link linkend="chainedPlanningVariable">chained</link> variable.</para>
</warning>
</section>
<section xml:id="valueRangeFactory">
<title><literal>ValueRangeFactory</literal></title>
<para>Instead of a <literal>Collection</literal>, you can also return a <literal>ValueRange</literal> or
<literal>CountableValueRange</literal>, build by the <literal>ValueRangeFactory</literal>:</para>
<programlisting language="java"> @ValueRangeProvider(id = "delayRange")
public CountableValueRange<Integer> getDelayRange() {
return ValueRangeFactory.createIntValueRange(0, 5000);
}</programlisting>
<para>A <literal>ValueRange</literal> uses far less memory, because it only holds the bounds. In the example
above, a <literal>Collection</literal> would need to hold all <literal>5000</literal> ints, instead of just
the two bounds.</para>
<para>Furthermore, an <literal>incrementUnit</literal> can be specified, for example if you have to buy stocks
in units of 200 pieces:</para>
<programlisting language="java"> @ValueRangeProvider(id = "stockAmountRange")
public CountableValueRange<Integer> getStockAmountRange() {
// Range: 0, 200, 400, 600, ..., 9999600, 9999800, 10000000
return ValueRangeFactory.createIntValueRange(0, 10000000, 200);
}</programlisting>
<note>
<para>Return <literal>CountableValueRange</literal> instead of <literal>ValueRange</literal> whenever
possible (so Planner knows that it's countable).</para>
</note>
<para>The <literal>ValueRangeFactory</literal> has creation methods for several value class types:</para>
<itemizedlist>
<listitem>
<para><literal>boolean</literal>: A boolean range.</para>
</listitem>
<listitem>
<para><literal>int</literal>: A 32bit integer range.</para>
</listitem>
<listitem>
<para><literal>long</literal>: A 64bit integer range.</para>
</listitem>
<listitem>
<para><literal>double</literal>: A 64bit floating point range which only supports random selection
(because it does not implement <literal>CountableValueRange</literal>).</para>
</listitem>
<listitem>
<para><literal>BigInteger</literal>: An arbitrary-precision integer range.</para>
</listitem>
<listitem>
<para><literal>BigDecimal</literal>: A decimal point range. By default, the increment unit is the lowest
non-zero value in the scale of the bounds.</para>
</listitem>
<listitem>
<para><literal>Temporal</literal> (such as <literal>LocalDate</literal>, <literal>LocalDateTime</literal>,
...): A time range.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="combineValueRangeProviders">
<title>Combine ValueRangeProviders</title>
<para>Value range providers can be combined, for example:</para>
<programlisting language="java"> @PlanningVariable(valueRangeProviderRefs = {"companyCarRange", "personalCarRange"})
public Car getCar() {
return car;
}</programlisting>
<programlisting language="java"> @ValueRangeProvider(id = "companyCarRange")
public List<CompanyCar> getCompanyCarList() {
return companyCarList;
}
@ValueRangeProvider(id = "personalCarRange")
public List<PersonalCar> getPersonalCarList() {
return personalCarList;
}</programlisting>
</section>
</section>
<section xml:id="planningValueStrength">
<title>Planning Value Strength</title>
<para>Some optimization algorithms work a bit more efficiently if they have an estimation of which planning
values are stronger, which means they are more likely to satisfy a planning entity. For example: in bin packing
bigger containers are more likely to fit an item and in course scheduling bigger rooms are less likely to break
the student capacity constraint. Usually, the efficiency gain of planning value strength is far less than that
of <link linkend="planningEntityDifficulty">planning entity difficulty</link>.</para>
<note>
<para><emphasis role="bold">Do not try to use planning value strength to implement a business
constraint.</emphasis> It will not affect the score function: if we have infinite solving time, the returned
solution will be the same.</para>
<para>To affect the score function, <link linkend="formalizeTheBusinessConstraints">add a score
constraint</link>. Only consider adding planning value strength too if it can make the solver more
efficient.</para>
</note>
<para>To allow the heuristics to take advantage of that domain specific information, set a
<literal>strengthComparatorClass</literal> to the <literal>@PlanningVariable</literal> annotation:</para>
<programlisting language="java"> @PlanningVariable(..., strengthComparatorClass = CloudComputerStrengthComparator.class)
public CloudComputer getComputer() {
return computer;
}</programlisting>
<programlisting language="java">public class CloudComputerStrengthComparator implements Comparator<CloudComputer> {
public int compare(CloudComputer a, CloudComputer b) {
return new CompareToBuilder()
.append(a.getMultiplicand(), b.getMultiplicand())
.append(b.getCost(), a.getCost()) // Descending (but this is debatable)
.append(a.getId(), b.getId())
.toComparison();
}
}</programlisting>
<note>
<para>If you have multiple planning value classes in the <emphasis>same</emphasis> value range, the
<literal>strengthComparatorClass</literal> needs to implement a <literal>Comparator</literal> of a common
superclass (for example <literal>Comparator<Object></literal>) and be able to handle comparing instances
of those different classes.</para>
</note>
<para>Alternatively, you can also set a <literal>strengthWeightFactoryClass</literal> to the
<literal>@PlanningVariable</literal> annotation, so you have access to the rest of the problem facts from the
solution too:</para>
<programlisting language="java"> @PlanningVariable(..., strengthWeightFactoryClass = RowStrengthWeightFactory.class)
public Row getRow() {
return row;
}</programlisting>
<para>See <link linkend="sortedSelection">sorted selection</link> for more information.</para>
<important>
<para>Strength should be implemented ascending: weaker values are lower, stronger values are higher. For
example in bin packing: small container < medium container < big container.</para>
</important>
<para><emphasis>None of the current planning variable state in any of the planning entities should be used to
compare planning values.</emphasis> During construction heuristics, those variables are likely to be
<literal>null</literal>. For example, none of the <literal>row</literal> variables of any
<literal>Queen</literal> may be used to determine the strength of a <literal>Row</literal>.</para>
</section>
<section xml:id="chainedPlanningVariable">
<title>Chained Planning Variable (TSP, VRP, ...)</title>
<para>Some use cases, such as TSP and Vehicle Routing, require <emphasis>chaining</emphasis>. This means the
planning entities point to each other and form a chain. By modeling the problem as a set of chains (instead of a
set of trees/loops), the search space is heavily reduced.</para>
<para>A planning variable that is chained either:</para>