Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make qasm output valid gate names to fix #7052 #7157

Merged
merged 9 commits into from
Oct 21, 2021
Merged

Make qasm output valid gate names to fix #7052 #7157

merged 9 commits into from
Oct 21, 2021

Conversation

epelaaez
Copy link
Contributor

Summary

Identifies invalid QASM gate names and converts them into valid names to fix #7052.

Details and comments

Right now, the same snippet of code is being run on the main for loop in the qasm function and in the _add_sub_instruction_to_existing_composite_circuits function that is called inside qasm for nested gate calls. Since the functionality is the same in both parts, should I create a function that does this so it can be called where needed and avoid having the same piece of code twice?

Such function would look like _ensure_name_validity(instruction: Instruction) -> Instruction, where the same instruction is returned if the name is valid and if it is invalid, a copy of the instruction with the modified valid name will be returned. If its best to create such function, should I add this to the end of the file with the other functions that start with _?

qiskit/circuit/quantumcircuit.py Outdated Show resolved Hide resolved
qiskit/circuit/quantumcircuit.py Outdated Show resolved Hide resolved
qiskit/circuit/quantumcircuit.py Outdated Show resolved Hide resolved
@epelaaez
Copy link
Contributor Author

Hi @jakelishman, thanks for the feedback!

I've implemented the function _qasm_escape_gate_name(name: str) -> str and use it where needed. Additionaly, I noticed that the unitary object sometimes created the qasm string for a unitary with its own method, and that is then used in the general quantum circuit qasm() method. Therefore, I also added the needed fix there.

@epelaaez epelaaez marked this pull request as ready for review October 19, 2021 18:29
@epelaaez epelaaez requested a review from a team as a code owner October 19, 2021 18:29
@epelaaez epelaaez requested a review from jakelishman October 20, 2021 20:09
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

This looks very close to me, thanks for the effort! I had one incredibly minor point about naming (that you wouldn't really have known about), and one about another test case I thought of. I think it's very close to being done, though, so thanks for this!

qiskit/circuit/quantumcircuit.py Outdated Show resolved Hide resolved
Comment on lines +372 to +374
def test_circuit_qasm_with_invalid_identifiers(self):
"""Test that qasm() detects and corrects invalid OpenQASM gate identifiers,
while not changing the instructions on the original circuit"""
Copy link
Member

Choose a reason for hiding this comment

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

This is a very good test, thanks! One other case that might be good to test as well: can we add two custom gates that have different invalid names, but their escaped forms would be the same? In other words, something like:

base = QuantumCircuit(1)

clash1 = QuantumCircuit(1, name="invalid??")
clash1.x(0)
base.append(clash1, [0])

clash2 = QuantumCircuit(1, name="invalid[]")
clash2.z(0)
base.append(clash2, [0])

and ensure that the de-duplication code correctly runs (so at least one of the names becomes "invalid__<id number>")? As written, both of these will escape their names to invalid__, so we need to make sure the de-duplication triggers correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for this idea!

The de-duplication code is ran correctly, but I think it's hard to check for that. The gates first go through the check that ensures they are valid identifiers, and, if they are not, a copy of the instruction is made with the valid name. Then, this copy is the one that goes through the de-duplication code, which appends the id at the end of the name.

For other tests, the id could be accessed since it is the one from the original instruction. But, in this case, the id appended is the one from the copy created by the code that ensures the name is valid. Therefore, doing the following in the test won't return the correct expected qasm.

expected_qasm = f"""OPENQASM 2.0;
include "qelib1.inc";
gate invalid__ q0 {{ x q0; }}
gate invalid___{id(clash2)} q0 {{ z q0; }}
qreg q[1];
invalid__ q[0];
invalid___{id(clash2)} q[0];\n"""

Is there a way to check that an id is appended to the end of the second instruction name without specifying that id (so, using regex)? Or what should be the approach here?

Copy link
Member

Choose a reason for hiding this comment

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

We could cheat a little - we don't necessarily care how the names are de-deduplicated, just that somehow or another, they are. Given we control the input, we can limit the expected output to things we can "parse" with regular expressions: perhaps something along the lines of:

names = set()
for match in re.findall(r"gate (\S+)", base.qasm()):
    gate_name = match.group(1)
    self.assertTrue(VALID_QASM2_IDENTIFIER.fullmatch(gate_name))
    names.add(gate_name)
self.assertEqual(len(names), ...)

In words:

  1. find each instance of the string "gate <gate_name> ..."
  2. extract the gate name from it
  3. check it's a valid name
  4. add it to a set (automatic uniqueness check)
  5. assert the length of the set is what we expect

That tests the behaviour we want (all names are valid, and there are no duplicates), without testing specifics of the implementation (how exactly the deduplication checker works). What I've just sketched there might not be 100% perfect, but I think the idea should work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added this test. Thanks for the code! It was very helpful. I just found out doing gate_name = match.group(1) wasn't neccesary since what is returned is already the string we are looking for because the regex pattern only has one group. Anyways, it is working now!

Copy link
Member

Choose a reason for hiding this comment

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

Ah yeah, nice spot thanks. I messed up the distinction between re.findall and re.finditer - finditer returns match objects, findall returns the results of capture groups.

jakelishman
jakelishman previously approved these changes Oct 21, 2021
Copy link
Member

@jakelishman jakelishman 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 to me, thanks for sticking through it!

Comment on lines +372 to +374
def test_circuit_qasm_with_invalid_identifiers(self):
"""Test that qasm() detects and corrects invalid OpenQASM gate identifiers,
while not changing the instructions on the original circuit"""
Copy link
Member

Choose a reason for hiding this comment

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

Ah yeah, nice spot thanks. I messed up the distinction between re.findall and re.finditer - finditer returns match objects, findall returns the results of capture groups.

@jakelishman
Copy link
Member

Oh no, wait, I'm sorry I forgot: could you add a bug fix release note (reno new --edit fix-qasm-invalid-names) mentioning that we've fixed the invalid names?

@jakelishman jakelishman added Changelog: Bugfix Include in the "Fixed" section of the changelog mod: qasm2 Relating to OpenQASM 2 import or export labels Oct 21, 2021
@jakelishman jakelishman added this to the 0.19 milestone Oct 21, 2021
@jakelishman jakelishman dismissed their stale review October 21, 2021 17:03

needs reno

@epelaaez
Copy link
Contributor Author

Added release note, thanks for all the help @jakelishman!

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Perfect, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: Bugfix Include in the "Fixed" section of the changelog mod: qasm2 Relating to OpenQASM 2 import or export
Projects
None yet
Development

Successfully merging this pull request may close these issues.

QuantumCircuit.qasm can output invalid gate names
2 participants