Skip to content

Conversation

@emlynsg
Copy link
Collaborator

@emlynsg emlynsg commented Nov 19, 2025

This commit introduces minor changes required to support interfacing with a brickwork transpiler (future "PR").
Changes include:

  • Additional namespace exports which are needed for brickwork
  • Changes from list/Iterable to Sequence types to match usage across JCZ and brickwork
  • Separation of j_commands which is used across both modules

Copy link

@mgarnier59 mgarnier59 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for the work!

Some of my comments span the original version that Thierry has done.

Two quick comments/questions:

  • the well-formedness check on patterns directly depend on the well-formedness of the circuit. Are we doing this here because it's not yet implemented for graphix Circuit? I think separating concerns is good.
  • I'm not clear on the JCZinstruction type. It seems to me that we would need only sequences of circuit instructions and sequences of JCZ so I don't understand why we would want to mix those.

Also I don't think we took into account the case of a CZ gate in the circuit (but I might be wrong on that point)

------
IllformedPatternError: if the pattern is ill-formed.
InternalInstructionError: if the circuit contains internal _XC or _ZC instructions.
IllformedPatternError: if the circuit has underdefined instructions.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange that an ill-formed circuit raises a pattern error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes agreed. Fixed.



def instruction_to_jcz(instr: JCZInstruction) -> Iterable[J | CZ]:
def instruction_to_jcz(instr: JCZInstruction) -> Sequence[J | CZ]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On line 263-264 (unchanged): I agree it's weird to have an explicit identity in a circuit. Formally we could always compile the identity into two Hadamard (hence J(0) o J(0)). Maybe comment that we explicitly remove identities? I don't think it's very important though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's odd, but I think this is the easiest way to deal with it and keep consistent outputs from the functions. Happy to leave this as is.



def instruction_to_jcz(instr: JCZInstruction) -> Iterable[J | CZ]:
def instruction_to_jcz(instr: JCZInstruction) -> Sequence[J | CZ]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function not suppose to take circuit instructions to JCZ? Hence the type annotation has to be modified. From JCZInstruction to JCZInstruction | Instruction.

What I don't understand is why we want to allow feeding a JCZInstructionto this function since it's just the identity on these objects. Circuits shouldn't contain such instructions.

As a side remark, the case where the circuit explicitely contains a CZ gate is not taken into account (I believe)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JCZInstruction is already defined as the union Instruction | J.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so to go from the bottom:

  • CZ gates are not in Graphix (yet) so shouldn't be a problem. I will add them in if/when my PR is merged.
  • JCZInstruction is defined as J | CZ | Instruction, so yes it will be the identity on J and CZ inputs. It is general because the function is recursive (might input CNOT, decomposed to H CZ H, which goes through the instruction_list_to_jcz and each is then sent to instruction_to_jcz.) So yes the initial inputs should just be Instruction type, but since the function is used for intermediate steps the typing needs to be the union.

]


def transpile_jcz(circuit: Circuit) -> TranspileResult:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment for line 374: why do we expect the circuit to contain _XCor _ZC instructions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't, but unfortunately the instructions _XC and _ZC exist in graphix (they are used internally by the original transpiler, so I hope they will disappear eventually).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe remove the . vscode that is local?

transpiled.pattern.perform_pauli_measurements()
transpiled.pattern.minimize_space()

def simulate_and_measure() -> int:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, I would add a comment saying what we are doing: since it's a circuit transpiled in JCZ, the pattern has causal flow so all measurement outcomes have probability 1/2 to occur (up to statistical fluctuations).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

def decompose_ccx(
instr: instruction.CCX,
) -> list[instruction.H | instruction.CNOT | instruction.RZ]:
) -> Sequence[instruction.H | instruction.CNOT | instruction.RZ]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually follow the convention of being as precise as possible in the returned datatype, so we prefer the type list over Sequence if the return value is a list. This practice is Python-specific. In a statically typed language without downcasting, it would make sense to hide the concrete data type in the interface for better encapsulation. In Python, since callers can freely treat the result as a list if it is one, the most helpful type annotation is the concrete type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to commit with this changed back, but I get typing issues.
Something to do with Sequences being covariant, and lists not being.
May be able to keep as lists but broaden their typing to JCZInstruction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you share the type error messages?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, where decompose_y is called in the instruction_to_jcz, I get the error ""Argument 1 to instruction_list_to_jcz" has incompatible type "list[X | Z]"; expected "list[CCX | RZZ | CNOT | SWAP | H | <10 more items>]""

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also adds ""List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
Consider using "Sequence" instead, which is covariant"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose a fix in this commit. There were two issues:

  • instruction_list_to_jcz took a list even though it only needs an Iterable. If a function f is defined with a parameter l: list[A], that parameter is mutable, so it would be unsafe to call f with an argument of type list[B] with B a subtype of A: f could append any A to l, including values that are not of type B. That's why list is invariant. Iterable provides read-only access and is covariant; since list[B] is an Iterable[B], it is also an Iterable[A] when B is a subtype of A.
  • The return type of instruction_to_jcz was list[J | CZ] but some branches return list[J]. I changed the return type to Sequence[J | CZ] which is a common supertype of both list[J | CZ] and list[J]. Alternatively, list[J | CZ] | list[J] would also work.

Copy link
Collaborator Author

@emlynsg emlynsg Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Thierry. Makes sense to me. Based on your logic above, should we not use Sequence rather than Iterable for instruction_list_to_jcz?
(Update: Ignore this question, I slept on it and it makes complete sense you would maximise generality on inputs and specificity on outputs.)



def instruction_to_jcz(instr: JCZInstruction) -> Iterable[J | CZ]:
def instruction_to_jcz(instr: JCZInstruction) -> Sequence[J | CZ]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JCZInstruction is already defined as the union Instruction | J.

]


def transpile_jcz(circuit: Circuit) -> TranspileResult:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't, but unfortunately the instructions _XC and _ZC exist in graphix (they are used internally by the original transpiler, so I hope they will disappear eventually).

Emlyn Graham and others added 5 commits December 4, 2025 15:40
…ation on larger patterns produced through Open Graphs.
Update the code for the latest Graphix master API. Disable Pauli
preprocessing in `test_circuit_simulation_og` because it can break the
flow (graphix#363), and `minimize_space` does not work well when no
flow is available (graphix#157).
@emlynsg
Copy link
Collaborator Author

emlynsg commented Dec 11, 2025

@thierry-martinez @mgarnier59 let's review again first thing next week. I can update with the changes to Graphix main once the CZ PR is merged - I think best to leave the J gate separate for now given the further changes that would be required for the qasm parser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants