Skip to content
Draft
54 changes: 54 additions & 0 deletions .github/workflows/pydoclint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: pydoclint (report only)

on: [push, pull_request]

jobs:
pydoclint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install pydoclint
run: pip install pydoclint

- name: Run pydoclint
run: |
echo "## pydoclint report" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"

exit_code=0
for f in $(find graphix -name '*.py' | sort); do
output=$(pydoclint "$f" --style=numpy 2>&1) || true

# Check for actual parser crash (traceback in output)
if echo "$output" | grep -q "^Traceback"; then
echo "### ❌ CRASH: \`$f\`" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "$output" | tail -20 >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
exit_code=2
continue
fi

# Check for violations (lines matching " 123: DOC456:")
violations=$(echo "$output" | grep -E '^\s+[0-9]+: DOC[0-9]{3}:' || true)
if [ -n "$violations" ]; then
count=$(echo "$violations" | wc -l)
echo "<details><summary><code>$f</code> — $count violation(s)</summary>" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "$violations" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "</details>" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
fi
done

if [ "$exit_code" -eq 2 ]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "**Job failed due to parser crash(es). Fix the docstring(s) above.**" >> "$GITHUB_STEP_SUMMARY"
exit 1
fi

echo "" >> "$GITHUB_STEP_SUMMARY"
echo "_Violations are reported but do not fail the job._" >> "$GITHUB_STEP_SUMMARY"
15 changes: 14 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
# Non-fixable
- id: check-case-conflict
Expand All @@ -10,3 +10,16 @@ repos:
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.1
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-format

# - repo: https://github.com/jsh9/pydoclint
# rev: 0.8.3
# hooks:
# - id: pydoclint
# args: ["--style=numpy", "--exclude=examples/|benchmarks/"]
18 changes: 8 additions & 10 deletions graphix/_linalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ def __new__(cls, data: npt.ArrayLike, copy: bool = True) -> Self:

Parameters
----------
data : array
data : npt.ArrayLike
Data in array
copy : bool
Optional, defaults to `True`. If `False` and if possible, data
is not copied.

Return
-------
MatGF2
MatGF2
New `MatGF2` object.
"""
arr = np.array(data, dtype=np.uint8, copy=copy)
return super().__new__(cls, shape=arr.shape, dtype=arr.dtype, buffer=arr)
Expand All @@ -38,7 +39,7 @@ def mat_mul(self, other: MatGF2 | npt.NDArray[np.uint8]) -> MatGF2:

Parameters
----------
other : array
other : MatGF2 | npt.NDArray[np.uint8]
Matrix that right-multiplies `self`.

Returns
Expand Down Expand Up @@ -67,7 +68,7 @@ def compute_rank(self) -> np.intp:

Returns
-------
int : int
int
Rank of the matrix.
"""
mat_a = self.row_reduction(copy=True)
Expand All @@ -78,10 +79,8 @@ def right_inverse(self) -> MatGF2 | None:

Returns
-------
rinv : MatGF2
Any right inverse of the matrix.
or `None`
If the matrix does not have a right inverse.
rinv : MatGF2 | None
Any right inverse of the matrix. None if the matrix does not have a right inverse.

Notes
-----
Expand All @@ -101,12 +100,11 @@ def right_inverse(self) -> MatGF2 | None:
# We don't use `MatGF2.compute_rank()` to avoid row-reducing twice.
if m != np.count_nonzero(red[:, :n].any(axis=1)):
return None
rinv = np.zeros((n, m), dtype=np.uint8).view(MatGF2)
rinv: MatGF2 = np.zeros((n, m), dtype=np.uint8).view(MatGF2)

for i, row in enumerate(red):
j = np.flatnonzero(row)[0] # Column index corresponding to the leading 1 in row `i`.
rinv[j, :] = red[i, n:]

return rinv

def null_space(self) -> MatGF2:
Expand Down
20 changes: 16 additions & 4 deletions graphix/graphsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,8 @@ def draw(self, fill_color: str = "C0") -> None:

Parameters
----------
fill_color : str
optional, fill color of nodes
fill_color : str (optional)
fill color of nodes
"""
nqubit = len(self.nodes)
nodes = list(self.nodes)
Expand All @@ -492,7 +492,13 @@ def draw(self, fill_color: str = "C0") -> None:
nx.draw(g, labels=labels, node_color=colors, edgecolors="k")

def to_statevector(self) -> Statevec:
"""Convert the graph state into a state vector."""
"""Convert the graph state into a state vector.

Returns
-------
gstate : Statevec
state vector representation of the graph state
"""
node_list = list(self.nodes)
nqubit = len(self.nodes)
gstate = Statevec(nqubit=nqubit)
Expand All @@ -513,5 +519,11 @@ def to_statevector(self) -> Statevec:
return gstate

def isolated_nodes(self) -> list[int]:
"""Return a list of isolated nodes (nodes with no edges)."""
"""Return a list of isolated nodes (nodes with no edges).

Returns
-------
list[int]
list of isolated nodes
"""
return list(nx.isolates(self))
2 changes: 1 addition & 1 deletion graphix/sim/base_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def vdot(a: Matrix, b: Matrix) -> ExpressionOrComplex:

Returns
-------
ExpressionOrFloat
ExpressionOrComplex
Dot product.

Raises
Expand Down
Loading
Loading