3

I'm trying to implement a solution with multiple entity classes, and it fails with the following error message:

no entityClass (null) configured and because there are multiple in the entityClassSet ([class com.myspace.wla.JobA, class com.myspace.wla.JobB]), it can not be deduced automatically

This is the solver configuration:

<?xml version="1.0" encoding="UTF-8"?>
<solver xStreamId="1">
    <solutionClass>com.myspace.wla.AllocationSolution</solutionClass>
    <entityClass>com.myspace.wla.JobA</entityClass>
    <entityClass>com.myspace.wla.JobB</entityClass>
    <scoreDirectorFactory xStreamId="3"/>
    <termination xStreamId="4">
        <millisecondsSpentLimit>0</millisecondsSpentLimit>
        <secondsSpentLimit>30</secondsSpentLimit>
        <minutesSpentLimit>0</minutesSpentLimit>
        <hoursSpentLimit>0</hoursSpentLimit>
        <daysSpentLimit>0</daysSpentLimit>
    </termination>
    <constructionHeuristic xStreamId="5">
        <constructionHeuristicType>FIRST_FIT</constructionHeuristicType>
        <entitySorterManner>NONE</entitySorterManner>
    </constructionHeuristic>
    <localSearch xStreamId="6">
        <unionMoveSelector>
          <changeMoveSelector>
            <entitySelector>
              <entityClass>com.myspace.wla.JobA</entityClass>
            </entitySelector>
            <valueSelector>
                <variableName>Computer</variableName>
                <selectionOrder>SORTED</selectionOrder>
            </valueSelector>
          </changeMoveSelector>
          <changeMoveSelector>
            <entitySelector>
              <entityClass>com.myspace.wla.JobB</entityClass>
            </entitySelector>
            <valueSelector>
                <variableName>Computer</variableName>
                <selectionOrder>SORTED</selectionOrder>
            </valueSelector>
          </changeMoveSelector>
        </unionMoveSelector>
    </localSearch>
</solver>

This is the solution entity:

package com.myspace.wla;

import java.util.List;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;

@PlanningSolution
@javax.xml.bind.annotation.XmlRootElement
@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD)
public class AllocationSolution implements java.io.Serializable {

 static final long serialVersionUID = 1L;

 @javax.annotation.Generated({"org.optaplanner.workbench.screens.domaineditor.client.widgets.planner.PlannerDataObjectEditor"})
 @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(org.optaplanner.persistence.jaxb.api.score.buildin.hardsoft.HardSoftScoreJaxbXmlAdapter.class)
 @org.kie.api.definition.type.Label("Generated Planner score field")
 private org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore score;

 private java.util.List<com.myspace.wla.Computer> computerList;

 private java.util.List<com.myspace.wla.JobA> jobAList;

 private int id;

 private java.util.List<com.myspace.wla.JobB> jobBList;

 public AllocationSolution() {
 }

 @PlanningScore
 public org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore getScore() {
  return this.score;
 }

 public void setScore(
   org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore score) {
  this.score = score;
 }

 @ValueRangeProvider(id = "computerRange")
 @ProblemFactCollectionProperty
 public java.util.List<com.myspace.wla.Computer> getComputerList() {
  return this.computerList;
 }

 public void setComputerList(
   List<com.myspace.wla.Computer> computerList) {
  this.computerList = computerList;
 }

 @PlanningEntityCollectionProperty
 @ValueRangeProvider(id = "jobARange")
 public List<com.myspace.wla.JobA> getJobAList() {
  return this.jobAList;
 }

 public void setJobAList(java.util.List<com.myspace.wla.JobA> jobAList) {
  this.jobAList = jobAList;
 }

 public int getId() {
  return this.id;
 }

 public void setId(int id) {
  this.id = id;
 }

 @PlanningEntityCollectionProperty
 @ValueRangeProvider(id = "jobBRange")
 public List<com.myspace.wla.JobB> getJobBList() {
  return this.jobBList;
 }

 public void setJobBList(java.util.List<com.myspace.wla.JobB> jobBList) {
  this.jobBList = jobBList;
 }

