OptaPlanner配置带过滤器的子支柱PillarMove时抛出异常求解答
首先明确:OptaPlanner是支持带filterClass的PillarMove的,你遇到的异常是启用子支柱(subPillarEnabled=true)后,默认支柱选择器无法计算总数量,而过滤移动选择器的默认逻辑依赖这个计算导致的冲突。
为什么会抛出这个异常?
当开启subPillarEnabled=true时,子支柱的数量是指数级增长的(比如一个包含N个实体的支柱,子支柱数量是2^N -1),OptaPlanner的DefaultPillarSelector无法准确计算这个数量,因此直接抛出UnsupportedOperationException拒绝提供getSize()方法。
而FilteringMoveSelector(配置filterClass后自动生成的包装器)默认会调用被包装选择器的getSize()来计算bailOutSize(防止无限迭代的安全阈值),这就触发了上述异常。
解决方法
按推荐优先级排序,你有以下可行方案:
1. 手动设置FilteringMoveSelector的bailOutSize
直接给pillarChangeMoveSelector添加bailOutSize属性,指定一个合理的阈值(比如1000,可根据业务场景调整)。这样FilteringMoveSelector就不会再调用getSize(),而是用你指定的值作为迭代上限。
修改后的配置示例:
<pillarChangeMoveSelector> <pillarSelector> <entitySelector> <entityClass>com.X.Y.Z.MyEntity</entityClass> <cacheType>STEP</cacheType> </entitySelector> <subPillarEnabled>true</subPillarEnabled> <minimumSubPillarSize>1</minimumSubPillarSize> <maximumSubPillarSize>40</maximumSubPillarSize> </pillarSelector> <valueSelector variableName="variable"> </valueSelector> <filterClass>com.X.Y.Z.MyEntityPillarMoveFilter</filterClass> <!-- 添加该属性,避免调用getSize() --> <bailOutSize>1000</bailOutSize> </pillarChangeMoveSelector>
2. 禁用子支柱(备选,若业务允许)
如果业务场景可以接受不使用子支柱,把subPillarEnabled设为false,这样DefaultPillarSelector就能正常计算getSize(),异常也会消失。但你提到这个不符合业务场景,所以仅作备选。
3. 自定义PillarSelector(不推荐,复杂度高)
若必须启用子支柱且需要准确的大小计算,可以自定义PillarSelector实现类,自行处理getSize()逻辑。但这需要深入理解OptaPlanner内部机制,开发和维护成本较高,不推荐作为优先方案。
原理补充
bailOutSize的作用是:当过滤后的移动数量极大时,FilteringMoveSelector会在迭代到指定数量的有效移动后停止,避免无限循环或性能问题。手动设置该值后,它就无需依赖被包装选择器的getSize()方法,从而绕过了子支柱场景下的大小计算限制。
内容的提问来源于stack exchange,提问作者Sachin Verma

