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

Allow measurement keys with differing qubits in OpenQASM #6803

Merged
merged 8 commits into from
Nov 22, 2024
17 changes: 10 additions & 7 deletions cirq-core/cirq/circuits/qasm_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

"""Utility classes for representing QASM."""

from typing import Callable, Dict, Iterator, Optional, Sequence, Set, Tuple, Union, TYPE_CHECKING
from typing import Callable, Dict, Iterator, Optional, Sequence, Tuple, Union, TYPE_CHECKING

import re
import numpy as np
Expand Down Expand Up @@ -289,22 +289,25 @@ def output(text):
output(f'qubit[{len(self.qubits)}] q;\n')
# Classical registers
# Pick an id for the creg that will store each measurement
already_output_keys: Set[str] = set()
# cregs will store key -> (#qubits, comment)
cregs: Dict[str, tuple[int, str]] = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

If you did this in the QasmOutput constructor and stored it in QasmArgs, you could pass it into KeyCondition.qasm, and fix #5691.

The reason it's required is that KeyCondition is triggered if meas[m_key] != 0. In OpenQasm2, the only logical comparator is equality, so != 0 can only be represented if the creg is a single bit (i.e. it would convert the condition to if m_key == 1). If the creg is multiple bits, then if m_key == 1 would be incorrect, and m_key != 0 is not representable. So that's why the existing behavior is to play it safe and just fail.

Going a little bit further, in OpenQasm3, it looks like m_key != 0 is representable, so checking QasmArgs.version in KeyCondition.qasm would allow supporting multi-bit classical controls if version == '3.0'. OpenQasm3 could also potentially support serializing additional types of logic in SympyConditions.

Copy link
Contributor

Choose a reason for hiding this comment

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

It might also be useful to have this be a function on Circuit or part of the measurement_keys protocol. I'd imagine "get the shapes of all the measurement keys in the circuit" would be a common need.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed to put into constructor, but I am not convinced that helps, since we don't pass QasmOutput during the qasm protocol. In any case, I will deal with classical controls in a follow-up PR.

Also, Circuit has enough cruft on it that I don't think it's a good idea to add more functions onto it, so I will leave it here.

for meas in self.measurements:
key = protocols.measurement_key_name(meas)
if key in already_output_keys:
continue
already_output_keys.add(key)
meas_id = self.args.meas_key_id_map[key]

if self.meas_comments[key] is not None:
comment = f' // Measurement: {self.meas_comments[key]}'
else:
comment = ''

if meas_id not in cregs or cregs[meas_id][0] < len(meas.qubits):
cregs[meas_id] = (len(meas.qubits), comment)
for meas_id in cregs:
if self.args.version == '2.0':
output(f'creg {meas_id}[{len(meas.qubits)}];{comment}\n')
output(f'creg {meas_id}[{cregs[meas_id][0]}];{cregs[meas_id][1]}\n')
else:
output(f'bit[{len(meas.qubits)}] {meas_id};{comment}\n')
output(f'bit[{cregs[meas_id][0]}] {meas_id};{cregs[meas_id][1]}\n')

# In OpenQASM 2.0, the transformation of global phase gates is ignored.
# Therefore, no newline is created when the operations contained in
# a circuit consist only of global phase gates.
Expand Down
27 changes: 27 additions & 0 deletions cirq-core/cirq/circuits/qasm_output_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,30 @@ def test_reset():
reset q[1];
""".strip()
)


def test_different_sized_registers():
qubits = cirq.LineQubit.range(2)
c = cirq.Circuit(cirq.measure(qubits[0], key='c'), cirq.measure(qubits, key='c'))
output = cirq.QasmOutput(
c.all_operations(), tuple(sorted(c.all_qubits())), header='Generated from Cirq!'
)
assert (
str(output).strip()
dstrain115 marked this conversation as resolved.
Show resolved Hide resolved
== """// Generated from Cirq!

OPENQASM 2.0;
include "qelib1.inc";


// Qubits: [q(0), q(1)]
qreg q[2];
creg m_c[2];


measure q[0] -> m_c[0];

// Gate: cirq.MeasurementGate(2, cirq.MeasurementKey(name='c'), ())
measure q[0] -> m_c[0];
measure q[1] -> m_c[1];"""
dstrain115 marked this conversation as resolved.
Show resolved Hide resolved
)
dstrain115 marked this conversation as resolved.
Show resolved Hide resolved