Skip to content

Commit e66eb6e

Browse files
authored
Clean up and maintenance of checks (#902)
* Clean up and maintenance of checks * note slowness * lint * serialization skips
1 parent e6d6e01 commit e66eb6e

5 files changed

Lines changed: 92 additions & 50 deletions

File tree

dev_tools/qualtran_dev_tools/bloq_report_card.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import time
15+
import warnings
1416
from typing import Any, Dict, Iterable, List, Optional, Set, Type
1517

1618
import pandas as pd
@@ -21,8 +23,8 @@
2123
BloqCheckResult,
2224
check_bloq_example_decompose,
2325
check_bloq_example_make,
24-
check_bloq_example_serialize,
25-
check_connections_preserve_preserves_types,
26+
check_bloq_example_qtyping,
27+
check_bloq_example_serializes,
2628
check_equivalent_bloq_example_counts,
2729
)
2830

@@ -67,33 +69,31 @@ def bloq_classes_with_no_examples(
6769

6870

6971
IDCOLS = ['package', 'bloq_cls', 'name']
70-
CHECKCOLS = ['make', 'decomp', 'counts', 'serialize', 'typing']
72+
CHECKCOLS = ['make', 'decomp', 'counts', 'serialize', 'qtyping']
7173

7274

7375
def record_for_class_with_no_examples(k: Type[Bloq]) -> Dict[str, Any]:
74-
return {
75-
'bloq_cls': k.__name__,
76-
'package': _get_package(k),
77-
'name': '-',
78-
'make': BloqCheckResult.MISSING,
79-
'decomp': BloqCheckResult.MISSING,
80-
'counts': BloqCheckResult.MISSING,
81-
'serialize': BloqCheckResult.MISSING,
82-
'typing': BloqCheckResult.MISSING,
76+
return {'bloq_cls': k.__name__, 'package': _get_package(k), 'name': '-'} | {
77+
check_name: BloqCheckResult.MISSING for check_name in CHECKCOLS
8378
}
8479

8580

8681
def record_for_bloq_example(be: BloqExample) -> Dict[str, Any]:
87-
return {
82+
start = time.perf_counter()
83+
record = {
8884
'bloq_cls': be.bloq_cls.__name__,
8985
'package': _get_package(be.bloq_cls),
9086
'name': be.name,
9187
'make': check_bloq_example_make(be)[0],
9288
'decomp': check_bloq_example_decompose(be)[0],
9389
'counts': check_equivalent_bloq_example_counts(be)[0],
94-
'serialize': check_bloq_example_serialize(be)[0],
95-
'typing': check_connections_preserve_preserves_types(be)[0],
90+
'serialize': check_bloq_example_serializes(be)[0],
91+
'qtyping': check_bloq_example_qtyping(be)[0],
9692
}
93+
dur = time.perf_counter() - start
94+
if dur > 1.0:
95+
warnings.warn(f"{be.name} took {dur} to check.")
96+
return record
9797

9898

9999
def show_bloq_report_card(df: pd.DataFrame) -> pandas.io.formats.style.Styler:
@@ -110,6 +110,10 @@ def get_bloq_report_card(
110110
bclasses = get_bloq_classes()
111111
if bexamples is None:
112112
bexamples = get_bloq_examples()
113+
# Default exclusions: pass explicit bexamples to override.
114+
# qubitization_qpi_hubbard_model_xxx -- too slow
115+
skips = ['qubitization_qpe_hubbard_model_small', 'qubitization_qpe_hubbard_model_large']
116+
bexamples = [bex for bex in bexamples if bex.name not in skips]
113117

114118
records: List[Dict[str, Any]] = []
115119
missing_bclasses = bloq_classes_with_no_examples(bclasses, bexamples)

qualtran/bloqs/hamiltonian_simulation/hamiltonian_simulation_by_gqsp_test.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ def test_examples(bloq_autotester):
3939

4040

4141
def test_symbolic_examples(bloq_autotester):
42-
if bloq_autotester.check_name == 'serialization':
43-
pytest.xfail(f"serialization for {_symbolic_hamsim_by_gqsp.name} not yet supported")
4442
bloq_autotester(_symbolic_hamsim_by_gqsp)
4543

4644

qualtran/bloqs/qsp/generalized_qsp_test.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,6 @@ def _gqsp_1d_ising() -> GeneralizedQSP:
227227

228228

229229
def test_gqsp_1d_ising_example(bloq_autotester):
230-
if bloq_autotester.check_name == 'serialization':
231-
pytest.xfail("Skipping serialization test for bloq examples that cannot yet be serialized.")
232230
bloq_autotester(_gqsp_1d_ising)
233231

234232

qualtran/conftest.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def assert_equivalent_bloq_example_counts_for_pytest(bloq_ex: BloqExample):
7575
raise bce from bce
7676

7777

78-
def assert_bloq_example_serialize_for_pytest(bloq_ex: BloqExample):
78+
def assert_bloq_example_serializes_for_pytest(bloq_ex: BloqExample):
7979
if bloq_ex.name in [
8080
'prep_sparse',
8181
'thc_prep',
@@ -88,19 +88,21 @@ def assert_bloq_example_serialize_for_pytest(bloq_ex: BloqExample):
8888
'select_pauli_lcu',
8989
'walk_op',
9090
'trott_unitary',
91-
"hubbard_time_evolution_by_gqsp",
91+
'hubbard_time_evolution_by_gqsp',
92+
'symbolic_hamsim_by_gqsp',
93+
'gqsp_1d_ising',
9294
]:
9395
pytest.xfail("Skipping serialization test for bloq examples that cannot yet be serialized.")
9496

9597
try:
96-
qlt_testing.assert_bloq_example_serialize(bloq_ex)
98+
qlt_testing.assert_bloq_example_serializes(bloq_ex)
9799
except qlt_testing.BloqCheckException as bce:
98100
raise bce from bce
99101

100102

101-
def assert_bloq_example_typing_for_pytest(bloq_ex: BloqExample):
103+
def assert_bloq_example_qtyping_for_pytest(bloq_ex: BloqExample):
102104
try:
103-
qlt_testing.assert_bloq_example_preserves_types(bloq_ex)
105+
qlt_testing.assert_bloq_example_qtyping(bloq_ex)
104106
except qlt_testing.BloqCheckException as bce:
105107
if bce.check_result is qlt_testing.BloqCheckResult.NA:
106108
pytest.skip(bce.msg)
@@ -112,8 +114,8 @@ def assert_bloq_example_typing_for_pytest(bloq_ex: BloqExample):
112114
('make', assert_bloq_example_make_for_pytest),
113115
('decompose', assert_bloq_example_decompose_for_pytest),
114116
('counts', assert_equivalent_bloq_example_counts_for_pytest),
115-
('serialization', assert_bloq_example_serialize_for_pytest),
116-
('typing', assert_bloq_example_typing_for_pytest),
117+
('serialize', assert_bloq_example_serializes_for_pytest),
118+
('qtyping', assert_bloq_example_qtyping_for_pytest),
117119
]
118120

119121

qualtran/testing.py

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,17 @@ def assert_connections_compatible(cbloq: CompositeBloq):
136136
raise BloqError(f"{cxn}'s right side is associated with a register with side {rr.side}")
137137

138138

139-
def assert_connections_preserve_types(
139+
def assert_connections_consistent_qdtypes(
140140
cbloq: CompositeBloq,
141141
type_checking_severity: QDTypeCheckingSeverity = QDTypeCheckingSeverity.LOOSE,
142142
):
143-
"""Check that connections have consistent dtypes accounting for type_checking_severity."""
143+
"""Check that a composite bloq's connections have consistent qdtypes.
144+
145+
Args:
146+
cbloq: The composite bloq.
147+
type_checking_severity: How strict to be in type checking. See the documentation
148+
for the QDTypeCheckingSeverity enum for details.
149+
"""
144150
for cxn in cbloq.connections:
145151
lr = cxn.left.reg
146152
rr = cxn.right.reg
@@ -525,7 +531,23 @@ def check_equivalent_bloq_example_counts(bloq_ex: BloqExample) -> Tuple[BloqChec
525531
return BloqCheckResult.PASS, ''
526532

527533

528-
def assert_bloq_example_serialize(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
534+
def assert_bloq_example_serializes(bloq_ex: BloqExample) -> None:
535+
"""Assert that the BloqExample has consistent serialization.
536+
537+
This function asserts that the given bloq can be serialized to a proto format and the
538+
corresponding proto can be deserialized back to a bloq which is equal to the original
539+
bloq.
540+
541+
If the given Bloq cannot be serialized / deserialized OR if the deserialized Bloq is not
542+
equal to the given Bloq, then the result is `FAIL`. If the roundtrip succeeds, the result
543+
is `PASS`.
544+
545+
Returns:
546+
None if the assertions are satisfied.
547+
548+
Raises:
549+
BloqCheckException if any assertions are violated.
550+
"""
529551
from qualtran.serialization.bloq import bloqs_from_proto, bloqs_to_proto
530552

531553
bloq = bloq_ex.make()
@@ -547,7 +569,7 @@ def assert_bloq_example_serialize(bloq_ex: BloqExample) -> Tuple[BloqCheckResult
547569
) from e
548570

549571

550-
def check_bloq_example_serialize(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
572+
def check_bloq_example_serializes(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
551573
"""Check that the BloqExample has consistent serialization.
552574
553575
This function checks that the given bloq can be serialized to a proto format and the
@@ -563,7 +585,7 @@ def check_bloq_example_serialize(bloq_ex: BloqExample) -> Tuple[BloqCheckResult,
563585
msg: A message providing details from the check.
564586
"""
565587
try:
566-
assert_bloq_example_serialize(bloq_ex)
588+
assert_bloq_example_serializes(bloq_ex)
567589
except BloqCheckException as bce:
568590
return bce.check_result, bce.msg
569591
except Exception as e: # pylint: disable=broad-except
@@ -572,55 +594,73 @@ def check_bloq_example_serialize(bloq_ex: BloqExample) -> Tuple[BloqCheckResult,
572594
return BloqCheckResult.PASS, ''
573595

574596

575-
def assert_bloq_example_preserves_types(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
576-
"""Check a bloq example preserves types throughout its decomposition.
597+
def assert_bloq_example_qtyping(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
598+
"""Assert that the bloq example has valid quantum data types throughout its decomposition.
577599
578-
Args:
579-
bloq_ex: The bloq example to test.
600+
If the bloq has no decomposition, this check is not applicable. Otherwise: we check the
601+
`connections` in the decomposed bloq with increasing levels of type checking severity.
602+
First, we check loose type checking (allowing conversions between numeric types). A
603+
failure here is raised as a FAIL.
604+
605+
Then `QDTypeCheckingSeverity.ANY` checking (allowing just conversions to and from QAny) and
606+
finally strict checking are performed. Currently, these are coded as an UNVERIFIED bloq
607+
check result.
580608
581609
Returns:
582-
None
610+
None if the assertions are satisfied.
583611
584612
Raises:
585-
BloqCheckException if any assertions are violated. Will raise a failure
586-
if loose type checking fails, and unverified if the stricter type checks
587-
fail.
613+
BloqCheckException if any assertions are violated.
588614
"""
589615
bloq = bloq_ex.make()
590-
# First check it's not atomic / doesn't decompose
616+
# First check for the presence of a decomposition
591617
try:
592618
cbloq = bloq.decompose_bloq()
593-
except (DecomposeTypeError, DecomposeNotImplementedError) as exc:
594-
raise BloqCheckException.missing(
595-
r"Atomic bloqs or non-decomposable bloqs don't require type checking."
596-
)
619+
except (DecomposeTypeError, DecomposeNotImplementedError) as e:
620+
raise BloqCheckException.na(
621+
r"QDType checking is only applicable for bloqs with decompositions"
622+
) from e
623+
597624
try:
598-
assert_connections_preserve_types(
625+
assert_connections_consistent_qdtypes(
599626
cbloq, type_checking_severity=QDTypeCheckingSeverity.LOOSE
600627
)
601628
except Exception as e:
602629
raise BloqCheckException.fail('Loose type checking failed.\n' + str(e)) from e
630+
603631
try:
604-
assert_connections_preserve_types(cbloq, type_checking_severity=QDTypeCheckingSeverity.ANY)
632+
assert_connections_consistent_qdtypes(
633+
cbloq, type_checking_severity=QDTypeCheckingSeverity.ANY
634+
)
605635
except Exception as e:
606636
raise BloqCheckException.unverified('QAny and QBit type checking failed.\n' + str(e)) from e
637+
607638
try:
608-
assert_connections_preserve_types(
639+
assert_connections_consistent_qdtypes(
609640
cbloq, type_checking_severity=QDTypeCheckingSeverity.STRICT
610641
)
611642
except Exception as e:
612643
raise BloqCheckException.unverified('Strict type checking failed.\n' + str(e)) from e
613644

614645

615-
def check_connections_preserve_preserves_types(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
616-
"""Check that the BloqExample has consistent typing.
646+
def check_bloq_example_qtyping(bloq_ex: BloqExample) -> Tuple[BloqCheckResult, str]:
647+
"""Check that the bloq example has valid quantum data types throughout its decomposition.
648+
649+
If the bloq has no decomposition, this check is not applicable. Otherwise: we check the
650+
`connections` in the decomposed bloq with increasing levels of type checking severity.
651+
First, we check loose type checking (allowing conversions between numeric types). A
652+
failure here is returned as a FAIL.
653+
654+
Then `QDTypeCheckingSeverity.ANY` checking (allowing just conversions to and from QAny) and
655+
finally strict checking are performed. Currently, these are coded as an UNVERIFIED bloq
656+
check result.
617657
618658
Returns:
619659
result: The `BloqCheckResult`.
620660
msg: A message providing details from the check.
621661
"""
622662
try:
623-
assert_bloq_example_preserves_types(bloq_ex)
663+
assert_bloq_example_qtyping(bloq_ex)
624664
except BloqCheckException as bce:
625665
return bce.check_result, bce.msg
626666
except Exception as e: # pylint: disable=broad-except

0 commit comments

Comments
 (0)