Search code examples
optaplanner

Nurse rostering modification


Hi I'm new on Optaplanner and I'm trying to adapt nurse rostering for airport checkin agent rostering. I've created A FlightAssignment in which an Employee is assigned to a Flight.

The Employee is the PlanningVariable.

The FlightAssignment is the PlanningEntity.

The CheckinRoster is the PlanningEntityCollectionProperty

Please help me, when I run the CheckinRosteringApp I get this error:

2017-03-03 16:35:47,574 [main] INFO  CloudBalance 1 has 9 flights and 15 employees with a search space of 10^14.
Exception in thread "main" java.lang.IllegalArgumentException: The solutionClass (class org.optaplanner.examples.handler.domain.CheckinRoster)'s entityCollectionProperty (flightAssignmentList) should never return null.
    at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.extractEntityCollection(SolutionDescriptor.java:657)
    at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.getEntityCount(SolutionDescriptor.java:516)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner$FieldAccessingSolutionClonerRun.cloneSolution(FieldAccessingSolutionCloner.java:201)
    at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner.cloneSolution(FieldAccessingSolutionCloner.java:72)
    at org.optaplanner.core.impl.score.director.AbstractScoreDirector.cloneSolution(AbstractScoreDirector.java:142)
    at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution(DefaultSolverScope.java:198)
    at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:196)
    at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:175)
    at org.optaplanner.examples.handler.app.CheckinRosteringApp.main(CheckinRosteringApp.java:30)

And if I initialize the flightAssignmentList the error disappear but the solution is empty.

2017-03-04 18:31:43,691 [main] INFO  CheckinRoster 1 has 8 flights and 20 employees with a search space of 10^18.
2017-03-04 18:31:44,061 [main] INFO  Solving started: time spent (350), best score (-25hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
2017-03-04 18:31:44,078 [main] INFO  Construction Heuristic phase (0) ended: step total (0), time spent (369), best score (-25hard/0soft).
2017-03-04 18:31:44,090 [main] WARN      No doable selected move at step index (0), time spent (382). Terminating phase early.
2017-03-04 18:31:44,091 [main] INFO  Local Search phase (1) ended: step total (0), time spent (383), best score (-25hard/0soft).
2017-03-04 18:31:44,091 [main] INFO  Solving ended: time spent (383), best score (-25hard/0soft), average calculate count per second (7), environment mode (REPRODUCIBLE).
[...]

Did I make something wrong in the xml config file? I used the one in nurserostering deleting the unionMoveSelector block hoping in optaplanner searching for a solution.

This is the checkinRosteringSolverConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<solver>
  <!--<environmentMode>FAST_ASSERT</environmentMode>-->
  <solutionClass>org.optaplanner.examples.handler.domain.CheckinRoster</solutionClass>
  <entityClass>org.optaplanner.examples.handler.domain.FlightAssignment</entityClass>

  <scoreDirectorFactory>
    <scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
    <scoreDrl>org/optaplanner/examples/handler/solver/checkinRosteringScoreRules.drl</scoreDrl>
  </scoreDirectorFactory>

  <termination>
    <!--
      Official benchmark secondsSpentLimit allowed on:
      - ge0ffrey's main pc: sprint 11, medium 700, long 42000
    -->
    <secondsSpentLimit>700</secondsSpentLimit>
    <!--<bestScoreLimit>-0hard/-999999soft</bestScoreLimit>-->
  </termination>
  <constructionHeuristic>
    <constructionHeuristicType>WEAKEST_FIT</constructionHeuristicType>
  </constructionHeuristic>
  <localSearch>
    <acceptor>
      <entityTabuSize>7</entityTabuSize>
    </acceptor>
    <forager>
      <acceptedCountLimit>800</acceptedCountLimit>
    </forager>
  </localSearch>
</solver>

And this is the checkingRosteringScoreRules.drl

/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.optaplanner.examples.handler.solver;
    dialect "java"

import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;


import org.optaplanner.examples.handler.domain.Employee;
import org.optaplanner.examples.handler.domain.Flight;
import org.optaplanner.examples.handler.domain.FlightAssignment;
import org.optaplanner.examples.handler.domain.CheckinRoster;

global HardSoftScoreHolder scoreHolder;

// ############################################################################
// Hard constraints
// ############################################################################

// This rule is build in
// All demanded shifts must be assigned to a nurse
rule "requiredEmployeeSizePerShift"
    when
        $flight : Flight(minimumEmployeesNumber > 0, $minimumEmployeesNumber : minimumEmployeesNumber)
        $totalEmployeeNumber : Number(intValue <= $minimumEmployeesNumber) from accumulate(
                $assignment : FlightAssignment(flight == $flight),
                count($assignment)
        )
    then
        scoreHolder.addHardConstraintMatch(kcontext, - Math.abs($minimumEmployeesNumber - $totalEmployeeNumber.intValue()));
end

//a nurse can only work on no clashing flights
//TODO


//TODO

// a nurse can only work one shift per day, i.e. no two shift can be assigned to the same nurse on a day.
//rule "oneShiftPerDay"
//    when
//        ShiftAssignment($leftId : id, $employee : employee, $shiftDate : shiftDate, employee != null)
//        ShiftAssignment(employee == $employee, shiftDate == $shiftDate, id > $leftId)
//    then
//        scoreHolder.addHardConstraintMatch(kcontext, -1);
//end

// ############################################################################
// Soft constraints
// ############################################################################

rule "employeeCost"
    when
        $employee : Employee($income : income)
        exists FlightAssignment(employee == $employee)
    then
        scoreHolder.addSoftConstraintMatch(kcontext, - $income);
end

Solution

  • Exception in thread "main" java.lang.IllegalArgumentException: The solutionClass (class org.optaplanner.examples.handler.domain.CheckinRoster)'s entityCollectionProperty (flightAssignmentList) should never return null.

    Check your method CheckinRoster.getFlightAssignmentList(). For that dataset, it's apparently returning null.