最佳解决方案包含移动过滤器拒绝的实体

时间:2014-10-21 14:56:57

标签: optaplanner

我有以下内容:

事实“工作”:

public class Job {
    String name;
    long duration;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }
}

事实“机器”:

public class Machine {

    String name;
    long capacity;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getCapacity() {
        return capacity;
    }

    public void setCapacity(long capacity) {
        this.capacity = capacity;
    }
}

规划实体:

@PlanningEntity
public class Assignment {
    Job job;
    Machine machine;

    @PlanningVariable(nullable = true, valueRangeProviderRefs = { "jobRange" })
    public Job getJob() {
        return job;
    }

    public void setJob(Job job) {
        this.job = job;
    }

    @PlanningVariable(nullable = true, valueRangeProviderRefs = { "machineRange" })
    public Machine getMachine() {
        return machine;
    }

    public void setMachine(Machine machine) {
        this.machine = machine;
    }

}

解决方案:

public class Schedule implements Solution<BendableScore> {

    private BendableScore score;
    private ArrayList<Job> jobs;
    private ArrayList<Machine> machines;
    private ArrayList<Assignment> assignments = new ArrayList<>();

    private final long CAPACITY = 4;
    private final long DURATION = 1;
    private final int JOBS = 4;
    private final int MACHINES = 1;

    @Override
    public BendableScore getScore() {
        return this.score;
    }

    @Override
    public void setScore(BendableScore score) {
        this.score = score;
    }

    @Override
    public Collection<? extends Object> getProblemFacts() {

        return new ArrayList<>();
    }

    @ValueRangeProvider(id = "jobRange")
    public ArrayList<Job> getJobs() {
        return jobs;
    }

    public void setJobs(ArrayList<Job> jobs) {
        this.jobs = jobs;
    }

    @ValueRangeProvider(id = "machineRange")
    public ArrayList<Machine> getMachines() {
        return machines;
    }

    public void setMachines(ArrayList<Machine> machines) {
        this.machines = machines;
    }

    @PlanningEntityCollectionProperty
    public ArrayList<Assignment> getAssignments() {
        return assignments;
    }

    public void setAssignments(ArrayList<Assignment> assignments) {
        this.assignments = assignments;
    }
    private void addMachines() {
        machines = new ArrayList<Machine>();
        for (int i = 0; i < MACHINES; i++) {
            Machine m = new Machine();
            m.name = "Machine " + i;
            m.capacity = CAPACITY;
            machines.add(m);
        }

    }

    private void addOperations() {
        jobs = new ArrayList<>();
        for (int i = 0; i < JOBS; i++) {
            Job job = new Job();
            job.name = "Job " + i;
            job.duration = DURATION;
            jobs.add(job);
        }

    }

    private void addAssignments() {
        for (Job job : jobs) {
            for (Machine m : machines) {
                Assignment a = new Assignment();
                a.setJob(job);
                a.setMachine(m);
                assignments.add(a);
            }
        }
    }

    public static void main(String[] args) {

        final Schedule s = new Schedule();
        s.addMachines();
        s.addOperations();
        s.addAssignments();
        SolverFactory solverFactory = SolverFactory
                .createFromXmlResource("mauro/test2/domain/mauro.xml");
        final Solver solver = solverFactory.buildSolver();
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                solver.solve(s);
            }
        });
        t.start();
        try {
            t.join(12000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (t.isAlive()) {
            solver.terminateEarly();
        }
        Schedule sol = (Schedule) solver.getBestSolution();
        for (Assignment a : sol.getAssignments()) {
            if (a.getMachine() == null) {
                System.out.println("Assignment without machine");
                continue;
            }
            if (a.getJob() == null) {
                System.out.println("Assignment without job");
                continue;
            }
            Machine m = a.getMachine();
            Job job = a.getJob();
            System.out.println("Job " + job.name + " on machine " + m.name );
        }
    }
}

配置xml

