From 78e5ec8dc7df71226193adc513fe3bdaf605931b Mon Sep 17 00:00:00 2001 From: Noah Shutty Date: Sun, 10 Aug 2025 22:46:23 -0400 Subject: [PATCH 1/2] Add visualization library to CMake build --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3111bd..33420c7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,10 +73,15 @@ target_include_directories(utils PUBLIC ${TESSERACT_SRC_DIR}) target_compile_options(utils PRIVATE ${OPT_COPTS}) target_link_libraries(utils PUBLIC common libstim Threads::Threads) +add_library(visualization ${TESSERACT_SRC_DIR}/visualization.cc ${TESSERACT_SRC_DIR}/visualization.h) +target_include_directories(visualization PUBLIC ${TESSERACT_SRC_DIR}) +target_compile_options(visualization PRIVATE ${OPT_COPTS}) +target_link_libraries(visualization PUBLIC common boost_headers) + add_library(tesseract_lib ${TESSERACT_SRC_DIR}/tesseract.cc ${TESSERACT_SRC_DIR}/tesseract.h) target_include_directories(tesseract_lib PUBLIC ${TESSERACT_SRC_DIR}) target_compile_options(tesseract_lib PRIVATE ${OPT_COPTS}) -target_link_libraries(tesseract_lib PUBLIC utils boost_headers) +target_link_libraries(tesseract_lib PUBLIC utils boost_headers visualization) add_library(simplex ${TESSERACT_SRC_DIR}/simplex.cc ${TESSERACT_SRC_DIR}/simplex.h) target_include_directories(simplex PUBLIC ${TESSERACT_SRC_DIR}) From fc0694ed30aa9e7c335680ed1488d7277aada87a Mon Sep 17 00:00:00 2001 From: noajshu Date: Thu, 14 Aug 2025 20:20:21 +0000 Subject: [PATCH 2/2] fix to_json for new error format --- viz/to_json.py | 59 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/viz/to_json.py b/viz/to_json.py index ecf63148..bf5e92ef 100644 --- a/viz/to_json.py +++ b/viz/to_json.py @@ -1,15 +1,23 @@ +# viz/to_json.py import json import argparse import re import numpy as np +INT_RE = re.compile(r'-?\d+') + +def parse_int_list(text): + """Extract all integers from a string, tolerant to spaces/commas/brackets.""" + return [int(x) for x in INT_RE.findall(text)] + def parse_implicit_list(line, prefix): if not line.startswith(prefix): raise ValueError(f"Expected line to start with '{prefix}', got: {line}") list_part = line[len(prefix):].strip().rstrip(',') if not list_part: return [] - return [int(x.strip()) for x in list_part.split(',') if x.strip()] + # Be tolerant: accept "1, 2, 3" or "1 2 3" + return parse_int_list(list_part) def parse_logfile(filepath): detector_coords = {} @@ -24,21 +32,37 @@ def parse_logfile(filepath): line = lines[i].strip() if not any(line.startswith(s) for s in ['Error', 'Detector', 'activated_errors', 'activated_detectors']): - continue + i += 1 + continue if line.startswith("Detector D"): - match = re.match(r'Detector D(\d+) coordinate \(([-\d.]+), ([-\d.]+), ([-\d.]+)\)', line) + # Example: "Detector D123 coordinate (1.0, 2.0, 3.0)" + match = re.match( + r'Detector D(\d+)\s+coordinate\s*\(\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*\)', + line + ) if match: idx = int(match.group(1)) coord = tuple(float(match.group(j)) for j in range(2, 5)) detector_coords[idx] = coord elif line.startswith("Error{"): - match = re.search(r'Symptom\{([^\}]+)\}', line) - if match: - dets = match.group(1).split() - det_indices = [int(d[1:]) for d in dets if d.startswith('D')] - error_to_detectors.append(det_indices) + # New format: Error{..., symptom=Symptom{detectors=[75 89 93 100], observables=[...]}} + # Fallback: old format with "D###" tokens inside Symptom{...} + dets = [] + + m_detlist = re.search(r'detectors=\[([^\]]*)\]', line) + if m_detlist: + dets = parse_int_list(m_detlist.group(1)) + else: + # Old fallback: scrape Symptom{...} and look for D### + m_sym = re.search(r'Symptom\{([^}]*)\}', line) + if m_sym: + tokens = m_sym.group(1).split() + dets = [int(t[1:]) for t in tokens if t.startswith('D') and t[1:].isdigit()] + + # Store (even if empty—we keep the index alignment with errors) + error_to_detectors.append(dets) elif line.startswith("activated_errors"): try: @@ -48,34 +72,39 @@ def parse_logfile(filepath): activated_errors = parse_implicit_list(error_line, "activated_errors =") activated_dets = parse_implicit_list(det_line, "activated_detectors =") - frame = { + frames.append({ "activated": activated_dets, "activated_errors": activated_errors - } - frames.append(frame) - i += 1 + }) + + # We consumed two lines in this block + i += 2 + continue # skip the unconditional i+=1 below (already advanced) except Exception as e: print(f"\n⚠️ Error parsing frame at lines {i}-{i+1}: {e}") print(f" {lines[i].strip()}") print(f" {lines[i+1].strip() if i+1 < len(lines) else ''}") + i += 1 if not detector_coords: raise RuntimeError("No detectors parsed!") + # Center detector coordinates coords_array = np.array(list(detector_coords.values())) mean_coord = coords_array.mean(axis=0) for k in detector_coords: detector_coords[k] = (np.array(detector_coords[k]) - mean_coord).tolist() + # Error coordinates as mean of their detectors (if known) error_coords = {} - for i, det_list in enumerate(error_to_detectors): + for ei, det_list in enumerate(error_to_detectors): try: pts = np.array([detector_coords[d] for d in det_list if d in detector_coords]) if len(pts) > 0: - error_coords[i] = pts.mean(axis=0).tolist() + error_coords[ei] = pts.mean(axis=0).tolist() except KeyError as e: - print(f"⚠️ Skipping error {i}: unknown detector {e}") + print(f"⚠️ Skipping error {ei}: unknown detector {e}") error_to_detectors_dict = {str(i): dets for i, dets in enumerate(error_to_detectors)}