diff --git a/.github/workflows/test_and_build.yml b/.github/workflows/test_and_build.yml
index 5f1ab7dde..e3930a853 100644
--- a/.github/workflows/test_and_build.yml
+++ b/.github/workflows/test_and_build.yml
@@ -206,7 +206,12 @@ jobs:
else
psgver=""
fi
- if [[ ${npver} == "=1.25" || ${npver} == "=1.26" ]] ; then
+ if [[ ${npver} == "=1.26" ]] ; then
+ numbaver=""
+ if [[ ${spver} == "=1.8" || ${spver} == "=1.9" ]] ; then
+ spver=$(python -c 'import random ; print(random.choice(["=1.10", "=1.11", ""]))')
+ fi
+ elif [[ ${npver} == "=1.25" ]] ; then
numbaver=""
if [[ ${spver} == "=1.8" ]] ; then
spver=$(python -c 'import random ; print(random.choice(["=1.9", "=1.10", "=1.11", ""]))')
@@ -260,7 +265,7 @@ jobs:
pyyaml${yamlver} ${sparse} pandas${pdver} scipy${spver} numpy${npver} ${awkward} \
networkx${nxver} ${numba} ${fmm} ${psg} \
${{ matrix.slowtask == 'pytest_bizarro' && 'black' || '' }} \
- ${{ matrix.slowtask == 'notebooks' && 'matplotlib nbconvert jupyter "ipython>=7"' || '' }} \
+ ${{ matrix.slowtask == 'notebooks' && 'matplotlib nbconvert jupyter "ipython>=7" drawsvg' || '' }} \
${{ steps.sourcetype.outputs.selected == 'upstream' && 'cython' || '' }} \
${{ steps.sourcetype.outputs.selected != 'wheel' && '"graphblas>=7.4"' || '' }} \
${{ contains(steps.pyver.outputs.selected, 'pypy') && 'pypy' || '' }} \
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index bff5b80cd..565e1dc0d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -51,7 +51,7 @@ repos:
- id: isort
# Let's keep `pyupgrade` even though `ruff --fix` probably does most of it
- repo: https://github.com/asottile/pyupgrade
- rev: v3.13.0
+ rev: v3.14.0
hooks:
- id: pyupgrade
args: [--py39-plus]
@@ -80,14 +80,14 @@ repos:
# These versions need updated manually
- flake8==6.1.0
- flake8-bugbear==23.9.16
- - flake8-simplify==0.20.0
+ - flake8-simplify==0.21.0
- repo: https://github.com/asottile/yesqa
rev: v1.5.0
hooks:
- id: yesqa
additional_dependencies: *flake8_dependencies
- repo: https://github.com/codespell-project/codespell
- rev: v2.2.5
+ rev: v2.2.6
hooks:
- id: codespell
types_or: [python, rst, markdown]
diff --git a/README.md b/README.md
index 4581ef54a..4509e44ac 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
[](https://anaconda.org/conda-forge/python-graphblas)
[](https://pypi.python.org/pypi/python-graphblas/)
diff --git a/binder/environment.yml b/binder/environment.yml
index ef72a4d2b..11cd98e0c 100644
--- a/binder/environment.yml
+++ b/binder/environment.yml
@@ -2,9 +2,11 @@ name: graphblas
channels:
- conda-forge
dependencies:
- - python=3.10
+ - python=3.11
- python-graphblas
- matplotlib
- networkx
- pandas
- scipy
+ - drawsvg
+ - cairosvg
diff --git a/docs/_static/img/logo-horizontal-dark.svg b/docs/_static/img/logo-horizontal-dark.svg
new file mode 100644
index 000000000..be9e5ccca
--- /dev/null
+++ b/docs/_static/img/logo-horizontal-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/logo-horizontal-light.svg b/docs/_static/img/logo-horizontal-light.svg
new file mode 100644
index 000000000..5894eed9a
--- /dev/null
+++ b/docs/_static/img/logo-horizontal-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/logo-horizontal-medium-big.svg b/docs/_static/img/logo-horizontal-medium-big.svg
new file mode 100644
index 000000000..649c2aef3
--- /dev/null
+++ b/docs/_static/img/logo-horizontal-medium-big.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/logo-horizontal-medium.svg b/docs/_static/img/logo-horizontal-medium.svg
new file mode 100644
index 000000000..038781a3f
--- /dev/null
+++ b/docs/_static/img/logo-horizontal-medium.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/logo-name-light.svg b/docs/_static/img/logo-name-light.svg
index e9d9738ee..3331ae561 100644
--- a/docs/_static/img/logo-name-light.svg
+++ b/docs/_static/img/logo-name-light.svg
@@ -1 +1 @@
-
+
diff --git a/docs/_static/img/logo-name-medium-big.svg b/docs/_static/img/logo-name-medium-big.svg
new file mode 100644
index 000000000..7bb245898
--- /dev/null
+++ b/docs/_static/img/logo-name-medium-big.svg
@@ -0,0 +1 @@
+
diff --git a/docs/_static/img/logo-name-medium.svg b/docs/_static/img/logo-name-medium.svg
index 2c718ba26..3128fda35 100644
--- a/docs/_static/img/logo-name-medium.svg
+++ b/docs/_static/img/logo-name-medium.svg
@@ -1 +1 @@
-
+
diff --git a/docs/_static/img/logo-vertical-dark.svg b/docs/_static/img/logo-vertical-dark.svg
new file mode 100644
index 000000000..25dcefc17
--- /dev/null
+++ b/docs/_static/img/logo-vertical-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/logo-vertical-light.svg b/docs/_static/img/logo-vertical-light.svg
new file mode 100644
index 000000000..1cb22644d
--- /dev/null
+++ b/docs/_static/img/logo-vertical-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/logo-vertical-medium.svg b/docs/_static/img/logo-vertical-medium.svg
new file mode 100644
index 000000000..db2fcaefe
--- /dev/null
+++ b/docs/_static/img/logo-vertical-medium.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/_static/img/python-graphblas-logo.svg b/docs/_static/img/python-graphblas-logo.svg
new file mode 100644
index 000000000..2422973ff
--- /dev/null
+++ b/docs/_static/img/python-graphblas-logo.svg
@@ -0,0 +1 @@
+
diff --git a/docs/conf.py b/docs/conf.py
index 2e6f616d8..283f6d047 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -55,14 +55,16 @@
#
html_theme = "pydata_sphinx_theme"
+html_favicon = "_static/img/python-graphblas-logo.svg"
+
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_theme_options = {
"logo": {
- "image_light": "_static/img/logo-name-light.svg",
- "image_dark": "_static/img/logo-name-dark.svg",
+ "image_light": "_static/img/logo-horizontal-light.svg",
+ "image_dark": "_static/img/logo-horizontal-dark.svg",
},
"github_url": "https://github.com/python-graphblas/python-graphblas",
}
diff --git a/environment.yml b/environment.yml
index 1a7fb6fa8..4455f4ac6 100644
--- a/environment.yml
+++ b/environment.yml
@@ -48,6 +48,9 @@ dependencies:
- numpydoc
- pydata-sphinx-theme
- sphinx-panels
+ # For building logo
+ - drawsvg
+ - cairosvg
# EXTRA (optional; uncomment as desired)
# - autoflake
# - black
diff --git a/notebooks/logos_and_colors.ipynb b/notebooks/logos_and_colors.ipynb
new file mode 100644
index 000000000..7b64a2208
--- /dev/null
+++ b/notebooks/logos_and_colors.ipynb
@@ -0,0 +1,1467 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "1ade2e62-38f4-4017-a0d3-e09f8587c376",
+ "metadata": {},
+ "source": [
+ "# Logos and Color Palette of Python-graphblas\n",
+ "\n",
+ "To create a minimal environment to run this notebook:\n",
+ "```bash\n",
+ "$ conda create -n drawsvg -c conda-forge drawsvg cairosvg scipy jupyter\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "bf42676c-190a-4803-a567-09e0ed260d6a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import drawsvg as draw\n",
+ "import numpy as np\n",
+ "from scipy.spatial.transform import Rotation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "876a6128-94e4-4fb0-938d-0980a2033701",
+ "metadata": {},
+ "source": [
+ "## Define color palette"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "786f9c9e-d999-4286-bf79-009ca1681604",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# primary\n",
+ "blue = \"#409DC1\"\n",
+ "orange = \"#FF8552\"\n",
+ "dark_gray = \"#39393A\"\n",
+ "light_gray = \"#C3C3C7\"\n",
+ "\n",
+ "# Neutral, light/dark compatible\n",
+ "medium_gray = \"#848487\"\n",
+ "\n",
+ "# secondary\n",
+ "light_blue = \"#81B7CC\"\n",
+ "light_orange = \"#FFBB9E\"\n",
+ "red = \"#6D213C\"\n",
+ "light_red = \"#BA708A\"\n",
+ "green = \"#85FFC7\"\n",
+ "\n",
+ "french_rose = \"#FA4B88\" # ;)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "adb66550-f1e8-4846-a12a-e178fe801295",
+ "metadata": {},
+ "source": [
+ "## Display color palette"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "983b0cb8-db8b-4ad0-ad5a-36975d59289e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "Primary\n",
+ "\n",
+ "#409DC1\n",
+ "\n",
+ "#FF8552\n",
+ "\n",
+ "#39393A\n",
+ "\n",
+ "#C3C3C7\n",
+ "\n",
+ "#848487\n",
+ "Secondary\n",
+ "\n",
+ "#81B7CC\n",
+ "\n",
+ "#FFBB9E\n",
+ "\n",
+ "#6D213C\n",
+ "\n",
+ "#BA708A\n",
+ "\n",
+ "#85FFC7\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "d = draw.Drawing(750, 500, origin=\"center\")\n",
+ "d.append(\n",
+ " draw.Rectangle(-375, -250, 750, 500, fill=\"white\")\n",
+ ") # Add `stroke=\"black\"` border to see boundaries for testing\n",
+ "\n",
+ "dy = 25\n",
+ "dx = 0\n",
+ "w = h = 150\n",
+ "b = 25\n",
+ "x = -400 + 62.5 + dx\n",
+ "y = -200 + dy\n",
+ "\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " \"Primary\",\n",
+ " x=x + 1.5 * (b + w) + w / 2,\n",
+ " y=y - b,\n",
+ " font_size=1.5 * b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x, y, w, h, fill=blue))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " blue.upper(),\n",
+ " x=x + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + b + w, y, w, h, fill=orange))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " orange.upper(),\n",
+ " x=x + (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + 2 * (b + w), y, w, h, fill=dark_gray))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " dark_gray.upper(),\n",
+ " x=x + 2 * (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"white\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + 3 * (b + w), y, w, h, fill=light_gray))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " light_gray.upper(),\n",
+ " x=x + 3 * (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "d.draw(draw.Rectangle(x, -25 + dy, 675, 45, fill=medium_gray))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " medium_gray.upper(),\n",
+ " x=x + 675 / 2,\n",
+ " y=-25 + 30 + dy,\n",
+ " font_size=22.5,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "y = 40 + dy\n",
+ "w = h = 119\n",
+ "b = 20\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " \"Secondary\",\n",
+ " x=x + 2 * (b + w) + w / 2,\n",
+ " y=y + h + 2 * b,\n",
+ " font_size=1.5 * b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x, y, w, h, fill=light_blue))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " light_blue.upper(),\n",
+ " x=x + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + b + w, y, w, h, fill=light_orange))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " light_orange.upper(),\n",
+ " x=x + (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + 2 * (b + w), y, w, h, fill=red))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " red.upper(),\n",
+ " x=x + 2 * (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"white\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + 3 * (b + w), y, w, h, fill=light_red))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " light_red.upper(),\n",
+ " x=x + 3 * (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "d.draw(draw.Rectangle(x + 4 * (b + w), y, w, h, fill=green))\n",
+ "d.draw(\n",
+ " draw.Text(\n",
+ " green.upper(),\n",
+ " x=x + 4 * (b + w) + w / 2,\n",
+ " y=y + h - b,\n",
+ " font_size=b,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Arial\",\n",
+ " fill=\"black\",\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "color_palette = d\n",
+ "d"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e59c3941-c73b-455e-88f2-4b3aae228421",
+ "metadata": {},
+ "source": [
+ "## Display color wheel"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "c27e8ef2-04f2-4752-9c3b-cf297a0c87a5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def create_color_wheel(color_wheel):\n",
+ " d = draw.Drawing(300, 300, origin=\"center\")\n",
+ " theta = np.pi / 3\n",
+ "\n",
+ " angle = 0\n",
+ " for i, color in enumerate(color_wheel):\n",
+ " angle = i * np.pi / 3\n",
+ " clip = draw.ClipPath()\n",
+ " if i == 5:\n",
+ " angle_offset = theta\n",
+ " else:\n",
+ " angle_offset = theta * 1.05\n",
+ " clip.append(\n",
+ " draw.Lines(\n",
+ " 0,\n",
+ " 0,\n",
+ " 300 * np.sin(angle),\n",
+ " 300 * np.cos(angle),\n",
+ " 300 * np.sin(angle + angle_offset),\n",
+ " 300 * np.cos(angle + angle_offset),\n",
+ " close=True,\n",
+ " )\n",
+ " )\n",
+ " if i == 0:\n",
+ " clip = None\n",
+ " d.append(draw.Circle(0, 0, 145, fill=color, clip_path=clip))\n",
+ "\n",
+ " angle = 3 * theta\n",
+ " for i, color in enumerate(color_wheel):\n",
+ " angle = ((i + 3) % 6) * np.pi / 3\n",
+ " clip = draw.ClipPath()\n",
+ " if i == 5:\n",
+ " angle_offset = theta\n",
+ " else:\n",
+ " angle_offset = theta * 1.05\n",
+ " clip.append(\n",
+ " draw.Lines(\n",
+ " 0,\n",
+ " 0,\n",
+ " 300 * np.sin(angle),\n",
+ " 300 * np.cos(angle),\n",
+ " 300 * np.sin(angle + angle_offset),\n",
+ " 300 * np.cos(angle + angle_offset),\n",
+ " close=True,\n",
+ " )\n",
+ " )\n",
+ " if i == 0:\n",
+ " clip = None\n",
+ " d.append(draw.Circle(0, 0, 105, fill=color, clip_path=clip))\n",
+ "\n",
+ " angle = theta\n",
+ " for i, color in enumerate(color_wheel):\n",
+ " angle = ((i + 1) % 6) * np.pi / 3\n",
+ " clip = draw.ClipPath()\n",
+ " if i == 5:\n",
+ " angle_offset = theta\n",
+ " else:\n",
+ " angle_offset = theta * 1.05\n",
+ " clip.append(\n",
+ " draw.Lines(\n",
+ " 0,\n",
+ " 0,\n",
+ " 300 * np.sin(angle),\n",
+ " 300 * np.cos(angle),\n",
+ " 300 * np.sin(angle + angle_offset),\n",
+ " 300 * np.cos(angle + angle_offset),\n",
+ " close=True,\n",
+ " )\n",
+ " )\n",
+ " if i == 0:\n",
+ " clip = None\n",
+ " d.append(draw.Circle(0, 0, 65, fill=color, clip_path=clip))\n",
+ "\n",
+ " d.append(draw.Circle(0, 0, 25, fill=medium_gray))\n",
+ " return d"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "2564bf63-8293-4828-8e38-d00a3b96b067",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Standard\n",
+ "standard_wheel = create_color_wheel(\n",
+ " [\n",
+ " blue,\n",
+ " light_gray,\n",
+ " light_blue,\n",
+ " dark_gray,\n",
+ " orange,\n",
+ " light_orange,\n",
+ " ]\n",
+ ")\n",
+ "standard_wheel"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "7a500a39-4114-49bb-aa19-912c6a8a8d95",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# High contrast\n",
+ "high_wheel = create_color_wheel(\n",
+ " [\n",
+ " light_gray,\n",
+ " blue,\n",
+ " green,\n",
+ " dark_gray,\n",
+ " orange,\n",
+ " red,\n",
+ " ]\n",
+ ")\n",
+ "high_wheel"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "8f404efe-2b88-4bdf-9102-2e6ad9389ca3",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Low contrast\n",
+ "low_wheel = create_color_wheel(\n",
+ " [\n",
+ " green,\n",
+ " light_red,\n",
+ " orange,\n",
+ " light_blue,\n",
+ " light_orange,\n",
+ " blue,\n",
+ " ]\n",
+ ")\n",
+ "low_wheel"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "fd913698-ea45-4219-8003-0fd30124d091",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Warm :)\n",
+ "warm_wheel = create_color_wheel(\n",
+ " [\n",
+ " light_gray, # or dark_gray\n",
+ " light_red,\n",
+ " french_rose, # ;)\n",
+ " red,\n",
+ " orange,\n",
+ " light_orange,\n",
+ " ]\n",
+ ")\n",
+ "warm_wheel"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "c7a3a5e6-4be4-4def-9687-00d1e3f80375",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Cool\n",
+ "cool_wheel = create_color_wheel(\n",
+ " [\n",
+ " light_blue,\n",
+ " light_gray,\n",
+ " blue,\n",
+ " light_red,\n",
+ " green,\n",
+ " dark_gray,\n",
+ " ]\n",
+ ")\n",
+ "cool_wheel"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "343256c8-35a7-4c89-aa60-c6bf60930c09",
+ "metadata": {},
+ "source": [
+ "## Create logos"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "7855cd3f-8155-4d11-9730-b6041578e112",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "default_angles = [\n",
+ " 180, # Don't modify this\n",
+ " 30, # How much of the \"left face\" to see\n",
+ " 22.5, # How much of the \"top face\" to see\n",
+ "]\n",
+ "R = Rotation.from_euler(\"ZYX\", default_angles, degrees=True).as_matrix()\n",
+ "\n",
+ "gcube = np.array(\n",
+ " [\n",
+ " [-1, 1, -1],\n",
+ " [-1, 1, 1],\n",
+ " [1, 1, 1],\n",
+ " [-1, -1, 1],\n",
+ " [1, -1, 1],\n",
+ " [1, 0, 1],\n",
+ " [0, 0, 1],\n",
+ " ]\n",
+ ")\n",
+ "gcube_major = gcube[:5] # Big circles\n",
+ "gcube_minor = gcube[5:] # Small circles\n",
+ "lines = np.array(\n",
+ " [\n",
+ " [gcube[1], gcube[0]],\n",
+ " ]\n",
+ ")\n",
+ "Gpath = np.array(\n",
+ " [\n",
+ " gcube[2],\n",
+ " gcube[1],\n",
+ " gcube[3],\n",
+ " gcube[4],\n",
+ " gcube[5],\n",
+ " gcube[6],\n",
+ " ]\n",
+ ")\n",
+ "\n",
+ "\n",
+ "def create_logo(\n",
+ " *,\n",
+ " bracket_color=None,\n",
+ " bg_color=None,\n",
+ " edge_color=None,\n",
+ " edge_width=8,\n",
+ " edge_border_color=\"white\",\n",
+ " edge_border_width=16,\n",
+ " node_color=None,\n",
+ " large_node_width=16,\n",
+ " small_node_width=8,\n",
+ " node_border_color=\"white\",\n",
+ " node_stroke_width=4,\n",
+ " large_border=True,\n",
+ " g_color=None,\n",
+ " angles=None,\n",
+ "):\n",
+ " if angles is None:\n",
+ " angles = default_angles\n",
+ " if edge_color is None:\n",
+ " edge_color = blue\n",
+ " if bracket_color is None:\n",
+ " bracket_color = edge_color\n",
+ " if node_color is None:\n",
+ " node_color = orange\n",
+ " if g_color is None:\n",
+ " g_color = edge_color\n",
+ "\n",
+ " d = draw.Drawing(190, 190, origin=\"center\")\n",
+ " if bg_color:\n",
+ " d.append(\n",
+ " draw.Rectangle(-95, -95, 190, 190, fill=bg_color)\n",
+ " ) # Add `stroke=\"black\"` border to see boundaries for testing\n",
+ "\n",
+ " scale = 40\n",
+ " dx = 0\n",
+ " dy = -2\n",
+ "\n",
+ " if edge_border_width:\n",
+ " # Add white border around lines\n",
+ " d.append(\n",
+ " draw.Lines(\n",
+ " *(((Gpath @ R) * scale)[:, :2] * [-1, 1]).ravel().tolist(),\n",
+ " fill=\"none\",\n",
+ " stroke=edge_border_color,\n",
+ " stroke_width=edge_border_width,\n",
+ " )\n",
+ " )\n",
+ " for (x0, y0, z0), (x1, y1, z1) in ((lines @ R) * scale).tolist():\n",
+ " x0 = -x0\n",
+ " x1 = -x1 # Just live with this\n",
+ " d.append(\n",
+ " draw.Line(\n",
+ " x0 + dx,\n",
+ " y0 + dy,\n",
+ " x1 + dx,\n",
+ " y1 + dy,\n",
+ " stroke=edge_border_color,\n",
+ " stroke_width=edge_border_width,\n",
+ " )\n",
+ " )\n",
+ "\n",
+ " # Add edges\n",
+ " d.append(\n",
+ " draw.Lines(\n",
+ " *(((Gpath @ R) * scale)[:, :2] * [-1, 1]).ravel().tolist(),\n",
+ " fill=\"none\",\n",
+ " stroke=g_color,\n",
+ " stroke_width=edge_width,\n",
+ " )\n",
+ " )\n",
+ " for (x0, y0, z0), (x1, y1, z1) in ((lines @ R) * scale).tolist():\n",
+ " x0 = -x0\n",
+ " x1 = -x1\n",
+ " d.append(\n",
+ " draw.Line(\n",
+ " x0 + dx, y0 + dy, x1 + dx, y1 + dy, stroke=edge_color, stroke_width=edge_width\n",
+ " )\n",
+ " )\n",
+ "\n",
+ " # Add vertices\n",
+ " for x, y, z in ((gcube_major @ R) * scale).tolist():\n",
+ " x = -x\n",
+ " d.append(\n",
+ " draw.Circle(\n",
+ " x + dx,\n",
+ " y + dy,\n",
+ " large_node_width,\n",
+ " fill=node_color,\n",
+ " stroke=node_border_color,\n",
+ " stroke_width=node_stroke_width if large_border else 0,\n",
+ " )\n",
+ " )\n",
+ " for x, y, z in ((gcube_minor @ R) * scale).tolist():\n",
+ " x = -x\n",
+ " d.append(\n",
+ " draw.Circle(\n",
+ " x + dx,\n",
+ " y + dy,\n",
+ " small_node_width,\n",
+ " fill=node_color,\n",
+ " stroke=node_border_color,\n",
+ " stroke_width=node_stroke_width,\n",
+ " )\n",
+ " )\n",
+ "\n",
+ " # Add brackets\n",
+ " d.append(\n",
+ " draw.Text(\n",
+ " \"[\",\n",
+ " x=-85,\n",
+ " y=52,\n",
+ " font_size=214,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Courier New\",\n",
+ " fill=bracket_color,\n",
+ " )\n",
+ " )\n",
+ " d.append(\n",
+ " draw.Text(\n",
+ " \"]\",\n",
+ " x=85,\n",
+ " y=52,\n",
+ " font_size=214,\n",
+ " text_anchor=\"middle\",\n",
+ " font_family=\"Courier New\",\n",
+ " fill=bracket_color,\n",
+ " )\n",
+ " )\n",
+ "\n",
+ " return d"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "4325e0b8-dbbb-4219-a2b3-4d9cdee2bdc8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "logo_defaults = dict(\n",
+ " bracket_color=blue,\n",
+ " edge_color=blue,\n",
+ " node_color=orange,\n",
+ " edge_border_width=0,\n",
+ " edge_width=12,\n",
+ " small_node_width=11,\n",
+ " large_node_width=17,\n",
+ " node_border_color=\"none\",\n",
+ " node_stroke_width=0,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "f886df89-b3b5-4671-bcc0-98e8705feb5a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "create_logo(bg_color=\"white\", **logo_defaults)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "68e01137-55e3-4973-bf97-4fcd36c8c662",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "create_logo(bg_color=\"black\", **logo_defaults)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "b1d5e928-16c5-4377-aee1-1489ab45efc8",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Transparent background\n",
+ "logo = create_logo(**logo_defaults)\n",
+ "logo"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b187c131-d337-4a7b-ab54-80ebe0f48ab4",
+ "metadata": {},
+ "source": [
+ "## Alternatives with gray brackets\n",
+ "### Background-agnostic (works with light and dark mode)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "acca9b2e-2f54-4b86-9a33-2c57502f6160",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "medium_logo = create_logo(**{**logo_defaults, \"bracket_color\": medium_gray})\n",
+ "create_logo(bg_color=\"white\", **{**logo_defaults, \"bracket_color\": medium_gray})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "f5d0086d-b50e-49eb-9aae-b0953cdc0045",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "create_logo(bg_color=\"black\", **{**logo_defaults, \"bracket_color\": medium_gray})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c4dce89d-e34c-4190-a068-7e78cdeea745",
+ "metadata": {},
+ "source": [
+ "### For light mode"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "27137343-141a-422e-abd6-123af3416ea4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "light_logo = create_logo(**{**logo_defaults, \"bracket_color\": dark_gray})\n",
+ "create_logo(bg_color=\"white\", **{**logo_defaults, \"bracket_color\": dark_gray})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8a70b0f7-c3c4-44ae-af09-8992400f362e",
+ "metadata": {},
+ "source": [
+ "### For dark mode"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "3ab9bb40-d7a8-4788-9971-54a5779d284d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "[\n",
+ "]\n",
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dark_logo = create_logo(**{**logo_defaults, \"bracket_color\": light_gray})\n",
+ "create_logo(bg_color=\"black\", **{**logo_defaults, \"bracket_color\": light_gray})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "d53046c1-8cbb-47fa-a88b-4d98958df26b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "if False:\n",
+ " logo.save_svg(\"python-graphblas-logo.svg\")\n",
+ " light_logo.save_svg(\"python-graphblas-logo-light.svg\")\n",
+ " medium_logo.save_svg(\"python-graphblas-logo-medium.svg\")\n",
+ " dark_logo.save_svg(\"python-graphblas-logo-dark.svg\")\n",
+ " color_palette.save_svg(\"color-palette.svg\")\n",
+ " standard_wheel.save_svg(\"color-wheel.svg\")\n",
+ " high_wheel.save_svg(\"color-wheel-high.svg\")\n",
+ " low_wheel.save_svg(\"color-wheel-low.svg\")\n",
+ " warm_wheel.save_svg(\"color-wheel-warm.svg\")\n",
+ " cool_wheel.save_svg(\"color-wheel-cool.svg\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "51093fab-600b-47d7-9809-fa0f16e7246f",
+ "metadata": {},
+ "source": [
+ "### *NOTE: The font in the SVG files should be converted to paths, because not all systems have Courier New*\n",
+ "Also, SVG files can be minified here: https://vecta.io/nano"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/scripts/check_versions.sh b/scripts/check_versions.sh
index a76fee1d2..9051ebe6e 100755
--- a/scripts/check_versions.sh
+++ b/scripts/check_versions.sh
@@ -4,12 +4,12 @@
# This may be helpful when updating dependency versions in CI.
# Tip: add `--json` for more information.
conda search 'flake8-bugbear[channel=conda-forge]>=23.9.16'
-conda search 'flake8-simplify[channel=conda-forge]>=0.20.0'
+conda search 'flake8-simplify[channel=conda-forge]>=0.21.0'
conda search 'numpy[channel=conda-forge]>=1.26.0'
conda search 'pandas[channel=conda-forge]>=2.1.1'
-conda search 'scipy[channel=conda-forge]>=1.11.2'
+conda search 'scipy[channel=conda-forge]>=1.11.3'
conda search 'networkx[channel=conda-forge]>=3.1'
-conda search 'awkward[channel=conda-forge]>=2.4.3'
+conda search 'awkward[channel=conda-forge]>=2.4.4'
conda search 'sparse[channel=conda-forge]>=0.14.0'
conda search 'fast_matrix_market[channel=conda-forge]>=1.7.3'
conda search 'numba[channel=conda-forge]>=0.57.1'