 public AllocationSolution(
   org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore score,
   java.util.List<com.myspace.wla.Computer> computerList,
   java.util.List<com.myspace.wla.JobA> jobAList, int id,
   java.util.List<com.myspace.wla.JobB> jobBList) {
  this.score = score;
  this.computerList = computerList;
  this.jobAList = jobAList;
  this.id = id;
  this.jobBList = jobBList;
 }

}

This is the fact entity:

package com.myspace.wla;

public class Computer implements java.io.Serializable {

 static final long serialVersionUID = 1L;

 private int id;

 private int color;

 public Computer() {
 }

 public int getId() {
  return this.id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public int getColor() {
  return this.color;
 }

 public void setColor(int color) {
  this.color = color;
 }

 public Computer(int id, int color) {
  this.id = id;
  this.color = color;
 }

}

This is the first planning entity (the second one is identical with a different name):

package com.myspace.wla;

import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable;

@PlanningEntity
public class JobA implements java.io.Serializable {

 static final long serialVersionUID = 1L;

 private int id;
 private com.myspace.wla.Computer computer;

 private int color;

 public JobA() {
 }

 public int getId() {
  return this.id;
 }

 public void setId(int id) {
  this.id = id;
 }

 @PlanningVariable(valueRangeProviderRefs = {"computerRange"})
 public com.myspace.wla.Computer getComputer() {
  return this.computer;
 }

 public void setComputer(com.myspace.wla.Computer computer) {
  this.computer = computer;
 }

 public int getColor() {
  return this.color;
 }

 public void setColor(int color) {
  this.color = color;
 }

 public JobA(int id, com.myspace.wla.Computer computer, int color) {
  this.id = id;
  this.computer = computer;
  this.color = color;
 }

}

Thanks, Eliezer

yurloc
  • 2,268
  • 1
  • 15
  • 20
eliezerb
  • 47
  • 4

1 Answers1

2

The problematic part of the configuration is the <constructionHeuristic> phase. You need to use an advanced configuration, that runs one Construction Heuristic phase per each planning entity type. In your case, it should look similar to this:

<constructionHeuristic>
    <entitySorterManner>NONE</entitySorterManner>
    <valueSorterManner>NONE</valueSorterManner>
    <queuedEntityPlacer>
        <entitySelector id="jobAEntitySelector">
            <cacheType>PHASE</cacheType>
            <entityClass>com.myspace.wla.JobA</entityClass>
        </entitySelector>
        <changeMoveSelector>
            <entitySelector mimicSelectorRef="jobAEntitySelector"/>
        </changeMoveSelector>
    </queuedEntityPlacer>
</constructionHeuristic>
<constructionHeuristic>
    <entitySorterManner>NONE</entitySorterManner>
    <valueSorterManner>NONE</valueSorterManner>
    <queuedEntityPlacer>
        <entitySelector id="jobBEntitySelector">
            <cacheType>PHASE</cacheType>
            <entityClass>com.myspace.wla.JobB</entityClass>
        </entitySelector>
        <changeMoveSelector>
            <entitySelector mimicSelectorRef="jobBEntitySelector"/>
        </changeMoveSelector>
    </queuedEntityPlacer>
</constructionHeuristic>

Note that it's not possible to use constructionHeuristicType=FIRST_FIT with advanced configuration but using entitySorterManner=NONE and valueSorterManner=NONE is equivalent to FIRST_FIT.

It's also possible to leave entitySorterManner and valueSorterManner out, in which case they'll default to entitySorterManner=DECREASING_DIFFICULTY_IF_AVAILABLE and valueSorterManner=INCREASING_STRENGTH_IF_AVAILABLE.

yurloc
  • 2,268
  • 1
  • 15
  • 20
  • I tried this, but now I get the following error message: Error creating solver factory for solver: The constructionHeuristicType (FIRST_FIT) must not be configured if the entityPlacerConfig (QueuedEntityPlacerConfig(EntitySelectorConfig(class com.myspace.wla.JobA), [ChangeMoveSelectorConfig(EntitySelectorConfig(null), null)])) is explicitly configured. – eliezerb Mar 17 '19 at 07:18
  • 1
    @eliezerb You're correct. I made a mistake when testing the configuration. I have updated the answer. – yurloc Mar 22 '19 at 15:19
  • My system is currently down, so I'll check this solution later. Thanks – eliezerb Mar 26 '19 at 08:06