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 @@
-