Skip to content

Commit

Permalink
Use np.random.default_rng instead of legacy functions (python-graph…
Browse files Browse the repository at this point in the history
…blas#392)

* Use `np.random.default_rng` instead of legacy functions

* bump ruff (and use pathlib)
  • Loading branch information
eriknw authored Feb 21, 2023
1 parent 1a836ec commit b7e9485
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ repos:
additional_dependencies: [tomli]
files: ^(graphblas|docs)/
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.247
rev: v0.0.249
hooks:
- id: ruff
- repo: https://github.com/sphinx-contrib/sphinx-lint
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
from pathlib import Path

sys.path.insert(0, os.path.abspath(".."))
sys.path.insert(0, str(Path("..").resolve()))


# -- Project information -----------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions graphblas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ def __reduce__(self):


def get_config():
import os
from pathlib import Path

import donfig
import yaml

filename = os.path.join(os.path.dirname(__file__), "graphblas.yaml")
config = donfig.Config("graphblas")
with open(filename) as f:
path = Path(__file__).parent / "graphblas.yaml"
with path.open() as f:
defaults = yaml.safe_load(f)
config.update_defaults(defaults)
return config
Expand Down
18 changes: 9 additions & 9 deletions graphblas/core/automethods.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def __ixor__(self, other):

# End auto-generated code
def _main():
import os
from pathlib import Path

from .utils import _autogenerate_code

Expand Down Expand Up @@ -460,7 +460,7 @@ def _main():
f' raise TypeError(f"{name!r} not supported for {{type(self).__name__}}")\n\n'
)

_autogenerate_code(__file__, "\n".join(lines))
_autogenerate_code(Path(__file__), "\n".join(lines))

# Copy to scalar.py and infix.py
lines = []
Expand All @@ -481,16 +481,16 @@ def _main():
continue
lines.append(f" {name} = automethods.{name}")

thisdir = os.path.dirname(__file__)
thisdir = Path(__file__).parent
infix_exclude = {"_get_value"}

def get_name(line):
return line.strip().split(" ", 1)[0]

text = "\n".join(lines) + "\n "
_autogenerate_code(os.path.join(thisdir, "scalar.py"), text, "Scalar")
_autogenerate_code(thisdir / "scalar.py", text, "Scalar")
text = "\n".join(line for line in lines if get_name(line) not in infix_exclude) + "\n "
_autogenerate_code(os.path.join(thisdir, "infix.py"), text, "Scalar")
_autogenerate_code(thisdir / "infix.py", text, "Scalar")

# Copy to vector.py and infix.py
lines = []
Expand Down Expand Up @@ -519,9 +519,9 @@ def get_name(line):
lines.append(f" {name} = automethods.{name}")

text = "\n".join(lines) + "\n "
_autogenerate_code(os.path.join(thisdir, "vector.py"), text, "Vector")
_autogenerate_code(thisdir / "vector.py", text, "Vector")
text = "\n".join(line for line in lines if get_name(line) not in infix_exclude) + "\n "
_autogenerate_code(os.path.join(thisdir, "infix.py"), text, "Vector")
_autogenerate_code(thisdir / "infix.py", text, "Vector")

# Copy to matrix.py and infix.py
lines = []
Expand Down Expand Up @@ -550,9 +550,9 @@ def get_name(line):
lines.append(f" {name} = automethods.{name}")

text = "\n".join(lines) + "\n "
_autogenerate_code(os.path.join(thisdir, "matrix.py"), text, "Matrix")
_autogenerate_code(thisdir / "matrix.py", text, "Matrix")
text = "\n".join(line for line in lines if get_name(line) not in infix_exclude) + "\n "
_autogenerate_code(os.path.join(thisdir, "infix.py"), text, "Matrix")
_autogenerate_code(thisdir / "infix.py", text, "Matrix")


if __name__ == "__main__":
Expand Down
4 changes: 3 additions & 1 deletion graphblas/core/infixmethods.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,11 @@ def _main():
" setattr(VectorIndexExpr, name, val)\n"
" setattr(MatrixIndexExpr, name, val)\n"
)
from pathlib import Path

from .utils import _autogenerate_code

_autogenerate_code(__file__, "\n".join(lines))
_autogenerate_code(Path(__file__), "\n".join(lines))


