From 41f949b2846c2476f52649deb879a8ff9b4966a2 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 16:06:42 +0100 Subject: [PATCH 1/7] testing pydoclint CI --- .github/workflows/pydoclint.yml | 91 +++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 15 +++++- graphix/_linalg.py | 18 +++---- graphix/sim/base_backend.py | 2 +- pyproject.toml | 8 ++- 5 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/pydoclint.yml diff --git a/.github/workflows/pydoclint.yml b/.github/workflows/pydoclint.yml new file mode 100644 index 000000000..c932f8237 --- /dev/null +++ b/.github/workflows/pydoclint.yml @@ -0,0 +1,91 @@ +name: pydoclint (report only) + +on: [push, pull_request] + +jobs: + pydoclint: + name: pydoclint (report only) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install pydoclint + run: | + python -m pip install --upgrade pip + python -m pip install pydoclint -c requirements-dev.txt + + - name: Run pydoclint and save raw output (allow violations) + continue-on-error: true + run: | + pydoclint . --style=numpy 2>&1 | tee pydoclint-report.txt || true + + - name: Produce JSON summary and fail only on parser crash + run: | + python - <<'PY' + import re, json, sys, pathlib + p = pathlib.Path('pydoclint-report.txt') + text = p.read_text(encoding='utf-8') if p.exists() else '' + # detect parser crash + crash = False + crash_tail = [] + if 'Traceback' in text or 'ValueError' in text or 'Exception' in text: + crash = True + crash_tail = text.strip().splitlines()[-200:] + # parse violations: map file -> {code: count} + violations = {} + totals = {} + current_file = None + file_line_re = re.compile(r'^(?:\./)?([^\s].*\.py)\s*$') + violation_re = re.compile(r'^\s*(\d+):\s*(DOC\d{3}):') + for line in text.splitlines(): + mfile = file_line_re.match(line) + if mfile: + current_file = mfile.group(1) + continue + mv = violation_re.match(line) + if mv and current_file: + code = mv.group(2) + violations.setdefault(current_file, {}) + violations[current_file].setdefault(code, 0) + violations[current_file][code] += 1 + totals.setdefault(code, 0) + totals[code] += 1 + summary = { + "crash": crash, + "crash_tail": crash_tail, + "violations": violations, + "totals": totals, + } + with open('pydoclint-summary.json', 'w', encoding='utf-8') as fh: + json.dump(summary, fh, indent=2) + print("pydoclint summary written to pydoclint-summary.json") + if crash: + print("pydoclint parser crash detected; failing job.") + for l in crash_tail[-50:]: + print(l) + sys.exit(1) + else: + print("No parser crash detected; keeping job green for lint violations.") + PY + + - name: Upload pydoclint raw output + if: always() + uses: actions/upload-artifact@v4 + with: + name: pydoclint-raw + path: pydoclint-report.txt + + - name: Upload pydoclint JSON summary + if: always() + uses: actions/upload-artifact@v4 + with: + name: pydoclint-summary + path: pydoclint-summary.json \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4d4cda8d..c01d6c8a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 @@ -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/"] diff --git a/graphix/_linalg.py b/graphix/_linalg.py index 3667b597c..2e1e75e65 100644 --- a/graphix/_linalg.py +++ b/graphix/_linalg.py @@ -20,7 +20,7 @@ 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 @@ -28,7 +28,8 @@ def __new__(cls, data: npt.ArrayLike, copy: bool = True) -> Self: 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) @@ -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 @@ -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) @@ -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 ----- @@ -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: diff --git a/graphix/sim/base_backend.py b/graphix/sim/base_backend.py index 6cef7e33e..2f01da7c1 100644 --- a/graphix/sim/base_backend.py +++ b/graphix/sim/base_backend.py @@ -158,7 +158,7 @@ def vdot(a: Matrix, b: Matrix) -> ExpressionOrComplex: Returns ------- - ExpressionOrFloat + ExpressionOrComplex Dot product. Raises diff --git a/pyproject.toml b/pyproject.toml index 5288e18c0..39f09a22c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ extend-ignore = [ "FIX", # Fixme "PLR091", # Too many XXX "PLR0904", # Too many public methods - "PLR2004", # Magic vavlue comparison + "PLR2004", # Magic value comparison "S101", # assert "T20", # print "TD", # Todo @@ -125,6 +125,12 @@ ban-relative-imports = "all" [tool.ruff.lint.isort] required-imports = ["from __future__ import annotations"] +[tool.pydoclint] +style = "numpy" +check-arg-order = true +check-return = true +check-yield = true + [tool.pytest.ini_options] addopts = ["--ignore=examples", "--ignore=docs", "--ignore=benchmarks", "--benchmark-autosave"] # Silence cotengra warning From 7f6a3721b35e2c0f2e86991f269089db8696b2e5 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 16:44:23 +0100 Subject: [PATCH 2/7] fixing crashes in pydoclint --- .github/workflows/pydoclint.yml | 4 +- graphix/graphsim.py | 20 +++- graphix/sim/density_matrix.py | 161 ++++++++++++++++++++++++-------- 3 files changed, 139 insertions(+), 46 deletions(-) diff --git a/.github/workflows/pydoclint.yml b/.github/workflows/pydoclint.yml index c932f8237..b5d73d6c8 100644 --- a/.github/workflows/pydoclint.yml +++ b/.github/workflows/pydoclint.yml @@ -25,7 +25,7 @@ jobs: - name: Run pydoclint and save raw output (allow violations) continue-on-error: true run: | - pydoclint . --style=numpy 2>&1 | tee pydoclint-report.txt || true + pydoclint . --style=numpy -v 2>&1 | tee pydoclint-report.txt || true - name: Produce JSON summary and fail only on parser crash run: | @@ -88,4 +88,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: pydoclint-summary - path: pydoclint-summary.json \ No newline at end of file + path: pydoclint-summary.json diff --git a/graphix/graphsim.py b/graphix/graphsim.py index 186392c73..8e8522be1 100644 --- a/graphix/graphsim.py +++ b/graphix/graphsim.py @@ -469,8 +469,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) @@ -490,7 +490,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) @@ -511,5 +517,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)) diff --git a/graphix/sim/density_matrix.py b/graphix/sim/density_matrix.py index cb5570e2a..bfbda4f12 100644 --- a/graphix/sim/density_matrix.py +++ b/graphix/sim/density_matrix.py @@ -33,7 +33,18 @@ class DensityMatrix(DenseState): - """DensityMatrix object.""" + """DensityMatrix object. + + Attributes + ---------- + rho : Matrix + density matrix + + See Also + -------- + graphix.statevec.Statevec + + """ rho: Matrix @@ -106,7 +117,13 @@ def cast_row( @property def nqubit(self) -> int: - """Return the number of qubits.""" + """Return the number of qubits. + + Returns + ------- + int + number of qubits + """ # Circumvent typing bug with numpy>=2.3 # `shape` field is typed `tuple[Any, ...]` instead of `tuple[int, ...]` # See https://github.com/numpy/numpy/issues/29830 @@ -114,7 +131,13 @@ def nqubit(self) -> int: return nqubit def __str__(self) -> str: - """Return a string description.""" + """Return a string description. + + Returns + ------- + str + string description + """ return f"DensityMatrix object, with density matrix {self.rho} and shape {self.dims()}." @override @@ -127,7 +150,7 @@ def add_nodes(self, nqubit: int, data: Data) -> None: nqubit : int The number of qubits to add to the density matrix. - data : Data, optional + data : Data The state in which to initialize the newly added nodes. - If a single basic state is provided, all new nodes are initialized in that state. @@ -141,6 +164,7 @@ def add_nodes(self, nqubit: int, data: Data) -> None: Notes ----- Previously existing nodes remain unchanged. + """ dm_to_add = DensityMatrix(nqubit=nqubit, data=data) self.tensor(dm_to_add) @@ -151,10 +175,10 @@ def evolve_single(self, op: Matrix, i: int) -> None: Parameters ---------- - op : np.ndarray - 2*2 matrix. - i : int - Index of qubit to apply operator. + op : Matrix + 2*2 matrix. + i : int + Index of qubit to apply operator. """ assert i >= 0 assert i < self.nqubit @@ -171,8 +195,10 @@ def evolve(self, op: Matrix, qargs: Sequence[int]) -> None: """Multi-qubit operation. Args: - op (np.array): 2^n*2^n matrix - qargs (list of ints): target qubits' indexes + op : Matrix + 2^n*2^n matrix + qargs : list of ints + target qubits' indexes """ d = op.shape # check it is a matrix. @@ -218,13 +244,17 @@ def evolve(self, op: Matrix, qargs: Sequence[int]) -> None: def expectation_single(self, op: Matrix, loc: int) -> complex: """Return the expectation value of single-qubit operator. - Args: - op (np.array): 2*2 Hermite operator - loc (int): Index of qubit on which to apply operator. + Parameters + ---------- + op : Matrix + 2*2 matrix. + loc : int + Index of qubit to apply operator. Returns ------- - complex: expectation value (real for hermitian ops!). + complex + expectation value (real for hermitian ops). """ if not (0 <= loc < self.nqubit): raise ValueError(f"Wrong target qubit {loc}. Must between 0 and {self.nqubit - 1}.") @@ -244,7 +274,13 @@ def expectation_single(self, op: Matrix, loc: int) -> complex: return complex(np.trace(rho_tensor.reshape((2**nqubit, 2**nqubit)))) def dims(self) -> tuple[int, ...]: - """Return the dimensions of the density matrix.""" + """Return the dimensions of the density matrix. + + Returns + ------- + tuple[int, int] + dimensions of the density matrix + """ return self.rho.shape def tensor(self, other: DensityMatrix) -> None: @@ -254,8 +290,8 @@ def tensor(self, other: DensityMatrix) -> None: Parameters ---------- - other : :class: `DensityMatrix` object - DensityMatrix object to be tensored with self. + other : DensityMatrix + DensityMatrix object to be tensored with self. """ if not isinstance(other, DensityMatrix): other = DensityMatrix(other) @@ -266,8 +302,8 @@ def cnot(self, edge: tuple[int, int]) -> None: Parameters ---------- - edge : (int, int) or [int, int] - Edge to apply CNOT gate. + edge : tuple[int, int] + Edge to apply CNOT gate. """ self.evolve(CNOT_TENSOR.reshape(4, 4), edge) @@ -277,8 +313,8 @@ def swap(self, qubits: tuple[int, int]) -> None: Parameters ---------- - qubits : (int, int) - (control, target) qubits indices. + qubits : tuple[int, int] + (control, target) qubits indices. """ self.evolve(SWAP_TENSOR.reshape(4, 4), qubits) @@ -287,8 +323,8 @@ def entangle(self, edge: tuple[int, int]) -> None: Parameters ---------- - edge : (int, int) or [int, int] - (control, target) qubit indices. + edge : tuple[int, int] + (control, target) qubit indices. """ self.evolve(CZ_TENSOR.reshape(4, 4), edge) @@ -307,7 +343,14 @@ def normalize(self) -> None: @override def remove_qubit(self, qarg: int) -> None: - """Remove a qubit.""" + """Remove a qubit. + + Parameters + ---------- + qarg : int + Index of qubit to remove. + + """ self.ptrace(qarg) self.normalize() @@ -316,8 +359,8 @@ def ptrace(self, qargs: Collection[int] | int) -> None: Parameters ---------- - qargs : list of ints or int - Indices of qubit to trace out. + qargs : list[int] or int + Indices of qubit to trace out. """ n = int(np.log2(self.rho.shape[0])) if isinstance(qargs, int): @@ -341,8 +384,13 @@ def fidelity(self, statevec: Statevec) -> ExpressionOrFloat: Parameters ---------- - statevec : numpy array - statevector (flattened numpy array) to compare with + statevec : Statevec + statevector (flattened numpy array) to compare with + + Returns + ------- + ExpressionOrFloat + fidelity between the density matrix and the reference statevector """ result = vdot(statevec.psi, matmul(self.rho, statevec.psi)) if isinstance(result, Expression): @@ -351,7 +399,13 @@ def fidelity(self, statevec: Statevec) -> ExpressionOrFloat: return result.real def flatten(self) -> Matrix: - """Return flattened density matrix.""" + """Return flattened density matrix. + + Returns + ------- + Matrix + flattened density matrix + """ return self.rho.flatten() def apply_channel(self, channel: KrausChannel, qargs: Sequence[int]) -> None: @@ -359,21 +413,18 @@ def apply_channel(self, channel: KrausChannel, qargs: Sequence[int]) -> None: Parameters ---------- - :rho: density matrix. - channel: :class:`graphix.channel.KrausChannel` object + channel : KrausChannel KrausChannel to be applied to the density matrix - qargs: target qubit indices - - Returns - ------- - nothing + qargs : list[int] + target qubits' indices Raises ------ ValueError If the final density matrix is not normalized after application of the channel. This shouldn't happen since :class:`graphix.channel.KrausChannel` objects are normalized by construction. - .... + TypeError + If the provided channel is not a :class:`graphix.channel.KrausChannel` object. """ result_array = np.zeros((2**self.nqubit, 2**self.nqubit), dtype=np.complex128) @@ -406,13 +457,37 @@ def apply_noise(self, qubits: Sequence[int], noise: Noise) -> None: self.apply_channel(channel, qubits) def subs(self, variable: Parameter, substitute: ExpressionOrSupportsFloat) -> DensityMatrix: - """Return a copy of the density matrix where all occurrences of the given variable in measurement angles are substituted by the given value.""" + """Return a copy of the density matrix where all occurrences of the given variable in measurement angles are substituted by the given value. + + Parameters + ---------- + variable : Parameter + The symbolic expression to be replaced within the measurement angles. + substitute : ExpressionOrSupportsFloat + The value or symbolic expression to substitute in place of `variable`. + + Returns + ------- + DensityMatrix + A new DensityMatrix instance with the specified substitutions applied to the measurement angles. + """ result = copy.copy(self) result.rho = np.vectorize(lambda value: parameter.subs(value, variable, substitute))(self.rho) return result def xreplace(self, assignment: Mapping[Parameter, ExpressionOrSupportsFloat]) -> DensityMatrix: - """Return a copy of the density matrix where all occurrences of the given keys in measurement angles are substituted by the given values in parallel.""" + """Return a copy of the density matrix where all occurrences of the given keys in measurement angles are substituted by the given values in parallel. + + Parameters + ---------- + assignment : Mapping[Parameter, ExpressionOrSupportsFloat] + A dictionary-like mapping where keys are the `Parameter` objects to be replaced and values are the new expressions or numerical values. + + Returns + ------- + DensityMatrix + A new DensityMatrix instance with the specified substitutions applied to the measurement angles. + """ result = copy.copy(self) result.rho = np.vectorize(lambda value: parameter.xreplace(value, assignment))(self.rho) return result @@ -420,6 +495,12 @@ def xreplace(self, assignment: Mapping[Parameter, ExpressionOrSupportsFloat]) -> @dataclass(frozen=True) class DensityMatrixBackend(DenseStateBackend[DensityMatrix]): - """MBQC simulator with density matrix method.""" + """MBQC simulator with density matrix method. + + Attributes + ---------- + state : DensityMatrix + density matrix + """ state: DensityMatrix = dataclasses.field(init=False, default_factory=lambda: DensityMatrix(nqubit=0)) From 7d7c4e710c455d58d490890b282abf3632c839d0 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 16:51:32 +0100 Subject: [PATCH 3/7] fixing crashes in pydoclint --- .github/workflows/pydoclint.yml | 119 +++++++++++--------------------- 1 file changed, 41 insertions(+), 78 deletions(-) diff --git a/.github/workflows/pydoclint.yml b/.github/workflows/pydoclint.yml index b5d73d6c8..7a72b2180 100644 --- a/.github/workflows/pydoclint.yml +++ b/.github/workflows/pydoclint.yml @@ -4,88 +4,51 @@ on: [push, pull_request] jobs: pydoclint: - name: pydoclint (report only) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Cache pip dependencies - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Install pydoclint - run: | - python -m pip install --upgrade pip - python -m pip install pydoclint -c requirements-dev.txt - - - name: Run pydoclint and save raw output (allow violations) - continue-on-error: true - run: | - pydoclint . --style=numpy -v 2>&1 | tee pydoclint-report.txt || true + run: pip install pydoclint - - name: Produce JSON summary and fail only on parser crash + - name: Run pydoclint run: | - python - <<'PY' - import re, json, sys, pathlib - p = pathlib.Path('pydoclint-report.txt') - text = p.read_text(encoding='utf-8') if p.exists() else '' - # detect parser crash - crash = False - crash_tail = [] - if 'Traceback' in text or 'ValueError' in text or 'Exception' in text: - crash = True - crash_tail = text.strip().splitlines()[-200:] - # parse violations: map file -> {code: count} - violations = {} - totals = {} - current_file = None - file_line_re = re.compile(r'^(?:\./)?([^\s].*\.py)\s*$') - violation_re = re.compile(r'^\s*(\d+):\s*(DOC\d{3}):') - for line in text.splitlines(): - mfile = file_line_re.match(line) - if mfile: - current_file = mfile.group(1) - continue - mv = violation_re.match(line) - if mv and current_file: - code = mv.group(2) - violations.setdefault(current_file, {}) - violations[current_file].setdefault(code, 0) - violations[current_file][code] += 1 - totals.setdefault(code, 0) - totals[code] += 1 - summary = { - "crash": crash, - "crash_tail": crash_tail, - "violations": violations, - "totals": totals, - } - with open('pydoclint-summary.json', 'w', encoding='utf-8') as fh: - json.dump(summary, fh, indent=2) - print("pydoclint summary written to pydoclint-summary.json") - if crash: - print("pydoclint parser crash detected; failing job.") - for l in crash_tail[-50:]: - print(l) - sys.exit(1) - else: - print("No parser crash detected; keeping job green for lint violations.") - PY - - - name: Upload pydoclint raw output - if: always() - uses: actions/upload-artifact@v4 - with: - name: pydoclint-raw - path: pydoclint-report.txt - - - name: Upload pydoclint JSON summary - if: always() - uses: actions/upload-artifact@v4 - with: - name: pydoclint-summary - path: pydoclint-summary.json + 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 "
$f — $count violation(s)" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "$violations" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "
" >> "$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" \ No newline at end of file From 7e3020f902b4dc57a4be4652523330cbe74cc4ae Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 16:53:28 +0100 Subject: [PATCH 4/7] fixing crashes in pydoclint --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 39f09a22c..4fca0654d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ extend-ignore = [ "FIX", # Fixme "PLR091", # Too many XXX "PLR0904", # Too many public methods - "PLR2004", # Magic value comparison + "PLR2004", # Magic vavlue comparison "S101", # assert "T20", # print "TD", # Todo From 873a5005385aefb8244bcb453fb8ed5e35a08deb Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 16:55:11 +0100 Subject: [PATCH 5/7] fixing crashes in pydoclint --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c01d6c8a3..4665bc4ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,8 +18,8 @@ repos: args: ["--fix"] - id: ruff-format - - repo: https://github.com/jsh9/pydoclint - rev: 0.8.3 - hooks: - - id: pydoclint - args: ["--style=numpy", "--exclude=examples/|benchmarks/"] + # - repo: https://github.com/jsh9/pydoclint + # rev: 0.8.3 + # hooks: + # - id: pydoclint + # args: ["--style=numpy", "--exclude=examples/|benchmarks/"] From 9550ef4a3af15cfe5fbd210f800930f87dc61b51 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 17:00:44 +0100 Subject: [PATCH 6/7] fixing crashes in pydoclint --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 12c6dcc20..df822dae7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -26,4 +26,4 @@ qiskit_qasm3_import qiskit-aer; python_version < "3.14" openqasm-parser>=3.1.0 -graphix-qasm-parser>=0.1.1 +# graphix-qasm-parser>=0.1.1 From 3fd0957bf9269990cf221b1c34fc6e852237b1f8 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 18 Feb 2026 17:16:19 +0100 Subject: [PATCH 7/7] fixing crashes in pydoclint --- graphix/sim/density_matrix.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graphix/sim/density_matrix.py b/graphix/sim/density_matrix.py index bfbda4f12..77061c701 100644 --- a/graphix/sim/density_matrix.py +++ b/graphix/sim/density_matrix.py @@ -184,7 +184,6 @@ def evolve_single(self, op: Matrix, i: int) -> None: assert i < self.nqubit if op.shape != (2, 2): raise ValueError("op must be 2*2 matrix.") - rho_tensor = self.rho.reshape((2,) * self.nqubit * 2) rho_tensor = tensordot(tensordot(op, rho_tensor, axes=(1, i)), op.conj().T, axes=(i + self.nqubit, 0)) rho_tensor = np.moveaxis(rho_tensor, (0, -1), (i, i + self.nqubit)) @@ -194,7 +193,8 @@ def evolve_single(self, op: Matrix, i: int) -> None: def evolve(self, op: Matrix, qargs: Sequence[int]) -> None: """Multi-qubit operation. - Args: + Parameters + ---------- op : Matrix 2^n*2^n matrix qargs : list of ints @@ -464,7 +464,7 @@ def subs(self, variable: Parameter, substitute: ExpressionOrSupportsFloat) -> De variable : Parameter The symbolic expression to be replaced within the measurement angles. substitute : ExpressionOrSupportsFloat - The value or symbolic expression to substitute in place of `variable`. + The value or symbolic expression to substitute in place of variable. Returns -------