kaiwu.core._expression 源代码

# -*- coding: utf-8 -*-
"""
模块: qubo

功能: QUBO及Ising表达式运算类
"""

import numbers
from kaiwu.core._error import KaiwuError
from kaiwu.core._constraint import Constraint


def is_zero(qubo_expr):
    """QUBO表达式为0"""
    if isinstance(qubo_expr, numbers.Number):
        return qubo_expr == 0
    return len(qubo_expr.coefficient) == 0 and qubo_expr.offset == 0


def expr_add(expr_left, expr_right, expr_result):
    """通用二次表达式相加"""
    if isinstance(expr_right, numbers.Number):
        expr_result.coefficient = expr_left.coefficient.copy()
        expr_result.offset = expr_left.offset + expr_right
    else:
        expr_result.offset = expr_left.offset + expr_right.offset
        if len(expr_right.coefficient) < len(expr_left.coefficient):
            expr_result.coefficient = expr_left.coefficient.copy()
            expr_other = expr_right
        else:
            expr_result.coefficient = expr_right.coefficient.copy()
            expr_other = expr_left

        for key in expr_other.coefficient:
            if key in expr_result.coefficient:
                expr_result.coefficient[key] += expr_other.coefficient[key]
                if expr_result.coefficient[key] == 0:
                    expr_result.coefficient.pop(key)
            else:
                expr_result.coefficient[key] = expr_other.coefficient[key]


def expr_neg(expr_origin, expr_result):
    """通用二次表达式取负"""
    for key in expr_origin.coefficient:
        expr_result.coefficient[key] = -expr_origin.coefficient[key]
    expr_result.offset = -expr_origin.offset


def _expr_dicts_mul(expr_left, expr_right, expr_result):
    for lkey in expr_left.coefficient:
        for rkey in expr_right.coefficient:
            key = list(set(lkey + rkey))
            key.sort()
            key = tuple(key)
            if key in expr_result.coefficient:
                expr_result.coefficient[key] += (
                    expr_left.coefficient[lkey] * expr_right.coefficient[rkey]
                )
                if expr_result.coefficient[key] == 0:
                    expr_result.coefficient.pop(key)
            else:
                expr_result.coefficient[key] = (
                    expr_left.coefficient[lkey] * expr_right.coefficient[rkey]
                )
        if not is_zero(expr_right.offset):
            if lkey in expr_result.coefficient:
                expr_result.coefficient[lkey] += (
                    expr_left.coefficient[lkey] * expr_right.offset
                )
            else:
                expr_result.coefficient[lkey] = (
                    expr_left.coefficient[lkey] * expr_right.offset
                )
    if not is_zero(expr_left.offset):
        for rkey in expr_right.coefficient:
            if rkey in expr_result.coefficient:
                expr_result.coefficient[rkey] += (
                    expr_right.coefficient[rkey] * expr_left.offset
                )
            else:
                expr_result.coefficient[rkey] = (
                    expr_right.coefficient[rkey] * expr_left.offset
                )
        expr_result.offset += expr_left.offset * expr_right.offset


def expr_mul(expr_left, expr_right, expr_result):
    """通用二次表达式相乘"""
    if isinstance(expr_right, numbers.Number):
        if not is_zero(expr_right):
            for lkey in expr_left.coefficient:
                expr_result.coefficient[lkey] = expr_left.coefficient[lkey] * expr_right
            expr_result.offset = expr_left.offset * expr_right
    else:
        _expr_dicts_mul(expr_left, expr_right, expr_result)


def expr_pow(expr_left, expr_right, expr_result):
    """通用二次表达式乘方,要求expr_right只能为1或者2"""
    if expr_right == 1:
        expr_result.offset = expr_left.offset
        expr_result.coefficient = expr_left.coefficient.copy()
    elif expr_right == 2:
        expr_mul(expr_left, expr_left, expr_result)
    else:
        raise KaiwuError("Items higher than quadratic.")


def _check_unit(checked_str):
    if len(checked_str) > 2:
        if checked_str[:2] == "1*":
            checked_str = checked_str[2:]
        elif checked_str[:3] == "-1*":
            checked_str = "-" + checked_str[3:]
    return checked_str


