Skip to content

Commit

Permalink
Migrate from python-graphblas to pygraphblas
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyaMuravjov committed May 18, 2024
1 parent 140e3f7 commit 331692a
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 127 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
)
):
Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion cfpq_cli/run_all_pairs_cflr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
3 changes: 2 additions & 1 deletion cfpq_eval/runners/kotgll_all_pairs_cflr_tool_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions cfpq_matrix/abstract_optimized_matrix_decorator.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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:
Expand Down
9 changes: 5 additions & 4 deletions cfpq_matrix/block/block_matrix.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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():
Expand Down
14 changes: 7 additions & 7 deletions cfpq_matrix/block/block_matrix_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
81 changes: 53 additions & 28 deletions cfpq_matrix/block/block_matrix_space_impl.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -69,45 +75,54 @@ 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:
rows = rows + (columns // self.n * 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 * 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:
shape = {
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)
Expand All @@ -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
11 changes: 6 additions & 5 deletions cfpq_matrix/empty_optimized_matrix.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,17 +19,17 @@ 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:
if self.nvals == 0 or other.nvals == 0:
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)

Expand Down
16 changes: 9 additions & 7 deletions cfpq_matrix/format_optimized_matrix.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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)

Expand Down
Loading

0 comments on commit 331692a

Please sign in to comment.