Skip to content
Merged
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:
39 changes: 39 additions & 0 deletions docs/user_guide/operators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,52 @@ 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. ``plus`` has no BOOL form, so for two boolean
matrices give the semiring an integer type, as in ``A.mxm(A, semiring.plus_first[int])``.

.. 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 @@ -74,6 +74,10 @@ def __setattr__(self, key, value):
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
12 changes: 11 additions & 1 deletion graphblas/core/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,11 @@ def wait(self, how="materialize"):
the computations may be delayed and not yet safe to use by multiple threads.
Use wait to force completion of the Matrix.

Has no effect in `blocking mode <../user_guide/init.html#graphblas-modes>`__.
In `blocking mode <../user_guide/init.html#graphblas-modes>`__, computations are
already complete, but with SuiteSparse:GraphBLAS, "materialize" may still build the
hyper_hash of a hypersparse Matrix that lacks one, such as one from ``from_coo`` or
an import (on recent versions, only with more non-empty rows or columns than the
``GxB_HYPER_HASH`` setting, 1024 by default); see ``Matrix.ss.unpack_hyperhash``.

"""
how = how.lower()
Expand Down Expand Up @@ -876,6 +880,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 @@ -700,6 +700,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 @@ -1412,7 +1417,7 @@ def apply(self, op, right=None, *, left=None):
method_name = "apply"
right_literal = None
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
Loading