Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ s(accum) << v.reduce(op)
## Creating new Vectors / Matrices

```python
A = Matrix.new(dtype, num_rows, num_cols) # new_type
A = Matrix(dtype, num_rows, num_cols) # new_type
B = A.dup() # dup
A = Matrix.from_coo([row_indices], [col_indices], [values]) # build
```
Expand Down Expand Up @@ -225,7 +225,7 @@ Python-graphblas requires `numba` which enables compiling user-defined Python fu
Example customized UnaryOp:

```python
from graphblas import unary
from graphblas import unary, Vector

def force_odd_func(x):
if x % 2 == 0:
Expand Down
2 changes: 2 additions & 0 deletions docs/api_reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ API Reference
:maxdepth: 2

collections
types
operators
io
utilities
exceptions
4 changes: 4 additions & 0 deletions docs/api_reference/io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ Visualization
~~~~~~~~~~~~~

.. autofunction:: graphblas.viz.draw

.. autofunction:: graphblas.viz.spy

.. autofunction:: graphblas.viz.datashade
12 changes: 12 additions & 0 deletions docs/api_reference/operators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,20 @@ IndexUnaryOp
.. autoclass:: graphblas.core.operator.IndexUnaryOp()
:members:

IndexBinaryOp
~~~~~~~~~~~~~

.. autoclass:: graphblas.core.operator.IndexBinaryOp()
:members:

SelectOp
~~~~~~~~

.. autoclass:: graphblas.core.operator.SelectOp()
:members:

Aggregator
~~~~~~~~~~

.. autoclass:: graphblas.core.operator.Aggregator()
:members:
22 changes: 22 additions & 0 deletions docs/api_reference/types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Types
-----

DataType
~~~~~~~~

The object returned by :func:`~graphblas.dtypes.register_new` and
:func:`~graphblas.dtypes.register_anonymous`, and carried by every collection's
``.dtype``. The JIT introspection properties below report what SuiteSparse
actually registered for a user-defined type; see :doc:`../user_guide/udt` for
the operator-side counterparts on a typed operator (``op.jit_c_name``,
``op.jit_c_source``).

.. autoclass:: graphblas.dtypes.DataType()
:members: jit_c_name, jit_c_definition

Registering user-defined types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. autofunction:: graphblas.dtypes.register_new

.. autofunction:: graphblas.dtypes.register_anonymous
13 changes: 13 additions & 0 deletions docs/api_reference/utilities.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Utilities
---------

Initialization
~~~~~~~~~~~~~~~

.. autofunction:: graphblas.init

Recorder
~~~~~~~~

.. autoclass:: graphblas.Recorder
:members:
38 changes: 38 additions & 0 deletions docs/user_guide/operators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,51 @@ Common semirings are:
- **min_second**
- **max_first**
- **max_second**
- **plus_first** (sum the left operand's values over the connection pattern)
- **plus_second** (sum the right operand's values over the connection pattern)
- **plus_min**
- **lor_land**
- **land_lor**

Semirings are located in the ``graphblas.semiring`` namespace. Additional semirings registered
from numpy are located in ``graphblas.semiring.numpy``.

The ``first`` and ``second`` semirings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``first`` binary operator returns its left input and ``second`` returns its right input,
each ignoring the other. Combined with the ``plus`` monoid they give two semirings that read
the values of only one input during a multiply:

- ``plus_first``: for ``C << A.mxm(B, semiring.plus_first)``, each ``C[i, k]`` is the sum of
``A[i, j]`` over the ``j`` where both ``A[i, j]`` and ``B[j, k]`` are present. ``B``
contributes only its structure; its stored values are never read.
- ``plus_second``: the mirror image. Each ``C[i, k]`` sums ``B[j, k]`` over those same ``j``,
and ``A`` contributes only its structure.

These are the natural choice when one operand is a boolean or *iso* (single-valued) adjacency
matrix, where a ``plus_times`` multiply either multiplies by one (a no-op) or rescales every
term by the same constant. ``first`` and ``second`` skip the product and accumulate one side's
values directly over the connection pattern. On a 0/1 matrix, ``plus_first`` counts the
length-two paths between each pair of nodes.

.. code-block:: python

from graphblas import Matrix, semiring

# A: 0->1 (10), 0->2 (20); B: 1->0 (3), 2->0 (7)
# A @ B reaches node 0 from node 0 through j = 1 and j = 2.
A = Matrix.from_coo([0, 0], [1, 2], [10, 20], nrows=3, ncols=3)
B = Matrix.from_coo([1, 2], [0, 0], [3, 7], nrows=3, ncols=3)

A.mxm(B, semiring.plus_first).new() # C[0, 0] == 30 (10 + 20, taken from A)
A.mxm(B, semiring.plus_second).new() # C[0, 0] == 10 ( 3 + 7, taken from B)
A.mxm(B, semiring.plus_times).new() # C[0, 0] == 170 (10*3 + 20*7)

The same ``first`` / ``second`` choice pairs with other monoids: ``min_first`` and ``min_second``
(listed above) carry a value along an edge without combining it, the pattern behind
label-propagation traversals.

IndexUnary Operators
--------------------

Expand Down
4 changes: 4 additions & 0 deletions graphblas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ def get_config():
config = get_config()
del get_config

# None until a backend is initialized. Touching a special attribute such as
# gb.Matrix auto-initializes, as does an explicit init(), and sets this to
# "suitesparse" or "suitesparse-vanilla". Reading gb.backend is not itself a
# special-attribute access, so it never triggers initialization.
backend = None
_init_params = None
_SPECIAL_ATTRS = {
Expand Down
6 changes: 6 additions & 0 deletions graphblas/core/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@ def from_coo(
):
"""Create a new Matrix from row and column indices and values.

.. warning::
When ``nrows`` or ``ncols`` is omitted, the shape is inferred from
the largest row or column index, so trailing all-empty rows or
columns are dropped. Pass ``nrows`` and ``ncols`` explicitly to pin
the shape.

Parameters
----------
rows : list or np.ndarray
Expand Down
21 changes: 21 additions & 0 deletions graphblas/core/operator/agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,27 @@ def _get_types(ops, initdtype):


class Aggregator:
"""A reduction operator that collapses the values of a Matrix or Vector.

An Aggregator is used with the ``reduce`` family of methods: ``Vector.reduce``,
``Matrix.reduce_rowwise``, ``Matrix.reduce_columnwise``, and ``Matrix.reduce_scalar``.
Some aggregators return a summary value (``sum``, ``mean``, ``max``); others return a
position (``ss.argmin``, ``ss.argmax``, ``ss.first_index``).

Built-in aggregators live in the ``graphblas.agg`` namespace, such as ``agg.sum``,
``agg.mean``, and ``agg.count``. The position aggregators are SuiteSparse-specific and
live under ``agg.ss``; the bare ``agg.argmin`` spellings are deprecated. Unlike the
other operators, an Aggregator is not a single GraphBLAS object; many are built from a
monoid or semiring plus an optional finalize step, composed per dtype on first use.

Examples
--------
>>> import graphblas as gb
>>> v = gb.Vector.from_coo([0, 1, 2], [1, 2, 3])
>>> int(v.reduce(gb.agg.sum).new())
6
"""

opclass = "Aggregator"

def __init__(
Expand Down
7 changes: 6 additions & 1 deletion graphblas/core/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,11 @@ def get(self, index, default=None):
def from_coo(cls, indices, values=1.0, dtype=None, *, size=None, dup_op=None, name=None):
"""Create a new Vector from indices and values.

.. warning::
When ``size`` is omitted, it is inferred from the largest index, so
trailing empty positions are dropped. Pass ``size`` explicitly to
pin the length.

Parameters
----------
indices : list or np.ndarray
Expand Down Expand Up @@ -1504,7 +1509,7 @@ def apply(self, op, right=None, *, left=None):
"""
method_name = "apply"
extra_message = (
"apply only accepts UnaryOp with no scalars or BinaryOp with `left` or `right` scalar"
"apply only accepts UnaryOp with no scalars or BinaryOp with `left` or `right` scalar "
"or IndexUnaryOp with `right` thunk."
)
if isinstance(op, str):
Expand Down