<?xml version="1.0" encoding="UTF-8"?>
<solver>

    <environmentMode>FULL_ASSERT</environmentMode>
    <solutionClass>mauro.test2.domain.Schedule</solutionClass>
    <entityClass>mauro.test2.domain.Assignment</entityClass>
    <!-- Score configuration -->
    <scoreDirectorFactory>
        <scoreDefinitionType>BENDABLE</scoreDefinitionType>
        <bendableHardLevelsSize>2</bendableHardLevelsSize>
        <bendableSoftLevelsSize>2</bendableSoftLevelsSize>
        <easyScoreCalculatorClass>mauro.test2.domain.ScheduleEasyScoreCalculator</easyScoreCalculatorClass>
    </scoreDirectorFactory>

    <!-- Optimization algorithms configuration -->
    <constructionHeuristic>
        <constructionHeuristicType>CHEAPEST_INSERTION</constructionHeuristicType>
    </constructionHeuristic>
    <localSearch>
        <termination>
            <secondsSpentLimit>10</secondsSpentLimit>
            <unimprovedStepCountLimit>500</unimprovedStepCountLimit>
        </termination>
        <unionMoveSelector>
            <changeMoveSelector>
                <cacheType>PHASE</cacheType>
                <selectionOrder>SHUFFLED</selectionOrder>
                <valueSelector>
                    <variableName>machine</variableName>
                </valueSelector>
                <entitySelector>
                    <filterClass>mauro.test2.domain.MachineChangeMoveFilter</filterClass>
                </entitySelector>
            </changeMoveSelector>
            <changeMoveSelector>
                <cacheType>PHASE</cacheType>
                <selectionOrder>SHUFFLED</selectionOrder>
                <valueSelector>
                    <variableName>job</variableName>
                </valueSelector>
                <entitySelector>
                    <filterClass>mauro.test2.domain.MachineChangeMoveFilter</filterClass>
                </entitySelector>
            </changeMoveSelector>
        </unionMoveSelector>
        <acceptor>
            <lateAcceptanceSize>400</lateAcceptanceSize>
            <entityTabuSize>7</entityTabuSize>
        </acceptor>
        <forager>
            <acceptedCountLimit>100</acceptedCountLimit>
        </forager>
    </localSearch>
</solver>

ScoreCalculator:

public class ScheduleEasyScoreCalculator implements
        EasyScoreCalculator<Schedule> {

    public Score<?> calculateScore(Schedule schedule) {
        int hardScore1 = 0;
        int softScore = 0;
        HashMap<Machine, Long> loads = new HashMap<Machine, Long>();
        for (Assignment a : schedule.getAssignments()) {
            Machine m = a.getMachine();
            Job job = a.getJob();
            if (m != null && job != null) {
                if (loads.containsKey(m)) {
                    loads.put(m, loads.get(m) + job.duration);
                } else {
                    loads.put(m, job.duration);
                }
                 softScore += job.duration;
            }
        }
        for (Machine m : loads.keySet()) {
            Long load = loads.get(m);
            if (load > m.capacity) {
                hardScore1 -= (load - m.capacity);
            }
        }
        HashMap<Job, Integer> jobAssignments = new HashMap<>();
        for (Assignment a : schedule.getAssignments()) {
            if (a.getJob() != null) {
                if (jobAssignments.containsKey(a.getJob())) {
                    jobAssignments.put(a.getJob(),
                            jobAssignments.get(a.getJob()) + 1);
                } else {
                    jobAssignments.put(a.getJob(), 1);
                }
            }
        }
        int hardScore2 = 0;
        for (Job job : jobAssignments.keySet()) {
            if (jobAssignments.get(job) > 1) {
                 hardScore2 -= jobAssignments.get(job);
            }
        }
        return BendableScore.valueOf(new int[] { hardScore1, hardScore2 },
                new int[] { 0, softScore });

    }
}

MoveFilter:

public class MachineChangeMoveFilter implements SelectionFilter<Assignment> {

    public boolean accept(ScoreDirector scoreDirector, ChangeMove selection) {
        return true;
    }

    @Override
    public boolean accept(ScoreDirector scoreDirector, Assignment selection) {
        if (selection.getMachine() != null && selection.getJob() != null
                && !selection.getJob().name.contains("1")) {
            System.out.println("assignment " + selection.getMachine().getName()
                    + " " + selection.getJob().getName() + " accepted");
            return true;
        }
        System.out.println("assignment " + selection.getMachine().getName()
                + " " + selection.getJob().getName() + " NOT accepted");
        return false;
    }

}

如果我运行求解器,我会得到“作业1”的作业。 当然,我可以从问题事实中删除“作业1”,但在实际情况下,我不接受具有某些阴影变量值的作业。 怎么了? 感谢

莫罗

1 个答案:

答案 0 :(得分:0)

您的实体类Assignment只有计划变量字段,这是非常奇怪的(可能是一个错误)。

@PlanningEntity
public class Assignment {
    Job job; // @PlanningVariable - Probably should NOT be one
    Machine machine; // @PlanningVariable
}

通常,总是(至少)1个字段唯一地定义实体和1个(或更多)规划变量。请参阅optaplanner示例,例如MachineReassignment。