[文档] class Expression: """QUBO/Ising 通用表达式基类(提供默认二次表达式实现)""" def __init__(self, coefficient: dict = None, offset: float = 0): super().__init__() if coefficient is None: self.coefficient = {} else: self.coefficient = coefficient self.offset = offset
[文档] def clear(self) -> None: """表达式置为0""" self.coefficient = {} self.offset = 0
def __repr__(self): return self.__str__() def __str__(self): expression_str = "" for p_key in self.coefficient: p_str = "" for p_var in p_key: p_str += "*" + p_var coe = self.coefficient[p_key] if not isinstance(coe, numbers.Number) and len(coe.coefficient) > 0: coe_str = "(" + str(coe) + ")" else: coe_str = str(coe) p_str = _check_unit(coe_str + p_str) if p_str[0] == "-": expression_str += p_str else: if expression_str == "": expression_str += p_str else: expression_str += "+" + p_str if not (is_zero(self.offset) and expression_str != ""): offset_str = str(self.offset) if offset_str[0] == "-": expression_str += offset_str else: if expression_str == "": expression_str += offset_str else: expression_str += "+" + offset_str return _check_unit(expression_str) def __radd__(self, other): return self.__add__(other) def __add__(self, other): q_ret = self.__class__() expr_add(self, other, q_ret) return q_ret def __rsub__(self, other): return (-self).__add__(other) def __sub__(self, other): return self.__add__(-other) def __rmul__(self, other): return self.__mul__(other) def __mul__(self, other): q_ret = self.__class__() expr_mul(self, other, q_ret) return q_ret def __truediv__(self, other): return self.__mul__(1 / other) def __pow__(self, other): q_ret = self.__class__() expr_pow(self, other, q_ret) return q_ret def __neg__(self): q_ret = self.__class__() expr_neg(self, q_ret) return q_ret def __eq__(self, other): return Constraint(self - other, "==") def __ge__(self, other): return Constraint(self - other, ">=") def __le__(self, other): return Constraint(self - other, "<=") def __gt__(self, other): return Constraint(self - other, ">") def __lt__(self, other): return Constraint(self - other, "<")
[文档] def get_variables(self): """获取变量名集合 Returns: dict: 返回构成expression的变量集合 """ variables = set() # 放目标函数obj所包含的所有变量 for key in self.coefficient: for var_x in key: variables.add(var_x) variables = tuple(sorted(list(variables))) return dict(zip(variables, range(len(variables))))
[文档] def get_max_deltas(self): """求出每个变量翻转引起目标函数变化的上界 返回值negative_delta,positive_delta分别为该变量1->0和0->1所引起的最大变化量 """ obj_variables = list(self.get_variables().keys()) positive_delta = {var_x: 0 for var_x in obj_variables} negative_delta = {var_x: 0 for var_x in obj_variables} for var_tuple in self.coefficient.keys(): for var_x in var_tuple: # 二次项受另一个变量影响,另一个变量为0时系数不引起变化 if len(var_tuple) == 2: positive_delta[var_x] += max(self.coefficient[var_tuple], 0) # 变量0->1引起的变化量为-coefficient negative_delta[var_x] += max(-self.coefficient[var_tuple], 0) # 一次项不受另一个变量影响,变量翻转一定引起变化 elif len(var_tuple) == 1: positive_delta[var_x] += self.coefficient[var_tuple] # 变量0->1引起的变化为-coefficient,与二次项的情况一致 negative_delta[var_x] -= self.coefficient[var_tuple] return negative_delta, positive_delta
[文档] def get_average_coefficient(self): """返回coefficient的平均值""" coe_sum = 0 num_item = 0 for _, coe in self.coefficient.items(): coe_sum += abs(coe) num_item += 1 return coe_sum / num_item
[文档] def get_val(self, sol_dict): """根据结果字典将spin值带入qubo变量. Args: qubo (QUBO表达式): QUBO表达式 sol_dict (dict): 由get_sol_dict生成的结果字典。 Returns: float: 带入qubo后所得的值 Examples: >>> import kaiwu as kw >>> import numpy as np >>> a = kw.core.Binary("a") >>> b = kw.core.Binary("b") >>> c = kw.core.Binary("c") >>> d = a + 2 * b + 4 * c >>> sol_dict = {"a": 1, "b": 0, "c": 1} >>> d.get_val(sol_dict) 5 """ value = self.offset # 结果加上offset for k, val in self.coefficient.items(): # 遍历所有键值对 for var in k: # 对于每个键也遍历元组 val *= sol_dict.get(var, 0.0) value += val # 累加一项的结果 return value