"""
基础模块 Optimizer base class
"""
import logging
import abc
import numpy as np
from kaiwu.core._get_val import get_sol_dict
from kaiwu.core._model_converter import qubo_model_to_ising_model
logger = logging.getLogger(__name__)
[文档]
def get_sorted_solutions(
matrix, solutions, bias=0.0, negtail_ff=True, sort_solutions=True
):
"""最优解采样.
Args:
matrix (np.ndarray): Ising 矩阵.
solutions (np.ndarray): 变量配置.
bias (float): 常数项.
negtail_ff (bool): negtail_flip flag 负尾翻转, Ising模型线性项二次化的矩阵需要负尾翻转.
Returns:
output (np.ndarray, np.ndarray): 按能量排序的变量配置, 能量.
Examples:
>>> import numpy as np
>>> import kaiwu as kw
>>> matrix = -np.array([[ 0. , 1. , 0. , 1. , 1. ],
... [ 1. , 0. , 0. , 1., 1. ],
... [ 0. , 0. , 0. , 1., 1. ],
... [ 1. , 1., 1. , 0. , 1. ],
... [ 1. , 1., 1. , 1. , 0. ]])
>>> solutions = np.array([[ 1, -1, 1, -1, -1],
... [-1, -1, 1, -1, -1],
... [-1, -1, -1, 1, 1],
... [ 1, 1, 1, -1, -1],
... [ 1, 1, 1, -1, -1],
... [ 1, 1, 1, -1, -1]])
>>> kw.core.get_sorted_solutions(matrix, solutions, 0)
(array([[-1, -1, -1, 1, 1],
[-1, -1, -1, 1, 1],
[-1, -1, -1, 1, 1],
[-1, -1, -1, 1, 1],
[-1, 1, -1, 1, 1],
[ 1, 1, -1, 1, 1]]), array([-8., -8., -8., -8., -4., 8.]))
"""
hamilton = -np.einsum("ij,ij->i", (solutions.dot(matrix)), solutions) + bias
if sort_solutions:
index = np.argsort(hamilton)
solutions = solutions[index]
hamilton = hamilton[index]
if negtail_ff:
solutions = solutions * solutions[:, [-1]]
return solutions, hamilton
[文档]
class IsingSolver:
"""
Ising求解器基类
"""
def __init__(self):
self.matrix = None
self._hamiltonian = None
[文档]
def set_matrix(self, ising_matrix):
"""设置矩阵并更新相关内容"""
if ising_matrix is not None:
self.matrix = ising_matrix
self.on_matrix_change()
logger.debug("Matrix set in the optimizer.")
if self.matrix is None:
raise ValueError("Ising matrix must be set")
[文档]
def on_matrix_change(self):
"""
更新矩阵相关信息, 继承IsingSolver时可以实现。当处理的ising矩阵发生变化时,这个函数的实现会被调用,从而有机会做相应动作
"""
logger.debug(
"Method on_matrix_change is not implemented in the class inherited from IsingSolver!"
)
def _solve(self, ising_matrix=None):
"求解逻辑"
raise NotImplementedError
[文档]
def solve(self, ising_matrix=None, negtail_flip=True, sort_solutions=False):
"""求解Ising矩阵
Args:
ising_matrix (np.ndarray): Ising矩阵
negtail_flip (bool): 是否进行负尾翻转
sort_solutions (bool): 是否对解进行排序
Returns:
output (np.ndarray): 解向量
"""
self.set_matrix(ising_matrix)
solutions = self._solve(self.matrix)
if solutions is None:
return None
solutions, self._hamiltonian = get_sorted_solutions(
self.matrix, solutions, 0, negtail_flip, sort_solutions
)
return solutions
[文档]
def get_hamiltonian(self):
"""
Returns:
hamiltonian (np.ndarray): 当前解的哈密顿量值
"""
return self._hamiltonian
class QuboSolverMeta(abc.ABCMeta):
"""Solver自定义元类"""
def __new__(mcs, name, bases, namespace, **kwargs):
if name != "Base" and "solve_qubo" in namespace:
original_execute = namespace["solve_qubo"]
def wrapped_execute(self, *args, **kwargs):
result = original_execute(self, *args, **kwargs)
if result[0] is None:
logger.warning("No solution found!")
return result
namespace["solve_qubo"] = wrapped_execute
return super().__new__(mcs, name, bases, namespace, **kwargs)
[文档]
class QuboSolver(metaclass=QuboSolverMeta):
"""Solver基类
Args:
optimizer (IsingSolver): Ising求解器
"""
def _to_ising_matrix(self, qubo_model):
ising_model = qubo_model_to_ising_model(qubo_model)
ising_mat = ising_model.get_matrix()
bias = ising_model.get_bias()
vars_dict = ising_model.get_variables()
return ising_mat, bias, vars_dict
[文档]
def solve_qubo(self, qubo_model):
"""求解QUBO模型
Args:
qubo_model (QuboModel): QUBO模型
sort_solutions (bool): 是否对解进行排序
Returns:
tuple: QUBO解与哈密顿量
dict: 解字典
float: 当前解的哈密顿量值
"""
if isinstance(self, IsingSolver):
ising_mat, bias, vars_dict = self._to_ising_matrix(qubo_model)
output = self.solve(ising_mat)
if output is None:
return None, None
solutions, hamiltons = get_sorted_solutions(
ising_mat, output, 0, negtail_ff=True, sort_solutions=True
)
solution_dict = get_sol_dict(
solutions[0][:-1] * solutions[0][-1], vars_dict
)
return solution_dict, hamiltons[0] + bias
raise NotImplementedError