OutreachSandboxAQ / TangeloFix #408
Draft PR #419 open @alexfleury-sb reviewed: "Code is good, 2 things" Both fixes actioned Awaiting re-review

SandboxAQ · Tangelo

sandbox-quantum/Tangelo · issue #408 · branch fix/408-simplify-returns-self

Circuit([Gate("H", 0)] * 2).simplify() returned None. The fix touches four methods, not one, because the same pattern was repeated identically across simplify, remove_redundant_gates, remove_small_rotations, and merge_rotations. Two of the four even documented a Returns clause that the implementation never matched. return self on each, plus one focused regression test.

4
Identical instances of the same bug, all fixed in one pass
2 / 4
Methods whose docstrings already promised Returns: Circuit
5 LOC
Actual code added, one line per method
0
Breaking changes for in-place callers
01 · The problem

A one-liner that should return a Circuit, doesn't

Two consecutive Hadamards cancel out to the identity. Calling .simplify() on that two-gate circuit should leave you with an empty Circuit on the same qubit. Instead it returns None and every subsequent attribute access raises.

The same pattern, four times in circuit.py

Each of the four convenience methods is the same shape:

def simplify(self, max_cycles=100, ...): opt_circuit = simplify(self, ...) # top-level function, returns Circuit self.__dict__ = opt_circuit.__dict__ # in-place mutation # ← no return statement: method returns None

Two of the four methods (remove_small_rotations, remove_redundant_gates) even have a Returns clause in their docstring: "Returns: Circuit: The circuit without ...". The documentation already promised what the code never delivered.

02 · The fix

One line per method, in-place semantic preserved

Adding return self at the end of each method makes the expression form work without changing anything for callers that ignore the return value.

flowchart LR
  Call["Circuit([H,H]).simplify()"] --> Wrap[method body]
  Wrap --> Inner[top-level simplify - returns Circuit]
  Inner --> Splat["self.__dict__ = opt.__dict__
in-place mutation"] Splat -- "before:
(no return)" --> NoneOut["⛔ returns None"] Splat -- "after:
return self" --> SelfOut["✓ returns self (the in-place mutated Circuit)"] style NoneOut fill:#1e0a0a,stroke:#ef4444,color:#fca5a5 style SelfOut fill:#0a1e10,stroke:#10b981,color:#86efac

Why return self instead of a separate simplified() method

  • The strictly cleanest design would be to add simplified() / without_redundant_gates() for the read-only form. But that doubles the API surface for every existing pass.
  • The reporter expected the existing method to behave as if it returned self, every popular Python quantum lib (PennyLane's qml.simplify, Qiskit's QuantumCircuit.decompose) does this.
  • In-place semantic is preserved: existing callers writing c.simplify(); use(c) behave identically.

If the SandboxAQ team prefers the split API, the patch is trivial to fold into a simplified() variant, happy to rewrite either way on signal.

03 · The test

One regression that pins the issue's exact failing line

def test_simplify_returns_circuit_when_everything_cancels(self): """Regression test for issue #408.""" # The original failing one-liner from the issue body. c = Circuit([Gate("H", 0)] * 2).simplify() assert c is not None assert isinstance(c, Circuit) assert len(c._gates) == 0 assert c.width == 1 # Analogous guarantee for the other three convenience methods. c1 = Circuit([Gate("X", 0)]).remove_redundant_gates() c2 = Circuit([Gate("RZ", 0, parameter=1e-9)]).remove_small_rotations() c3 = Circuit([Gate("RZ", 0, parameter=0.1), Gate("RZ", 0, parameter=0.2)]).merge_rotations() for name, returned in [("remove_redundant_gates", c1), ("remove_small_rotations", c2), ("merge_rotations", c3)]: assert returned is not None assert isinstance(returned, Circuit)

The existing test_simplify_circuit (which uses c.simplify(); assert c == ...) continues to pass, in-place behaviour is identical, the new return is just additive.

04 · The outreach

Where this stands

Done

Branch + commit + test + README

Local branch fix/408-simplify-returns-self, commit 4e172f3.

Next

Fork + push + draft PR

Mail to a Tangelo maintainer at SandboxAQ, anchored to the branch URL.

Goal

Conversation within 2 weeks

Realistic odds for this lead alone: ~25-40% conversation, ~5-10% contract. SandboxAQ is hiring quantum + AI roles; this is most likely a referral path rather than an immediate retainer.

05 · How to verify

Reproduce in 4 commands

cd c:/Users/FRA/Documents/github/workrepo/Tangelo git switch fix/408-simplify-returns-self git log --stat -1 # commit 4e172f3, 3 files pip install -e . pytest tangelo/linq/tests/test_circuits.py::TestCircuits::test_simplify_returns_circuit_when_everything_cancels -v pytest tangelo/linq/tests/test_circuits.py::TestCircuits::test_simplify_circuit -v # existing, still passes