kaiwu.core._penalty_method_constraint 源代码

# -*- coding: utf-8 -*-
"""
模块: core.PMConstraint

功能: 生成基于penalty method的约束项
"""
import logging
from kaiwu.core._binary_expression import Integer
from kaiwu.core._get_val import get_val
from kaiwu.core._constraint import Constraint

logger = logging.getLogger(__name__)


[文档] class PenaltyMethodConstraint: """有约束转无约束的penalty method方法 Args: expr (Expression): 编译后的约束项表达式 penalty (float): 约束项惩罚系数 """ def __init__(self, expr, penalty=1, parent_model=None): self.constraint_expr = expr self.previous_penalty = 1 self.penalty = penalty self.pre_constr_val = 0 self.current_value = 0 self._parent_model = parent_model
[文档] @classmethod def from_constraint_definition(cls, name, constraint: Constraint, parent_model): """Prepare QUBO expression for the given constraint, automatically determining slack variables if needed. Args: name: Name of the constraint. constraint: The relation constraint to process. parent_model: the model it belongs to. """ if constraint.relation is None: # 非表达式约束直接平方处理 expr = constraint.left_operand elif constraint.relation == "==": expr = (constraint.left_operand) ** 2 else: # 处理不等式约束的方向 diff_qubo = _adjust_inequality_direction(constraint) if constraint.slack_var_expr is not None: # 如果用户提供了松弛变量表达式,直接使用 slack_expr = constraint.slack_var_expr else: slack_expr = _create_slack_variable( name, diff_qubo, constraint.relation ) # 构建最终的约束表达式(等式平方形式) expr = diff_qubo + slack_expr expr = expr**2 logger.debug("Constraint expression: %s", expr) return PenaltyMethodConstraint(expr, constraint.default_penalty, parent_model)
[文档] def set_penalty(self, penalty): """设置惩罚系数""" if penalty is None: penalty = 1 self.previous_penalty = self.penalty self.penalty = penalty if self._parent_model: self._parent_model.invalidate_made_state() logger.debug( "Penalty: %s Constraint expression: %s", self.penalty, self.constraint_expr )
[文档] def penalize_more(self): """增加惩罚系数""" self.set_penalty(self.penalty * 2)
[文档] def penalize_less(self): """降低惩罚系数""" if self.previous_penalty is None: self.previous_penalty = 0 self.set_penalty((self.previous_penalty + self.penalty) / 2)
def __str__(self): return f"Constraint(penalty={self.penalty}, constraint_expr={self.constraint_expr})" def __repr__(self) -> str: return self.__str__()
[文档] def is_satisfied(self, solution_dict): """验证约束满足情况""" self.current_value = float(get_val(self.constraint_expr, solution_dict)) return abs(self.current_value) < 1e-5
def _find_min_interval(diff_qubo): """找到系数间的最小正间隔""" coefficients = sorted(diff_qubo.coefficient.values()) # 添加0以处理边界情况 coefficients.append(0) # 计算相邻系数的最小正差值 min_diff = min( abs(coefficients[i + 1] - coefficients[i]) for i in range(len(coefficients) - 1) if coefficients[i + 1] != coefficients[i] ) return min_diff def _calculate_slack_range(diff_qubo): """计算松弛变量的最大取值范围""" # 累加所有负系数(考虑最坏情况) negative_sum = sum(-v for v in diff_qubo.coefficient.values() if v < 0) # 计算初始范围并确保非负 slack_range = max(0, negative_sum - diff_qubo.offset) return slack_range def _create_slack_variable(name, diff_qubo, relation): """自动创建松弛变量表达式""" # 确定松弛变量的最小值 slack_min = 0 if relation in [">=", "<="] else 1 # 计算松弛变量的取值范围 slack_range = _calculate_slack_range(diff_qubo) if slack_range == 0: raise ValueError("Slack range cannot be zero for non-equality constraint") # 确定离散化精度 min_diff = _find_min_interval(diff_qubo) precision_steps = int(round(slack_range / min_diff)) # 创建整数变量并线性缩放 slack_var = Integer(f"_slack_{name}", slack_min, precision_steps + slack_min) return slack_var * (slack_range / precision_steps) def _adjust_inequality_direction(constraint): """根据不等式方向调整QUBO表达式符号""" if constraint.relation in [">", ">="]: return -constraint.left_operand return constraint.left_operand