-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Introduce gauge compilation #6526
Introduce gauge compilation #6526
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6526 +/- ##
========================================
Coverage 97.78% 97.79%
========================================
Files 1107 1124 +17
Lines 95188 95460 +272
========================================
+ Hits 93079 93351 +272
Misses 2109 2109 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! (already tested and gave feedback on an earlier version via email)
Thanks for this great work, Nour!
cirq-core/cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the sqrt_iswap gauges, since the gauges that you found are special cases of the continuous ones, I'm not sure it makes sense to include both. Maybe just include the continuous ones?
done |
pre_q0: _SINGLE_QUBIT_GATES_T = None | ||
pre_q1: _SINGLE_QUBIT_GATES_T = None | ||
post_q0: _SINGLE_QUBIT_GATES_T = None | ||
post_q1: _SINGLE_QUBIT_GATES_T = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the sake of type simplicity - can this be just a tuple[ops.Gate, ...]
with a ()
default instead?
Alternatively you could use Sequence[ops.Gate]
, but those arguments should be converted to tuples on initialization to make them immutable. This would let you also remove the _as_sequence
conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Union[ops.Gate, Tuple[ops.Gate, ...]]
is more like what I want since I want users to be assign just a gate to these fields.
those arguments should be converted to tuples on initialization
The class is defined as dataclass(froze=True)
and dataclasses
library doesn't have field transformers like (unlike attrs
), unless I use object.__setattr__
in __post_init
, wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to require either the (a) tuple[ops.Gate, ...]
or (b) Sequence[ops.Gate, ...]
type for the pre_q / post_q fields. It is unlikely users would be defining these classes interactively; in such case it is OK to have them provide the sequence type that is stored in the instance.
For option (b) I am fine with using object.__setattr__
in __post_init
to convert a sequence argument to tuple.
If that feels ugly, I am also fine with adding a dependency on attrs and using attrs.frozen with a field converter here.
Converting to a tuple on initialization would let you remove the _as_sequence
helper and property caching which effectively store gate sequences twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used attrs
with a field(converter=)
q0, q1 = op.qubits | ||
left.extend([g(q) for g in gs] for q, gs in zip(op.qubits, gauge.pre)) | ||
center.append(gauge.two_qubit_gate(q0, q1)) | ||
right.extend([g(q) for g in gs] for q, gs in zip(op.qubits, gauge.post)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, the order of pre and post gates from different operations in the moment does not matter, correct?
Otherwise I would expect to add the post gates in a reversed order to pre-gates, e.g., [PreA, PreB, A, B, PostB, PostA]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, the order doesn't matter since the pre and post operations are single qubit ops. there is only one two-qubit gate here and that this the gauge.two_qubit_gate
. so it doesn't matter if we do postA, postB
or postB, postA
The Gauge class in general represents a family of closely related gauges | ||
(e.g. random z-rotations); Use `sample` method to get a specific gauge. | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can add annotation to make it clear derived classes should have that attribute.
two_qubit_gate: ops.Gate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not all derived classes have it. only ConstantGauge have it.
cirq-core/cirq/transformers/gauge_compiling/gauge_compiling_test.py
Outdated
Show resolved
Hide resolved
cirq-core/cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the inline comments. Otherwise LGTM.
|
||
|
||
def _select(choices: Sequence[Gauge], probabilites: np.ndarray, prng: np.random.Generator) -> Gauge: | ||
return choices[prng.choice(len(choices), p=probabilites)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _select
is only used here - consider moving that line to GaugeSelector.__call__
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's needed here in order to force select specific gauges in tests
|
||
def weight(self) -> float: | ||
"""Returns the relative frequency for selecting this gauge.""" | ||
return 1.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The weight seems to be only used in the GaugeSelector.
How about moving weights to a field of GaugeSelector?
This would allow reuse of Gauge classes in several selectors with a different weights.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no the weight is a property of the gauge, the GaugeSelector just asks each gauge about its weights and normalizes them to get probabilities. but the weight is a property of a gauge.
cirq-core/cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py
Outdated
Show resolved
Hide resolved
gauge_transformer: GaugeTransformer | ||
|
||
@pytest.mark.parametrize('generation_seed', [*range(5)]) | ||
@pytest.mark.parametrize('transformation_seed', [*range(5)]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 2 parametrize decorators do Cartesian product so there is 25 test cases - is that necessary?
Perhaps we can combine to a single parametrization of 2 seed values.
Also consider using np.random.randint(2**31, size=(5, 2)).tolist()
for the seed values in parametrization to check with non-repetitive seeding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, but instead of using np.random.randint
I used np.random.RandomState(0).randint
inorder to make the test reproducible
cirq-core/cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py
Outdated
Show resolved
Hide resolved
cirq-core/cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py
Outdated
Show resolved
Hide resolved
@pytest.mark.xfail(strict=True) | ||
class TestInvalidTransformer(GaugeTester): | ||
two_qubit_gate = _TEST_TARGET | ||
gauge_transformer = _BAD_TRANSFORMER |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds quite a few xfailed outcomes to pytest. Here the xfail is required, but in general it marks tests that are for features not yet working. Please consider removing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I replaced xfail with a flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks, Nour!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with 2 comments.
def sample(self, gate: ops.Gate, prng: np.random.Generator) -> "ConstantGauge": | ||
return self | ||
|
||
def pre(self) -> Tuple[Tuple[ops.Gate, ...], Tuple[ops.Gate, ...]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant using a regular @property
decorator instead of the cached_property
.
Sorry about constant nitpicking - just that you can have your initial interface here if you want. :-)
if self.must_fail: | ||
with pytest.raises(AssertionError): | ||
_check_equivalent_with_error_message(c, nc, gauge) | ||
else: | ||
_check_equivalent_with_error_message(c, nc, gauge) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider removing the must_fail logic for the sake of simplicity and for whoever is going to read this in the future.
We don't check all assertions in tests to make sure they fail for bad tested functions. I feel it is sufficient that you verified your test class catches a bad transformer in an intermediate commit, but it is probably better to have a simpler rather than all covering final code here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree, without this the test module morph into something that accepts faulty gauges. this is essential for the integrity of the tests.
so either using the flag or xfail is need. for now I will keep the flag and we can revisit this later.
This PR introduces the abstraction for Gauge compilation as well as implementation for Sycamore gate, CZ gate, SqrtCZ gate, ZZ (a.k.a spin inversion), ISWAP gate, and SQRT_ISWAP gate
This PR introduces the abstraction for Gauge compilation as well as implementation for Sycamore gate, CZ gate, SqrtCZ gate, ZZ (a.k.a spin inversion), ISWAP gate, and SQRT_ISWAP gate
This PR introduces the abstraction for Gauge compilation as well as implementation for Sycamore gate, CZ gate, SqrtCZ gate, ZZ (a.k.a spin inversion), ISWAP gate, and SQRT_ISWAP gate