Navigation Menu

Skip to content

Commit

Permalink
Michelp/diag 5 (#95)
Browse files Browse the repository at this point in the history
* more cleanups removing docker deps from dev scripts

* notebook updates, some tweaks to gviz for font path, test case for bool reduce.

* update version

* bump mmparse
  • Loading branch information
michelp committed Aug 2, 2021
1 parent 41e446c commit 4b6f9f5
Show file tree
Hide file tree
Showing 25 changed files with 7,472 additions and 7,797 deletions.
1,928 changes: 1,235 additions & 693 deletions demo/Centrality.ipynb

Large diffs are not rendered by default.

1,138 changes: 567 additions & 571 deletions demo/Hypersparse-RadiX-Net-with-pygraphblas.ipynb

Large diffs are not rendered by default.

2,277 changes: 1,134 additions & 1,143 deletions demo/Intro-Prez.ipynb

Large diffs are not rendered by default.

502 changes: 238 additions & 264 deletions demo/K-Truss.ipynb

Large diffs are not rendered by default.

189 changes: 87 additions & 102 deletions demo/Louvain.ipynb

Large diffs are not rendered by default.

264 changes: 134 additions & 130 deletions demo/PageRank.ipynb

Large diffs are not rendered by default.

7,063 changes: 3,068 additions & 3,995 deletions demo/Sierpinski-Graph.ipynb

Large diffs are not rendered by default.

327 changes: 161 additions & 166 deletions demo/SuiteSparseMatrixMarket.ipynb

Large diffs are not rendered by default.

296 changes: 146 additions & 150 deletions demo/Triangle-Counting.ipynb

Large diffs are not rendered by default.

799 changes: 478 additions & 321 deletions demo/TriangleCentrality.ipynb

Large diffs are not rendered by default.

282 changes: 82 additions & 200 deletions demo/User-Defined-Types.ipynb

Large diffs are not rendered by default.

Binary file modified demo/animated_from_images.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/imgs/bfs_step.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/imgs/binary_op_A.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/imgs/binary_op_B.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/imgs/select_op_A.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/imgs/unary_op_A.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 1 addition & 10 deletions doctest.sh
@@ -1,11 +1,2 @@
#!/bin/bash
if [ "$1" = "build" ]
then
SS_COMPACT=1 ./docker_build.sh v4.0.1 test minimal
fi
docker run --rm \
-v `pwd`/pygraphblas:/pygraphblas/pygraphblas \
-v `pwd`/demo:/pygraphblas/demo \
-v `pwd`/docs:/docs \
-it graphblas/pygraphblas-minimal:test \
python3 -c 'import pygraphblas; pygraphblas.run_doctests()'
python3 -c 'import pygraphblas; pygraphblas.run_doctests()'
9 changes: 0 additions & 9 deletions ipython.sh

This file was deleted.

2 changes: 1 addition & 1 deletion minimal-requirements.txt
Expand Up @@ -11,4 +11,4 @@ coveralls
ssgetpy
matplotlib
graphviz
mmparse==0.4
mmparse
6 changes: 0 additions & 6 deletions notebook.sh

This file was deleted.

23 changes: 14 additions & 9 deletions pygraphblas/gviz.py
Expand Up @@ -260,6 +260,8 @@ def draw_matrix(
font = ImageFont.truetype(
str(font_path / "FantasqueSansMono-Bold.ttf"), int(scale * 0.5)
)
else:
font = ImageFont.load_default()

if isinstance(M, Vector):
return draw_vector(
Expand Down Expand Up @@ -355,35 +357,38 @@ def draw_matrix_op(
op,
right,
result,
font_path=Path("demo"),
font_path=None,
filename=None,
eqstr="=",
**kwargs,
): # pragma: nocover
scale = kwargs["scale"]
cosmic_font = ImageFont.truetype(
str(font_path / "FantasqueSansMono-Bold.ttf"), int(scale * 0.5)
)
font = None
if font_path is not None:
font = ImageFont.truetype(
str(font_path / "FantasqueSansMono-Bold.ttf"), int(scale * 0.5)
)
else:
font = ImageFont.load_default()

kwargs["font_path"] = font_path
left = draw_matrix(left, **kwargs)
right = draw_matrix(right, **kwargs)
result = draw_matrix(result, **kwargs)
op_width = cosmic_font.getsize(op)[0]
op_width = font.getsize(op)[0]
spacer = int(scale * 2)
width = left.size[0] + op_width + spacer + right.size[0] + spacer + result.size[0]
height = max(op_width + spacer, left.size[1], right.size[1], result.size[1])
im = Image.new(left.mode, (width, height), color="white")
d = ImageDraw.Draw(im)
im.paste(left, (0, 0))
d.text(
(left.size[0] + int(spacer / 2), height / 2), op, fill="black", font=cosmic_font
)
d.text((left.size[0] + int(spacer / 2), height / 2), op, fill="black", font=font)
im.paste(right, (left.size[0] + op_width + spacer, 0))
d.text(
(left.size[0] + right.size[0] + op_width + (spacer * 1.5), height / 2),
eqstr,
fill="black",
font=cosmic_font,
font=font,
)
im.paste(result, (left.size[0] + right.size[0] + op_width + spacer + spacer, 0))
if filename is not None:
Expand Down
139 changes: 124 additions & 15 deletions pygraphblas/matrix.py
Expand Up @@ -267,6 +267,50 @@ def from_lists(cls, I, J, V, nrows=None, ncols=None, typ=None):
m[i, j] = v
return m

@classmethod
def from_diag(cls, v, k=0, desc=None):
"""
GxB_Matrix_diag constructs a matrix from a vector. Let n be the length of
the v vector, from GrB_Vector_size (&n, v). If k = 0, then C is an n-by-n
diagonal matrix with the entries from v along the main diagonal of C, with
C(i,i) = v(i). If k is nonzero, C is square with dimension n+abs(k). If k
is positive, it denotes diagonals above the main diagonal, with C(i,i+k) =
v(i). If k is negative, it denotes diagonals below the main diagonal of C,
with C(i-k,i) = v(i). This behavior is identical to the MATLAB statement
C = diag(v,k), where v is a vector, except that GxB_Matrix_diag can also
do typecasting.
>>> v = Vector.from_lists([0, 1, 2], [1, 2, 3])
>>> print(Matrix.from_diag(v))
0 1 2
0| 1 | 0
1| 2 | 1
2| 3| 2
0 1 2
>>> print(Matrix.from_diag(v, 1))
0 1 2 3
0| 1 | 0
1| 2 | 1
2| 3| 2
3| | 3
0 1 2 3
>>> print(Matrix.from_diag(v, -1))
0 1 2 3
0| | 0
1| 1 | 1
2| 2 | 2
3| 3 | 3
0 1 2 3
"""
l = v.size + abs(k)
C = cls.sparse(v.type, l, l)
if desc is None: # pragma: nocover
desc = current_desc.get(NULL)
if desc is not NULL: # pragma: nocover
desc = desc.get_desc()
C._check(lib.GxB_Matrix_diag(C._matrix[0], v._vector[0], k, desc))
return C

@classmethod
def from_mm(cls, mm_file):
"""Create a new matrix by reading a Matrix Market file.
Expand Down Expand Up @@ -296,8 +340,6 @@ def from_mm(cls, mm_file):
typ = get_mm_type_converter(mm_type)
m = cls.sparse(typ, nrows, ncols)
for l, i, j, v in row_iter:
i -= 1
j -= 1
m[i, j] = v
if symmetric:
m[j, i] = v
Expand Down Expand Up @@ -382,11 +424,11 @@ def from_csv(
return M

@classmethod
def binread(cls, bin_file, compression=None): # pragma: nocover
def binread(cls, bin_file, opener=Path.open): # pragma: nocover
"""Create a new matrix by reading a SuiteSparse specific binary file."""
from suitesparse_graphblas.io import binread
from suitesparse_graphblas.io import binary

matrix = binread(bin_file, compression)
matrix = binary.binread(bin_file, opener)
return cls(matrix)

from_binfile = binread
Expand Down Expand Up @@ -511,11 +553,11 @@ def ssget(cls, name_or_id=None, binary_cache_dir=None): # pragma: nocover
for m in mm_path.glob("*.mtx"):
Mbin = mm_path / (m.name + ".grb")
if binary_cache_dir and Mbin.exists():
M = cls.from_binfile(bytes(Mbin))
M = cls.from_binfile(Mbin)
else:
M = cls.from_mm(mm_path / m)
if binary_cache_dir:
M.to_binfile(bytes(Mbin))
M.to_binfile(Mbin)
M.wait()
yield m.name, M

Expand Down Expand Up @@ -822,11 +864,11 @@ def S(self):
"""
return self.pattern()

def binwrite(self, filename, comments="", compression=None): # pragma: nocover
def binwrite(self, filename, comments="", opener=Path.open): # pragma: nocover
"""Write this matrix using custom SuiteSparse binary format."""
from suitesparse_graphblas.io import binwrite
from suitesparse_graphblas.io import binary

binwrite(self._matrix, filename, comments, compression)
binary.binwrite(self._matrix, filename, comments, opener)
return

to_binfile = binwrite
Expand Down Expand Up @@ -1601,23 +1643,23 @@ def reduce_float(self, mon=None, mask=None, accum=None, desc=None):
)
return result[0]

def reduce_vector(self, mon=None, out=None, mask=None, accum=None, desc=None):
def reduce(self, mon=None, out=None, mask=None, accum=None, desc=None):
"""Reduce matrix to a vector.
>>> M = Matrix.sparse(types.FP32, 3, 3)
>>> print(M.reduce_vector())
>>> print(M.reduce())
0|
1|
2|
>>> M[0,1] = 42.0
>>> M[0,2] = 42.0
>>> M[2,0] = -42.0
>>> print(M.reduce_vector())
>>> print(M.reduce())
0|84.0
1|
2|-42.0
>>> print(M.reduce_vector(types.FP32.MIN_MONOID))
>>> print(M.reduce(types.FP32.MIN_MONOID))
0|42.0
1|
2|-42.0
Expand All @@ -1627,9 +1669,27 @@ def reduce_vector(self, mon=None, out=None, mask=None, accum=None, desc=None):
0|84.0
1|
2|-42.0
>>> M = Matrix.sparse(types.BOOL, 3, 3)
>>> print(M.reduce())
0|
1|
2|
>>> M[0,1] = True
>>> M[0,2] = True
>>> M[2,0] = True
>>> print(M.reduce())
0| t
1|
2| t
"""
if mon is None:
mon = current_monoid.get(getattr(self.type, "PLUS_MONOID", NULL))
if self.type is types.BOOL:
mon = current_monoid.get(getattr(self.type, "lor_monoid"))
else:
mon = current_monoid.get(getattr(self.type, "plus_monoid"))
mon = mon.get_monoid(self.type)
if out is None:
out = Vector.sparse(self.type, self.nrows)
Expand All @@ -1641,6 +1701,8 @@ def reduce_vector(self, mon=None, out=None, mask=None, accum=None, desc=None):
)
return out

reduce_vector = reduce # deprecated

def max(self):
"""Return the max of the matrix.
Expand Down Expand Up @@ -1993,6 +2055,53 @@ def diag(self, offset=None):
"""
return self.select(lib.GxB_DIAG, thunk=offset)

def vector_diag(self, k=0, desc=None):
"""
GxB_Vector_diag extracts a vector v from an input matrix A, which
may be rectangular. If k = 0, the main diagonal of A is
extracted; k > 0 denotes diagonals above the main diagonal of
A, and k < 0 denotes diagonals below the main diagonal of A.
Let A have dimension m-by-n. If k is in the range 0 to n-1,
then v has length min(m,n-k). If k is negative and in the
range -1 to -m+1, then v has length min(m+k,n). If k is
outside these ranges, v has length 0 (this is not an error).
This function computes the same thing as the MATLAB statement
v = diag(A,k) when A is a matrix, except that GxB_Vector_diag
can also do typecasting.
>>> from pygraphblas import UINT8
>>> A = Matrix.dense(UINT8, 2, 2, fill=1)
>>> print(A)
0 1
0| 1 1| 0
1| 1 1| 1
0 1
>>> print(A.vector_diag())
0| 1
1| 1
>>> print(A.vector_diag(1))
0| 1
>>> A.vector_diag(2)
<Vector (0: 0:UINT8)>
>>> print(A.vector_diag(-1))
0| 1
>>> A.vector_diag(-2)
<Vector (0: 0:UINT8)>
"""
n, m = self.shape
if k in range(0, n):
l = min(m, n - k)
elif k in range(-1, -m, -1):
l = min(m + k, n)
else:
l = 0
v = Vector.sparse(self.type, l)

_, _, desc = self._get_args(desc=desc)

self._check(lib.GxB_Vector_diag(v._vector[0], self._matrix[0], k, desc))
return v

def offdiag(self, offset=None):
"""Select the off-diagonal Matrix.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -3,7 +3,7 @@

setup(
name='pygraphblas',
version='5.1.5.0',
version='5.1.5.1',
description='GraphBLAS Python bindings.',
author='Michel Pelletier',
packages=['pygraphblas'],
Expand Down
12 changes: 1 addition & 11 deletions test.sh
@@ -1,12 +1,2 @@
#!/bin/bash
if [ "$1" = "build" ]
then
SS_COMPACT=1 ./docker_build.sh master test minimal
fi
docker run --rm \
-v `pwd`/pygraphblas:/pygraphblas/pygraphblas \
-v `pwd`/demo:/pygraphblas/demo \
-v `pwd`/docs:/docs \
-v `pwd`/tests:/pygraphblas/tests \
-it graphblas/pygraphblas-minimal:test \
python3 -m pytest --cov=pygraphblas --cov-report=term-missing --cov-branch $@
python3 -m pytest --cov=pygraphblas --cov-report=term-missing --cov-branch $@

0 comments on commit 4b6f9f5

Please sign in to comment.