kaiwu.core._matrix 源代码

# -*- coding: utf-8 -*-
"""
时间: 2021-07-07
作者: wangyong@boseq.com
"""
from typing import Union, Tuple, List
import numpy as np
from kaiwu.core._binary_expression import quicksum
from kaiwu.core._expression import Expression
from kaiwu.core._get_val import get_val


[文档] def dot(mat_left, mat_right): """矩阵乘法 Args: mat_left (numpy.array): 矩阵1 mat_right (numpy.array): 矩阵2 Raises: ValueError: 两个输入都必须是np.ndarray ValueError: 两个输入的维度必须匹配 Returns: np.ndarray: 乘积矩阵 """ # 检查输入是否为NumPy数组 if not isinstance(mat_left, np.ndarray) or not isinstance(mat_right, np.ndarray): raise ValueError("Both inputs must be NumPy arrays") left_is_vector = len(mat_left.shape) == 1 right_is_vector = len(mat_right.shape) == 1 # 为了方便使用矩阵运算的写法进行运算,根据位置把向量补全成为矩阵 if left_is_vector: mat_left = mat_left[np.newaxis, :] if right_is_vector: mat_right = mat_right[:, np.newaxis] mat_right = mat_right.swapaxes(-1, -2) # 获取数组的形状 mat_left_shape = mat_left.shape mat_right_shape = mat_right.shape # 确保两个输入的最后一个维度相同,或者其中一个是1 if mat_left_shape[-1] != mat_right_shape[-1]: raise ValueError( "The last dimension of A must be equal to the" " second last dimension of B for dot product" ) # 初始化结果数组 result_shape = mat_left_shape[:-1] + mat_right_shape[:-1] result = BinaryExpressionNDArray(result_shape, dtype=Expression) # 使用嵌套循环和np.sum来实现多维点积 for a_idx in np.ndindex(mat_left_shape[:-1]): for b_idx in np.ndindex(mat_right_shape[:-1]): result[a_idx + b_idx] = quicksum( (mat_left[a_idx] * mat_right[b_idx]).tolist() ) if right_is_vector: result = result.squeeze(-1) if left_is_vector: result = result[0] return result
def _is_less(qubo_left, qubo_right): return qubo_left < qubo_right def _is_less_equal(qubo_left, qubo_right): return qubo_left <= qubo_right def _is_greater(qubo_left, qubo_right): return qubo_left > qubo_right def _is_greater_equal(qubo_left, qubo_right): return qubo_left >= qubo_right def _is_equal(qubo_left, qubo_right): return qubo_left == qubo_right
[文档] class BinaryExpressionNDArray(np.ndarray): """基于 np.ndarray 的QUBO容器. 该容器支持各种 numpy 原生的向量化运算 """ is_array_less = np.vectorize(_is_less) is_array_less_equal = np.vectorize(_is_less_equal) is_array_greater = np.vectorize(_is_greater) is_array_greater_equal = np.vectorize(_is_greater_equal) is_array_equal = np.vectorize(_is_equal) def __lt__(self, other): return BinaryExpressionNDArray.is_array_less(self, other) def __le__(self, other): return BinaryExpressionNDArray.is_array_less_equal(self, other) def __gt__(self, other): return BinaryExpressionNDArray.is_array_greater(self, other) def __ge__(self, other): return BinaryExpressionNDArray.is_array_greater_equal(self, other) def __eq__(self, other): return BinaryExpressionNDArray.is_array_equal(self, other) def __matmul__(self, other): return self.dot(other)
[文档] def dot(self, b, out=None): """使用quicksum的矩阵乘法 Args: b (BinaryExpressionNDArray): 另一个矩阵 out:可选输出数组,用于存储结果。需与预期输出形状一致。 Returns: BinaryExpressionNDArray: 乘积 """ if out is not None: idx = (slice(None),) * len(out.shape) out[idx] = dot(self, b) return out return dot(self, b)
# pylint: disable=W0613
[文档] def sum( self, axis=None, dtype=None, out=None, keepdims=False, initial=0, where=True ): """使用quicksum的求和方法 Args: axis:指定求和的轴(维度)。默认为 None,表示对所有元素求和;若为整数或元组,则沿指定轴求和。 dtype:指定输出数据类型。若未提供,则默认使用输入数组的 dtype,但整数类型可能提升为平台整数精度。暂不支持。 out:可选输出数组,用于存储结果。需与预期输出形状一致。 keepdims:布尔值。若为 True,则保留被求和的轴作为长度为1的维度。暂不支持 。 initial:求和的初始值(标量),默认为0。暂不支持。 where:布尔数组,指定哪些元素参与求和(NumPy 1.20+支持)。暂不支持。 Returns: BinaryExpressionNDArray: 乘积 """ # 检查输入是否为NumPy数组 if not isinstance(self, np.ndarray): raise ValueError("Input must be a NumPy array") # 如果没有指定轴,则对整个数组求和 if axis is None: # 将数组展平成一个列表并使用quicksum求和 return quicksum(self.flatten().tolist()) # 指定轴的情况 if axis < 0: axis += self.ndim # 转换负轴索引为正轴索引 # 初始化结果数组 new_shape = self.shape[:axis] + self.shape[axis + 1 :] result = BinaryExpressionNDArray(new_shape, dtype=Expression) # 对指定轴进行求和 for idx in np.ndindex(new_shape): # 获取当前轴的子数组 sub_array = self[idx[:axis] + (slice(None),) + idx[axis:]] result[idx] = quicksum(sub_array.flatten().tolist()) if out is not None: idx = (slice(None),) * len(out.shape) out[idx] = result return out if not result.shape: result = result.tolist() return result
[文档] def get_val(self, sol_dict): """根据结果字典将spin值带入qubo数组变量. Args: array (QUBOArray): QUBO数组 sol_dict (dict): 由get_sol_dict生成的结果字典。 Returns: np.ndarray: 带入qubo数组后所得的值数组 Examples: >>> import kaiwu as kw >>> import numpy as np >>> x = kw.core.ndarray((2, 2), "x", kw.core.Binary) >>> y = x.sum() >>> y_vars = y.get_variables() >>> s = np.array([1, -1, 1, -1]) >>> sol_dict = kw.core.get_sol_dict(s, y_vars) >>> x.get_val(sol_dict) array([[1., 0.], [1., 0.]]) """ val_array = np.zeros(self.shape) # 创建与qubo数组相同形状的空数组 for index in np.ndindex(self.shape): # 遍历数组的所有下标 val_array[index] = get_val(self[index], sol_dict) # 每个下标都调用get_val return val_array
[文档] def zeros(shape) -> BinaryExpressionNDArray: """创建一个与输入数组形状相同的零数组。 Args: shape (tuple): 矩阵维度 Returns: QUBOArray: 形状相同但所有元素为 0 的数组。 Examples: >>> import kaiwu as kw >>> Z = zeros((2, 3)) >>> Z BinaryExpressionNDArray([[0, 0, 0], [0, 0, 0]], dtype=object) """ # 创建一个形状相同的数组,直接填充 0 zeros_arr = BinaryExpressionNDArray(shape, dtype=Expression) zeros_arr.fill(0) return zeros_arr
[文档] def ndarray( shape: Union[int, Tuple[int, ...], List[int]], name, var_func, var_func_param=None ): """基于 np.ndarray 的QUBO容器. 该容器支持各种 numpy 原生的向量化运算 Args: shape (Union[int, Tuple[int, ...]]): 形状 name (str): 生成的变量的标识符. var_func (class for func): 用于生成元素的方法或类. 第一个参数必须是name var_func_param (tuple): var_func除了name以外的参数 Returns: np.ndarray: 多维容器. Examples: >>> import numpy as np >>> import kaiwu as kw >>> A = kw.core.ndarray((2,3,4), "A", kw.core.Binary) >>> A BinaryExpressionNDArray([[[A[0][0][0], A[0][0][1], A[0][0][2], A[0][0][3]], [A[0][1][0], A[0][1][1], A[0][1][2], A[0][1][3]], [A[0][2][0], A[0][2][1], A[0][2][2], A[0][2][3]]], <BLANKLINE> [[A[1][0][0], A[1][0][1], A[1][0][2], A[1][0][3]], [A[1][1][0], A[1][1][1], A[1][1][2], A[1][1][3]], [A[1][2][0], A[1][2][1], A[1][2][2], A[1][2][3]]]], dtype=object) >>> A[1,2] BinaryExpressionNDArray([A[1][2][0], A[1][2][1], A[1][2][2], A[1][2][3]], dtype=object) >>> A[:, [0,2]] BinaryExpressionNDArray([[[A[0][0][0], A[0][0][1], A[0][0][2], A[0][0][3]], [A[0][2][0], A[0][2][1], A[0][2][2], A[0][2][3]]], <BLANKLINE> [[A[1][0][0], A[1][0][1], A[1][0][2], A[1][0][3]], [A[1][2][0], A[1][2][1], A[1][2][2], A[1][2][3]]]], dtype=object) >>> B = kw.core.ndarray(3, "B", kw.core.Binary) >>> B BinaryExpressionNDArray([B[0], B[1], B[2]], dtype=object) >>> C = kw.core.ndarray([3,3], "C", kw.core.Binary) >>> C BinaryExpressionNDArray([[C[0][0], C[0][1], C[0][2]], [C[1][0], C[1][1], C[1][2]], [C[2][0], C[2][1], C[2][2]]], dtype=object) >>> D = 2 * B.dot(C) + 2 >>> str(D[0]) '2*B[0]*C[0][0]+2*B[1]*C[1][0]+2*B[2]*C[2][0]+2' >>> E = B.sum() >>> str(E) 'B[0]+B[1]+B[2]' >>> F = np.diag(C) >>> F BinaryExpressionNDArray([C[0][0], C[1][1], C[2][2]], dtype=object) """ if isinstance(shape, int): shape = (shape,) if isinstance(shape, list): shape = tuple(shape) if 0 in shape: raise ValueError("The argument shape cannot contain 0.") if var_func_param is None: var_func_param = () arr = BinaryExpressionNDArray(shape, dtype=Expression) for idx in np.ndindex(shape): item_str = name for i, idx_i in enumerate(idx): len_str_k = len(str(shape[i] - 1)) item_str += f"[{str(idx_i).zfill(len_str_k)}]" arr[idx] = var_func(item_str, *var_func_param) return arr
if __name__ == "__main__": import doctest doctest.testmod()