首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >优化算法、约束条件和Optapy评分计算配置

优化算法、约束条件和Optapy评分计算配置
EN

Stack Overflow用户
提问于 2022-02-23 13:52:18
回答 1查看 246关注 0票数 0

我在python中使用Optapy库,并使用GitHub上的学校时间表实例作为基础。关于库的配置,我有几个问题:

如何选择优化算法(例如,禁忌搜索或模拟annealing)?

  • How,做选项法,计算解决方案的分数)?我是否可以选择更改python中的分数计算类型?

  • 如何确定除硬约束或软约束外的每个约束的权重?

我看了OptaPlanner用户指南,但是我不知道如何在python上实现它。

感谢你的指导。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-23 14:29:50

OptaPy可以使用编程API进行配置。可以在optapy.config包中找到配置类。特别是,通过withPhases选择优化算法。

代码语言:javascript
运行
复制
import optapy.config
solver_config = optapy.config.solver.SolverConfig().withEntityClasses(get_class(Lesson)) \
    .withSolutionClass(get_class(TimeTable)) \
    .withConstraintProviderClass(get_class(define_constraints)) \
    .withTerminationSpentLimit(Duration.ofSeconds(30)) \
    .withPhases([
        optapy.config.constructionheuristic.ConstructionHeuristicPhaseConfig(),
        optapy.config.localsearch.LocalSearchPhaseConfig()
            .withAcceptorConfig(optapy.config.localsearch.decider.acceptor.LocalSearchAcceptorConfig()
                                .withSimulatedAnnealingStartingTemperature("0hard/0soft"))
    ])

(上述配置模拟退火)。

最近添加了@easy_score_calculator@incremental_score_calculator装饰器,它们允许您分别定义EasyScoreCalculator或IncrementalScoreCalculator。例如,(EasyScoreCalculator,最大化价值):

代码语言:javascript
运行
复制
@optapy.easy_score_calculator
def my_score_calculator(solution: Solution):
    total_score = 0
    for entity in solution.entity_list:
        total_score += 0 if entity.value is None else entity.value
    return optapy.score.SimpleScore.of(total_score)

solver_config = optapy.config.solver.SolverConfig()
termination_config = optapy.config.solver.termination.TerminationConfig()
termination_config.setBestScoreLimit('9')
solver_config.withSolutionClass(optapy.get_class(Solution)) \
    .withEntityClasses(optapy.get_class(Entity)) \
    .withEasyScoreCalculatorClass(optapy.get_class(my_score_calculator)) \
    .withTerminationConfig(termination_config)

或使用IncrementalScoreCalculator (NQueens):

代码语言:javascript
运行
复制
@optapy.incremental_score_calculator
class IncrementalScoreCalculator:
    score: int
    row_index_map: dict
    ascending_diagonal_index_map: dict
    descending_diagonal_index_map: dict

    def resetWorkingSolution(self, working_solution: Solution):
        n = working_solution.n
        self.row_index_map = dict()
        self.ascending_diagonal_index_map = dict()
        self.descending_diagonal_index_map = dict()
        for i in range(n):
            self.row_index_map[i] = list()
            self.ascending_diagonal_index_map[i] = list()
            self.descending_diagonal_index_map[i] = list()
            if i != 0:
                self.ascending_diagonal_index_map[n - 1 + i] = list()
                self.descending_diagonal_index_map[-i] = list()
        self.score = 0
        for queen in working_solution.queen_list:
            self.insert(queen)

    def beforeEntityAdded(self, entity: any):
        pass

    def afterEntityAdded(self, entity: any):
        self.insert(entity)

    def beforeVariableChanged(self, entity: any, variableName: str):
        self.retract(entity)

    def afterVariableChanged(self, entity: any, variableName: str):
        self.insert(entity)

    def beforeEntityRemoved(self, entity: any):
        self.retract(entity)

    def afterEntityRemoved(self, entity: any):
        pass

    def insert(self, queen: Queen):
        row = queen.row
        if row is not None:
            row_index = queen.row
            row_index_list = self.row_index_map[row_index]
            self.score -= len(row_index_list)
            row_index_list.append(queen)
            ascending_diagonal_index_list = self.ascending_diagonal_index_map[queen.getAscendingDiagonalIndex()]
            self.score -= len(ascending_diagonal_index_list)
            ascending_diagonal_index_list.append(queen)
            descending_diagonal_index_list = self.descending_diagonal_index_map[queen.getDescendingDiagonalIndex()]
            self.score -= len(descending_diagonal_index_list)
            descending_diagonal_index_list.append(queen)

    def retract(self, queen: Queen):
        row = queen.row
        if row is not None:
            row_index = queen.row
            row_index_list = self.row_index_map[row_index]
            row_index_list.remove(queen)
            self.score += len(row_index_list)
            ascending_diagonal_index_list = self.ascending_diagonal_index_map[queen.getAscendingDiagonalIndex()]
            ascending_diagonal_index_list.remove(queen)
            self.score += len(ascending_diagonal_index_list)
            descending_diagonal_index_list = self.descending_diagonal_index_map[queen.getDescendingDiagonalIndex()]
            descending_diagonal_index_list.remove(queen)
            self.score += len(descending_diagonal_index_list)

    def calculateScore(self) -> optapy.score.SimpleScore:
        return optapy.score.SimpleScore.of(self.score)

solver_config = optapy.config.solver.SolverConfig()
termination_config = optapy.config.solver.termination.TerminationConfig()
termination_config.setBestScoreLimit('0')
solver_config.withSolutionClass(optapy.get_class(Solution)) \
    .withEntityClasses(optapy.get_class(Queen)) \
    .withScoreDirectorFactory(optapy.config.score.director.ScoreDirectorFactoryConfig() \
                              .withIncrementalScoreCalculatorClass(optapy.get_class(IncrementalScoreCalculator))) \
    .withTerminationConfig(termination_config)

如果您所说的权重是指ConstraintConfiguration (它允许您在每个问题上定义自定义约束权重),那么它还没有通过OptaPy公开。如果您的意思是如何使约束权重更多/更少,可以将第二个参数更改为penalize/reward (如果常量),或者添加第三个参数来计算约束乘数(第二个参数将被乘以),如下所示:

代码语言:javascript
运行
复制
def undesired_day_for_employee(constraint_factory: ConstraintFactory):
    return constraint_factory.forEach(shift_class) \
        .join(availability_class, [Joiners.equal(lambda shift: shift.employee,
                                                 lambda availability: availability.employee),
                                   Joiners.equal(lambda shift: shift.start.date(),
                                                 lambda availability: availability.date)
                                   ]) \
        .filter(lambda shift, availability: availability.availability_type == AvailabilityType.UNDESIRED) \
        .penalize('Undesired day for employee', HardSoftScore.ofSoft(2),
                  lambda shift, availability: get_shift_duration_in_minutes(shift))

(此约束对员工在不想要的一天工作的每分钟进行2次软处罚)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71238147

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档