Peak Wu
Peak Wu

Reputation: 1

optaplanner project job scheduling example - cannot compile successfully when switching to drl file

I am trying to reuse the project job scheduling example in the 7.4.1.Final distribution. I changed projectJobSchedulingSolverConfig.xml file to use

<scoreDrl>org/optaplanner/examples/projectjobscheduling/solver/projectJobSchedulingScoreRules.drl</scoreDrl>

instead of <incrementalScoreCalculatorClass>org.optaplanner.examples.projectjobscheduling.solver.score.ProjectJobSchedulingIncrementalScoreCalculator</incrementalScoreCalculatorClass>.

To be clearer:

I only change this part of projectJobSchedulingSolverConfig.xml file:

<scoreDirectorFactory>
  <!--<incrementalScoreCalculatorClass>org.optaplanner.examples.projectjobscheduling.solver.score.ProjectJobSchedulingIncrementalScoreCalculator</incrementalScoreCalculatorClass>-->
  <scoreDrl>org/optaplanner/examples/projectjobscheduling/solver/projectJobSchedulingScoreRules.drl</scoreDrl>
</scoreDirectorFactory>

But then I encountered compilation error:

Exception in thread "main" java.lang.IllegalStateException: There are errors in a score DRL:
Error Messages:
Message [id=1, kieBase=defaultKieBase, level=ERROR, path=org/optaplanner/examples/projectjobscheduling/solver/projectJobSchedulingScoreRules.drl, line=95, column=0
   text=Rule Compilation error The operator - is undefined for the argument type(s) Comparable]
---
Warning Messages:
---
Info Messages:

at org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildDroolsScoreDirectorFactory(ScoreDirectorFactoryConfig.java:507)
at org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.buildScoreDirectorFactory(ScoreDirectorFactoryConfig.java:331)
at org.optaplanner.core.config.solver.SolverConfig.buildSolver(SolverConfig.java:220)
at org.optaplanner.core.impl.solver.AbstractSolverFactory.buildSolver(AbstractSolverFactory.java:61)
at org.optaplanner.examples.common.app.CommonApp.createSolver(CommonApp.java:103)
at org.optaplanner.examples.common.app.CommonApp.createSolutionBusiness(CommonApp.java:97)
at org.optaplanner.examples.common.app.CommonApp.init(CommonApp.java:84)
at org.optaplanner.examples.common.app.CommonApp.init(CommonApp.java:80)
at org.optaplanner.examples.projectjobscheduling.app.ProjectJobSchedulingApp.main(ProjectJobSchedulingApp.java:34)

I did not modify the projectJobSchedulingScoreRules.drl file, but here is the file content:

/*
 * 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.projectjobscheduling.solver;
    dialect "java"

import org.optaplanner.core.api.score.buildin.bendable.BendableScoreHolder;

import org.optaplanner.examples.projectjobscheduling.domain.Allocation;
import org.optaplanner.examples.projectjobscheduling.domain.ExecutionMode;
import org.optaplanner.examples.projectjobscheduling.domain.Job;
import org.optaplanner.examples.projectjobscheduling.domain.JobType;
import org.optaplanner.examples.projectjobscheduling.domain.Project;
import org.optaplanner.examples.projectjobscheduling.domain.ResourceRequirement;
import org.optaplanner.examples.projectjobscheduling.domain.resource.Resource;
import org.optaplanner.examples.projectjobscheduling.solver.score.capacity.NonrenewableResourceCapacityTracker;
import org.optaplanner.examples.projectjobscheduling.solver.score.capacity.RenewableResourceCapacityTracker;
import org.optaplanner.examples.projectjobscheduling.solver.score.capacity.ResourceCapacityTracker;
import org.optaplanner.examples.projectjobscheduling.solver.score.drools.RenewableResourceUsedDay;

global BendableScoreHolder scoreHolder;

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

rule "nonrenewableResourceCapacity"
    when
        $resource : Resource(renewable == false, $capacity : capacity)
        accumulate(
            ResourceRequirement(resource == $resource,
                    $executionMode : executionMode,
                    $requirement : requirement)
            and Allocation(executionMode == $executionMode);
            $used : sum($requirement);
            $used > $capacity
        )
    then
        scoreHolder.addHardConstraintMatch(kcontext, 0, $capacity - $used);
end

rule "renewableResourceUsedDay"
        salience 1 // Do these rules first (optional, for performance)
    when
        ResourceRequirement(resourceRenewable == true, $executionMode : executionMode, $resource : resource)
        Allocation(executionMode == $executionMode,
                $startDate : startDate, $endDate : endDate)
    then
        for (int i = $startDate; i < $endDate; i++) {
            insertLogical(new RenewableResourceUsedDay($resource, i));
        }
end

rule "renewableResourceCapacity"
    when
        RenewableResourceUsedDay($resource : resource, $capacity : resourceCapacity, $usedDay : usedDay)
        accumulate(
            ResourceRequirement(resource == $resource,
                    $executionMode : executionMode,
                    $requirement : requirement)
            and Allocation(executionMode == $executionMode, $usedDay >= startDate, $usedDay < endDate);
            $used : sum($requirement);
            $used > $capacity
        )
    then
        scoreHolder.addHardConstraintMatch(kcontext, 0, $capacity - $used);
end

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

rule "totalProjectDelay"
    when
        Allocation(jobType == JobType.SINK, endDate != null, $endDate : endDate,
               $criticalPathEndDate : projectCriticalPathEndDate)
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 0,  $criticalPathEndDate - $endDate);
end


rule "totalMakespan"
    when
        accumulate(
            Allocation(jobType == JobType.SINK, $endDate : endDate);
            $maxProjectEndDate : max($endDate)
        )
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 1, -$maxProjectEndDate);
end

Have anyone encountered similar issue? Any clue how to fix it? Thanks in advance.

Upvotes: 0

Views: 413

Answers (2)

the.wizard
the.wizard

Reputation: 1109

As yurloc explain, the result of max() drools accumulation function was a comparable object, and that's why it can't be negated using the - operator. This answer just another alternative to yurloc answer :

rule "totalMakespan"
    when
        $maxProjectEndDate : Number() from accumulate(
            Allocation(jobType == JobType.SINK, $endDate : endDate);
            max($endDate)
        )
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 1, -$maxProjectEndDate.intValue());
end

Upvotes: 1

yurloc
yurloc

Reputation: 2358

The error message is:

The operator - is undefined for the argument type(s) Comparable

This is a result of a limitation of the max() Drools accumulator function, which is not generic and its return type is always Comparable even if you feed it with Integer argument. Therefore the type of the $maxProjectEndDate variable is Comparable (which is not a number) and so you cannot negate its value using the - operator.

As a quick fix, you can cast it to Integer before negating it in the totalMakespan rule:

scoreHolder.addSoftConstraintMatch(kcontext, 1, -((Integer) $maxProjectEndDate));

Upvotes: 3

Related Questions