if __name__ == "__main__":
Expand Down
8 changes: 4 additions & 4 deletions graphblas/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,14 +352,14 @@ def __init__(self, matrices, exc_arg=None, *, name):


def _autogenerate_code(
filename,
filepath,
text,
specializer=None,
begin="# Begin auto-generated code",
end="# End auto-generated code",
):
"""Super low-tech auto-code generation used by automethods.py and infixmethods.py."""
with open(filename) as f: # pragma: no branch (flaky)
with filepath.open() as f: # pragma: no branch (flaky)
orig_text = f.read()
if specializer:
begin = f"{begin}: {specializer}"
Expand All @@ -379,11 +379,11 @@ def _autogenerate_code(
new_text = orig_text
for start, stop in reversed(boundaries):
new_text = f"{new_text[:start]}{begin}{text}{new_text[stop:]}"
with open(filename, "w") as f: # pragma: no branch (flaky)
with filepath.open("w") as f: # pragma: no branch (flaky)
f.write(new_text)
import subprocess

try:
subprocess.check_call(["black", filename])
subprocess.check_call(["black", filepath])
except FileNotFoundError: # pragma: no cover (safety)
pass # It's okay if `black` isn't installed; pre-commit hooks will do linting
16 changes: 9 additions & 7 deletions graphblas/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import atexit
import functools
import itertools
from pathlib import Path

import numpy as np
import pytest
Expand All @@ -12,26 +13,27 @@


def pytest_configure(config):
rng = np.random.default_rng()
randomly = config.getoption("--randomly", False)
backend = config.getoption("--backend", None)
if backend is None:
if randomly:
backend = "suitesparse" if np.random.rand() < 0.5 else "suitesparse-vanilla"
backend = "suitesparse" if rng.random() < 0.5 else "suitesparse-vanilla"
else:
backend = "suitesparse"
blocking = config.getoption("--blocking", True)
if blocking is None: # pragma: no branch
blocking = np.random.rand() < 0.5 if randomly else True
blocking = rng.random() < 0.5 if randomly else True
record = config.getoption("--record", False)
if record is None: # pragma: no branch
record = np.random.rand() < 0.5 if randomly else False
record = rng.random() < 0.5 if randomly else False
mapnumpy = config.getoption("--mapnumpy", False)
if mapnumpy is None:
mapnumpy = np.random.rand() < 0.5 if randomly else False
mapnumpy = rng.random() < 0.5 if randomly else False
runslow = config.getoption("--runslow", False)
if runslow is None:
# Add a small amount of randomization to be safer
runslow = np.random.rand() < 0.05 if randomly else False
runslow = rng.random() < 0.05 if randomly else False
config.runslow = runslow

gb.config.set(autocompute=False, mapnumpy=mapnumpy)
Expand All @@ -46,7 +48,7 @@ def pytest_configure(config):
rec.start()

def save_records():
with open("record.txt", "w") as f: # pragma: no cover
with Path("record.txt").open("w") as f: # pragma: no cover
f.write("\n".join(rec.data))

# I'm sure there's a `pytest` way to do this...
Expand Down Expand Up @@ -83,7 +85,7 @@ def pytest_runtest_setup(item):
pytest.skip("need --runslow option to run")


@pytest.fixture(autouse=True, scope="function")
@pytest.fixture(autouse=True)
def _reset_name_counters():
"""Reset automatic names for each test for easier comparison of record.txt."""
gb.Matrix._name_counter = itertools.count()
Expand Down
3 changes: 2 additions & 1 deletion graphblas/tests/test_dtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ def test_bad_register():


def test_auto_register():
n = np.random.randint(10, 64)
rng = np.random.default_rng()
n = rng.integers(10, 64, endpoint=True)
np_type = np.dtype(f"({n},)int16")
assert lookup_dtype(np_type).np_type == np_type

Expand Down
9 changes: 5 additions & 4 deletions graphblas/tests/test_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -3794,13 +3794,14 @@ def get_expected(row_offset, col_offset, nrows, ncols, is_transposed):
def test_to_coo_sort():
# How can we get a matrix to a jumbled state in SS so that export won't be sorted?
N = 1000000
r = np.unique(np.random.randint(N, size=100))
c = np.unique(np.random.randint(N, size=100))
rng = np.random.default_rng()
r = np.unique(rng.integers(N, size=100))
c = np.unique(rng.integers(N, size=100))
r = r[: c.size].copy() # make sure same length
c = c[: r.size].copy()
expected_rows = r.copy()
np.random.shuffle(r)
np.random.shuffle(c)
rng.shuffle(r)
rng.shuffle(c)
A = Matrix.from_coo(r, c, r, nrows=N, ncols=N)
rows, cols, values = A.to_coo(sort=False)
A = Matrix.from_coo(r, c, r, nrows=N, ncols=N)
Expand Down
18 changes: 9 additions & 9 deletions graphblas/tests/test_pickle.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
import pickle
from pathlib import Path

import numpy as np
import pytest
Expand Down Expand Up @@ -38,12 +38,12 @@ def extra():

@pytest.mark.slow
def test_deserialize(extra):
thisdir = os.path.dirname(__file__)
with open(os.path.join(thisdir, f"pickle1{extra}.pkl"), "rb") as f:
path = Path(__file__).parent / f"pickle1{extra}.pkl"
with path.open("rb") as f:
d = pickle.load(f)
check_values(d)
# Again!
with open(os.path.join(thisdir, f"pickle1{extra}.pkl"), "rb") as f:
with path.open("rb") as f:
d = pickle.load(f)
check_values(d)

Expand Down Expand Up @@ -287,11 +287,11 @@ def test_serialize_parameterized():

@pytest.mark.slow
def test_deserialize_parameterized(extra):
thisdir = os.path.dirname(__file__)
with open(os.path.join(thisdir, f"pickle2{extra}.pkl"), "rb") as f:
path = Path(__file__).parent / f"pickle2{extra}.pkl"
with path.open("rb") as f:
pickle.load(f) # TODO: check results
# Again!
with open(os.path.join(thisdir, f"pickle2{extra}.pkl"), "rb") as f:
with path.open("rb") as f:
pickle.load(f) # TODO: check results


Expand All @@ -306,8 +306,8 @@ def test_udt(extra):
assert udt2._is_anonymous
assert pickle.loads(pickle.dumps(udt2)).np_type == udt2.np_type

thisdir = os.path.dirname(__file__)
with open(os.path.join(thisdir, f"pickle3{extra}.pkl"), "rb") as f:
path = Path(__file__).parent / f"pickle3{extra}.pkl"
with path.open("rb") as f:
d = pickle.load(f)
udt3 = d["PickledUDT"]
v = d["v"]
Expand Down
6 changes: 4 additions & 2 deletions graphblas/tests/test_prefix_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
@pytest.mark.parametrize("do_random", [False, True])
def test_scan_matrix(method, length, do_random):
if do_random:
A = np.random.randint(10, size=2 * length).reshape((2, length))
rng = np.random.default_rng()
A = rng.integers(10, size=2 * length).reshape((2, length))
mask = (A % 2).astype(bool)
A[~mask] = 0
M = Matrix.ss.import_bitmapr(values=A, bitmap=mask, name="A")
Expand Down Expand Up @@ -44,7 +45,8 @@ def test_scan_matrix(method, length, do_random):
@pytest.mark.parametrize("do_random", [False, True])
def test_scan_vector(length, do_random):
if do_random:
a = np.random.randint(10, size=length)
rng = np.random.default_rng()
a = rng.integers(10, size=length)
mask = (a % 2).astype(bool)
a[~mask] = 0
v = Vector.ss.import_bitmap(values=a, bitmap=mask)
Expand Down
5 changes: 3 additions & 2 deletions graphblas/tests/test_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2347,9 +2347,10 @@ def get_expected(offset, size):
def test_to_coo_sort():
# How can we get a vector to a jumbled state in SS so that export won't be sorted?
N = 1000000
a = np.unique(np.random.randint(N, size=100))
rng = np.random.default_rng()
a = np.unique(rng.integers(N, size=100))
expected = a.copy()
np.random.shuffle(a)
rng.shuffle(a)
v = Vector.from_coo(a, a, size=N)
indices, values = v.to_coo(sort=False)
v = Vector.from_coo(a, a, size=N)
Expand Down
Loading

0 comments on commit b7e9485

Please sign in to comment.