diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6173fafb..3aef0286 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -453,16 +453,16 @@ jobs: - run: pytest glue/zx - run: dev/doctest_proper.py --module stimzx test_stimjs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: mymindstorm/setup-emsdk@v14 with: - version: 2.0.18 + version: 4.0.1 actions-cache-folder: 'emsdk-cache' - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v4 with: - node-version: 16.x + node-version: 18.x - run: npm install - run: bash glue/javascript/build_wasm.sh - run: node puppeteer_run_tests.js diff --git a/.gitignore b/.gitignore index 397c53e5..9998b9fe 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,6 @@ venv .cmake/* coverage/* cmake-build-debug-coverage/* -# clangd generated files compile_commands.json .cache build.ninja @@ -43,3 +42,4 @@ build.ninja .ninja_log .pytest_cache node_modules +MODULE.bazel.lock diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock deleted file mode 100644 index 4c8df156..00000000 --- a/MODULE.bazel.lock +++ /dev/null @@ -1,116 +0,0 @@ -{ - "lockFileVersion": 11, - "registryFileHashes": { - "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", - "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", - "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", - "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", - "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/source.json": "06cc0842d241da0c5edc755edb3c7d0d008d304330e57ecf2d6449fb0b633a82", - "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef", - "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015", - "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", - "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", - "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", - "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", - "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", - "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/source.json": "082ed5f9837901fada8c68c2f3ddc958bb22b6d654f71dd73f3df30d45d4b749", - "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", - "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", - "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", - "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", - "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5", - "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", - "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", - "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", - "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", - "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", - "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6", - "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", - "https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b", - "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", - "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", - "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", - "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", - "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", - "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", - "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", - "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430", - "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", - "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", - "https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35", - "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", - "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", - "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c", - "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9", - "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", - "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", - "https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014", - "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", - "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", - "https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459", - "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", - "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d" - }, - "selectedYankedVersions": {}, - "moduleExtensions": { - "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { - "general": { - "bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=", - "usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "local_config_apple_cc": { - "bzlFile": "@@apple_support~//crosstool:setup.bzl", - "ruleClassName": "_apple_cc_autoconf", - "attributes": {} - }, - "local_config_apple_cc_toolchains": { - "bzlFile": "@@apple_support~//crosstool:setup.bzl", - "ruleClassName": "_apple_cc_autoconf_toolchains", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [ - [ - "apple_support~", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, - "@@platforms//host:extension.bzl%host_platform": { - "general": { - "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", - "usagesDigest": "meSzxn3DUCcYEhq4HQwExWkWtU4EjriRBQLsZN+Q0SU=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "host_platform": { - "bzlFile": "@@platforms//host:extension.bzl", - "ruleClassName": "host_platform_repo", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [] - } - } - } -} diff --git a/doc/gates.md b/doc/gates.md index d31f36d6..1d2386ed 100644 --- a/doc/gates.md +++ b/doc/gates.md @@ -3344,6 +3344,15 @@ determining the expected value, and `X_ERROR` is noise, causing deviations from It is not recommended for the measurement set of an observable to have inconsistent parity. For example, the circuit-to-detector-error-model conversion will refuse to operate on circuits containing such observables. +In addition to targeting measurements, observables can target Pauli operators. This has no effect when running the +quantum computation, but is used when configuring the decoder. For example, when performing a logical Z initialization, +it allows a logical X operator to be introduced (by marking its Pauli terms) despite the fact that it anticommutes +with the initialization. In practice, when physically sampling a circuit or simulating sampling its measurements and +then computing the observables from the measurements, these Pauli terms are effectively ignored. However, they affect +detection event simulations and affect whether the observable is included in errors in the detector error model. This +makes it easier to benchmark all observables of a code, without having to introduce noiseless qubits entangled with the +logical qubit to avoid the testing of the X observable anticommuting with the testing of the Z observable. + Parens Arguments: A non-negative integer specifying the index of the logical observable to add the measurement records to. @@ -3370,6 +3379,22 @@ Example: # ...and the one before that. OBSERVABLE_INCLUDE(1) rec[-2] + # Unphysically tracking two anticommuting observables of a 2x2 surface code. + QUBIT_COORDS(0, 0) 0 + QUBIT_COORDS(1, 0) 1 + QUBIT_COORDS(0, 1) 2 + QUBIT_COORDS(1, 1) 3 + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DEPOLARIZE1(0.001) 0 1 2 3 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DETECTOR rec[-1] rec[-4] + DETECTOR rec[-2] rec[-5] + DETECTOR rec[-3] rec[-6] + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + ### The 'QUBIT_COORDS' Instruction diff --git a/src/stim/circuit/circuit.test.cc b/src/stim/circuit/circuit.test.cc index 8f876d12..0276776b 100644 --- a/src/stim/circuit/circuit.test.cc +++ b/src/stim/circuit/circuit.test.cc @@ -1807,12 +1807,14 @@ Circuit stim::generate_test_circuit_with_all_operations() { DETECTOR(1, 2, 3) rec[-1] OBSERVABLE_INCLUDE(0) rec[-1] MPAD 0 1 0 + OBSERVABLE_INCLUDE(1) Z2 Z3 TICK # Inverted measurements. MRX !0 MY !1 MZZ !2 3 + OBSERVABLE_INCLUDE(1) rec[-1] MYY !4 !5 MPP X6*!Y7*Z8 TICK diff --git a/src/stim/circuit/circuit_instruction.cc b/src/stim/circuit/circuit_instruction.cc index e762624a..e4f42c9a 100644 --- a/src/stim/circuit/circuit_instruction.cc +++ b/src/stim/circuit/circuit_instruction.cc @@ -228,9 +228,17 @@ void CircuitInstruction::validate() const { valid_target_mask |= TARGET_RECORD_BIT | TARGET_SWEEP_BIT; } if (gate.flags & GATE_ONLY_TARGETS_MEASUREMENT_RECORD) { - for (GateTarget q : targets) { - if (!(q.data & TARGET_RECORD_BIT)) { - throw std::invalid_argument("Gate " + std::string(gate.name) + " only takes rec[-k] targets."); + if (gate.flags & GATE_TARGETS_PAULI_STRING) { + for (GateTarget q : targets) { + if (!q.is_measurement_record_target() && !q.is_pauli_target()) { + throw std::invalid_argument("Gate " + std::string(gate.name) + " only takes measurement record targets and Pauli targets (rec[-k], Xk, Yk, Zk)."); + } + } + } else { + for (GateTarget q : targets) { + if (!q.is_measurement_record_target()) { + throw std::invalid_argument("Gate " + std::string(gate.name) + " only takes measurement record targets (rec[-k])."); + } } } } else if (gate.flags & GATE_TARGETS_PAULI_STRING) { diff --git a/src/stim/cmd/command_m2d.cc b/src/stim/cmd/command_m2d.cc index cc359c9d..c21fac8c 100644 --- a/src/stim/cmd/command_m2d.cc +++ b/src/stim/cmd/command_m2d.cc @@ -1,17 +1,3 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - #include "stim/cmd/command_m2d.h" #include "command_help.h" diff --git a/src/stim/cmd/command_m2d.test.cc b/src/stim/cmd/command_m2d.test.cc index f32b88f6..7e91e788 100644 --- a/src/stim/cmd/command_m2d.test.cc +++ b/src/stim/cmd/command_m2d.test.cc @@ -154,3 +154,30 @@ TEST(command_m2d, m2d_obs_size_misalign_11_obs) { trim(std::string(1024, '0') + "\n")); ASSERT_EQ(tmp_obs.read_contents(), "00000000000\n"); } + +TEST(command_m2d, unphysical_observable_annotations) { + RaiiTempNamedFile tmp_circuit(R"CIRCUIT( + QUBIT_COORDS(0, 0) 0 + QUBIT_COORDS(1, 0) 1 + QUBIT_COORDS(0, 1) 2 + QUBIT_COORDS(1, 1) 3 + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DEPOLARIZE1(0.001) 0 1 2 3 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DETECTOR rec[-1] rec[-4] + DETECTOR rec[-2] rec[-5] + DETECTOR rec[-3] rec[-6] + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + )CIRCUIT"); + RaiiTempNamedFile tmp_obs; + + ASSERT_EQ( + trim(run_captured_stim_main( + {"m2d", "--in_format=01", "--obs_out", tmp_obs.path.c_str(), "--circuit", tmp_circuit.path.c_str()}, + "000000\n100100\n000110\n")), + trim("000\n000\n011\n")); + ASSERT_EQ(tmp_obs.read_contents(), "00\n00\n00\n"); +} diff --git a/src/stim/diagram/circuit_timeline_helper.cc b/src/stim/diagram/circuit_timeline_helper.cc index 232f2b1c..cf1e2f40 100644 --- a/src/stim/diagram/circuit_timeline_helper.cc +++ b/src/stim/diagram/circuit_timeline_helper.cc @@ -1,11 +1,9 @@ #include "stim/diagram/circuit_timeline_helper.h" -#include "stim/diagram/diagram_util.h" - using namespace stim; using namespace stim_draw_internal; -void CircuitTimelineHelper::skip_loop_iterations(CircuitTimelineLoopData loop_data, uint64_t skipped_reps) { +void CircuitTimelineHelper::skip_loop_iterations(const CircuitTimelineLoopData &loop_data, uint64_t skipped_reps) { if (loop_data.num_repetitions > 0) { vec_pad_add_mul(cur_coord_shift, loop_data.shift_per_iteration, skipped_reps); measure_offset += loop_data.measurements_per_iteration * skipped_reps; @@ -95,6 +93,11 @@ GateTarget CircuitTimelineHelper::rec_to_qubit(const GateTarget &target) { } GateTarget CircuitTimelineHelper::pick_pseudo_target_representing_measurements(const CircuitInstruction &op) { + for (const auto &t : op.targets) { + if (t.is_qubit_target() || t.is_pauli_target()) { + return t; + } + } // First check if coordinates prefix-match a qubit's coordinates. if (!op.args.empty()) { auto coords = shifted_coordinates_in_workspace(op.args); diff --git a/src/stim/diagram/circuit_timeline_helper.h b/src/stim/diagram/circuit_timeline_helper.h index d23ef4e8..e81ccccb 100644 --- a/src/stim/diagram/circuit_timeline_helper.h +++ b/src/stim/diagram/circuit_timeline_helper.h @@ -69,7 +69,7 @@ struct CircuitTimelineHelper { stim::GateTarget rec_to_qubit(const stim::GateTarget &target); stim::GateTarget pick_pseudo_target_representing_measurements(const stim::CircuitInstruction &op); - void skip_loop_iterations(CircuitTimelineLoopData loop_data, uint64_t skipped_reps); + void skip_loop_iterations(const CircuitTimelineLoopData &loop_data, uint64_t skipped_reps); void do_record_measure_result(uint32_t target_qubit); void do_repeat_block(const stim::Circuit &circuit, const stim::CircuitInstruction &op); void do_next_operation(const stim::Circuit &circuit, const stim::CircuitInstruction &op); diff --git a/src/stim/diagram/timeline/timeline_ascii_drawer.cc b/src/stim/diagram/timeline/timeline_ascii_drawer.cc index 942ce1f7..ef84e32a 100644 --- a/src/stim/diagram/timeline/timeline_ascii_drawer.cc +++ b/src/stim/diagram/timeline/timeline_ascii_drawer.cc @@ -427,27 +427,54 @@ void DiagramTimelineAsciiDrawer::do_observable_include(const ResolvedTimelineOpe SpanRef rec_targets = op.targets; rec_targets.ptr_start++; + bool had_paulis = false; + for (const auto &t : rec_targets) { + if (t.is_pauli_target()) { + had_paulis = true; + std::stringstream ss; + ss << "L" << (op.args.empty() ? 0 : op.args[0]) << "*="; + ss << t.pauli_type(); + diagram.add_entry( + AsciiDiagramEntry{ + { + m2x(cur_moment), + q2y(t.qubit_value()), + GATE_ALIGNMENT_X, + GATE_ALIGNMENT_Y, + }, + ss.str(), + }); + } + } + + bool had_rec = false; std::stringstream ss; - ss << "OBSERVABLE_INCLUDE:L" << (op.args.empty() ? 0 : op.args[0]) << "*="; - for (size_t k = 0; k < rec_targets.size(); k++) { - if (k) { - ss << "*"; + ss << "OBSERVABLE_INCLUDE:L" << (op.args.empty() ? 0 : op.args[0]); + ss << "*="; + for (const auto &t : rec_targets) { + if (t.is_measurement_record_target()) { + if (had_rec) { + ss << "*"; + } + had_rec = true; + write_rec_index(ss, t.value()); } - write_rec_index(ss, rec_targets[k].value()); } - if (rec_targets.empty()) { - ss.put('1'); + if (had_rec || !had_paulis) { + if (rec_targets.empty()) { + ss.put('1'); + } + diagram.add_entry( + AsciiDiagramEntry{ + { + m2x(cur_moment), + q2y(pseudo_target.qubit_value()), + GATE_ALIGNMENT_X, + GATE_ALIGNMENT_Y, + }, + ss.str(), + }); } - diagram.add_entry( - AsciiDiagramEntry{ - { - m2x(cur_moment), - q2y(pseudo_target.qubit_value()), - GATE_ALIGNMENT_X, - GATE_ALIGNMENT_Y, - }, - ss.str(), - }); } void DiagramTimelineAsciiDrawer::do_resolved_operation(const ResolvedTimelineOperation &op) { diff --git a/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc b/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc index f75dc4f9..4963ddbf 100644 --- a/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc +++ b/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc @@ -1,17 +1,3 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - #include "stim/diagram/timeline/timeline_ascii_drawer.h" #include "gtest/gtest.h" @@ -185,6 +171,36 @@ q3: -R----------------------------MRY:rec[7]------------M:rec[12]--MPP[Z]:rec[14 )DIAGRAM"); } +TEST(circuit_diagram_timeline_text, unphysical_observable_include) { + Circuit circuit(R"CIRCUIT( + QUBIT_COORDS(0, 0) 0 + QUBIT_COORDS(1, 0) 1 + QUBIT_COORDS(0, 1) 2 + QUBIT_COORDS(1, 1) 3 + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + OBSERVABLE_INCLUDE(2) X2 X3 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DEPOLARIZE1(0.001) 0 1 2 3 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DETECTOR rec[-1] rec[-4] + DETECTOR rec[-2] rec[-5] + DETECTOR rec[-3] rec[-6] + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + OBSERVABLE_INCLUDE(2) X2 X3 + )CIRCUIT"); + ASSERT_EQ("\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n", R"DIAGRAM( +q0: -QUBIT_COORDS(0,0)-L0*=X-L1*=Z-------MPP[X]:rec[0]-MPP[Z]:rec[1]-DEPOLARIZE1(0.001)-MPP[X]:rec[3]-MPP[Z]:rec[4]-DETECTOR:D1=rec[4]*rec[1]-DETECTOR:D2=rec[3]*rec[0]-L0*=X-L1*=Z------- + | | | | | | | | +q1: -QUBIT_COORDS(1,0)-L0*=X-|-----------MPP[X]:rec[0]-MPP[Z]:rec[1]-DEPOLARIZE1(0.001)-MPP[X]:rec[3]-MPP[Z]:rec[4]-----------------------------------------------------L0*=X-|----------- + | | | | +q2: -QUBIT_COORDS(0,1)-------L1*=Z-L2*=X-MPP[X]:rec[0]-MPP[Z]:rec[2]-DEPOLARIZE1(0.001)-MPP[X]:rec[3]-MPP[Z]:rec[5]-DETECTOR:D0=rec[5]*rec[2]---------------------------------L1*=Z-L2*=X- + | | | | | | +q3: -QUBIT_COORDS(1,1)-------------L2*=X-MPP[X]:rec[0]-MPP[Z]:rec[2]-DEPOLARIZE1(0.001)-MPP[X]:rec[3]-MPP[Z]:rec[5]-----------------------------------------------------------------L2*=X- +)DIAGRAM"); +} + TEST(circuit_diagram_timeline_text, measurement_looping) { Circuit circuit(R"CIRCUIT( M 0 @@ -514,42 +530,42 @@ TEST(circuit_diagram_timeline_text, repetition_code_transposed) { TEST(circuit_diagram_timeline_text, test_circuit_all_ops) { auto circuit = generate_test_circuit_with_all_operations(); ASSERT_EQ("\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n", R"DIAGRAM( - /-------------------\ /--------------\ /---------------------\ /-----------------------------------------------------------------------------------------------------------------------------------------------------\ /------------------------------------------------------\ /REP 3 /---\ \ /--------------------------------------------------------------------------------------------------------------------------\ - q0: -QUBIT_COORDS(1,2,3)-I-C_XYZ-SQRT_X-----ZSWAP-----SQRT_XX-----X------------DEPOLARIZE1(0.02)---------------X_ERROR(0.01)------------------------------------------------------------------------------------------------MPP[X]:rec[2]-MPP[Z]:rec[3]-SPP[X]-SPP_DAG[X]------------MRX:rec[4]-MXX:rec[11]-|------H-@---|---MR:rec[15]-X_ERROR(0.1)-MR(0.01):rec[16]-DETECTOR(2,4,6):D0=rec[16]-OBSERVABLE_INCLUDE:L0*=rec[16]-MPAD:rec[17]-MPAD:rec[19]-MRX:rec[20]----X^rec[24]-- + /-------------------\ /--------------\ /---------------------\ /-----------------------------------------------------------------------------------------------------------------------------------------------------\ /------------------------------------------------------\ /REP 3 /---\ \ /--------------------------------------------------------------------------------------------------------------------------\ /----------------------------------------\ + q0: -QUBIT_COORDS(1,2,3)-I-C_XYZ-SQRT_X-----ZSWAP-----SQRT_XX-----X------------DEPOLARIZE1(0.02)---------------X_ERROR(0.01)------------------------------------------------------------------------------------------------MPP[X]:rec[2]-MPP[Z]:rec[3]-SPP[X]-SPP_DAG[X]------------MRX:rec[4]-MXX:rec[11]-|------H-@---|---MR:rec[15]-X_ERROR(0.1)-MR(0.01):rec[16]-DETECTOR(2,4,6):D0=rec[16]-OBSERVABLE_INCLUDE:L0*=rec[16]-MPAD:rec[17]-MPAD:rec[19]-MRX:rec[20]--------------------------------X^rec[24]-- | | | | | | | | | | | - q1: ---------------------X-C_ZYX-SQRT_X_DAG-XSWAP-----SQRT_XX-----X-E[X](0.01)-DEPOLARIZE2(0.03)---------------Y_ERROR(0.02)------------------------------------------------------------------------------------------------MPP[Y]:rec[2]-MPP[Z]:rec[3]-SPP[Y]-SPP_DAG[Y]------------MRY:rec[5]-MXX---------|--------X-S-|------------------------------------------------------------------------------------------------------MPAD:rec[18]--------------MY:rec[21]-----Y^sweep[0]- + q1: ---------------------X-C_ZYX-SQRT_X_DAG-XSWAP-----SQRT_XX-----X-E[X](0.01)-DEPOLARIZE2(0.03)---------------Y_ERROR(0.02)------------------------------------------------------------------------------------------------MPP[Y]:rec[2]-MPP[Z]:rec[3]-SPP[Y]-SPP_DAG[Y]------------MRY:rec[5]-MXX---------|--------X-S-|------------------------------------------------------------------------------------------------------MPAD:rec[18]--------------MY:rec[21]---------------------------------Y^sweep[0]- | | | | | | | - q2: ---------------------Y-H_XY--SQRT_Y-----ISWAP-----SQRT_XX_DAG-X-E[Y](0.01)-DEPOLARIZE2(0.03)---------------Z_ERROR(0.03)------------------------------------------------------------------------------------------------MPP[Z]:rec[2]---------------SPP[Z]-SPP_DAG[Z]-SPP_DAG[X]-MR:rec[6]--MXX:rec[12]-|------------|--------------------------------------------------------------------------------------------------------------------------------MZZ:rec[22]----Z^rec[24]-- - | | | | | | | | - q3: ---------------------Z-H-----SQRT_Y_DAG-ISWAP-----SQRT_XX_DAG-Y-E[Z](0.01)-PAULI_CHANNEL_1(0.01,0.02,0.03)-HERALDED_ERASE(0.04):rec[0]--------------------------------------------------------------------------------------------------------------SPP[X]-----------------------MX:rec[7]--MXX---------|------------|--------------------------------------------------------------------------------------------------------------------------------MZZ----------------------- + q2: ---------------------Y-H_XY--SQRT_Y-----ISWAP-----SQRT_XX_DAG-X-E[Y](0.01)-DEPOLARIZE2(0.03)---------------Z_ERROR(0.03)------------------------------------------------------------------------------------------------MPP[Z]:rec[2]---------------SPP[Z]-SPP_DAG[Z]-SPP_DAG[X]-MR:rec[6]--MXX:rec[12]-|------------|-------------------------------------------------------------------------------------------------------------------L1*=Z--------MZZ:rec[22]-OBSERVABLE_INCLUDE:L1*=rec[22]-Z^rec[24]-- + | | | | | | | | | + q3: ---------------------Z-H-----SQRT_Y_DAG-ISWAP-----SQRT_XX_DAG-Y-E[Z](0.01)-PAULI_CHANNEL_1(0.01,0.02,0.03)-HERALDED_ERASE(0.04):rec[0]--------------------------------------------------------------------------------------------------------------SPP[X]-----------------------MX:rec[7]--MXX---------|------------|-------------------------------------------------------------------------------------------------------------------L1*=Z--------MZZ--------------------------------------------------- | | - q4: -----------------------H_YZ--S----------ISWAP_DAG-SQRT_YY-----X------------ELSE_CORRELATED_ERROR[X](0.02)--PAULI_CHANNEL_2[0](0.001,0.002,0.003,0.004,0.005,0.006,0.007,0.008,0.009,0.01,0.011,0.012,0.013,0.014,0.015)----------------------------------------------------------MY:rec[8]--MYY:rec[13]-|------------|--------------------------------------------------------------------------------------------------------------------------------MYY:rec[23]--------------- - | | | | | | | | | - q5: -----------------------------S_DAG------ISWAP_DAG-SQRT_YY-----@------------|-------------------------------PAULI_CHANNEL_2[1](0.001,0.002,0.003,0.004,0.005,0.006,0.007,0.008,0.009,0.01,0.011,0.012,0.013,0.014,0.015)----------------------------------------------------------M:rec[9]---MYY---------|------------|--------------------------------------------------------------------------------------------------------------------------------MYY----------------------- + q4: -----------------------H_YZ--S----------ISWAP_DAG-SQRT_YY-----X------------ELSE_CORRELATED_ERROR[X](0.02)--PAULI_CHANNEL_2[0](0.001,0.002,0.003,0.004,0.005,0.006,0.007,0.008,0.009,0.01,0.011,0.012,0.013,0.014,0.015)----------------------------------------------------------MY:rec[8]--MYY:rec[13]-|------------|--------------------------------------------------------------------------------------------------------------------------------------------MYY:rec[23]------------------------------- + | | | | | | | | | + q5: -----------------------------S_DAG------ISWAP_DAG-SQRT_YY-----@------------|-------------------------------PAULI_CHANNEL_2[1](0.001,0.002,0.003,0.004,0.005,0.006,0.007,0.008,0.009,0.01,0.011,0.012,0.013,0.014,0.015)----------------------------------------------------------M:rec[9]---MYY---------|------------|--------------------------------------------------------------------------------------------------------------------------------------------MYY--------------------------------------- | | | - q6: ----------------------------------------SWAP------SQRT_YY_DAG-Y------------ELSE_CORRELATED_ERROR[Z](0.02)--HERALDED_PAULI_CHANNEL_1(0.01,0.02,0.03,0.04):rec[1]------------------------------------------------------------------------------------------------------------------M:rec[10]--MZZ:rec[14]-|------------|--------------------------------------------------------------------------------------------------------------------------------MPP[X]:rec[24]------------ - | | | | | | | | - q7: ----------------------------------------SWAP------SQRT_YY_DAG-X------------ELSE_CORRELATED_ERROR[Y](0.02)------------------------------------------------------------------------------------------------------------------------------------------------------------------------RX---------MZZ---------|------------|--------------------------------------------------------------------------------------------------------------------------------MPP[Y]:rec[24]------------ - | | | - q8: ----------------------------------------XSWAP-----SQRT_ZZ-----Y------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------RY---------------------|------------|--------------------------------------------------------------------------------------------------------------------------------MPP[Z]:rec[24]------------ + q6: ----------------------------------------SWAP------SQRT_YY_DAG-Y------------ELSE_CORRELATED_ERROR[Z](0.02)--HERALDED_PAULI_CHANNEL_1(0.01,0.02,0.03,0.04):rec[1]------------------------------------------------------------------------------------------------------------------M:rec[10]--MZZ:rec[14]-|------------|--------------------------------------------------------------------------------------------------------------------------------------------MPP[X]:rec[24]---------------------------- + | | | | | | | | + q7: ----------------------------------------SWAP------SQRT_YY_DAG-X------------ELSE_CORRELATED_ERROR[Y](0.02)------------------------------------------------------------------------------------------------------------------------------------------------------------------------RX---------MZZ---------|------------|--------------------------------------------------------------------------------------------------------------------------------------------MPP[Y]:rec[24]---------------------------- + | | | + q8: ----------------------------------------XSWAP-----SQRT_ZZ-----Y------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------RY---------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------MPP[Z]:rec[24]---------------------------- | | | | | - q9: ----------------------------------------ZSWAP-----SQRT_ZZ-----Y------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------R----------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- + q9: ----------------------------------------ZSWAP-----SQRT_ZZ-----Y------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------R----------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -q10: ----------------------------------------ZSWAP-----SQRT_ZZ_DAG-Y-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q10: ----------------------------------------ZSWAP-----SQRT_ZZ_DAG-Y-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | | | -q11: ----------------------------------------ZSWAP-----SQRT_ZZ_DAG-@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q11: ----------------------------------------ZSWAP-----SQRT_ZZ_DAG-@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -q12: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q12: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | -q13: --------------------------------------------------------------X-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q13: --------------------------------------------------------------X-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -q14: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q14: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | -q15: --------------------------------------------------------------Y-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q15: --------------------------------------------------------------Y-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -q16: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- +q16: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | -q17: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------- - \-------------------/ \--------------/ \---------------------/ \-----------------------------------------------------------------------------------------------------------------------------------------------------/ \------------------------------------------------------/ \ \---/ / \--------------------------------------------------------------------------------------------------------------------------/ +q17: --------------------------------------------------------------@-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + \-------------------/ \--------------/ \---------------------/ \-----------------------------------------------------------------------------------------------------------------------------------------------------/ \------------------------------------------------------/ \ \---/ / \--------------------------------------------------------------------------------------------------------------------------/ \----------------------------------------/ )DIAGRAM"); } diff --git a/src/stim/diagram/timeline/timeline_svg_drawer.cc b/src/stim/diagram/timeline/timeline_svg_drawer.cc index 67621fa4..ba475293 100644 --- a/src/stim/diagram/timeline/timeline_svg_drawer.cc +++ b/src/stim/diagram/timeline/timeline_svg_drawer.cc @@ -743,31 +743,67 @@ void DiagramTimelineSvgDrawer::do_observable_include(const ResolvedTimelineOpera SpanRef rec_targets = op.targets; rec_targets.ptr_start++; - auto c = q2xy(pseudo_target.qubit_value()); - auto span = (uint16_t)(1 + std::max(std::max(op.targets.size(), op.args.size()), (size_t)2)); - std::stringstream ss; - ss << "OBS_INCLUDE(" << op.args[0] << ")"; - draw_annotated_gate(c.xyz[0], c.xyz[1], SvgGateData{span, ss.str(), "", "", "lightgray", "black", 0, 10}, {}); - c.xyz[0] += (span - 1) * GATE_PITCH * 0.5f; - - svg_out << ""; - svg_out << "L" << op.args[0] << " *= "; - for (size_t k = 0; k < rec_targets.size(); k++) { - if (k) { - svg_out << "*"; + // Draw per-quit L#*=P boxes. + bool had_paulis = false; + bool had_rec = false; + for (const auto &t : rec_targets) { + if (t.is_measurement_record_target()) { + had_rec = true; + } + if (t.is_pauli_target()) { + had_paulis = true; + std::stringstream ss; + ss << "L" << (op.args.empty() ? 0 : op.args[0]) << "*="; + ss << t.pauli_type(); + auto c = q2xy(t.qubit_value()); + draw_annotated_gate(c.xyz[0], c.xyz[1], SvgGateData{ + .span=2, + .body=ss.str(), + .subscript="", + .superscript="", + .fill="lightgray", + .text_color="black", + .font_size=0, + .sub_font_size=10 + }, {}); } - write_rec_index(svg_out, rec_targets[k].value()); } - if (rec_targets.empty()) { - svg_out << "1 (vacuous)"; + + // Draw OBS_INCLUDE(#) box with rec annotations above it, if there are measurement records. + if (had_rec) { + had_rec = false; + auto span = (uint16_t)(1 + std::max(std::max(op.targets.size(), op.args.size()), (size_t)2)); + auto c = q2xy(pseudo_target.qubit_value()); + std::stringstream ss; + ss << "OBS_INCLUDE(" << (op.args.empty() ? 0 : op.args[0]) << ")"; + if (!had_paulis) { + draw_annotated_gate(c.xyz[0], c.xyz[1], SvgGateData{span, ss.str(), "", "", "lightgray", "black", 0, 10}, {}); + } + c.xyz[0] += (span - 1) * GATE_PITCH * 0.5f; + + svg_out << ""; + svg_out << "L" << op.args[0] << " *= "; + ss << "*="; + for (const auto &t : rec_targets) { + if (t.is_measurement_record_target()) { + if (had_rec) { + svg_out << "*"; + } + had_rec = true; + write_rec_index(svg_out, t.value()); + } + } + if (!had_rec && !had_paulis) { + svg_out << "1 (vacuous)"; + } + svg_out << "\n"; } - svg_out << "\n"; } void DiagramTimelineSvgDrawer::do_resolved_operation(const ResolvedTimelineOperation &op) { diff --git a/src/stim/gates/gate_data_annotations.cc b/src/stim/gates/gate_data_annotations.cc index c95027a0..3dad7f3b 100644 --- a/src/stim/gates/gate_data_annotations.cc +++ b/src/stim/gates/gate_data_annotations.cc @@ -95,7 +95,7 @@ Parens Arguments: .id = GateType::OBSERVABLE_INCLUDE, .best_candidate_inverse_id = GateType::OBSERVABLE_INCLUDE, .arg_count = 1, - .flags = (GateFlags)(GATE_ONLY_TARGETS_MEASUREMENT_RECORD | GATE_IS_NOT_FUSABLE | + .flags = (GateFlags)(GATE_ONLY_TARGETS_MEASUREMENT_RECORD | GATE_TARGETS_PAULI_STRING | GATE_IS_NOT_FUSABLE | GATE_ARGS_ARE_UNSIGNED_INTEGERS | GATE_HAS_NO_EFFECT_ON_QUBITS), .category = "Z_Annotations", .help = R"MARKDOWN( @@ -128,6 +128,15 @@ determining the expected value, and `X_ERROR` is noise, causing deviations from It is not recommended for the measurement set of an observable to have inconsistent parity. For example, the circuit-to-detector-error-model conversion will refuse to operate on circuits containing such observables. +In addition to targeting measurements, observables can target Pauli operators. This has no effect when running the +quantum computation, but is used when configuring the decoder. For example, when performing a logical Z initialization, +it allows a logical X operator to be introduced (by marking its Pauli terms) despite the fact that it anticommutes +with the initialization. In practice, when physically sampling a circuit or simulating sampling its measurements and +then computing the observables from the measurements, these Pauli terms are effectively ignored. However, they affect +detection event simulations and affect whether the observable is included in errors in the detector error model. This +makes it easier to benchmark all observables of a code, without having to introduce noiseless qubits entangled with the +logical qubit to avoid the testing of the X observable anticommuting with the testing of the Z observable. + Parens Arguments: A non-negative integer specifying the index of the logical observable to add the measurement records to. @@ -153,6 +162,22 @@ Parens Arguments: OBSERVABLE_INCLUDE(1) rec[-1] # ...and the one before that. OBSERVABLE_INCLUDE(1) rec[-2] + + # Unphysically tracking two anticommuting observables of a 2x2 surface code. + QUBIT_COORDS(0, 0) 0 + QUBIT_COORDS(1, 0) 1 + QUBIT_COORDS(0, 1) 2 + QUBIT_COORDS(1, 1) 3 + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DEPOLARIZE1(0.001) 0 1 2 3 + MPP X0*X1*X2*X3 Z0*Z1 Z2*Z3 + DETECTOR rec[-1] rec[-4] + DETECTOR rec[-2] rec[-5] + DETECTOR rec[-3] rec[-6] + OBSERVABLE_INCLUDE(0) X0 X1 + OBSERVABLE_INCLUDE(1) Z0 Z2 )MARKDOWN", .unitary_data = {}, .flow_data = {}, diff --git a/src/stim/gates/gates.h b/src/stim/gates/gates.h index ad145b75..7448358a 100644 --- a/src/stim/gates/gates.h +++ b/src/stim/gates/gates.h @@ -184,8 +184,11 @@ enum GateFlags : uint16_t { // Controls validation code checking for arguments coming in pairs. GATE_TARGETS_PAIRS = 1 << 6, // Controls instructions like CORRELATED_ERROR taking Pauli product targets ("X1 Y2 Z3"). + // Note that this enables the Pauli terms but not the combine terms like X1*Y2. GATE_TARGETS_PAULI_STRING = 1 << 7, // Controls instructions like DETECTOR taking measurement record targets ("rec[-1]"). + // The "ONLY" refers to the fact that this flag switches the default behavior to not allowing qubit targets. + // Further flags can then override that default. GATE_ONLY_TARGETS_MEASUREMENT_RECORD = 1 << 8, // Controls instructions like CX operating allowing measurement record targets and sweep bit targets. GATE_CAN_TARGET_BITS = 1 << 9, diff --git a/src/stim/mem/bitword_128_sse.h b/src/stim/mem/bitword_128_sse.h index a84e9875..3a585868 100644 --- a/src/stim/mem/bitword_128_sse.h +++ b/src/stim/mem/bitword_128_sse.h @@ -49,17 +49,17 @@ struct bitword<128> { _mm_free(ptr); } - inline bitword<128>() : val(__m128i{}) { + inline bitword() : val(__m128i{}) { } - inline bitword<128>(__m128i val) : val(val) { + inline bitword(__m128i val) : val(val) { } - inline bitword<128>(std::array val) : val{_mm_set_epi64x(val[1], val[0])} { + inline bitword(std::array val) : val{_mm_set_epi64x(val[1], val[0])} { } - inline bitword<128>(uint64_t val) : val{_mm_set_epi64x(0, val)} { + inline bitword(uint64_t val) : val{_mm_set_epi64x(0, val)} { } - inline bitword<128>(int64_t val) : val{_mm_set_epi64x(-(val < 0), val)} { + inline bitword(int64_t val) : val{_mm_set_epi64x(-(val < 0), val)} { } - inline bitword<128>(int val) : val{_mm_set_epi64x(-(val < 0), val)} { + inline bitword(int val) : val{_mm_set_epi64x(-(val < 0), val)} { } inline static bitword<128> tile8(uint8_t pattern) { diff --git a/src/stim/mem/bitword_256_avx.h b/src/stim/mem/bitword_256_avx.h index 4b5959f9..aac4964d 100644 --- a/src/stim/mem/bitword_256_avx.h +++ b/src/stim/mem/bitword_256_avx.h @@ -48,17 +48,17 @@ struct bitword<256> { _mm_free(ptr); } - inline bitword<256>() : val(__m256i{}) { + inline bitword() : val(__m256i{}) { } - inline bitword<256>(__m256i val) : val(val) { + inline bitword(__m256i val) : val(val) { } - inline bitword<256>(std::array val) : val{_mm256_set_epi64x(val[3], val[2], val[1], val[0])} { + inline bitword(std::array val) : val{_mm256_set_epi64x(val[3], val[2], val[1], val[0])} { } - inline bitword<256>(uint64_t val) : val{_mm256_set_epi64x(0, 0, 0, val)} { + inline bitword(uint64_t val) : val{_mm256_set_epi64x(0, 0, 0, val)} { } - inline bitword<256>(int64_t val) : val{_mm256_set_epi64x(-(val < 0), -(val < 0), -(val < 0), val)} { + inline bitword(int64_t val) : val{_mm256_set_epi64x(-(val < 0), -(val < 0), -(val < 0), val)} { } - inline bitword<256>(int val) : val{_mm256_set_epi64x(-(val < 0), -(val < 0), -(val < 0), val)} { + inline bitword(int val) : val{_mm256_set_epi64x(-(val < 0), -(val < 0), -(val < 0), val)} { } inline static bitword<256> tile8(uint8_t pattern) { diff --git a/src/stim/mem/bitword_64.h b/src/stim/mem/bitword_64.h index 4a4a16ec..3ffaf69b 100644 --- a/src/stim/mem/bitword_64.h +++ b/src/stim/mem/bitword_64.h @@ -45,15 +45,15 @@ struct bitword<64> { free(ptr); } - inline constexpr bitword<64>() : val{} { + inline constexpr bitword() : val{} { } - inline bitword<64>(std::array val) : val{val[0]} { + inline bitword(std::array val) : val{val[0]} { } - inline constexpr bitword<64>(uint64_t v) : val{v} { + inline constexpr bitword(uint64_t v) : val{v} { } - inline constexpr bitword<64>(int64_t v) : val{(uint64_t)v} { + inline constexpr bitword(int64_t v) : val{(uint64_t)v} { } - inline constexpr bitword<64>(int v) : val{(uint64_t)v} { + inline constexpr bitword(int v) : val{(uint64_t)v} { } constexpr inline static bitword<64> tile64(uint64_t pattern) { diff --git a/src/stim/mem/sparse_xor_vec.h b/src/stim/mem/sparse_xor_vec.h index 787bef07..6f73330f 100644 --- a/src/stim/mem/sparse_xor_vec.h +++ b/src/stim/mem/sparse_xor_vec.h @@ -224,6 +224,14 @@ struct SparseXorVec { return sorted_items.data() + size(); } + bool operator==(const std::vector &other) const { + return sorted_items == other; + } + + bool operator!=(const std::vector &other) const { + return sorted_items != other; + } + bool operator==(const SparseXorVec &other) const { return sorted_items == other.sorted_items; } diff --git a/src/stim/simulators/error_analyzer.test.cc b/src/stim/simulators/error_analyzer.test.cc index a97beb09..88b799db 100644 --- a/src/stim/simulators/error_analyzer.test.cc +++ b/src/stim/simulators/error_analyzer.test.cc @@ -3657,3 +3657,74 @@ TEST(ErrorAnalyzer, heralded_pauli_channel_1) { )DEM"), 1e-6)); } + + +TEST(ErrorAnalyzer, OBS_INCLUDE_PAULIS) { + auto circuit = Circuit(R"CIRCUIT( + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + X_ERROR(0.125) 0 + Y_ERROR(0.25) 0 + Z_ERROR(0.375) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + )CIRCUIT"); + ASSERT_EQ(circuit_to_dem(circuit), DetectorErrorModel(R"DEM( + error(0.375) L0 L1 + error(0.25) L0 L2 + error(0.125) L1 L2 + )DEM")); + + circuit = Circuit(R"CIRCUIT( + DEPOLARIZE1(0.125) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + X_ERROR(0.25) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + DEPOLARIZE1(0.125) 0 + )CIRCUIT"); + ASSERT_EQ(circuit_to_dem(circuit), DetectorErrorModel(R"DEM( + error(0.25) L1 L2 + logical_observable L0 + logical_observable L0 + )DEM")); + + circuit = Circuit(R"CIRCUIT( + DEPOLARIZE1(0.125) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + Y_ERROR(0.25) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + DEPOLARIZE1(0.125) 0 + )CIRCUIT"); + ASSERT_EQ(circuit_to_dem(circuit), DetectorErrorModel(R"DEM( + error(0.25) L0 L2 + logical_observable L1 + logical_observable L1 + )DEM")); + + circuit = Circuit(R"CIRCUIT( + DEPOLARIZE1(0.125) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + Z_ERROR(0.25) 0 + OBSERVABLE_INCLUDE(0) X0 + OBSERVABLE_INCLUDE(1) Y0 + OBSERVABLE_INCLUDE(2) Z0 + DEPOLARIZE1(0.125) 0 + )CIRCUIT"); + ASSERT_EQ(circuit_to_dem(circuit), DetectorErrorModel(R"DEM( + error(0.25) L0 L1 + logical_observable L2 + logical_observable L2 + )DEM")); +} diff --git a/src/stim/simulators/frame_simulator.inl b/src/stim/simulators/frame_simulator.inl index 662362cb..3a3f5bcd 100644 --- a/src/stim/simulators/frame_simulator.inl +++ b/src/stim/simulators/frame_simulator.inl @@ -234,8 +234,19 @@ void FrameSimulator::do_OBSERVABLE_INCLUDE(const CircuitInstruction &inst) { if (keeping_detection_data) { auto r = obs_record[(size_t)inst.args[0]]; for (auto t : inst.targets) { - uint32_t lookback = t.data & TARGET_VALUE_MASK; - r ^= m_record.lookback(lookback); + if (t.is_measurement_record_target()) { + uint32_t lookback = t.data & TARGET_VALUE_MASK; + r ^= m_record.lookback(lookback); + } else if (t.is_pauli_target()) { + if (t.data & TARGET_PAULI_X_BIT) { + r ^= x_table[t.qubit_value()]; + } + if (t.data & TARGET_PAULI_Z_BIT) { + r ^= z_table[t.qubit_value()]; + } + } else { + throw std::invalid_argument("Unexpected target for OBSERVABLE_INCLUDE: " + t.str()); + } } } } diff --git a/src/stim/simulators/measurements_to_detection_events.inl b/src/stim/simulators/measurements_to_detection_events.inl index 77a652ef..725a2054 100644 --- a/src/stim/simulators/measurements_to_detection_events.inl +++ b/src/stim/simulators/measurements_to_detection_events.inl @@ -94,11 +94,17 @@ void measurements_to_detection_events_helper( simd_bits_range_ref obs_row = frame_sim.obs_record[(uint64_t)op.args[0]]; bool expectation = false; for (const auto &t : op.targets) { - uint32_t lookback = t.data & TARGET_VALUE_MASK; - // Include dependence from physical measurement results. - obs_row ^= measurements__minor_shot_index[measure_count_so_far - lookback]; - // Include dependence from reference sample expectation. - expectation ^= reference_sample[measure_count_so_far - lookback]; + if (t.is_classical_bit_target()) { + uint32_t lookback = t.data & TARGET_VALUE_MASK; + // Include dependence from physical measurement results. + obs_row ^= measurements__minor_shot_index[measure_count_so_far - lookback]; + // Include dependence from reference sample expectation. + expectation ^= reference_sample[measure_count_so_far - lookback]; + } else if (t.is_pauli_target()) { + // Ignored. + } else { + throw std::invalid_argument("Unexpected target for OBSERVABLE_INCLUDE: " + t.str()); + } } if (expectation) { obs_row.invert_bits(); diff --git a/src/stim/simulators/sparse_rev_frame_tracker.cc b/src/stim/simulators/sparse_rev_frame_tracker.cc index 1122b415..4a6bb833 100644 --- a/src/stim/simulators/sparse_rev_frame_tracker.cc +++ b/src/stim/simulators/sparse_rev_frame_tracker.cc @@ -810,11 +810,22 @@ void SparseUnsignedRevFrameTracker::undo_DETECTOR(const CircuitInstruction &dat) void SparseUnsignedRevFrameTracker::undo_OBSERVABLE_INCLUDE(const CircuitInstruction &dat) { auto obs = DemTarget::observable_id((int32_t)dat.args[0]); for (auto t : dat.targets) { - int64_t index = t.rec_offset() + (int64_t)num_measurements_in_past; - if (index < 0) { - throw std::invalid_argument("Referred to a measurement result before the beginning of time."); + if (t.is_measurement_record_target()) { + int64_t index = t.rec_offset() + (int64_t)num_measurements_in_past; + if (index < 0) { + throw std::invalid_argument("Referred to a measurement result before the beginning of time."); + } + rec_bits[index].xor_item(obs); + } else if (t.is_pauli_target()) { + if (t.data & TARGET_PAULI_X_BIT) { + xs[t.qubit_value()].xor_item(obs); + } + if (t.data & TARGET_PAULI_Z_BIT) { + zs[t.qubit_value()].xor_item(obs); + } + } else { + throw std::invalid_argument("Unexpected target for OBSERVABLE_INCLUDE: " + t.str()); } - rec_bits[index].xor_item(obs); } } diff --git a/src/stim/simulators/sparse_rev_frame_tracker.test.cc b/src/stim/simulators/sparse_rev_frame_tracker.test.cc index 58de3477..69e5548b 100644 --- a/src/stim/simulators/sparse_rev_frame_tracker.test.cc +++ b/src/stim/simulators/sparse_rev_frame_tracker.test.cc @@ -745,3 +745,17 @@ TEST(SparseUnsignedRevFrameTracker, fail_anticommute) { circuit.count_qubits(), circuit.count_measurements(), circuit.count_detectors(), true); ASSERT_THROW({ rev.undo_circuit(circuit); }, std::invalid_argument); } + +TEST(SparseUnsignedRevFrameTracker, OBS_INCLUDE_PAULIS) { + SparseUnsignedRevFrameTracker rev(4, 4, 4); + + rev.undo_circuit(Circuit("OBSERVABLE_INCLUDE(5) X1 Y2 Z3 rec[-1]")); + ASSERT_TRUE(rev.xs[0].empty()); + ASSERT_TRUE(rev.zs[0].empty()); + ASSERT_EQ(rev.xs[1], (std::vector{DemTarget::observable_id(5)})); + ASSERT_TRUE(rev.zs[1].empty()); + ASSERT_EQ(rev.xs[2], (std::vector{DemTarget::observable_id(5)})); + ASSERT_EQ(rev.zs[2], (std::vector{DemTarget::observable_id(5)})); + ASSERT_TRUE(rev.xs[3].empty()); + ASSERT_EQ(rev.zs[3], (std::vector{DemTarget::observable_id(5)})); +} diff --git a/src/stim/util_bot/test_util.test.cc b/src/stim/util_bot/test_util.test.cc index aed7e289..62f92a37 100644 --- a/src/stim/util_bot/test_util.test.cc +++ b/src/stim/util_bot/test_util.test.cc @@ -1,20 +1,7 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - #include "stim/util_bot/test_util.test.h" #include +#include #include "stim/util_bot/probability_util.h" @@ -67,8 +54,8 @@ void expect_string_is_identical_to_saved_file(std::string_view actual, std::stri out.close(); EXPECT_TRUE(false) << "Diagram didn't agree.\n" << " key=" << key << "\n" - << " expected: file://" << path << "\n" - << " actual: file://" << new_path << "\n"; + << " expected: file://" << std::filesystem::weakly_canonical(std::filesystem::path(path)).c_str() << "\n" + << " actual: file://" << std::filesystem::weakly_canonical(std::filesystem::path(new_path)).c_str() << "\n"; } } diff --git a/src/stim/util_top/export_crumble_url.test.cc b/src/stim/util_top/export_crumble_url.test.cc index 34cc6eca..6eaf8b50 100644 --- a/src/stim/util_top/export_crumble_url.test.cc +++ b/src/stim/util_top/export_crumble_url.test.cc @@ -97,10 +97,12 @@ TEST(export_crumble, all_operations) { "DT(1,2,3)rec[-1];" "OI(0)rec[-1];" "MPAD_0_1_0;" + "OI(1)Z2_Z3;" "TICK;" "MRX_!0;" "MY_!1;" "MZZ_!2_3;" + "OI(1)rec[-1];" "MYY_!4_!5;" "MPP_X6*!Y7*Z8;" "TICK;" diff --git a/src/stim/util_top/export_qasm.cc b/src/stim/util_top/export_qasm.cc index c4fbed58..18153f5e 100644 --- a/src/stim/util_top/export_qasm.cc +++ b/src/stim/util_top/export_qasm.cc @@ -496,13 +496,23 @@ struct QasmExporter { } int ref_value = 0; + bool had_paulis = false; for (auto t : instruction.targets) { - assert(t.is_measurement_record_target()); - auto i = measurement_offset + t.rec_offset(); - ref_value ^= reference_sample[i]; - out << "rec[" << (measurement_offset + t.rec_offset()) << "] ^ "; + if (t.is_measurement_record_target()) { + auto i = measurement_offset + t.rec_offset(); + ref_value ^= reference_sample[i]; + out << "rec[" << (measurement_offset + t.rec_offset()) << "] ^ "; + } else if (t.is_pauli_target()) { + had_paulis = true; + } else { + throw std::invalid_argument("Unexpected target for OBSERVABLE_INCLUDE: " + t.str()); + } } out << ref_value << ";\n"; + + if (had_paulis) { + out << "// Warning: ignored pauli terms in " << instruction << "\n"; + } return; } diff --git a/src/stim/util_top/export_qasm.test.cc b/src/stim/util_top/export_qasm.test.cc index 34a7d89b..a635bb75 100644 --- a/src/stim/util_top/export_qasm.test.cc +++ b/src/stim/util_top/export_qasm.test.cc @@ -340,7 +340,7 @@ def ry(qubit q0) { reset q0; h q0; s q0; } qreg q[18]; creg rec[25]; creg dets[1]; -creg obs[1]; +creg obs[2]; creg sweep[1]; id q[0]; @@ -475,11 +475,14 @@ obs[0] = obs[0] ^ rec[16] ^ 0; rec[17] = 0; rec[18] = 1; rec[19] = 0; +obs[1] = obs[1] ^ 0; +// Warning: ignored pauli terms in OBSERVABLE_INCLUDE(1) Z2 Z3 barrier q; rec[20] = mrx(q[0]) ^ 1; rec[21] = my(q[1]) ^ 1; rec[22] = mzz(q[2], q[3]) ^ 1; +obs[1] = obs[1] ^ rec[22] ^ 1; rec[23] = myy(q[4], q[5]); // --- begin decomposed MPP X6*!Y7*Z8 h q[6]; diff --git a/testdata/circuit_all_ops_timeline.svg b/testdata/circuit_all_ops_timeline.svg index 16937b33..d7c1a115 100644 --- a/testdata/circuit_all_ops_timeline.svg +++ b/testdata/circuit_all_ops_timeline.svg @@ -1,40 +1,40 @@ - + - + q0 - + q1 - + q2 - + q3 - + q4 - + q5 - + q6 - + q7 - + q8 - + q9 - + q10 - + q11 - + q12 - + q13 - + q14 - + q15 - + q16 - + q17 @@ -341,36 +341,46 @@ MPAD rec[19] - - - -MRX -rec[20] - -MY -rec[21] - - -MZZ - -MZZ - - -MYY - -MYY - - -MPPX -rec[24] - -MPPY - -MPPZ - -Xrec[24] - -Ysweep[0] - -Zrec[24] + + +L1*=Z + +L1*=Z + + + +MRX +rec[20] + +MY +rec[21] + + +MZZ + +MZZ + +OBS_INCLUDE(1) +L1 *= rec[22] + + +MYY + +MYY + + +MPPX +rec[24] + +MPPY + +MPPZ + + + +Xrec[24] + +Ysweep[0] + +Zrec[24] \ No newline at end of file