From 331692a161376dc1a2116e7d6c34bff03c63b35e Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Sat, 18 May 2024 16:41:37 +0300 Subject: [PATCH] Migrate from python-graphblas to pygraphblas --- .../abstract_all_pairs_cfl_reachability.py | 13 +-- cfpq_cli/run_all_pairs_cflr.py | 2 +- .../kotgll_all_pairs_cflr_tool_runner.py | 3 +- .../abstract_optimized_matrix_decorator.py | 6 +- cfpq_matrix/block/block_matrix.py | 9 ++- cfpq_matrix/block/block_matrix_space.py | 14 ++-- cfpq_matrix/block/block_matrix_space_impl.py | 81 ++++++++++++------- cfpq_matrix/empty_optimized_matrix.py | 11 +-- cfpq_matrix/format_optimized_matrix.py | 16 ++-- cfpq_matrix/lazy_add_optimized_matrix.py | 20 ++--- cfpq_matrix/matrix_to_optimized_adapter.py | 19 ++--- cfpq_matrix/matrix_utils.py | 26 +++--- cfpq_matrix/optimized_matrix.py | 21 ++--- cfpq_matrix/subtractable_semiring.py | 4 +- cfpq_model/label_decomposed_graph.py | 37 ++++----- 15 files changed, 155 insertions(+), 127 deletions(-) diff --git a/cfpq_algo/all_pairs/matrix/abstract_all_pairs_cfl_reachability.py b/cfpq_algo/all_pairs/matrix/abstract_all_pairs_cfl_reachability.py index 651bd32..9f20694 100644 --- a/cfpq_algo/all_pairs/matrix/abstract_all_pairs_cfl_reachability.py +++ b/cfpq_algo/all_pairs/matrix/abstract_all_pairs_cfl_reachability.py @@ -1,9 +1,10 @@ from abc import ABC, abstractmethod from typing import List -import graphblas -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Semiring, Monoid +import pygraphblas.types +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring from cfpq_algo.all_pairs.all_pairs_cfl_reachability_algo import AllPairsCflReachabilityAlgoInstance from cfpq_algo.setting.algo_setting import AlgoSetting @@ -22,7 +23,7 @@ def __init__( settings: List[AlgoSetting], algebraic_structure: SubtractableSemiring = SubtractableSemiring( one=True, - semiring=graphblas.semiring.any_pair, + semiring=pygraphblas.types.BOOL.ANY_PAIR, sub_op=complimentary_mask ) ): @@ -39,8 +40,8 @@ def semiring(self) -> Semiring: return self.algebraic_structure.semiring @property - def monoid(self) -> Monoid: - return self.semiring.monoid + def monoid(self) -> BinaryOp: + return getattr(self.semiring.ztype, self.semiring.pls) def solve(self) -> Matrix: self.add_epsilon_edges() diff --git a/cfpq_cli/run_all_pairs_cflr.py b/cfpq_cli/run_all_pairs_cflr.py index 4bdfc22..378ace2 100644 --- a/cfpq_cli/run_all_pairs_cflr.py +++ b/cfpq_cli/run_all_pairs_cflr.py @@ -39,7 +39,7 @@ def run_all_pairs_cflr( if out_dir != "" and not os.path.exists(out_dir): os.makedirs(out_dir) with open(out_path, 'w', encoding="utf-8") as out_file: - for (source, target) in res: + for (source, target, _) in res: out_file.write(f"{source}\t{target}\n") except TimeoutException: print("AnalysisTime\tNaN") diff --git a/cfpq_eval/runners/kotgll_all_pairs_cflr_tool_runner.py b/cfpq_eval/runners/kotgll_all_pairs_cflr_tool_runner.py index 9fae028..999bada 100644 --- a/cfpq_eval/runners/kotgll_all_pairs_cflr_tool_runner.py +++ b/cfpq_eval/runners/kotgll_all_pairs_cflr_tool_runner.py @@ -92,7 +92,8 @@ def _write_kotgll_graph(graph: LabelDecomposedGraph, graph_path: Path) -> None: with open(graph_path, 'w', encoding="utf-8") as output_file: for symbol, matrix in graph.matrices.items(): edge_label = symbol.label - (rows, columns, _) = matrix.to_coo() + rows = matrix.npI + columns = matrix.npJ edges_df = pd.DataFrame({ 'source': rows, 'destination': columns, diff --git a/cfpq_matrix/abstract_optimized_matrix_decorator.py b/cfpq_matrix/abstract_optimized_matrix_decorator.py index dfb4c81..1c78af3 100644 --- a/cfpq_matrix/abstract_optimized_matrix_decorator.py +++ b/cfpq_matrix/abstract_optimized_matrix_decorator.py @@ -1,8 +1,8 @@ from abc import ABC, abstractmethod from typing import Tuple -from graphblas.core.dtypes import DataType -from graphblas.core.matrix import Matrix +from pygraphblas import Matrix +from pygraphblas.types import Type from cfpq_matrix.optimized_matrix import OptimizedMatrix, MatrixFormat @@ -26,7 +26,7 @@ def format(self) -> MatrixFormat: return self.base.format @property - def dtype(self) -> DataType: + def dtype(self) -> Type: return self.base.dtype def to_unoptimized(self) -> Matrix: diff --git a/cfpq_matrix/block/block_matrix.py b/cfpq_matrix/block/block_matrix.py index 11a42a8..201322a 100644 --- a/cfpq_matrix/block/block_matrix.py +++ b/cfpq_matrix/block/block_matrix.py @@ -1,7 +1,8 @@ from abc import ABC -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Monoid, Semiring +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring from cfpq_matrix.abstract_optimized_matrix_decorator import AbstractOptimizedMatrixDecorator from cfpq_matrix.optimized_matrix import OptimizedMatrix @@ -47,7 +48,7 @@ def rsub(self, other: Matrix, op: SubOp) -> Matrix: assert self.block_matrix_space.is_single_cell(other.shape) return self.base.rsub(other, op) - def iadd(self, other: Matrix, op: Monoid): + def iadd(self, other: Matrix, op: BinaryOp): self.base.iadd(self.block_matrix_space.reduce_hyper_vector_or_cell(other, op), op) def __sizeof__(self): @@ -107,7 +108,7 @@ def rsub(self, other: Matrix, op: SubOp) -> Matrix: other_shape = self.block_matrix_space.get_block_matrix_orientation(other.shape) return self.matrices[other_shape].rsub(other, op) - def iadd(self, other: Matrix, op: Monoid): + def iadd(self, other: Matrix, op: BinaryOp): if self.block_matrix_space.is_single_cell(other.shape): other = self.block_matrix_space.repeat_into_hyper_column(other) for (orientation, m) in self.matrices.items(): diff --git a/cfpq_matrix/block/block_matrix_space.py b/cfpq_matrix/block/block_matrix_space.py index 883ddf7..c657f51 100644 --- a/cfpq_matrix/block/block_matrix_space.py +++ b/cfpq_matrix/block/block_matrix_space.py @@ -2,9 +2,9 @@ from enum import Enum from typing import Tuple, List -from graphblas.core.dtypes import DataType -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Monoid +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.types import Type from cfpq_matrix.optimized_matrix import OptimizedMatrix @@ -60,7 +60,7 @@ def get_block_matrix_orientation(self, matrix_shape: Tuple[int, int]) -> BlockMa pass @abstractmethod - def reduce_hyper_vector_or_cell(self, hyper_vector_or_cell: Matrix, op: Monoid) -> Matrix: + def reduce_hyper_vector_or_cell(self, hyper_vector_or_cell: Matrix, op: BinaryOp) -> Matrix: """ If `hyper_vector_or_cell` is a hyper vector, then sum of its blocks is returned. If `hyper_vector_or_cell` is a cell, then underlying matrix is return. @@ -75,14 +75,14 @@ def to_block_diag_matrix(self, hyper_vector: Matrix) -> Matrix: pass @abstractmethod - def create_hyper_vector(self, typ: DataType, orientation: BlockMatrixOrientation) -> Matrix: + def create_hyper_vector(self, typ: Type, orientation: BlockMatrixOrientation) -> Matrix: pass @abstractmethod - def create_cell(self, typ: DataType) -> Matrix: + def create_cell(self, typ: Type) -> Matrix: pass - def create_space_element(self, typ: DataType, is_vector: bool) -> Matrix: + def create_space_element(self, typ: Type, is_vector: bool) -> Matrix: return ( self.create_hyper_vector(typ, BlockMatrixOrientation.VERTICAL) if is_vector diff --git a/cfpq_matrix/block/block_matrix_space_impl.py b/cfpq_matrix/block/block_matrix_space_impl.py index df9226b..958f6ed 100644 --- a/cfpq_matrix/block/block_matrix_space_impl.py +++ b/cfpq_matrix/block/block_matrix_space_impl.py @@ -1,9 +1,7 @@ from typing import Tuple, List -import graphblas.ss -from graphblas.core.matrix import Matrix -import graphblas.monoid -from graphblas.core.operator import Monoid +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp from cfpq_matrix.optimized_matrix import OptimizedMatrix from cfpq_matrix.block.block_matrix import BlockMatrix, CellBlockMatrix, VectorBlockMatrix @@ -36,31 +34,39 @@ def get_block_matrix_orientation(self, matrix_shape: Tuple[int, int]) -> BlockMa (self.n, self.n * self.block_count): BlockMatrixOrientation.HORIZONTAL }[matrix_shape] - def reduce_hyper_vector_or_cell(self, hyper_vector_or_cell: Matrix, op: Monoid) -> Matrix: + def reduce_hyper_vector_or_cell(self, hyper_vector_or_cell: Matrix, op: BinaryOp) -> Matrix: if self.is_single_cell(hyper_vector_or_cell.shape): return hyper_vector_or_cell + if hyper_vector_or_cell.nvals == 0: + return self.create_cell(hyper_vector_or_cell.type) input_orientation = self.get_block_matrix_orientation(hyper_vector_or_cell.shape) - (rows, columns, values) = hyper_vector_or_cell.to_coo() + rows = hyper_vector_or_cell.npI + columns = hyper_vector_or_cell.npJ + values = hyper_vector_or_cell.npV if input_orientation == BlockMatrixOrientation.VERTICAL: rows = rows % self.n elif input_orientation == BlockMatrixOrientation.HORIZONTAL: columns = columns % self.n else: assert False - return Matrix.from_coo( - rows, - columns, - values, + return Matrix.from_lists( + rows.tolist(), + columns.tolist(), + values.tolist(), nrows=self.n, ncols=self.n, - dup_op=op + typ=hyper_vector_or_cell.type, ) def hyper_rotate(self, hyper_vector: Matrix, orientation: BlockMatrixOrientation) -> Matrix: input_orientation = self.get_block_matrix_orientation(hyper_vector.shape) if input_orientation == orientation: return hyper_vector - (rows, columns, values) = hyper_vector.to_coo() + if hyper_vector.nvals == 0: + return self.create_hyper_vector(hyper_vector.type, orientation) + rows = hyper_vector.npI + columns = hyper_vector.npJ + values = hyper_vector.npV if orientation == BlockMatrixOrientation.VERTICAL: rows = rows + (columns // self.n * self.n) columns = columns % self.n @@ -69,18 +75,22 @@ def hyper_rotate(self, hyper_vector: Matrix, orientation: BlockMatrixOrientation rows = rows % self.n else: assert False - - return Matrix.from_coo( - rows, - columns, - values, + return Matrix.from_lists( + rows.tolist(), + columns.tolist(), + values.tolist(), nrows=hyper_vector.ncols, - ncols=hyper_vector.nrows + ncols=hyper_vector.nrows, + typ=hyper_vector.type, ) def to_block_diag_matrix(self, hyper_vector: Matrix) -> Matrix: + if hyper_vector.nvals == 0: + return Matrix.sparse(hyper_vector.type, self.n * self.block_count, self.n * self.block_count) input_orientation = self.get_block_matrix_orientation(hyper_vector.shape) - (rows, columns, values) = hyper_vector.to_coo() + rows = hyper_vector.npI + columns = hyper_vector.npJ + values = hyper_vector.npV if input_orientation == BlockMatrixOrientation.VERTICAL: columns = columns + (rows // self.n * self.n) elif input_orientation == BlockMatrixOrientation.HORIZONTAL: @@ -88,10 +98,13 @@ def to_block_diag_matrix(self, hyper_vector: Matrix) -> Matrix: else: assert False - return Matrix.from_coo( - rows, columns, values, + return Matrix.from_lists( + rows.tolist(), + columns.tolist(), + values.tolist(), nrows=self.n * self.block_count, - ncols=self.n * self.block_count + ncols=self.n * self.block_count, + typ=hyper_vector.type, ) def create_hyper_vector(self, typ, orientation: BlockMatrixOrientation) -> Matrix: @@ -99,15 +112,17 @@ def create_hyper_vector(self, typ, orientation: BlockMatrixOrientation) -> Matri BlockMatrixOrientation.VERTICAL: (self.n * self.block_count, self.n), BlockMatrixOrientation.HORIZONTAL: (self.n, self.n * self.block_count) }[orientation] - return Matrix(dtype=typ, nrows=shape[0], ncols=shape[1]) + return Matrix.sparse(typ=typ, nrows=shape[0], ncols=shape[1]) def create_cell(self, typ) -> Matrix: - return Matrix(dtype=typ, nrows=self.n, ncols=self.n) + return Matrix.sparse(typ=typ, nrows=self.n, ncols=self.n) def stack_into_hyper_column(self, matrices: List[Matrix]) -> Matrix: - assert len(matrices) == self.block_count - tiles = [[m] for m in matrices] - return graphblas.ss.concat(tiles) + res = self.create_hyper_vector(matrices[0].type, BlockMatrixOrientation.VERTICAL) + for i, matrix in enumerate(matrices): + for (row, col, value) in matrix: + res[i * self.n + row, col] = value + return res def repeat_into_hyper_column(self, matrix: Matrix) -> Matrix: return self.stack_into_hyper_column([matrix] * self.block_count) @@ -120,4 +135,14 @@ def automize_block_operations(self, base: OptimizedMatrix) -> BlockMatrix: ) def get_hyper_vector_blocks(self, hyper_vector: Matrix) -> List[Matrix]: - return [cell for row in hyper_vector.ss.split(self.n) for cell in row] + res = [Matrix.sparse(hyper_vector.type, self.n, self.n) for _ in range(self.block_count)] + orientation = self.get_block_matrix_orientation(hyper_vector.shape) + if orientation == BlockMatrixOrientation.HORIZONTAL: + for (row, col, value) in hyper_vector: + res[col // self.n][row, col % self.n] = value + elif orientation == BlockMatrixOrientation.VERTICAL: + for (row, col, value) in hyper_vector: + res[row // self.n][row % self.n, col] = value + else: + assert False + return res diff --git a/cfpq_matrix/empty_optimized_matrix.py b/cfpq_matrix/empty_optimized_matrix.py index 53292d6..2d53139 100644 --- a/cfpq_matrix/empty_optimized_matrix.py +++ b/cfpq_matrix/empty_optimized_matrix.py @@ -1,5 +1,6 @@ -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Semiring, Monoid +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring from cfpq_matrix.abstract_optimized_matrix_decorator import AbstractOptimizedMatrixDecorator from cfpq_matrix.optimized_matrix import OptimizedMatrix @@ -18,9 +19,9 @@ def mxm(self, other: Matrix, op: Semiring, swap_operands: bool = False) -> Matri if self.nvals == 0 or other.nvals == 0: if swap_operands: assert self.shape[0] == other.shape[1] - return Matrix(self.dtype, self.shape[1], other.shape[0]) + return Matrix.sparse(self.dtype, self.shape[1], other.shape[0]) assert self.shape[1] == other.shape[0] - return Matrix(self.dtype, self.shape[0], other.shape[1]) + return Matrix.sparse(self.dtype, self.shape[0], other.shape[1]) return self.base.mxm(other, op, swap_operands) def rsub(self, other: Matrix, op: SubOp) -> Matrix: @@ -28,7 +29,7 @@ def rsub(self, other: Matrix, op: SubOp) -> Matrix: return other return self.base.rsub(other, op) - def iadd(self, other: Matrix, op: Monoid): + def iadd(self, other: Matrix, op: BinaryOp): if other.nvals != 0: self.base.iadd(other, op=op) diff --git a/cfpq_matrix/format_optimized_matrix.py b/cfpq_matrix/format_optimized_matrix.py index 14acaa5..982030d 100644 --- a/cfpq_matrix/format_optimized_matrix.py +++ b/cfpq_matrix/format_optimized_matrix.py @@ -1,7 +1,9 @@ import warnings -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Monoid, Semiring +import pygraphblas +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring from cfpq_matrix.abstract_optimized_matrix_decorator import AbstractOptimizedMatrixDecorator from cfpq_matrix.optimized_matrix import OptimizedMatrix @@ -33,7 +35,7 @@ def base(self) -> OptimizedMatrix: def _force_init_format(self, desired_format: str) -> OptimizedMatrix: if desired_format not in self.matrices: base_matrix = self.base.to_unoptimized().dup() - base_matrix.ss.config["format"] = desired_format + base_matrix.format = desired_format self.matrices[desired_format] = self.base.optimize_similarly(base_matrix) if self.discard_base_on_reformat: del self.matrices[self.base.format] @@ -45,18 +47,18 @@ def _force_init_format(self, desired_format: str) -> OptimizedMatrix: def mxm(self, other: Matrix, op: Semiring, swap_operands: bool = False) -> Matrix: left_nvals = other.nvals if swap_operands else self.nvals right_nvals = self.nvals if swap_operands else other.nvals - desired_format = "by_row" if left_nvals < right_nvals else "by_col" + desired_format = pygraphblas.lib.GxB_BY_ROW if left_nvals < right_nvals else pygraphblas.lib.GxB_BY_COL if desired_format in self.matrices or other.nvals < self.nvals / self.reformat_threshold: - other.ss.config["format"] = desired_format + other.format = desired_format reformatted_self = self._force_init_format(desired_format) return reformatted_self.mxm(other, op, swap_operands=swap_operands) return self.base.mxm(other, op, swap_operands=swap_operands) def rsub(self, other: Matrix, op: SubOp) -> Matrix: - return self.matrices.get(other.ss.config["format"], self.base).rsub(other, op) + return self.matrices.get(other.format, self.base).rsub(other, op) - def iadd(self, other: Matrix, op: Monoid): + def iadd(self, other: Matrix, op: BinaryOp): for m in self.matrices.values(): m.iadd(other, op) diff --git a/cfpq_matrix/lazy_add_optimized_matrix.py b/cfpq_matrix/lazy_add_optimized_matrix.py index 09c8af4..a584a78 100644 --- a/cfpq_matrix/lazy_add_optimized_matrix.py +++ b/cfpq_matrix/lazy_add_optimized_matrix.py @@ -1,5 +1,6 @@ -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Monoid, Semiring +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring from cfpq_matrix.abstract_optimized_matrix_decorator import AbstractOptimizedMatrixDecorator from cfpq_matrix.optimized_matrix import OptimizedMatrix @@ -54,10 +55,10 @@ def to_unoptimized(self) -> Matrix: return self.base.to_unoptimized() def mxm(self, other: Matrix, op: Semiring, swap_operands: bool = False) -> Matrix: - self.update_monoid(op.monoid) + self.update_monoid(getattr(op.ztype, op.pls)) return self._map_and_fold( mapper=lambda m: m.mxm(other, op=op, swap_operands=swap_operands), - combiner=lambda acc, cur: acc.ewise_add(cur, op=op.monoid).new(), + combiner=lambda acc, cur: acc.eadd(cur, add_op=getattr(op.ztype, op.pls)), nvals_combine_threshold=other.nvals ) @@ -70,11 +71,11 @@ def rsub(self, other, op: SubOp) -> Matrix: nvals_combine_threshold=other.nvals ) - def iadd(self, other: Matrix, op: Monoid): + def iadd(self, other: Matrix, op: BinaryOp): self.update_monoid(op) other = other.dup() if self.format is not None: - other.ss.config["format"] = self.format + other.format = self.format base = self.base while True: other_nvals = max(other.nvals, self.min_size) @@ -89,13 +90,14 @@ def iadd(self, other: Matrix, op: Monoid): if i is None: self.matrices.append(base.optimize_similarly(other)) return self - other << other.ewise_add( + other.eadd( self.matrices[i].to_unoptimized(), - op=op + add_op=op, + out=other ) del self.matrices[i] - def update_monoid(self, op: Monoid): + def update_monoid(self, op: BinaryOp): if self.last_used_monoid is not op: self.force_combine_small_matrices(nvals_combine_threshold=float("inf")) self.last_used_monoid = op diff --git a/cfpq_matrix/matrix_to_optimized_adapter.py b/cfpq_matrix/matrix_to_optimized_adapter.py index 89b29e9..09dd534 100644 --- a/cfpq_matrix/matrix_to_optimized_adapter.py +++ b/cfpq_matrix/matrix_to_optimized_adapter.py @@ -1,8 +1,9 @@ from typing import Tuple -from graphblas.core.dtypes import DataType -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Monoid, Semiring +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring +from pygraphblas.types import Type from cfpq_matrix.optimized_matrix import OptimizedMatrix, MatrixFormat from cfpq_matrix.subtractable_semiring import SubOp @@ -24,11 +25,11 @@ def shape(self) -> Tuple[int, int]: @property def format(self) -> MatrixFormat: - return self.base.ss.config["format"] + return self.base.format @property - def dtype(self) -> DataType: - return self.base.dtype + def dtype(self) -> Type: + return self.base.type def to_unoptimized(self) -> Matrix: return self.base @@ -38,13 +39,13 @@ def mxm(self, other: Matrix, op: Semiring, swap_operands: bool = False) -> Matri other.mxm(self.base, op) if swap_operands else self.base.mxm(other, op) - ).new(self.dtype) + ) def rsub(self, other: Matrix, op: SubOp) -> Matrix: return op(other, self.base) - def iadd(self, other: Matrix, op: Monoid): - self.base << self.base.ewise_add(other, op=op) + def iadd(self, other: Matrix, op: BinaryOp): + self.base.eadd(other, add_op=op, out=self.base) def optimize_similarly(self, other: Matrix) -> OptimizedMatrix: return MatrixToOptimizedAdapter(other) diff --git a/cfpq_matrix/matrix_utils.py b/cfpq_matrix/matrix_utils.py index 5e8d0ee..54cef00 100644 --- a/cfpq_matrix/matrix_utils.py +++ b/cfpq_matrix/matrix_utils.py @@ -1,24 +1,22 @@ from typing import Any -import graphblas -from graphblas.core.dtypes import DataType -from graphblas.core.matrix import Matrix -from graphblas.core.vector import Vector +from pygraphblas import Matrix, Vector, descriptor +from pygraphblas.types import Type def complimentary_mask(matrix: Matrix, mask: Matrix) -> Matrix: larger_matrix = matrix if matrix.nvals > mask.nvals else mask - zero = Matrix(matrix.dtype, nrows=matrix.nrows, ncols=matrix.ncols) - zero.ss.config["format"] = larger_matrix.ss.config["format"] - res = Matrix(matrix.dtype, nrows=matrix.nrows, ncols=matrix.ncols) - res.ss.config["format"] = larger_matrix.ss.config["format"] - res(~mask.S) << zero.ewise_add(matrix, op=graphblas.monoid.any) + zero = Matrix.sparse(matrix.type, nrows=matrix.nrows, ncols=matrix.ncols) + zero.format = larger_matrix.format + res = Matrix.sparse(matrix.type, nrows=matrix.nrows, ncols=matrix.ncols) + res.format = larger_matrix.format + zero.eadd(matrix, add_op=matrix.type.ANY, mask=mask, desc=descriptor.C & descriptor.S, out=res) return res -def identity_matrix(one: Any, dtype: DataType, size: int) -> Matrix: - return Vector.from_scalar( - value=one, +def identity_matrix(one: Any, dtype: Type, size: int) -> Matrix: + return Matrix.from_diag(Vector.dense( + typ=dtype, size=size, - dtype=dtype - ).diag() + fill=one + )) diff --git a/cfpq_matrix/optimized_matrix.py b/cfpq_matrix/optimized_matrix.py index 57fdcdd..f6165a8 100644 --- a/cfpq_matrix/optimized_matrix.py +++ b/cfpq_matrix/optimized_matrix.py @@ -1,20 +1,15 @@ -import weakref from abc import ABC, abstractmethod from typing import Optional, Tuple -import graphblas -from graphblas.core.dtypes import DataType -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Semiring, Monoid +from pygraphblas import Matrix +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring +from pygraphblas.types import Type from cfpq_matrix.subtractable_semiring import SubOp -MatrixFormat = Optional[str] - -# This is a hack that prevents `graphblas` from creating strong reference cycles -# This hack makes reference-counting garbage collection possible -old_ss_init = graphblas.core.ss.matrix.ss.__init__ -graphblas.core.ss.matrix.ss.__init__ = lambda self, parent: old_ss_init(self, weakref.proxy(parent)) +# see pygraphblas.lib.GxB_BY_ROW and pygraphblas.lib.GxB_BY_COL +MatrixFormat = Optional[int] class OptimizedMatrix(ABC): @@ -39,7 +34,7 @@ def format(self) -> MatrixFormat: @property @abstractmethod - def dtype(self) -> DataType: + def dtype(self) -> Type: pass @abstractmethod @@ -57,7 +52,7 @@ def rsub(self, other: Matrix, op: SubOp) -> Matrix: """ @abstractmethod - def iadd(self, other: Matrix, op: Monoid): + def iadd(self, other: Matrix, op: BinaryOp): """ Adds `other` to `self` in-place. """ diff --git a/cfpq_matrix/subtractable_semiring.py b/cfpq_matrix/subtractable_semiring.py index bdec7db..a8db6d0 100644 --- a/cfpq_matrix/subtractable_semiring.py +++ b/cfpq_matrix/subtractable_semiring.py @@ -1,8 +1,8 @@ from dataclasses import dataclass from typing import Callable, Any -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Semiring +from pygraphblas import Matrix +from pygraphblas.semiring import Semiring SubOp = Callable[[Matrix, Matrix], Matrix] diff --git a/cfpq_model/label_decomposed_graph.py b/cfpq_model/label_decomposed_graph.py index 8166716..b6cf405 100644 --- a/cfpq_model/label_decomposed_graph.py +++ b/cfpq_model/label_decomposed_graph.py @@ -2,14 +2,14 @@ from pathlib import Path from typing import Dict, Optional, Union, Callable -import graphblas -import graphblas.core.matrix import numpy as np import pandas as pd -from graphblas.core.dtypes import DataType, BOOL -from graphblas.core.matrix import Matrix -from graphblas.core.operator import Monoid, Semiring -from graphblas.exceptions import IndexOutOfBound +import pygraphblas.types +from pygraphblas import Matrix +from pygraphblas.base import IndexOutOfBound +from pygraphblas.binaryop import BinaryOp +from pygraphblas.semiring import Semiring +from pygraphblas.types import Type from cfpq_matrix.block.block_matrix_space import BlockMatrixSpace from cfpq_matrix.block.block_matrix_space_impl import BlockMatrixSpaceImpl @@ -37,7 +37,7 @@ def __init__( self, vertex_count: int, block_matrix_space: BlockMatrixSpace, - dtype: DataType, + dtype: Type, matrices: Dict[Symbol, Matrix], ): self.vertex_count = vertex_count @@ -104,10 +104,10 @@ def read_from_pocr_graph_file(path: Union[Path, str]) -> "LabelDecomposedGraph": edge_sources += label_indices * vertex_count try: - matrices[symbol] = Matrix.from_coo( - rows=edge_sources, - columns=edge_destinations, - values=True, + matrices[symbol] = Matrix.from_lists( + I=edge_sources.tolist(), + J=edge_destinations.tolist(), + V=True, nrows=block_count * vertex_count if symbol.is_indexed else vertex_count, ncols=vertex_count ) @@ -121,7 +121,7 @@ def read_from_pocr_graph_file(path: Union[Path, str]) -> "LabelDecomposedGraph": return LabelDecomposedGraph( vertex_count=vertex_count, block_matrix_space=BlockMatrixSpaceImpl(n=vertex_count, block_count=block_count), - dtype=BOOL, + dtype=pygraphblas.types.BOOL, matrices=matrices ) except Exception as e: @@ -139,7 +139,8 @@ def write_to_pocr_graph_file(self, path: Union[Path, str]): with open(path, 'w', encoding="utf-8") as output_file: for symbol, matrix in self.matrices.items(): edge_label = symbol.label - (rows, columns, _) = matrix.to_coo() + rows = matrix.npI + columns = matrix.npJ if matrix.shape[0] == self.vertex_count: edges_df = pd.DataFrame({ 'source': rows, @@ -179,7 +180,7 @@ def __init__( self, vertex_count: int, block_matrix_space: BlockMatrixSpace, - dtype: DataType, + dtype: Type, matrix_optimizer: Callable[[Matrix], OptimizedMatrix] ): self.vertex_count = vertex_count @@ -199,7 +200,7 @@ def from_unoptimized( dtype=unoptimized_graph.dtype, matrix_optimizer=matrix_optimizer ) - optimized_graph.iadd(unoptimized_graph, op=graphblas.monoid.any) + optimized_graph.iadd(unoptimized_graph, op=unoptimized_graph.dtype.ANY) return optimized_graph def empty_copy(self) -> "OptimizedLabelDecomposedGraph": @@ -225,14 +226,14 @@ def to_unoptimized(self) -> LabelDecomposedGraph: def nvals(self) -> int: return sum(matrix.nvals for matrix in self.matrices.values()) - def iadd_by_symbol(self, symbol: Symbol, matrix: Matrix, op: Monoid): + def iadd_by_symbol(self, symbol: Symbol, matrix: Matrix, op: BinaryOp): if symbol not in self: self.matrices[symbol] = self.block_matrix_space.automize_block_operations( self.matrix_optimizer(self._create_matrix_for_symbol(symbol)) ) self.matrices[symbol].iadd(matrix, op) - def iadd(self, other: LabelDecomposedGraph, op: Monoid): + def iadd(self, other: LabelDecomposedGraph, op: BinaryOp): for symbol, matrix in other.matrices.items(): self.iadd_by_symbol(symbol, matrix, op) return self @@ -267,7 +268,7 @@ def mxm( swap_operands=swap_operands, op=op, ) - accum.iadd_by_symbol(lhs, mxm, op.monoid) + accum.iadd_by_symbol(lhs, mxm, getattr(op.ztype, op.pls)) return accum def rmxm(