diff --git a/Cargo.lock b/Cargo.lock index 146686da4..e353bfd01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -597,6 +597,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -880,9 +889,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b788601e7e3e6944d9b37efbae0bee7ee44d9aab533838d4854f631534a1a49" +checksum = "a7620f6cfc4dcca21f2b085b7a890e16c60fd66f560cd69ee60594908dc72ab1" dependencies = [ "cc", "cxx-build", @@ -895,9 +904,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e11d62eb0de451f6d3aa83f2cec0986af61c23bd7515f1e2d6572c6c9e53c96" +checksum = "7a9bc1a22964ff6a355fbec24cf68266a0ed28f8b84c0864c386474ea3d0e479" dependencies = [ "cc", "codespan-reporting", @@ -910,9 +919,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a368ed4a0fd83ebd3f2808613842d942a409c41cc24cd9d83f1696a00d78afe" +checksum = "b1f29a879d35f7906e3c9b77d7a1005a6a0787d330c09dfe4ffb5f617728cb44" dependencies = [ "clap", "codespan-reporting", @@ -924,15 +933,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9571a7c69f236d7202f517553241496125ed56a86baa1ce346d02aa72357c74" +checksum = "d67109015f93f683e364085aa6489a5b2118b4a40058482101d699936a7836d6" [[package]] name = "cxxbridge-macro" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba2aaae28ca1d721d3f364bb29d51811921e7194c08bb9eaf745c8ab8d81309" +checksum = "d187e019e7b05a1f3e69a8396b70800ee867aa9fc2ab972761173ccee03742df" dependencies = [ "indexmap 2.12.1", "proc-macro2", @@ -1038,7 +1047,7 @@ version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", @@ -1047,21 +1056,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ + "convert_case 0.10.0", "proc-macro2", "quote", + "rustc_version", "syn 2.0.111", "unicode-xid", ] @@ -1687,7 +1698,7 @@ dependencies = [ "base64", "cgmath", "delegate", - "derive_more 2.0.1", + "derive_more 2.1.0", "downcast-rs", "enum_dispatch", "html-escape", @@ -1725,7 +1736,7 @@ dependencies = [ "anyhow", "cc", "delegate", - "derive_more 2.0.1", + "derive_more 2.1.0", "hugr-core", "inkwell", "insta", @@ -1745,7 +1756,7 @@ dependencies = [ "base64", "bumpalo", "capnp", - "derive_more 2.0.1", + "derive_more 2.1.0", "indexmap 2.12.1", "itertools 0.14.0", "ordered-float", @@ -1765,7 +1776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e170464e6c94176cd39099f52bac95552bf360b971959cd8a4a27d76e645ab9" dependencies = [ "ascent", - "derive_more 2.0.1", + "derive_more 2.1.0", "hugr-core", "itertools 0.14.0", "pastey", @@ -1815,9 +1826,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", @@ -2148,9 +2159,9 @@ dependencies = [ [[package]] name = "lambert_w" -version = "1.2.31" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c567f2087fc83535a312e683b6ed8811395690ef896df7b82966b21b7526580" +checksum = "9bf449fa972fa5f9c412c4697dc472bfbf84edbaaeb078d8e42f5889fee41cde" dependencies = [ "num-complex", "num-traits", @@ -2188,9 +2199,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -2264,9 +2275,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru-slab" @@ -3329,9 +3340,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a6df7eab65fc7bee654a421404947e10a0f7085b6951bf2ea395f4659fb0cf" +checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" dependencies = [ "indoc", "libc", @@ -3347,9 +3358,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb" +checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" dependencies = [ "python3-dll-a", "target-lexicon", @@ -3357,9 +3368,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be" +checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" dependencies = [ "libc", "pyo3-build-config", @@ -3367,9 +3378,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf8f9f1108270b90d3676b8679586385430e5c0bb78bb5f043f95499c821a71" +checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -3379,9 +3390,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a3b2274450ba5288bc9b8c1b69ff569d1d61189d4bff38f8d22e03d17f932b" +checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3956,7 +3967,7 @@ source = "git+https://github.com/CQCL/selene.git?rev=1794e8d1dba26120a18e904940c dependencies = [ "anyhow", "delegate", - "derive_more 2.0.1", + "derive_more 2.1.0", "libloading", "ouroboros", "thiserror 2.0.17", @@ -4457,7 +4468,7 @@ dependencies = [ "crossbeam-channel", "csv", "delegate", - "derive_more 2.0.1", + "derive_more 2.1.0", "fxhash", "hugr", "hugr-core", @@ -4488,7 +4499,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c424855b86bde6c2d1651241abf829f1bfcd8e5428356576ac11bd825357bc" dependencies = [ - "derive_more 2.0.1", + "derive_more 2.1.0", "serde", "serde_json", "strum", @@ -4503,7 +4514,7 @@ checksum = "e222042a5c63511a753ffe23908a4d7d36b1a667b88e3f4eba2ad2329fce16ec" dependencies = [ "anyhow", "delegate", - "derive_more 2.0.1", + "derive_more 2.1.0", "hugr", "indexmap 2.12.1", "itertools 0.14.0", @@ -4706,6 +4717,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.2.2" @@ -4768,13 +4785,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -4903,12 +4920,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.242.0" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67f90e55bc9c6ee6954a757cc6eb3424d96b442e5252ed10fea627e518878d36" +checksum = "c55db9c896d70bd9fa535ce83cd4e1f2ec3726b0edd2142079f594fc3be1cb35" dependencies = [ "leb128fmt", - "wasmparser 0.242.0", + "wasmparser 0.243.0", ] [[package]] @@ -4926,9 +4943,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.242.0" +version = "0.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3c6e611f4cd748d85c767815823b777dc56afca793fcda27beae4e85028849" +checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d" dependencies = [ "bitflags", "indexmap 2.12.1", @@ -5117,22 +5134,22 @@ dependencies = [ [[package]] name = "wast" -version = "242.0.0" +version = "243.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a61ae2997784a4ae2a47b3a99f7cf0ad2a54db09624a28a0c2e9d7a24408ce" +checksum = "df21d01c2d91e46cb7a221d79e58a2d210ea02020d57c092e79255cc2999ca7f" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width", - "wasm-encoder 0.242.0", + "wasm-encoder 0.243.0", ] [[package]] name = "wat" -version = "1.242.0" +version = "1.243.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ae8cf6adfb79b5d89cb3fe68bd56aaab9409d9cf23b588097eae7d75585dae2" +checksum = "226a9a91cd80a50449312fef0c75c23478fcecfcc4092bdebe1dc8e760ef521b" dependencies = [ "wast", ] @@ -5489,18 +5506,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", diff --git a/Makefile b/Makefile index 56082da06..d12a2dc2d 100644 --- a/Makefile +++ b/Makefile @@ -120,9 +120,12 @@ build-cuda: installreqs ## Compile and install for development with CUDA support docs-build: ## Clean, install deps, and build documentation @uv run mkdocs build --clean -.PHONY: docs-serve -docs-serve: ## Serve documentation (for other ports add... -dev-addr=127.0.0.1:9000) - @uv run mkdocs serve +.PHONY: docs +docs: ## Serve documentation and open in browser (PORT=9000 to change port) + @uv run mkdocs serve -a 127.0.0.1:$(or $(PORT),8000) 2>&1 | while IFS= read -r line; do \ + echo "$$line"; \ + case "$$line" in *"Serving on"*) xdg-open http://127.0.0.1:$(or $(PORT),8000)/PECOS/ 2>/dev/null ;; esac; \ + done .PHONY: docs-test docs-test: ## Test all code examples in documentation diff --git a/crates/pecos-qsim/src/lib.rs b/crates/pecos-qsim/src/lib.rs index e41883eae..1df832651 100644 --- a/crates/pecos-qsim/src/lib.rs +++ b/crates/pecos-qsim/src/lib.rs @@ -18,9 +18,12 @@ pub mod pauli_prop; pub mod arbitrary_rotation_gateable; pub mod prelude; pub mod quantum_simulator; +pub mod sign_algebra; pub mod sparse_stab; pub mod stabilizer_tableau; pub mod state_vec; +pub mod symbolic_gens; +pub mod symbolic_sparse_stab; pub use arbitrary_rotation_gateable::ArbitraryRotationGateable; pub use clifford_gateable::{CliffordGateable, MeasurementResult}; @@ -30,6 +33,11 @@ pub use gens::Gens; pub use pauli_prop::{PauliProp, StdPauliProp}; pub use pecos_core::VecSet; pub use quantum_simulator::QuantumSimulator; +pub use sign_algebra::{PhaseSign, SignAlgebra, SymbolicSign}; pub use sparse_stab::{SparseStab, StdSparseStab}; pub use stabilizer_tableau::StabilizerTableauSimulator; pub use state_vec::StateVec; +pub use symbolic_gens::SymbolicGens; +pub use symbolic_sparse_stab::{ + StdSymbolicSparseStab, SymbolicMeasurementResult, SymbolicSparseStab, +}; diff --git a/crates/pecos-qsim/src/prelude.rs b/crates/pecos-qsim/src/prelude.rs index 889e88567..cc0078d6e 100644 --- a/crates/pecos-qsim/src/prelude.rs +++ b/crates/pecos-qsim/src/prelude.rs @@ -14,11 +14,13 @@ pub use pecos_core::{IndexableElement, VecSet}; pub use crate::{ arbitrary_rotation_gateable::ArbitraryRotationGateable, - clifford_gateable::CliffordGateable, + clifford_gateable::{CliffordGateable, MeasurementResult}, coin_toss::CoinToss, pauli_prop::{PauliProp, StdPauliProp}, quantum_simulator::QuantumSimulator, + sign_algebra::{PhaseSign, SignAlgebra, SymbolicSign}, sparse_stab::{SparseStab, StdSparseStab}, stabilizer_tableau::StabilizerTableauSimulator, state_vec::StateVec, + symbolic_sparse_stab::{StdSymbolicSparseStab, SymbolicMeasurementResult, SymbolicSparseStab}, }; diff --git a/crates/pecos-qsim/src/sign_algebra.rs b/crates/pecos-qsim/src/sign_algebra.rs new file mode 100644 index 000000000..822eb0487 --- /dev/null +++ b/crates/pecos-qsim/src/sign_algebra.rs @@ -0,0 +1,367 @@ +// Copyright 2025 The PECOS Developers +// +// 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 +// +// https://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. + +//! Sign algebra abstractions for stabilizer simulators. +//! +//! This module provides traits and types for representing stabilizer signs in different ways: +//! - [`PhaseSign`]: Traditional phase representation (+1, -1, +i, -i) for standard simulation +//! - [`SymbolicSign`]: Measurement index sets for symbolic/deferred measurement simulation +//! +//! The [`SignAlgebra`] trait abstracts over these representations, allowing the stabilizer +//! simulator to be generic over the sign type. + +use core::fmt::Debug; +use std::collections::BTreeSet; + +/// Trait for sign algebras used in stabilizer simulation. +/// +/// This trait abstracts over different representations of stabilizer signs: +/// - Traditional phases (+1, -1, +i, -i) that collapse to concrete measurement outcomes +/// - Symbolic signs that track which measurements contribute to an outcome +/// +/// # Type Parameters +/// - `Outcome`: The type returned when reading a measurement result +pub trait SignAlgebra: Clone + Debug + Default + Send + Sync { + /// The type returned when reading a measurement outcome. + /// For traditional signs, this is `bool` (0 or 1). + /// For symbolic signs, this is a set of measurement indices. + type Outcome: Clone + Debug; + + /// Returns the identity element (corresponds to +1 or empty set). + fn identity() -> Self; + + /// Multiply two signs together. + /// For traditional signs: phase multiplication. + /// For symbolic signs: XOR / symmetric difference of index sets. + #[must_use] + fn multiply(&self, other: &Self) -> Self; + + /// Multiply-assign: self = self * other + fn multiply_assign(&mut self, other: &Self); + + /// Create a sign representing a non-deterministic measurement outcome. + /// For traditional signs: creates +1 or -1 based on the random outcome. + /// For symbolic signs: creates a set containing just this measurement's index. + fn from_measurement(measurement_index: usize, outcome: bool) -> Self; + + /// Convert the sign to a measurement outcome. + fn to_outcome(&self) -> Self::Outcome; + + /// Check if this represents a "negative" sign (for traditional: has minus sign). + /// For symbolic signs, this is not meaningful and returns false. + fn is_negative(&self) -> bool; + + /// Negate the sign (flip the minus component). + /// For symbolic signs, this is a no-op since we don't track absolute phase. + fn negate(&mut self); +} + +/// Traditional phase sign representation for standard stabilizer simulation. +/// +/// Represents signs as (-1)^minus * i^imag, giving the four phases: +1, -1, +i, -i. +/// +/// This is used in the standard `StdSparseStab` simulator where measurements +/// collapse to concrete 0/1 outcomes. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct PhaseSign { + /// True if the sign has a factor of -1 + pub minus: bool, + /// True if the sign has a factor of i + pub imag: bool, +} + +impl PhaseSign { + /// Create a new phase sign with the given components. + #[inline] + #[must_use] + pub fn new(minus: bool, imag: bool) -> Self { + Self { minus, imag } + } + + /// Create the +1 phase. + #[inline] + #[must_use] + pub fn plus_one() -> Self { + Self { + minus: false, + imag: false, + } + } + + /// Create the -1 phase. + #[inline] + #[must_use] + pub fn minus_one() -> Self { + Self { + minus: true, + imag: false, + } + } + + /// Create the +i phase. + #[inline] + #[must_use] + pub fn plus_i() -> Self { + Self { + minus: false, + imag: true, + } + } + + /// Create the -i phase. + #[inline] + #[must_use] + pub fn minus_i() -> Self { + Self { + minus: true, + imag: true, + } + } +} + +impl SignAlgebra for PhaseSign { + type Outcome = bool; + + #[inline] + fn identity() -> Self { + Self::plus_one() + } + + #[inline] + fn multiply(&self, other: &Self) -> Self { + // (-1)^a * i^b * (-1)^c * i^d = (-1)^(a+c) * i^(b+d) + // But i^2 = -1, so if both have i, we get an extra -1 + let extra_minus = self.imag && other.imag; + Self { + minus: self.minus ^ other.minus ^ extra_minus, + imag: self.imag ^ other.imag, + } + } + + #[inline] + fn multiply_assign(&mut self, other: &Self) { + let extra_minus = self.imag && other.imag; + self.minus ^= other.minus ^ extra_minus; + self.imag ^= other.imag; + } + + #[inline] + fn from_measurement(_measurement_index: usize, outcome: bool) -> Self { + // For traditional simulation, we just record whether the outcome is 0 or 1 + // outcome=true means we measured |1⟩, which corresponds to -Z eigenvalue + Self { + minus: outcome, + imag: false, + } + } + + #[inline] + fn to_outcome(&self) -> bool { + // The measurement outcome is determined by the minus sign + // minus=true means -1 eigenvalue, which is |1⟩ + self.minus + } + + #[inline] + fn is_negative(&self) -> bool { + self.minus + } + + #[inline] + fn negate(&mut self) { + self.minus = !self.minus; + } +} + +/// Symbolic sign representation for deferred/symbolic measurement simulation. +/// +/// Instead of collapsing to concrete +1/-1 values, this tracks which measurements +/// contribute to the sign via XOR (symmetric difference) of measurement indices. +/// +/// An empty set corresponds to +1 (identity). A set {m1, m2, ...} means the sign +/// is the XOR of the outcomes of measurements m1, m2, etc. +/// +/// This is useful for: +/// - Analyzing measurement dependency graphs +/// - Understanding which measurements affect which outcomes +/// - Pauli frame tracking / deferred measurement patterns +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SymbolicSign { + /// Set of measurement indices whose outcomes XOR together to give this sign. + /// Empty set = +1 (deterministic 0 outcome). + pub measurements: BTreeSet, +} + +impl SymbolicSign { + /// Create a new symbolic sign with the given measurement indices. + #[inline] + #[must_use] + pub fn new(measurements: BTreeSet) -> Self { + Self { measurements } + } + + /// Create an empty (identity) symbolic sign. + #[inline] + #[must_use] + pub fn empty() -> Self { + Self { + measurements: BTreeSet::new(), + } + } + + /// Create a symbolic sign from a single measurement index. + #[inline] + #[must_use] + pub fn single(measurement_index: usize) -> Self { + let mut measurements = BTreeSet::new(); + measurements.insert(measurement_index); + Self { measurements } + } +} + +impl SignAlgebra for SymbolicSign { + type Outcome = BTreeSet; + + #[inline] + fn identity() -> Self { + Self::empty() + } + + #[inline] + fn multiply(&self, other: &Self) -> Self { + // XOR / symmetric difference of the measurement sets + let measurements: BTreeSet = self + .measurements + .symmetric_difference(&other.measurements) + .copied() + .collect(); + Self { measurements } + } + + #[inline] + fn multiply_assign(&mut self, other: &Self) { + // In-place symmetric difference + for &idx in &other.measurements { + if !self.measurements.remove(&idx) { + self.measurements.insert(idx); + } + } + } + + #[inline] + fn from_measurement(measurement_index: usize, _outcome: bool) -> Self { + // For symbolic simulation, we ignore the concrete outcome and just + // record which measurement this is + Self::single(measurement_index) + } + + #[inline] + fn to_outcome(&self) -> BTreeSet { + self.measurements.clone() + } + + #[inline] + fn is_negative(&self) -> bool { + // Symbolic signs don't have a concrete "negative" state + // The "sign" is the set of measurements + false + } + + #[inline] + fn negate(&mut self) { + // No-op for symbolic signs - we don't track absolute phase + // The sign is purely relational (XOR of measurement outcomes) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_phase_sign_multiply() { + // +1 * +1 = +1 + assert_eq!( + PhaseSign::plus_one().multiply(&PhaseSign::plus_one()), + PhaseSign::plus_one() + ); + + // +1 * -1 = -1 + assert_eq!( + PhaseSign::plus_one().multiply(&PhaseSign::minus_one()), + PhaseSign::minus_one() + ); + + // -1 * -1 = +1 + assert_eq!( + PhaseSign::minus_one().multiply(&PhaseSign::minus_one()), + PhaseSign::plus_one() + ); + + // +i * +i = -1 + assert_eq!( + PhaseSign::plus_i().multiply(&PhaseSign::plus_i()), + PhaseSign::minus_one() + ); + + // +i * -i = +1 + assert_eq!( + PhaseSign::plus_i().multiply(&PhaseSign::minus_i()), + PhaseSign::plus_one() + ); + + // -1 * +i = -i + assert_eq!( + PhaseSign::minus_one().multiply(&PhaseSign::plus_i()), + PhaseSign::minus_i() + ); + } + + #[test] + fn test_phase_sign_outcome() { + assert!(!PhaseSign::plus_one().to_outcome()); + assert!(PhaseSign::minus_one().to_outcome()); + } + + #[test] + fn test_symbolic_sign_multiply() { + let s1 = SymbolicSign::single(0); + let s2 = SymbolicSign::single(1); + let s3 = SymbolicSign::single(0); + + // {0} * {1} = {0, 1} + let result = s1.multiply(&s2); + assert_eq!(result.measurements.len(), 2); + assert!(result.measurements.contains(&0)); + assert!(result.measurements.contains(&1)); + + // {0} * {0} = {} (XOR cancels) + let result = s1.multiply(&s3); + assert!(result.measurements.is_empty()); + + // {} * {0} = {0} + let result = SymbolicSign::empty().multiply(&s1); + assert_eq!(result.measurements.len(), 1); + assert!(result.measurements.contains(&0)); + } + + #[test] + fn test_symbolic_sign_from_measurement() { + let sign = SymbolicSign::from_measurement(42, true); + assert_eq!(sign.measurements.len(), 1); + assert!(sign.measurements.contains(&42)); + + // Outcome is ignored for symbolic signs + let sign2 = SymbolicSign::from_measurement(42, false); + assert_eq!(sign, sign2); + } +} diff --git a/crates/pecos-qsim/src/symbolic_gens.rs b/crates/pecos-qsim/src/symbolic_gens.rs new file mode 100644 index 000000000..f9468f16a --- /dev/null +++ b/crates/pecos-qsim/src/symbolic_gens.rs @@ -0,0 +1,228 @@ +// Copyright 2025 The PECOS Developers +// +// 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 +// +// https://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. + +//! Symbolic generators for stabilizer states with measurement-indexed signs. +//! +//! This module provides [`SymbolicGens`], a variant of the generator storage that tracks +//! stabilizer signs in two parts: +//! 1. **Measurement dependencies** (`signs`): Sets of measurement indices that XOR together +//! 2. **Phase flips** (`signs_minus`, `signs_i`): Traditional phase tracking from unitary gates +//! +//! The final measurement outcome is: `XOR(measurement_outcomes) XOR phase_flip` + +use crate::sign_algebra::{SignAlgebra, SymbolicSign}; +use core::fmt::Debug; +use core::marker::PhantomData; +use pecos_core::{IndexableElement, Set}; + +/// Generators for symbolic stabilizer simulation. +/// +/// Tracks stabilizer signs in two parts: +/// 1. **Measurement dependencies** (`signs`): Per-generator sets of measurement indices +/// 2. **Phase tracking** (`signs_minus`, `signs_i`): Traditional phase from unitary gates +/// +/// The final sign is: `{measurement_deps} ^ phase_flip` where `phase_flip` is computed +/// from `signs_minus` and `signs_i`. +#[derive(Clone, Debug)] +pub struct SymbolicGens +where + T: for<'a> Set<'a, Element = E>, + E: IndexableElement, +{ + num_qubits: usize, + /// Column-wise storage of X operators: `col_x`[qubit] = set of generator indices with X on that qubit + pub col_x: Vec, + /// Column-wise storage of Z operators: `col_z`[qubit] = set of generator indices with Z on that qubit + pub col_z: Vec, + /// Row-wise storage of X operators: `row_x`[gen] = set of qubits where this generator has X + pub row_x: Vec, + /// Row-wise storage of Z operators: `row_z`[gen] = set of qubits where this generator has Z + pub row_z: Vec, + /// Symbolic signs for each generator: signs[gen] = set of measurement indices + pub signs: Vec, + /// Traditional phase tracking: generators with a minus sign (from unitaries) + pub signs_minus: T, + /// Traditional phase tracking: generators with an imaginary component (from unitaries) + pub signs_i: T, + _marker: PhantomData, +} + +impl SymbolicGens +where + T: for<'a> Set<'a, Element = E>, + E: IndexableElement, +{ + /// Create new symbolic generators for the given number of qubits. + #[must_use] + #[inline] + pub fn new(num_qubits: usize) -> Self { + Self { + num_qubits, + col_x: vec![T::new(); num_qubits], + col_z: vec![T::new(); num_qubits], + row_x: vec![T::new(); num_qubits], + row_z: vec![T::new(); num_qubits], + signs: vec![SymbolicSign::empty(); num_qubits], + signs_minus: T::new(), + signs_i: T::new(), + _marker: PhantomData, + } + } + + /// Get the number of qubits. + #[inline] + #[must_use] + pub fn get_num_qubits(&self) -> usize { + self.num_qubits + } + + /// Clear all generator data. + #[inline] + fn clear(&mut self) { + self.col_x.clear(); + self.col_z.clear(); + self.row_x.clear(); + self.row_z.clear(); + self.signs.clear(); + self.signs_minus.clear(); + self.signs_i.clear(); + } + + /// Initialize all generators as Z operators (stabilizers start as `Z_i` for each qubit). + #[inline] + pub fn init_all_z(&mut self) { + self.clear(); + self.col_x = vec![T::new(); self.num_qubits]; + self.col_z = new_index_set::(self.num_qubits); + self.row_x = vec![T::new(); self.num_qubits]; + self.row_z = new_index_set::(self.num_qubits); + self.signs = vec![SymbolicSign::empty(); self.num_qubits]; + // signs_minus and signs_i are already cleared + } + + /// Initialize all generators as X operators (destabilizers start as `X_i` for each qubit). + #[inline] + pub fn init_all_x(&mut self) { + self.clear(); + self.col_x = new_index_set::(self.num_qubits); + self.col_z = vec![T::new(); self.num_qubits]; + self.row_x = new_index_set::(self.num_qubits); + self.row_z = vec![T::new(); self.num_qubits]; + self.signs = vec![SymbolicSign::empty(); self.num_qubits]; + // signs_minus and signs_i are already cleared + } + + /// Multiply the sign of generator `target` by the sign of generator `source`. + /// This performs XOR (symmetric difference) of the measurement index sets. + #[inline] + pub fn multiply_signs(&mut self, target: usize, source: usize) { + // We need to clone source sign to avoid borrow issues + let source_sign = self.signs[source].clone(); + self.signs[target].multiply_assign(&source_sign); + } + + /// Set the sign of a generator to a specific symbolic sign. + #[inline] + pub fn set_sign(&mut self, gen_idx: usize, sign: SymbolicSign) { + self.signs[gen_idx] = sign; + } + + /// Get the sign of a generator. + #[inline] + #[must_use] + pub fn get_sign(&self, gen_idx: usize) -> &SymbolicSign { + &self.signs[gen_idx] + } + + /// Clear the sign of a generator (set to identity/empty set). + #[inline] + pub fn clear_sign(&mut self, gen_idx: usize) { + self.signs[gen_idx] = SymbolicSign::empty(); + } +} + +/// Helper function to create a vector of sets where set[i] contains just element i. +#[inline] +fn new_index_set(num_qubits: usize) -> Vec +where + T: for<'a> Set<'a, Element = E>, + E: IndexableElement, +{ + let mut sets = Vec::with_capacity(num_qubits); + for i in 0..num_qubits { + let mut set = T::new(); + set.insert(E::from_index(i)); + sets.push(set); + } + sets +} + +#[cfg(test)] +mod tests { + use super::*; + use pecos_core::VecSet; + + #[test] + fn test_symbolic_gens_new() { + let gens: SymbolicGens, usize> = SymbolicGens::new(3); + assert_eq!(gens.get_num_qubits(), 3); + assert_eq!(gens.signs.len(), 3); + for sign in &gens.signs { + assert!(sign.measurements.is_empty()); + } + } + + #[test] + fn test_symbolic_gens_init_all_z() { + let mut gens: SymbolicGens, usize> = SymbolicGens::new(2); + gens.init_all_z(); + + // col_z should have generator i in set for qubit i + assert!(gens.col_z[0].contains(&0)); + assert!(gens.col_z[1].contains(&1)); + + // col_x should be empty + assert!(gens.col_x[0].is_empty()); + assert!(gens.col_x[1].is_empty()); + + // All signs should be empty (identity) + for sign in &gens.signs { + assert!(sign.measurements.is_empty()); + } + } + + #[test] + fn test_symbolic_gens_multiply_signs() { + let mut gens: SymbolicGens, usize> = SymbolicGens::new(3); + + // Set some signs + gens.set_sign(0, SymbolicSign::single(0)); // {0} + gens.set_sign(1, SymbolicSign::single(1)); // {1} + gens.set_sign(2, SymbolicSign::empty()); // {} + + // Multiply sign[2] by sign[0]: {} * {0} = {0} + gens.multiply_signs(2, 0); + assert_eq!(gens.signs[2].measurements.len(), 1); + assert!(gens.signs[2].measurements.contains(&0)); + + // Multiply sign[2] by sign[1]: {0} * {1} = {0, 1} + gens.multiply_signs(2, 1); + assert_eq!(gens.signs[2].measurements.len(), 2); + assert!(gens.signs[2].measurements.contains(&0)); + assert!(gens.signs[2].measurements.contains(&1)); + + // Multiply sign[2] by sign[0] again: {0, 1} * {0} = {1} + gens.multiply_signs(2, 0); + assert_eq!(gens.signs[2].measurements.len(), 1); + assert!(gens.signs[2].measurements.contains(&1)); + } +} diff --git a/crates/pecos-qsim/src/symbolic_sparse_stab.rs b/crates/pecos-qsim/src/symbolic_sparse_stab.rs new file mode 100644 index 000000000..b9acb1301 --- /dev/null +++ b/crates/pecos-qsim/src/symbolic_sparse_stab.rs @@ -0,0 +1,1292 @@ +// Copyright 2025 The PECOS Developers +// +// 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 +// +// https://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. + +//! Symbolic stabilizer simulator with measurement-indexed signs. +//! +//! This module provides [`SymbolicSparseStab`], a stabilizer simulator that tracks +//! measurement dependencies rather than collapsing to concrete outcomes. +//! +//! Instead of randomly choosing 0 or 1 for non-deterministic measurements, this simulator +//! assigns each measurement a unique index and tracks which measurements contribute to +//! each stabilizer's sign via XOR (symmetric difference). +//! +//! The measurement outcome is represented as: `{measurement_indices} ^ flip` +//! - `measurement_indices`: Set of measurement indices whose outcomes XOR together +//! - `flip`: Boolean indicating whether to flip the result (from unitary gate phases) + +use crate::QuantumSimulator; +use crate::sign_algebra::{SignAlgebra, SymbolicSign}; +use crate::symbolic_gens::SymbolicGens; +use core::mem; +use pecos_core::{IndexableElement, Set, VecSet}; +use std::collections::BTreeSet; + +/// Standard type alias for symbolic sparse stabilizer simulator. +pub type StdSymbolicSparseStab = SymbolicSparseStab, usize>; + +/// Result of a symbolic measurement. +/// +/// The outcome is represented as: `XOR(measurement_outcomes[i] for i in outcome) XOR flip` +/// +/// For example: +/// - `outcome = {}, flip = false`: deterministic 0 +/// - `outcome = {}, flip = true`: deterministic 1 +/// - `outcome = {0}, flip = false`: same as measurement 0 +/// - `outcome = {0}, flip = true`: opposite of measurement 0 +/// - `outcome = {0, 2}, flip = false`: XOR of measurements 0 and 2 +/// +/// # Display Format +/// +/// The `Display` implementation formats results as `m{index}^m{dep1}^m{dep2}^...={flip}`: +/// - `m5=0`: measurement 5 is deterministic 0 +/// - `m5=1`: measurement 5 is deterministic 1 +/// - `m5^m2=0`: measurement 5 equals measurement 2 +/// - `m5^m3^m1=1`: measurement 5 equals `m3 XOR m1 XOR 1` +/// +/// Dependencies are ordered from largest to smallest index. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SymbolicMeasurementResult { + /// The set of measurement indices whose outcomes XOR together. + /// Empty set means no measurement dependency (outcome is just `flip`). + pub outcome: BTreeSet, + /// Whether to flip the XOR result (accumulated from unitary gate phases). + pub flip: bool, + /// Whether this measurement was deterministic (outcome determined by prior measurements). + pub is_deterministic: bool, + /// The index assigned to this measurement. + pub index: usize, +} + +impl std::fmt::Display for SymbolicMeasurementResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Start with this measurement's index + write!(f, "m{}", self.index)?; + + if self.is_deterministic { + // Add dependencies in reverse order (largest to smallest) + for &dep in self.outcome.iter().rev() { + write!(f, "^m{dep}")?; + } + // Add the flip value + write!(f, "={}", u8::from(self.flip)) + } else { + // Non-deterministic: show as random/unknown + write!(f, "=?") + } + } +} + +/// History of all measurements performed during simulation. +/// +/// The index in the history equals the measurement index assigned to that measurement. +/// Provides methods to filter by deterministic/non-deterministic status. +#[derive(Clone, Debug, Default)] +pub struct MeasurementHistory { + measurements: Vec, +} + +impl MeasurementHistory { + /// Create a new empty measurement history. + #[inline] + #[must_use] + pub fn new() -> Self { + Self { + measurements: Vec::new(), + } + } + + /// Add a measurement result to the history. + #[inline] + pub fn push(&mut self, result: SymbolicMeasurementResult) { + self.measurements.push(result); + } + + /// Clear all measurements from the history. + #[inline] + pub fn clear(&mut self) { + self.measurements.clear(); + } + + /// Returns the number of measurements in the history. + #[inline] + #[must_use] + pub fn len(&self) -> usize { + self.measurements.len() + } + + /// Returns true if the history is empty. + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool { + self.measurements.is_empty() + } + + /// Returns a reference to the measurement at the given index. + #[inline] + #[must_use] + pub fn get(&self, index: usize) -> Option<&SymbolicMeasurementResult> { + self.measurements.get(index) + } + + /// Returns the full list of measurements as a slice. + #[inline] + #[must_use] + pub fn as_slice(&self) -> &[SymbolicMeasurementResult] { + &self.measurements + } + + /// Returns an iterator over all measurements. + #[inline] + pub fn iter(&self) -> impl Iterator { + self.measurements.iter() + } + + /// Returns only the deterministic measurements. + /// + /// Each result contains its measurement index in the `index` field. + #[inline] + #[must_use] + pub fn deterministic(&self) -> Vec<&SymbolicMeasurementResult> { + self.measurements + .iter() + .filter(|m| m.is_deterministic) + .collect() + } + + /// Returns only the non-deterministic measurements. + /// + /// Each result contains its measurement index in the `index` field. + #[inline] + #[must_use] + pub fn nondeterministic(&self) -> Vec<&SymbolicMeasurementResult> { + self.measurements + .iter() + .filter(|m| !m.is_deterministic) + .collect() + } + + /// Formats all measurements as a bracketed list. + /// + /// Example: `[m0^m0=0, m1^m0=0, m2=0]` + #[must_use] + pub fn format_all(&self) -> String { + let formatted: Vec = self.measurements.iter().map(ToString::to_string).collect(); + format!("[{}]", formatted.join(", ")) + } + + /// Formats only deterministic measurements as a bracketed list. + /// + /// Example: `[m1^m0=0, m2=0]` + #[must_use] + pub fn format_deterministic(&self) -> String { + let formatted: Vec = self + .measurements + .iter() + .filter(|m| m.is_deterministic) + .map(ToString::to_string) + .collect(); + format!("[{}]", formatted.join(", ")) + } + + /// Formats only non-deterministic measurements as a bracketed list. + /// + /// Example: `[m0^m0=0, m3^m3=0]` + #[must_use] + pub fn format_nondeterministic(&self) -> String { + let formatted: Vec = self + .measurements + .iter() + .filter(|m| !m.is_deterministic) + .map(ToString::to_string) + .collect(); + format!("[{}]", formatted.join(", ")) + } +} + +impl std::fmt::Display for MeasurementHistory { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.format_all()) + } +} + +impl std::ops::Index for MeasurementHistory { + type Output = SymbolicMeasurementResult; + + fn index(&self, index: usize) -> &Self::Output { + &self.measurements[index] + } +} + +/// A symbolic stabilizer simulator that tracks measurement dependencies. +/// +/// This simulator is based on the same stabilizer/destabilizer formalism as [`SparseStab`], +/// but instead of collapsing measurements to concrete outcomes, it tracks which measurements +/// contribute to each outcome. +/// +/// # Type Parameters +/// - `T`: Set type for sparse storage +/// - `E`: Element type (qubit index type) +/// +/// # Use Cases +/// - Analyzing measurement dependency graphs +/// - Understanding which measurements affect which outcomes +/// - Pauli frame tracking / deferred measurement patterns +/// - Verifying measurement patterns in quantum error correction +/// +/// # Example +/// ```rust +/// use pecos_qsim::symbolic_sparse_stab::StdSymbolicSparseStab; +/// use pecos_qsim::QuantumSimulator; +/// +/// let mut sim = StdSymbolicSparseStab::new(2); +/// +/// // Create Bell state +/// sim.h(0).cx(0, 1); +/// +/// // Measure both qubits +/// let r0 = sim.mz(0); // Non-deterministic: outcome depends on measurement 0 +/// let r1 = sim.mz(1); // Deterministic: outcome equals measurement 0's outcome +/// +/// // r0.outcome = {0} (depends on measurement 0) +/// // r1.outcome = {0} (also depends on measurement 0, showing correlation) +/// assert!(!r0.is_deterministic); +/// assert!(r1.is_deterministic); +/// assert_eq!(r0.outcome, r1.outcome); // Same dependency = correlated +/// ``` +#[derive(Clone, Debug)] +pub struct SymbolicSparseStab +where + T: for<'a> Set<'a, Element = E>, + E: IndexableElement, +{ + num_qubits: usize, + stabs: SymbolicGens, + destabs: SymbolicGens, + /// Counter for assigning unique indices to measurements + measurement_counter: usize, + /// History of all measurements performed. + measurement_history: MeasurementHistory, +} + +impl SymbolicSparseStab +where + E: IndexableElement, + T: for<'a> Set<'a, Element = E>, +{ + /// Create a new symbolic stabilizer simulator. + #[inline] + #[must_use] + pub fn new(num_qubits: usize) -> Self { + let mut sim = Self { + num_qubits, + stabs: SymbolicGens::::new(num_qubits), + destabs: SymbolicGens::::new(num_qubits), + measurement_counter: 0, + measurement_history: MeasurementHistory::new(), + }; + sim.reset(); + sim + } + + /// Returns the number of qubits in the system. + #[inline] + #[must_use] + pub fn num_qubits(&self) -> usize { + self.num_qubits + } + + /// Returns the current measurement counter (number of measurements made so far). + #[inline] + #[must_use] + pub fn measurement_count(&self) -> usize { + self.measurement_counter + } + + /// Returns a reference to the measurement history. + /// + /// The history provides methods to access all measurements, or filter by + /// deterministic/non-deterministic status. + /// + /// # Example + /// ```rust + /// use pecos_qsim::symbolic_sparse_stab::StdSymbolicSparseStab; + /// + /// let mut sim = StdSymbolicSparseStab::new(2); + /// sim.h(0).cx(0, 1); + /// sim.mz(0); + /// sim.mz(1); + /// + /// let history = sim.measurement_history(); + /// assert_eq!(history.len(), 2); + /// + /// // Get deterministic measurements (each result has its index in result.index) + /// let det = history.deterministic(); + /// for result in det { + /// println!("Measurement {}: {:?}", result.index, result.outcome); + /// } + /// ``` + #[inline] + #[must_use] + pub fn measurement_history(&self) -> &MeasurementHistory { + &self.measurement_history + } + + /// Produces a textual representation of the stabilizer tableau with symbolic signs. + /// + /// Format: `{measurement_indices} PauliString` + /// Example: `{} ZII` means identity sign (deterministic 0), `{0,1} XIZ` means XOR of measurements 0 and 1. + #[must_use] + pub fn stab_tableau(&self) -> String { + Self::tableau_string(self.num_qubits, &self.stabs) + } + + /// Produces a textual representation of the destabilizer tableau with symbolic signs. + #[must_use] + pub fn destab_tableau(&self) -> String { + Self::tableau_string(self.num_qubits, &self.destabs) + } + + /// Utility that creates a string representation of generators with symbolic signs. + /// + /// Format: `{measurement_indices} ^ flip PauliString` + /// - `{measurement_indices}`: Set of measurement indices whose outcomes XOR together + /// - `flip`: 0 or 1 indicating whether to flip the result (from unitary phases) + /// + /// Examples: + /// - `{} ^ 0 ZII`: Identity sign, no flip (deterministic 0) + /// - `{} ^ 1 ZII`: Identity sign, flipped (deterministic 1) + /// - `{0} ^ 0 XIZ`: Depends on measurement 0, no flip + /// - `{0,1} ^ 1 XIZ`: XOR of measurements 0,1, flipped + fn tableau_string(num_qubits: usize, gens: &SymbolicGens) -> String { + use std::fmt::Write; + + let mut result = String::new(); + for i in 0..num_qubits { + let gen_e = E::from_index(i); + + // Compute the flip from signs_minus and signs_i + // For stabilizers, we only care about the minus component + // (imaginary components should cancel out in valid stabilizer states) + let has_minus = gens.signs_minus.contains(&gen_e); + let flip = u8::from(has_minus); + + // Format the symbolic sign with flip + let sign = &gens.signs[i]; + if sign.measurements.is_empty() { + let _ = write!(result, "{{}} ^ {flip} "); + } else { + let indices: Vec = sign + .measurements + .iter() + .map(std::string::ToString::to_string) + .collect(); + let _ = write!(result, "{{{}}} ^ {flip} ", indices.join(",")); + } + + // Format the Pauli string + for qubit in 0..num_qubits { + let qubit_e = E::from_index(qubit); + let in_row_x = gens.row_x[i].contains(&qubit_e); + let in_row_z = gens.row_z[i].contains(&qubit_e); + + let c = match (in_row_x, in_row_z) { + (false, false) => 'I', + (true, false) => 'X', + (false, true) => 'Z', + (true, true) => 'Y', + }; + result.push(c); + } + result.push('\n'); + } + result + } + + /// Reset the simulator to the initial |00...0⟩ state. + #[inline] + pub fn reset(&mut self) -> &mut Self { + self.stabs.init_all_z(); + self.destabs.init_all_x(); + self.measurement_counter = 0; + self.measurement_history.clear(); + self + } + + // ==================== Gate Operations ==================== + // Same as SparseStab, tracking phase changes via signs_minus/signs_i + + /// Pauli X gate. X -> X, Z -> -Z + #[inline] + pub fn x(&mut self, q: E) -> &mut Self { + let qu = q.to_index(); + self.stabs.signs_minus ^= &self.stabs.col_z[qu]; + self + } + + /// Pauli Y gate. X -> -X, Z -> -Z + #[inline] + pub fn y(&mut self, q: E) -> &mut Self { + let qu = q.to_index(); + for i in self.stabs.col_x[qu].symmetric_difference(&self.stabs.col_z[qu]) { + self.stabs.signs_minus ^= i; + } + self + } + + /// Pauli Z gate. X -> -X, Z -> Z + #[inline] + pub fn z(&mut self, q: E) -> &mut Self { + self.stabs.signs_minus ^= &self.stabs.col_x[q.to_index()]; + self + } + + /// Sqrt of Z gate (S gate). X -> Y, Z -> Z + #[inline] + pub fn sz(&mut self, q: E) -> &mut Self { + let qu = q.to_index(); + + // X -> i: track phase changes + // i * i = -1, so if already has i, add minus and remove i + for i in self.stabs.signs_i.intersection(&self.stabs.col_x[qu]) { + self.stabs.signs_minus ^= i; + } + self.stabs.signs_i ^= &self.stabs.col_x[qu]; + + // Update the Pauli structure (X -> Y means add Z component) + for g in [&mut self.stabs, &mut self.destabs] { + g.col_z[qu] ^= &g.col_x[qu]; + + for &i in g.col_x[qu].iter() { + let iu = i.to_index(); + g.row_z[iu] ^= &q; + } + } + self + } + + /// Hadamard gate. X -> Z, Z -> X, Y -> -Y + #[inline] + pub fn h(&mut self, q: E) -> &mut Self { + let qu = q.to_index(); + + // Y -> -Y: add minus for generators that have both X and Z on this qubit + for i in self.stabs.col_x[qu].intersection(&self.stabs.col_z[qu]) { + self.stabs.signs_minus ^= i; + } + + // Swap X and Z for this qubit + for g in [&mut self.stabs, &mut self.destabs] { + for i in g.col_x[qu].difference(&g.col_z[qu]) { + let iu = i.to_index(); + g.row_x[iu].remove(&q); + g.row_z[iu].insert(q); + } + + for i in g.col_z[qu].difference(&g.col_x[qu]) { + let iu = i.to_index(); + g.row_z[iu].remove(&q); + g.row_x[iu].insert(q); + } + + mem::swap(&mut g.col_x[qu], &mut g.col_z[qu]); + } + self + } + + /// CNOT gate. IX -> IX, XI -> XX, IZ -> ZZ, ZI -> ZI + #[inline] + pub fn cx(&mut self, q1: E, q2: E) -> &mut Self { + let qu1 = q1.to_index(); + let qu2 = q2.to_index(); + + for g in &mut [&mut self.stabs, &mut self.destabs] { + let (qu_min, qu_max) = if qu1 < qu2 { (qu1, qu2) } else { (qu2, qu1) }; + + // Handle col_x: XI -> XX + { + let (_left, right) = g.col_x.split_at_mut(qu_min); + let (mid, right) = right.split_at_mut(qu_max - qu_min); + let col_x_min = &mut mid[0]; + let col_x_max = &mut right[0]; + + let (col_x_qu1, col_x_qu2) = if qu1 < qu2 { + (col_x_min, col_x_max) + } else { + (col_x_max, col_x_min) + }; + + let mut q2_set = T::new(); + q2_set.insert(q2); + + for i in col_x_qu1.iter() { + let iu = i.to_index(); + g.row_x[iu].symmetric_difference_update(&q2_set); + } + col_x_qu2.symmetric_difference_update(col_x_qu1); + } + + // Handle col_z: IZ -> ZZ + { + let (_left, right) = g.col_z.split_at_mut(qu_min); + let (mid, right) = right.split_at_mut(qu_max - qu_min); + let col_z_min = &mut mid[0]; + let col_z_max = &mut right[0]; + + let (col_z_qu1, col_z_qu2) = if qu1 < qu2 { + (col_z_min, col_z_max) + } else { + (col_z_max, col_z_min) + }; + + let mut q1_set = T::new(); + q1_set.insert(q1); + + for i in col_z_qu2.iter() { + let iu = i.to_index(); + g.row_z[iu].symmetric_difference_update(&q1_set); + } + col_z_qu1.symmetric_difference_update(col_z_qu2); + } + } + self + } + + // ==================== Measurement ==================== + + /// Measure a qubit in the Z basis. + /// + /// Returns a [`SymbolicMeasurementResult`] containing the set of measurement indices + /// whose outcomes XOR together to determine this measurement's result. + #[inline] + pub fn mz(&mut self, q: E) -> SymbolicMeasurementResult { + let qu = q.to_index(); + + let result = if self.stabs.col_x[qu].is_empty() { + // Deterministic measurement + self.deterministic_meas(q) + } else { + // Non-deterministic measurement + self.nondeterministic_meas(q) + }; + + // Record in measurement history + self.measurement_history.push(result.clone()); + + result + } + + /// Handle a deterministic measurement. + /// The outcome is determined by combining: + /// 1. XOR of measurement dependencies from contributing stabilizers + /// 2. Phase flip from `signs_minus` and `signs_i` (same logic as `SparseStab`) + fn deterministic_meas(&mut self, q: E) -> SymbolicMeasurementResult { + let qu = q.to_index(); + + // Assign index and increment counter + let index = self.measurement_counter; + self.measurement_counter += 1; + + // --- Phase flip calculation (from SparseStab) --- + // Count minuses from destabilizers that anti-commute with Z_q + let mut num_minuses = self.destabs.col_x[qu] + .intersection(&self.stabs.signs_minus) + .count(); + + let num_is = self.destabs.col_x[qu] + .intersection(&self.stabs.signs_i) + .count(); + + // Account for Pauli multiplication phases + let mut cumulative_x = T::new(); + for row in self.destabs.col_x[qu].iter() { + let rowu = row.to_index(); + num_minuses += self.stabs.row_z[rowu].intersection(&cumulative_x).count(); + cumulative_x ^= &self.stabs.row_x[rowu]; + } + + if num_is & 3 != 0 { + // num_is % 4 != 0 + num_minuses += 1; + } + + let flip = num_minuses & 1 != 0; // num_minuses % 2 != 0 (is odd) + + // --- Measurement dependencies --- + // XOR together the symbolic signs of all stabilizers corresponding to destabilizers + // that have X on qubit q + let mut result_sign = SymbolicSign::empty(); + + for row in self.destabs.col_x[qu].iter() { + let rowu = row.to_index(); + result_sign.multiply_assign(&self.stabs.signs[rowu]); + } + + SymbolicMeasurementResult { + outcome: result_sign.measurements, + flip, + is_deterministic: true, + index, + } + } + + /// Handle a non-deterministic measurement. + /// Assigns a new measurement index and updates the stabilizer tableau. + #[allow(clippy::too_many_lines)] + fn nondeterministic_meas(&mut self, q: E) -> SymbolicMeasurementResult { + let qu = q.to_index(); + + // Non-deterministic measurements always get an index (required for tracking) + let measurement_index = self.measurement_counter; + self.measurement_counter += 1; + + let mut anticom_stabs_col = self.stabs.col_x[qu].clone(); + let mut anticom_destabs_col = self.destabs.col_x[qu].clone(); + + // Find a stabilizer to replace (choose smallest weight for efficiency) + let mut smallest_wt = 2 * self.num_qubits + 2; + let mut removed_id: Option = None; + + for stab_id in anticom_stabs_col.iter() { + let stab_usize = stab_id.to_index(); + let weight = self.stabs.row_x[stab_usize].len() + self.stabs.row_z[stab_usize].len(); + + if weight < smallest_wt { + smallest_wt = weight; + removed_id = Some(*stab_id); + } + } + + let id = removed_id.expect("Critical error: removed_id was None"); + anticom_stabs_col.remove(&id); + let id_usize = id.to_index(); + let removed_row_x = self.stabs.row_x[id_usize].clone(); + let removed_row_z = self.stabs.row_z[id_usize].clone(); + + // --- Phase tracking for anticommuting stabilizers (from SparseStab) --- + // If removed stabilizer has minus sign, propagate to others + if self.stabs.signs_minus.contains(&id) { + self.stabs.signs_minus ^= &anticom_stabs_col; + } + + // Handle imaginary component propagation + if self.stabs.signs_i.contains(&id) { + self.stabs.signs_i.remove(&id); + + let gens_common: Vec<_> = self + .stabs + .signs_i + .intersection(&anticom_stabs_col) + .copied() + .collect(); + let gens_only_stabs: Vec<_> = anticom_stabs_col + .difference(&self.stabs.signs_i) + .copied() + .collect(); + + for i in gens_common { + self.stabs.signs_minus ^= &i; + self.stabs.signs_i.remove(&i); + } + + for i in gens_only_stabs { + self.stabs.signs_i.insert(i); + } + } + + // Multiply all other anticommuting stabilizers by the removed one + // This includes both measurement dependencies AND phase from Pauli multiplication + let removed_sign = self.stabs.signs[id_usize].clone(); + for g in anticom_stabs_col.iter() { + let gen_usize = g.to_index(); + + // Multiply the symbolic measurement signs (XOR) + self.stabs.signs[gen_usize].multiply_assign(&removed_sign); + + // Track phase from Pauli multiplication + let num_minuses = removed_row_z + .intersection(&self.stabs.row_x[gen_usize]) + .count(); + if num_minuses & 1 != 0 { + self.stabs.signs_minus ^= g; + } + + // Update the Pauli structure + self.stabs.row_x[gen_usize] ^= &removed_row_x; + self.stabs.row_z[gen_usize] ^= &removed_row_z; + } + + // Update column storage for stabilizers + for i in removed_row_x.iter() { + let iu = i.to_index(); + self.stabs.col_x[iu] ^= &anticom_stabs_col; + } + + for i in removed_row_z.iter() { + let iu = i.to_index(); + self.stabs.col_z[iu] ^= &anticom_stabs_col; + } + + // Remove the old stabilizer + for i in self.stabs.row_x[id_usize].iter() { + let iu = i.to_index(); + self.stabs.col_x[iu].remove(&id); + } + + for i in self.stabs.row_z[id_usize].iter() { + let iu = i.to_index(); + self.stabs.col_z[iu].remove(&id); + } + + // Replace with the measured stabilizer Z_q + self.stabs.col_z[qu].insert(id); + self.stabs.row_x[id_usize].clear(); + self.stabs.row_z[id_usize].clear(); + self.stabs.row_z[id_usize].insert(q); + + // Set the sign of the new stabilizer to this measurement's index + // Also clear any phase tracking for this stabilizer (fresh start) + self.stabs.signs[id_usize] = SymbolicSign::single(measurement_index); + self.stabs.signs_minus.remove(&id); + self.stabs.signs_i.remove(&id); + + // Update destabilizers + for i in self.destabs.row_x[id_usize].iter() { + let iu = i.to_index(); + self.destabs.col_x[iu].remove(&id); + } + + for i in self.destabs.row_z[id_usize].iter() { + let iu = i.to_index(); + self.destabs.col_z[iu].remove(&id); + } + + anticom_destabs_col.remove(&id); + + for i in removed_row_x.iter() { + let iu = i.to_index(); + self.destabs.col_x[iu].insert(id); + self.destabs.col_x[iu] ^= &anticom_destabs_col; + } + + for i in removed_row_z.iter() { + let iu = i.to_index(); + self.destabs.col_z[iu].insert(id); + self.destabs.col_z[iu] ^= &anticom_destabs_col; + } + + for row in anticom_destabs_col.iter() { + let ru = row.to_index(); + self.destabs.row_x[ru] ^= &removed_row_x; + self.destabs.row_z[ru] ^= &removed_row_z; + } + + self.destabs.row_x[id_usize] = removed_row_x; + self.destabs.row_z[id_usize] = removed_row_z; + + // The outcome is just this measurement's index, with no flip + // (the measurement result is "fresh" - no accumulated phase) + let mut outcome = BTreeSet::new(); + outcome.insert(measurement_index); + + SymbolicMeasurementResult { + outcome, + flip: false, + is_deterministic: false, + index: measurement_index, + } + } +} + +impl QuantumSimulator for SymbolicSparseStab +where + E: IndexableElement, + T: for<'a> Set<'a, Element = E>, +{ + #[inline] + fn reset(&mut self) -> &mut Self { + Self::reset(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bell_state_symbolic() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Create Bell state + sim.h(0).cx(0, 1); + + // Measure qubit 0 - should be non-deterministic + let r0 = sim.mz(0); + assert!(!r0.is_deterministic); + assert_eq!(r0.outcome.len(), 1); + assert!(r0.outcome.contains(&0)); // First measurement has index 0 + assert_eq!(r0.index, 0); + + // Measure qubit 1 - should be deterministic but still gets an index + let r1 = sim.mz(1); + assert!(r1.is_deterministic); + assert_eq!(r0.outcome, r1.outcome); // Same measurement dependency = correlated + assert_eq!(r1.index, 1); + } + + #[test] + fn test_product_state_symbolic() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Measure qubit 0 without any gates - should be deterministic |0⟩ + let r0 = sim.mz(0); + assert!(r0.is_deterministic); + assert!(r0.outcome.is_empty()); // Empty set = deterministic 0 + assert_eq!(r0.index, 0); + + // Measure qubit 1 - also deterministic |0⟩ + let r1 = sim.mz(1); + assert!(r1.is_deterministic); + assert!(r1.outcome.is_empty()); + assert_eq!(r1.index, 1); + } + + #[test] + fn test_hadamard_measurement_symbolic() { + let mut sim = StdSymbolicSparseStab::new(1); + + // Apply H to put in superposition + sim.h(0); + + // Measure - should be non-deterministic + let r = sim.mz(0); + assert!(!r.is_deterministic); + assert_eq!(r.outcome.len(), 1); + assert!(r.outcome.contains(&0)); + assert_eq!(r.index, 0); + } + + #[test] + fn test_ghz_state_symbolic() { + let mut sim = StdSymbolicSparseStab::new(3); + + // Create GHZ state: (|000⟩ + |111⟩)/√2 + sim.h(0).cx(0, 1).cx(1, 2); + + // Measure qubit 0 - non-deterministic + let r0 = sim.mz(0); + assert!(!r0.is_deterministic); + assert!(r0.outcome.contains(&0)); + assert_eq!(r0.index, 0); + + // Measure qubit 1 - deterministic, depends on measurement 0 + let r1 = sim.mz(1); + assert!(r1.is_deterministic); + assert_eq!(r0.outcome, r1.outcome); + assert_eq!(r1.index, 1); + + // Measure qubit 2 - deterministic, depends on measurement 0 + let r2 = sim.mz(2); + assert!(r2.is_deterministic); + assert_eq!(r0.outcome, r2.outcome); + assert_eq!(r2.index, 2); + } + + #[test] + fn test_multiple_independent_measurements() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Put both qubits in superposition independently + sim.h(0).h(1); + + // Measure qubit 0 - non-deterministic, index 0 + let r0 = sim.mz(0); + assert!(!r0.is_deterministic); + assert!(r0.outcome.contains(&0)); + assert_eq!(r0.index, 0); + + // Measure qubit 1 - non-deterministic, index 1 + let r1 = sim.mz(1); + assert!(!r1.is_deterministic); + assert!(r1.outcome.contains(&1)); + assert_eq!(r1.index, 1); + + // They should have different measurement indices (independent) + assert_ne!(r0.outcome, r1.outcome); + } + + #[test] + fn test_measurement_counter() { + let mut sim = StdSymbolicSparseStab::new(3); + assert_eq!(sim.measurement_count(), 0); + + // All deterministic measurements - counter always increments + sim.mz(0); + assert_eq!(sim.measurement_count(), 1); + + sim.mz(1); + assert_eq!(sim.measurement_count(), 2); + + sim.mz(2); + assert_eq!(sim.measurement_count(), 3); + } + + #[test] + fn test_measurement_counter_with_nondet() { + let mut sim = StdSymbolicSparseStab::new(3); + assert_eq!(sim.measurement_count(), 0); + + // Make non-deterministic measurements + sim.h(0).h(1).h(2); + + sim.mz(0); + assert_eq!(sim.measurement_count(), 1); + + sim.mz(1); + assert_eq!(sim.measurement_count(), 2); + + sim.mz(2); + assert_eq!(sim.measurement_count(), 3); + } + + #[test] + fn test_deterministic_flag() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Deterministic measurement on |0⟩ + let r0 = sim.mz(0); + assert!(r0.is_deterministic); + + // Reset and make non-deterministic + sim.reset(); + sim.h(0); + let r1 = sim.mz(0); + assert!(!r1.is_deterministic); + } + + #[test] + fn test_x_gate_flip() { + // User's example: start with |0⟩, apply X to flip to |1⟩ + // The stabilizer goes from +Z to -Z, so outcome should be {} ^ 1 + let mut sim = StdSymbolicSparseStab::new(1); + + // Initial state: stabilized by +Z + assert_eq!(sim.stab_tableau(), "{} ^ 0 Z\n"); + + // Apply X: stabilizer becomes -Z + sim.x(0); + assert_eq!(sim.stab_tableau(), "{} ^ 1 Z\n"); + + // Measure - should be deterministic 1 (empty set XOR 1 = 1) + let r = sim.mz(0); + assert!(r.is_deterministic); + assert!(r.outcome.is_empty()); // No measurement dependencies + assert!(r.flip); // Flip is true, so result is 1 + } + + #[test] + fn test_y_gate_flip() { + // Y gate: X -> -X, Z -> -Z + let mut sim = StdSymbolicSparseStab::new(1); + + // Apply Y: +Z becomes -Z + sim.y(0); + assert_eq!(sim.stab_tableau(), "{} ^ 1 Z\n"); + + let r = sim.mz(0); + assert!(r.is_deterministic); + assert!(r.outcome.is_empty()); + assert!(r.flip); + } + + #[test] + fn test_z_gate_no_flip() { + // Z gate: X -> -X, Z -> Z (no change to Z stabilizer) + let mut sim = StdSymbolicSparseStab::new(1); + + // Initial state: +Z + assert_eq!(sim.stab_tableau(), "{} ^ 0 Z\n"); + + // Apply Z: +Z stays +Z + sim.z(0); + assert_eq!(sim.stab_tableau(), "{} ^ 0 Z\n"); + + let r = sim.mz(0); + assert!(r.is_deterministic); + assert!(r.outcome.is_empty()); + assert!(!r.flip); // No flip + } + + #[test] + fn test_double_x_cancels() { + // X X = I, so two X gates should cancel + let mut sim = StdSymbolicSparseStab::new(1); + + sim.x(0); + assert_eq!(sim.stab_tableau(), "{} ^ 1 Z\n"); + + sim.x(0); + assert_eq!(sim.stab_tableau(), "{} ^ 0 Z\n"); + + let r = sim.mz(0); + assert!(r.is_deterministic); + assert!(r.outcome.is_empty()); + assert!(!r.flip); + } + + #[test] + fn test_hadamard_then_measure() { + // H on |0⟩ gives |+⟩ which is non-deterministic + let mut sim = StdSymbolicSparseStab::new(1); + + // H transforms Z stabilizer to X stabilizer + sim.h(0); + assert_eq!(sim.stab_tableau(), "{} ^ 0 X\n"); + + // Measure - non-deterministic, no flip since no accumulated phase + let r = sim.mz(0); + assert!(!r.is_deterministic); + assert!(r.outcome.contains(&0)); + assert!(!r.flip); + } + + #[test] + fn test_flip_propagates_through_bell() { + // Create Bell state with an X gate first to introduce a flip + let mut sim = StdSymbolicSparseStab::new(2); + + // Apply X to qubit 0 first, then create Bell state + sim.x(0).h(0).cx(0, 1); + + // Measure qubit 0 + let r0 = sim.mz(0); + assert!(!r0.is_deterministic); + assert!(r0.outcome.contains(&0)); + // The X gate introduces a flip that should propagate + // Note: The exact flip value depends on how phases propagate through H and CX + + // Measure qubit 1 - should be correlated with qubit 0 + let r1 = sim.mz(1); + assert!(r1.is_deterministic); + assert_eq!(r0.outcome, r1.outcome); + } + + #[test] + fn test_tableau_format() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Initial state + let tableau = sim.stab_tableau(); + assert!(tableau.contains("{} ^ 0")); + assert!(tableau.contains("ZI")); + assert!(tableau.contains("IZ")); + + // After X on qubit 0 + sim.x(0); + let tableau = sim.stab_tableau(); + assert!(tableau.contains("{} ^ 1 ZI")); // First stabilizer gets flipped + assert!(tableau.contains("{} ^ 0 IZ")); // Second stabilizer unchanged + } + + #[test] + fn test_measurement_history() { + let mut sim = StdSymbolicSparseStab::new(3); + + // Initially no measurements + assert!(sim.measurement_history().is_empty()); + + // Create GHZ state and measure + sim.h(0).cx(0, 1).cx(1, 2); + + let r0 = sim.mz(0); + assert_eq!(sim.measurement_history().len(), 1); + assert_eq!(sim.measurement_history()[0], r0); + + let r1 = sim.mz(1); + assert_eq!(sim.measurement_history().len(), 2); + assert_eq!(sim.measurement_history()[1], r1); + + let r2 = sim.mz(2); + assert_eq!(sim.measurement_history().len(), 3); + assert_eq!(sim.measurement_history()[2], r2); + + // Check indices match positions + assert_eq!(sim.measurement_history()[0].index, 0); + assert_eq!(sim.measurement_history()[1].index, 1); + assert_eq!(sim.measurement_history()[2].index, 2); + + // Check deterministic filtering + let det = sim.measurement_history().deterministic(); + let nondet = sim.measurement_history().nondeterministic(); + + // First measurement is non-deterministic, others are deterministic + assert_eq!(nondet.len(), 1); + assert_eq!(det.len(), 2); + // The result contains its own index + assert_eq!(nondet[0].index, 0); + assert!(!nondet[0].is_deterministic); + } + + #[test] + fn test_measurement_history_reset() { + let mut sim = StdSymbolicSparseStab::new(2); + + sim.h(0); + sim.mz(0); + sim.mz(1); + assert_eq!(sim.measurement_history().len(), 2); + + // Reset should clear history + sim.reset(); + assert!(sim.measurement_history().is_empty()); + assert_eq!(sim.measurement_count(), 0); + } + + #[test] + fn test_display_format() { + // Test deterministic 0: m0=0 + let r = SymbolicMeasurementResult { + outcome: BTreeSet::new(), + flip: false, + is_deterministic: true, + index: 0, + }; + assert_eq!(format!("{r}"), "m0=0"); + + // Test deterministic 1: m1=1 + let r = SymbolicMeasurementResult { + outcome: BTreeSet::new(), + flip: true, + is_deterministic: true, + index: 1, + }; + assert_eq!(format!("{r}"), "m1=1"); + + // Test single dependency: m2^m0=0 + let mut outcome = BTreeSet::new(); + outcome.insert(0); + let r = SymbolicMeasurementResult { + outcome, + flip: false, + is_deterministic: true, + index: 2, + }; + assert_eq!(format!("{r}"), "m2^m0=0"); + + // Test multiple dependencies (should be largest to smallest): m5^m3^m1=1 + let mut outcome = BTreeSet::new(); + outcome.insert(1); + outcome.insert(3); + let r = SymbolicMeasurementResult { + outcome, + flip: true, + is_deterministic: true, + index: 5, + }; + assert_eq!(format!("{r}"), "m5^m3^m1=1"); + + // Test non-deterministic: m0=? + let mut outcome = BTreeSet::new(); + outcome.insert(0); + let r = SymbolicMeasurementResult { + outcome, + flip: false, + is_deterministic: false, + index: 0, + }; + assert_eq!(format!("{r}"), "m0=?"); + } + + #[test] + fn test_display_in_simulation() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Deterministic 0 + let r = sim.mz(0); + assert_eq!(format!("{r}"), "m0=0"); + + sim.reset(); + + // Apply X for deterministic 1 + sim.x(0); + let r = sim.mz(0); + assert_eq!(format!("{r}"), "m0=1"); + + sim.reset(); + + // Bell state + sim.h(0).cx(0, 1); + let r0 = sim.mz(0); + let r1 = sim.mz(1); + + // r0 is non-deterministic + assert_eq!(format!("{r0}"), "m0=?"); + // r1 is deterministic, depends on m0 + assert_eq!(format!("{r1}"), "m1^m0=0"); + } + + #[test] + fn test_history_formatting() { + let mut sim = StdSymbolicSparseStab::new(3); + + // Create GHZ state: first measurement non-deterministic, rest deterministic + sim.h(0).cx(0, 1).cx(1, 2); + sim.mz(0); // non-det + sim.mz(1); // det + sim.mz(2); // det + + let history = sim.measurement_history(); + + // Test format_all (also tests Display) + assert_eq!(history.format_all(), "[m0=?, m1^m0=0, m2^m0=0]"); + assert_eq!(format!("{history}"), "[m0=?, m1^m0=0, m2^m0=0]"); + + // Test format_deterministic + assert_eq!(history.format_deterministic(), "[m1^m0=0, m2^m0=0]"); + + // Test format_nondeterministic + assert_eq!(history.format_nondeterministic(), "[m0=?]"); + + // Test Debug format shows struct fields + let r = &history[0]; + let debug_str = format!("{r:?}"); + assert!(debug_str.contains("outcome")); + assert!(debug_str.contains("flip")); + assert!(debug_str.contains("is_deterministic")); + assert!(debug_str.contains("index")); + } + + #[test] + fn test_history_formatting_empty() { + let sim = StdSymbolicSparseStab::new(2); + let history = sim.measurement_history(); + + assert_eq!(history.format_all(), "[]"); + assert_eq!(history.format_deterministic(), "[]"); + assert_eq!(history.format_nondeterministic(), "[]"); + assert_eq!(format!("{history}"), "[]"); + } + + #[test] + fn test_history_formatting_with_flips() { + let mut sim = StdSymbolicSparseStab::new(2); + + // Apply X to get flip=1, then measure + sim.x(0); + sim.mz(0); // det with flip=1 + sim.mz(1); // det with flip=0 + + let history = sim.measurement_history(); + assert_eq!(history.format_all(), "[m0=1, m1=0]"); + assert_eq!(history.format_deterministic(), "[m0=1, m1=0]"); + assert_eq!(history.format_nondeterministic(), "[]"); + } +} diff --git a/crates/pecos/Cargo.toml b/crates/pecos/Cargo.toml index 4ae747f2c..2c9dc20ca 100644 --- a/crates/pecos/Cargo.toml +++ b/crates/pecos/Cargo.toml @@ -43,7 +43,7 @@ pecos-qulacs = { workspace = true, optional = true } pecos-wasm = { workspace = true, optional = true } [features] -default = ["selene", "qasm", "phir", "wasm", "all-simulators"] +default = ["qasm", "phir", "wasm", "all-simulators"] qasm = [] llvm = ["pecos-qis-core/llvm", "pecos-llvm", "pecos-hugr-qis", "pecos-hugr-qis?/llvm"] phir = [] diff --git a/docs/PYTHON_SYMBOL_FIX.md b/docs/PYTHON_SYMBOL_FIX.md deleted file mode 100644 index 0f3bf470e..000000000 --- a/docs/PYTHON_SYMBOL_FIX.md +++ /dev/null @@ -1,184 +0,0 @@ -# Python Symbol Conflict Fix - -## Problem - -When running tests from Python, the code was encountering segmentation faults due to symbol conflicts between: -1. The Python extension (`_pecos_rslib.abi3.so`) which has `__quantum__rt__*` and `__quantum__qis__*` symbols statically linked from the rlib -2. The dynamically loaded cdylib (`libpecos_qis_ffi.so`) which exports the same symbols with `RTLD_GLOBAL` - -This caused symbol resolution conflicts and segfaults. - -## Root Cause - -The pecos-qis-selene executor was unconditionally loading `libpecos_qis_ffi.so` using `dlopen` with `RTLD_GLOBAL`, which made its symbols globally available. However, when running from Python, the Python extension already had these symbols statically linked, creating a conflict. - -## Solution - -### Architecture Changes - -1. **pecos-qis-ffi Cargo.toml** (`crates/pecos-qis-ffi/Cargo.toml:14-18`) - - Changed crate-type from `["rlib", "staticlib"]` to `["rlib", "cdylib"]` - - The cdylib provides the `__quantum__*` symbols for dynamic loading by Rust binaries - - The rlib provides the same functionality for static linking in the Python extension - -2. **pecos-qis-selene Cargo.toml** (`crates/pecos-qis-selene/Cargo.toml:14-16`) - - Changed crate-type from `["cdylib", "rlib"]` to just `["rlib"]` - - The interface layer no longer needs to be a cdylib - only the FFI layer does - -### Code Changes - -#### 1. Added Helper Functions to pecos-qis-ffi (`crates/pecos-qis-ffi/src/lib.rs:140-148`) - -```rust -/// Get a clone of the thread-local operation collector -pub fn get_interface_clone() -> OperationCollector { - with_interface(|interface| interface.clone()) -} - -/// Set measurement results in the thread-local operation collector -pub fn set_measurements(measurements: HashMap) { - with_interface(|interface| interface.set_measurement_results(measurements)); -} -``` - -These functions allow direct access to the rlib functionality without going through the FFI. - -#### 2. Updated executor.rs - execute_program() Method (`crates/pecos-qis-selene/src/executor.rs:196-350`) - -Added Python detection and conditional library loading: - -```rust -// Detect if running from Python -let is_python = std::env::current_exe() - .ok() - .and_then(|exe| exe.file_name().map(|n| n.to_string_lossy().contains("python"))) - .unwrap_or(false); - -// Load libpecos_qis_ffi.so (or skip if Python) -let pecos_qis_lib = if !is_python { - // Running from Rust binary - dynamically load the cdylib - Some(unsafe { Library::new(&pecos_qis_lib_path)? }) -} else { - // Running from Python - symbols already available, no need to load - None -}; - -// Reset interface (use either cdylib or rlib) -if let Some(ref lib) = pecos_qis_lib { - let reset_interface_fn: Symbol = unsafe { - lib.get(b"pecos_qis_reset_interface\0")? - }; - unsafe { reset_interface_fn() }; -} else { - pecos_qis_ffi::reset_interface(); -} - -// ... execute program ... - -// Collect operations (use either cdylib or rlib) -let operations = if let Some(ref lib) = pecos_qis_lib { - // Get from dynamically loaded cdylib - let get_operations_fn: Symbol = unsafe { - lib.get(b"pecos_qis_get_operations\0")? - }; - let operations_ptr = unsafe { get_operations_fn() }; - let operations = unsafe { Box::from_raw(operations_ptr) }; - *operations -} else { - // Get directly from rlib (Python case) - pecos_qis_ffi::get_interface_clone() -}; -``` - -#### 3. Updated executor.rs - execute_with_measurements() Method (`crates/pecos-qis-selene/src/executor.rs:403-556`) - -Applied the same Python detection pattern: - -```rust -// Detect if running from Python -let is_python = std::env::current_exe() - .ok() - .and_then(|exe| exe.file_name().map(|n| n.to_string_lossy().contains("python"))) - .unwrap_or(false); - -// Conditionally load library -let pecos_qis_lib = if !is_python { - Some(unsafe { Library::new(&pecos_qis_lib_path)? }) -} else { - None -}; - -// Set measurements (use either cdylib or rlib) -if let Some(ref lib) = pecos_qis_lib { - let set_measurements_fn: Symbol = unsafe { - lib.get(b"pecos_qis_set_measurements\0")? - }; - let measurements_vec: Vec<(usize, bool)> = measurements.into_iter().collect(); - unsafe { - set_measurements_fn(measurements_vec.as_ptr(), measurements_vec.len()); - } -} else { - pecos_qis_ffi::set_measurements(measurements); -} - -// ... execute program ... - -// Collect operations (use either cdylib or rlib) -let operations = if let Some(ref lib) = pecos_qis_lib { - // Get from cdylib - let get_operations_fn: Symbol = unsafe { - lib.get(b"pecos_qis_get_operations\0")? - }; - let operations_ptr = unsafe { get_operations_fn() }; - let operations = unsafe { Box::from_raw(operations_ptr) }; - *operations -} else { - // Get from rlib - pecos_qis_ffi::get_interface_clone() -}; -``` - -## How It Works - -### When Running from Rust Binary - -1. Executor detects it's NOT running from Python (`is_python = false`) -2. Dynamically loads `libpecos_qis_ffi.so` with `RTLD_GLOBAL` -3. Calls FFI functions through dlopen/libloading symbols -4. QIS programs can resolve `__quantum__*` symbols from the globally loaded cdylib - -### When Running from Python - -1. Executor detects it's running from Python (`is_python = true`) -2. Skips dynamic library loading (symbols already available in Python extension) -3. Calls rlib functions directly (same implementation, different linking) -4. QIS programs resolve `__quantum__*` symbols from the Python extension's statically linked symbols - -## Benefits - -1. **No Symbol Conflicts**: Python and Rust use different code paths, avoiding symbol conflicts -2. **Same Implementation**: Both paths use the same underlying Rust code (rlib vs cdylib are built from same source) -3. **Unified Architecture**: Single source of truth for QIS FFI symbols in `pecos-qis-ffi` -4. **Maintainable**: Changes to QIS interface automatically apply to both Python and Rust execution - -## Testing - -### Rust Tests -All 8 bell_state tests pass: -``` -cargo test --test bell_state_tests --release -``` - -### Python Tests -All 9 HUGR integration tests pass: -``` -uv run pytest python/pecos-rslib/tests/test_hugr_integration.py -v -``` - -The Python tests exercise the Guppy → HUGR → Helios → QIS pipeline, which is the primary use case for the Helios interface from Python. - -## Future Improvements - -1. More robust Python detection (e.g., check for Python in process name or use an environment variable) -2. Explicit configuration option to choose between cdylib and rlib paths -3. Potential unification with similar patterns in other interfaces (if any) diff --git a/docs/development/DEVELOPMENT.md b/docs/development/DEVELOPMENT.md index dd421c630..f2ad33d7c 100644 --- a/docs/development/DEVELOPMENT.md +++ b/docs/development/DEVELOPMENT.md @@ -26,7 +26,7 @@ For developers who want to contribute or modify PECOS: cargo build ``` - For detailed installation instructions for all platforms (macOS, Linux, Windows), see the [**LLVM Setup Guide**](../user-guide/getting-started.md#llvm-for-qis-support) in the Getting Started documentation. + For detailed installation instructions for all platforms (macOS, Linux, Windows), see the [**LLVM Setup Guide**](../user-guide/llvm-setup.md). 5. You may wish to explicitly activate the environment for development. To do so: diff --git a/docs/user-guide/decoders.md b/docs/user-guide/decoders.md index 7629baedd..dfc7ee82d 100644 --- a/docs/user-guide/decoders.md +++ b/docs/user-guide/decoders.md @@ -432,6 +432,5 @@ For large codes, use sparse representations: ## See Also -- [DECODERS.md](../../DECODERS.md) - Detailed decoder documentation -- [Quantum Error Correction Guide](qec.md) - QEC fundamentals -- [Examples](../examples/decoder-examples.md) - More decoder examples +- [Getting Started Guide](getting-started.md) - Main installation guide +- [LLVM Setup Guide](llvm-setup.md) - For building with LLVM support diff --git a/docs/user-guide/llvm-setup.md b/docs/user-guide/llvm-setup.md index ae6b9872a..a165de9b9 100644 --- a/docs/user-guide/llvm-setup.md +++ b/docs/user-guide/llvm-setup.md @@ -299,4 +299,3 @@ LLVM_SYS_140_PREFIX = { value = "/path/to/llvm", force = true } ## See Also - [Getting Started Guide](getting-started.md) - Main installation guide -- [QIS Documentation](qis.md) - Using QIS with LLVM IR/QIR execution diff --git a/examples/autodetect.ipynb b/examples/autodetect.ipynb new file mode 100644 index 000000000..b29932a33 --- /dev/null +++ b/examples/autodetect.ipynb @@ -0,0 +1,843 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "6f8c87e6-32f9-4433-ae77-7d338a906b3f", + "metadata": {}, + "outputs": [], + "source": [ + ":clear" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ce6726e3-3007-464a-9557-6702150c7052", + "metadata": {}, + "outputs": [], + "source": [ + ":dep pecos = { version = \"0.1.1\", path = \"../crates/pecos\" }" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "49efdc8c-966f-4c29-a17c-124c1602cd18", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bell State Measurement Results:\n", + " Qubit 0: 1 (deterministic: false)\n", + " Qubit 1: 1 (deterministic: true)\n", + "\n", + "Success! Measurements are correlated as expected for a Bell state.\n" + ] + } + ], + "source": [ + "// Bell State Example using SparseStab simulator\n", + "use pecos::prelude::*;\n", + "\n", + "// Create a 2-qubit stabilizer simulator\n", + "let mut sim = StdSparseStab::new(2);\n", + "\n", + "// Create Bell state: (|00⟩ + |11⟩)/√2\n", + "sim.h(0) // Apply Hadamard to qubit 0: |00⟩ -> (|00⟩ + |10⟩)/√2\n", + " .cx(0, 1); // Apply CNOT: (|00⟩ + |10⟩)/√2 -> (|00⟩ + |11⟩)/√2\n", + "\n", + "// Measure both qubits\n", + "let result0: MeasurementResult = sim.mz(0);\n", + "let result1: MeasurementResult = sim.mz(1);\n", + "\n", + "println!(\"Bell State Measurement Results:\");\n", + "println!(\" Qubit 0: {} (deterministic: {})\", \n", + " if result0.outcome { \"1\" } else { \"0\" },\n", + " result0.is_deterministic);\n", + "println!(\" Qubit 1: {} (deterministic: {})\", \n", + " if result1.outcome { \"1\" } else { \"0\" },\n", + " result1.is_deterministic);\n", + "\n", + "// Verify Bell state correlation: both qubits should always measure the same\n", + "assert_eq!(result0.outcome, result1.outcome, \n", + " \"Bell state measurements must be correlated!\");\n", + "println!(\"\\nSuccess! Measurements are correlated as expected for a Bell state.\");" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8ga67hg7ijx", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Symbolic Bell State Measurement Results:\n", + " Measurement 0: m0=? (deterministic: false)\n", + " Measurement 1: m1^m0=0 (deterministic: true)\n", + "\n", + "Analysis:\n", + " - Qubit 0 measurement was non-deterministic (random outcome)\n", + " - Qubit 1 measurement is deterministic and depends on measurement 0\n", + " - This shows the Bell state correlation: both qubits always measure the same!\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// Symbolic Bell State Example using SymbolicSparseStab\n", + "// This simulator tracks measurement dependencies instead of collapsing to concrete outcomes\n", + "\n", + "let mut sym_sim = StdSymbolicSparseStab::new(2);\n", + "\n", + "// Create Bell state: (|00⟩ + |11⟩)/√2\n", + "sym_sim.h(0).cx(0, 1);\n", + "\n", + "// Measure both qubits\n", + "let r0: SymbolicMeasurementResult = sym_sim.mz(0);\n", + "let r1: SymbolicMeasurementResult = sym_sim.mz(1);\n", + "\n", + "println!(\"Symbolic Bell State Measurement Results:\");\n", + "println!(\" Measurement 0: {} (deterministic: {})\", r0, r0.is_deterministic);\n", + "println!(\" Measurement 1: {} (deterministic: {})\", r1, r1.is_deterministic);\n", + "\n", + "// The first measurement is non-deterministic (creates measurement index 0)\n", + "// The second measurement is deterministic and depends on measurement 0\n", + "println!(\"\\nAnalysis:\");\n", + "if !r0.is_deterministic {\n", + " println!(\" - Qubit 0 measurement was non-deterministic (random outcome)\");\n", + "}\n", + "if r1.is_deterministic && r0.outcome == r1.outcome {\n", + " println!(\" - Qubit 1 measurement is deterministic and depends on measurement 0\");\n", + " println!(\" - This shows the Bell state correlation: both qubits always measure the same!\");\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "kte36trq3vr", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== 3-Qubit Repetition Code: Logical |+_L⟩ ===\n", + "\n", + "After encoding |+_L⟩ = (|000⟩ + |111⟩)/√2:\n", + "Stabilizers:\n", + "{} ^ 0 XXXII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "After Z0Z1 syndrome circuit (before measurement):\n", + "Stabilizers:\n", + "{} ^ 0 XXXII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "Syndrome S0 (Z0Z1) = m0=0, det=true\n", + "Stabilizers:\n", + "{} ^ 0 XXXII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "After Z1Z2 syndrome circuit (before measurement):\n", + "Stabilizers:\n", + "{} ^ 0 XXXII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "Syndrome S1 (Z1Z2) = m1=0, det=true\n", + "Stabilizers:\n", + "{} ^ 0 XXXII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "D0 (q0) = m2=?, det=false\n", + "Stabilizers:\n", + "{2} ^ 0 ZIIII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "D1 (q1) = m3^m2=0, det=true\n", + "Stabilizers:\n", + "{2} ^ 0 ZIIII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "D2 (q2) = m4^m2=0, det=true\n", + "Stabilizers:\n", + "{2} ^ 0 ZIIII\n", + "{} ^ 0 ZZIII\n", + "{} ^ 0 ZIZII\n", + "{} ^ 0 IIIZI\n", + "{} ^ 0 IIIIZ\n", + "\n", + "\n", + "=== Measurement History ===\n", + "Total measurements: 5\n", + "Deterministic: 4\n", + "Non-deterministic: 1\n", + "\n", + "All measurements: [m0=0, m1=0, m2=?, m3^m2=0, m4^m2=0]\n", + "Deterministic only: [m0=0, m1=0, m3^m2=0, m4^m2=0]\n", + "Non-deterministic only: [m2=?]\n", + "\n", + "Debug format for first measurement:\n", + " SymbolicMeasurementResult { outcome: {}, flip: false, is_deterministic: true, index: 0 }\n", + "\n", + "=== Summary ===\n", + "Measurement indices: S0=0, S1=1, D0=2, D1=3, D2=4\n", + "\n", + "Syndromes should be deterministic {} (no errors in code space).\n", + "Data qubits: D0 is random, D1 and D2 are determined by D0 and syndromes.\n", + "\n", + "Detectors: {S0=0, S1=0, D1^D0=0, D2^D0=0}\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// 3-qubit Repetition Code in Logical |+_L⟩ with Syndrome Measurements\n", + "//\n", + "// Qubits:\n", + "// q0, q1, q2: Data qubits \n", + "// q3: Ancilla for Z0Z1 check\n", + "// q4: Ancilla for Z1Z2 check\n", + "//\n", + "// Logical states:\n", + "// |0_L⟩ = |000⟩\n", + "// |1_L⟩ = |111⟩\n", + "// |+_L⟩ = (|000⟩ + |111⟩)/√2\n", + "//\n", + "// Encoding circuit for |+_L⟩:\n", + "// - Start with |+⟩|00⟩ (H on q0)\n", + "// - CX(0,1), CX(0,2) to spread → (|000⟩ + |111⟩)/√2\n", + "//\n", + "// Syndrome extraction:\n", + "// - Measure Z0Z1 using ancilla q3\n", + "// - Measure Z1Z2 using ancilla q4\n", + "//\n", + "// Then measure data qubits in Z basis\n", + "\n", + "let mut sim = StdSymbolicSparseStab::new(5);\n", + "\n", + "println!(\"=== 3-Qubit Repetition Code: Logical |+_L⟩ ===\\n\");\n", + "\n", + "// Encode logical |+_L⟩ = (|000⟩ + |111⟩)/√2\n", + "sim.h(0); // |+⟩|00⟩\n", + "sim.cx(0, 1); // (|00⟩ + |11⟩)|0⟩\n", + "sim.cx(0, 2); // |000⟩ + |111⟩\n", + "println!(\"After encoding |+_L⟩ = (|000⟩ + |111⟩)/√2:\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "// Syndrome measurement: Z0Z1 via ancilla q3\n", + "// Circuit: H(q3), CX(q0,q3), CX(q1,q3), H(q3), Measure(q3)\n", + "sim.h(3);\n", + "sim.cx(0, 3);\n", + "sim.cx(1, 3);\n", + "sim.h(3);\n", + "println!(\"After Z0Z1 syndrome circuit (before measurement):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let s0: SymbolicMeasurementResult = sim.mz(3);\n", + "println!(\"Syndrome S0 (Z0Z1) = {}, det={}\", s0, s0.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "// Syndrome measurement: Z1Z2 via ancilla q4\n", + "sim.h(4);\n", + "sim.cx(1, 4);\n", + "sim.cx(2, 4);\n", + "sim.h(4);\n", + "println!(\"After Z1Z2 syndrome circuit (before measurement):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let s1: SymbolicMeasurementResult = sim.mz(4);\n", + "println!(\"Syndrome S1 (Z1Z2) = {}, det={}\", s1, s1.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "// Now measure data qubits in Z basis\n", + "let d0: SymbolicMeasurementResult = sim.mz(0);\n", + "println!(\"D0 (q0) = {}, det={}\", d0, d0.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let d1: SymbolicMeasurementResult = sim.mz(1);\n", + "println!(\"D1 (q1) = {}, det={}\", d1, d1.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let d2: SymbolicMeasurementResult = sim.mz(2);\n", + "println!(\"D2 (q2) = {}, det={}\", d2, d2.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "println!(\"\\n=== Measurement History ===\");\n", + "println!(\"Total measurements: {}\", sim.measurement_history().len());\n", + "println!(\"Deterministic: {}\", sim.measurement_history().deterministic().len());\n", + "println!(\"Non-deterministic: {}\", sim.measurement_history().nondeterministic().len());\n", + "\n", + "// Use the new formatting convenience methods\n", + "println!(\"\\nAll measurements: {}\", sim.measurement_history());\n", + "println!(\"Deterministic only: {}\", sim.measurement_history().format_deterministic());\n", + "println!(\"Non-deterministic only: {}\", sim.measurement_history().format_nondeterministic());\n", + "\n", + "// Show Debug format for one result (wrap in block to avoid lifetime issue)\n", + "println!(\"\\nDebug format for first measurement:\");\n", + "{\n", + " let first = &sim.measurement_history()[0];\n", + " println!(\" {:?}\", first);\n", + "}\n", + "\n", + "println!(\"\\n=== Summary ===\");\n", + "println!(\"Measurement indices: S0=0, S1=1, D0=2, D1=3, D2=4\");\n", + "println!(\"\\nSyndromes should be deterministic {{}} (no errors in code space).\");\n", + "println!(\"Data qubits: D0 is random, D1 and D2 are determined by D0 and syndromes.\");\n", + "println!(\"\\nDetectors: {{S0=0, S1=0, D1^D0=0, D2^D0=0}}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "eaed012f-132b-477a-b7f9-006e0fba9fb6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Measurement history: [m0=0, m1=0, m2=?, m3^m2=0, m4^m2=0]\n" + ] + } + ], + "source": [ + "println!(\"Measurement history: {}\", sim.measurement_history());" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65175906-b965-4e15-8f5a-3328ab3cb20f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "bi1o9e2ne8p", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3-Qubit Chain Measurement Results:\n", + " M0 (qubit 0): m0=? (deterministic: false)\n", + " M1 (qubit 1): m1=? (deterministic: false)\n", + " M2 (qubit 2): m2^m1=0 (deterministic: true)\n", + "\n", + "Interpretation:\n", + " - M0 outcome is random: m0=?\n", + " - M1 outcome is random: m1=?\n", + " - M2 outcome depends on: m2^m1=0\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// More complex example: 3-qubit circuit with interesting measurement dependencies\n", + "// \n", + "// Circuit:\n", + "// q0: --H--@-------M0\n", + "// |\n", + "// q1: -----X--H--@-M1\n", + "// |\n", + "// q2: -----------X-M2\n", + "//\n", + "// This creates a chain where:\n", + "// - q0 and q1 become entangled (Bell pair)\n", + "// - Then q1 and q2 become entangled\n", + "// - Measuring in this order should show interesting dependencies\n", + "\n", + "let mut sim = StdSymbolicSparseStab::new(3);\n", + "\n", + "// Create the entanglement chain\n", + "sim.h(0); // q0 in superposition\n", + "sim.cx(0, 1); // Entangle q0-q1\n", + "sim.h(1); // q1 in superposition (relative to q0)\n", + "sim.cx(1, 2); // Entangle q1-q2\n", + "\n", + "// Measure all qubits\n", + "let m0: SymbolicMeasurementResult = sim.mz(0);\n", + "let m1: SymbolicMeasurementResult = sim.mz(1);\n", + "let m2: SymbolicMeasurementResult = sim.mz(2);\n", + "\n", + "println!(\"3-Qubit Chain Measurement Results:\");\n", + "println!(\" M0 (qubit 0): {} (deterministic: {})\", m0, m0.is_deterministic);\n", + "println!(\" M1 (qubit 1): {} (deterministic: {})\", m1, m1.is_deterministic);\n", + "println!(\" M2 (qubit 2): {} (deterministic: {})\", m2, m2.is_deterministic);\n", + "\n", + "println!(\"\\nInterpretation:\");\n", + "println!(\" - M0 outcome is random: {}\", m0);\n", + "println!(\" - M1 outcome is random: {}\", m1);\n", + "println!(\" - M2 outcome depends on: {}\", m2);\n", + "\n", + "// Show what XOR means\n", + "if m2.outcome.len() == 2 {\n", + " println!(\"\\n M2 = {{0, 1}} means: M2_outcome = M0_outcome XOR M1_outcome\");\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ddz6fpggss", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initial state:\n", + "Stabilizers:\n", + "{} ^ 0 ZII\n", + "{} ^ 0 IZI\n", + "{} ^ 0 IIZ\n", + "\n", + "After H(0):\n", + "Stabilizers:\n", + "{} ^ 0 XII\n", + "{} ^ 0 IZI\n", + "{} ^ 0 IIZ\n", + "\n", + "After CX(0,1):\n", + "Stabilizers:\n", + "{} ^ 0 XXI\n", + "{} ^ 0 ZZI\n", + "{} ^ 0 IIZ\n", + "\n", + "After H(1):\n", + "Stabilizers:\n", + "{} ^ 0 XZI\n", + "{} ^ 0 ZXI\n", + "{} ^ 0 IIZ\n", + "\n", + "After CX(1,2):\n", + "Stabilizers:\n", + "{} ^ 0 XZI\n", + "{} ^ 0 ZXX\n", + "{} ^ 0 IZZ\n", + "\n", + "After measuring qubit 0 (m0=?, det=false):\n", + "Stabilizers:\n", + "{0} ^ 0 ZII\n", + "{} ^ 0 ZXX\n", + "{} ^ 0 IZZ\n", + "\n", + "After measuring qubit 1 (m1=?, det=false):\n", + "Stabilizers:\n", + "{0} ^ 0 ZII\n", + "{1} ^ 0 IZI\n", + "{} ^ 0 IZZ\n", + "\n", + "After measuring qubit 2 (m2^m1=0, det=true):\n", + "Stabilizers:\n", + "{0} ^ 0 ZII\n", + "{1} ^ 0 IZI\n", + "{} ^ 0 IZZ\n", + "\n" + ] + } + ], + "source": [ + "// Trace through the 3-qubit circuit step by step with tableau display\n", + "// Note: The tableau format is \"{measurement_indices} ^ flip PauliString\"\n", + "// - {} ^ 0: identity sign, no flip (deterministic 0)\n", + "// - {} ^ 1: identity sign, flipped (deterministic 1)\n", + "// - {0} ^ 0: depends on measurement 0, no flip\n", + "// - {0,1} ^ 1: XOR of measurements 0,1, flipped\n", + "//\n", + "// Measurement results use Display format: m{index}^m{dep1}^m{dep2}^...={flip}\n", + "// - m0=0: measurement 0 is deterministic 0\n", + "// - m1^m0=0: measurement 1 equals measurement 0\n", + "// - m2^m1^m0=1: measurement 2 equals m1 XOR m0 XOR 1\n", + "\n", + "let mut sim = StdSymbolicSparseStab::new(3);\n", + "\n", + "println!(\"Initial state:\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.h(0);\n", + "println!(\"After H(0):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.cx(0, 1);\n", + "println!(\"After CX(0,1):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.h(1);\n", + "println!(\"After H(1):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.cx(1, 2);\n", + "println!(\"After CX(1,2):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "// Now measure\n", + "let m0: SymbolicMeasurementResult = sim.mz(0);\n", + "println!(\"After measuring qubit 0 ({}, det={}):\", m0, m0.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let m1: SymbolicMeasurementResult = sim.mz(1);\n", + "println!(\"After measuring qubit 1 ({}, det={}):\", m1, m1.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let m2: SymbolicMeasurementResult = sim.mz(2);\n", + "println!(\"After measuring qubit 2 ({}, det={}):\", m2, m2.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "46jepsxwmhy", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Circuit where M2 = M0 XOR M1 ===\n", + "\n", + "After H(0), CX(0,1), CX(1,2) - GHZ state:\n", + "Stabilizers:\n", + "{} ^ 0 XXX\n", + "{} ^ 0 ZZI\n", + "{} ^ 0 IZZ\n", + "\n", + "After H(2):\n", + "Stabilizers:\n", + "{} ^ 0 XXZ\n", + "{} ^ 0 ZZI\n", + "{} ^ 0 IZX\n", + "\n", + "After M0 (m0=?, det=false):\n", + "Stabilizers:\n", + "{0} ^ 0 ZII\n", + "{} ^ 0 ZZI\n", + "{} ^ 0 IZX\n", + "\n", + "After M1 (m1^m0=0, det=true):\n", + "Stabilizers:\n", + "{0} ^ 0 ZII\n", + "{} ^ 0 ZZI\n", + "{} ^ 0 IZX\n", + "\n", + "After M2 (m2=?, det=false):\n", + "Stabilizers:\n", + "{0} ^ 0 ZII\n", + "{} ^ 0 ZZI\n", + "{2} ^ 0 IIZ\n", + "\n", + "Summary:\n", + " M0 = m0=?\n", + " M1 = m1^m0=0\n", + " M2 = m2=?\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// Example where M2 depends on BOTH M0 and M1 (XOR)\n", + "//\n", + "// Circuit: Create a 3-qubit GHZ-like state, then do local rotations\n", + "//\n", + "// q0: --H--@--------M0\n", + "// |\n", + "// q1: -----X--@-----M1\n", + "// |\n", + "// q2: --------X--H--M2\n", + "//\n", + "// The key insight: after CX gates, we have correlations.\n", + "// The H on q2 at the end converts Z2 to X2, which should\n", + "// create an XOR dependency when measured in Z basis.\n", + "\n", + "let mut sim = StdSymbolicSparseStab::new(3);\n", + "\n", + "println!(\"=== Circuit where M2 = M0 XOR M1 ===\\n\");\n", + "\n", + "sim.h(0);\n", + "sim.cx(0, 1);\n", + "sim.cx(1, 2);\n", + "println!(\"After H(0), CX(0,1), CX(1,2) - GHZ state:\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.h(2);\n", + "println!(\"After H(2):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "// Now measure\n", + "let m0: SymbolicMeasurementResult = sim.mz(0);\n", + "println!(\"After M0 ({}, det={}):\", m0, m0.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let m1: SymbolicMeasurementResult = sim.mz(1);\n", + "println!(\"After M1 ({}, det={}):\", m1, m1.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let m2: SymbolicMeasurementResult = sim.mz(2);\n", + "println!(\"After M2 ({}, det={}):\", m2, m2.is_deterministic);\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "println!(\"Summary:\");\n", + "println!(\" M0 = {}\", m0);\n", + "println!(\" M1 = {}\", m1);\n", + "println!(\" M2 = {}\", m2);\n", + "if m2.outcome.len() == 2 && m2.outcome.contains(&0) && m2.outcome.contains(&1) {\n", + " println!(\"\\n m2^m1^m0=0 means: M2_outcome = M0_outcome XOR M1_outcome\");\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "7decd9ad-282a-421a-95fd-30bdd6b5f24f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Demonstrating the flip flag ===\n", + "\n", + "Initial state |0⟩:\n", + "Stabilizers:\n", + "{} ^ 0 Z\n", + "\n", + "After X gate (now |1⟩):\n", + "Stabilizers:\n", + "{} ^ 1 Z\n", + "\n", + "Notice: {} ^ 1 Z means the stabilizer is -Z (flip=1)\n", + "\n", + "Measurement result: m0=1\n", + " - Empty dependency set means no measurement dependencies\n", + " - =1 means the result is flipped, giving outcome 1\n", + "\n", + "After X·X (identity):\n", + "Stabilizers:\n", + "{} ^ 0 Z\n", + "\n", + "Notice: {} ^ 0 Z means we're back to +Z (flip=0)\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// Example demonstrating the flip flag from unitary operations\n", + "//\n", + "// The flip flag tracks phase changes from unitary gates:\n", + "// - X gate on |0⟩: Z stabilizer becomes -Z, so flip=1\n", + "// - Measurement of |1⟩ returns m0=1 (deterministic 1)\n", + "\n", + "let mut sim = StdSymbolicSparseStab::new(1);\n", + "\n", + "println!(\"=== Demonstrating the flip flag ===\\n\");\n", + "\n", + "println!(\"Initial state |0⟩:\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "// Apply X gate to flip |0⟩ to |1⟩\n", + "sim.x(0);\n", + "println!(\"After X gate (now |1⟩):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "println!(\"Notice: {{}} ^ 1 Z means the stabilizer is -Z (flip=1)\\n\");\n", + "\n", + "// Measure - should be deterministic 1\n", + "let m: SymbolicMeasurementResult = sim.mz(0);\n", + "println!(\"Measurement result: {}\", m);\n", + "println!(\" - Empty dependency set means no measurement dependencies\");\n", + "println!(\" - =1 means the result is flipped, giving outcome 1\");\n", + "\n", + "// Reset and show double-X cancellation\n", + "sim.reset();\n", + "sim.x(0);\n", + "sim.x(0);\n", + "println!(\"\\nAfter X·X (identity):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "println!(\"Notice: {{}} ^ 0 Z means we're back to +Z (flip=0)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "5babf742-0955-4de9-aee2-3f0d9be68bf1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Combining measurement dependencies with flip ===\n", + "\n", + "After X(0):\n", + "Stabilizers:\n", + "{} ^ 1 ZI\n", + "{} ^ 0 IZ\n", + "\n", + "After H(0):\n", + "Stabilizers:\n", + "{} ^ 1 XI\n", + "{} ^ 0 IZ\n", + "\n", + "After CX(0,1) - Bell state with phase:\n", + "Stabilizers:\n", + "{} ^ 1 XX\n", + "{} ^ 0 ZZ\n", + "\n", + "Measurement results:\n", + " M0: m0=?\n", + " M1: m1^m0=0\n", + "\n", + "Both measurements depend on measurement index 0.\n", + "The flips indicate phase accumulated from the initial X gate.\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// Example combining measurement dependencies with flip flag\n", + "//\n", + "// Circuit: X on qubit 0, then create Bell state\n", + "// q0: --X--H--@--M0\n", + "// |\n", + "// q1: --------X--M1\n", + "//\n", + "// This shows how the flip propagates through the circuit\n", + "\n", + "let mut sim = StdSymbolicSparseStab::new(2);\n", + "\n", + "println!(\"=== Combining measurement dependencies with flip ===\\n\");\n", + "\n", + "sim.x(0);\n", + "println!(\"After X(0):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.h(0);\n", + "println!(\"After H(0):\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "sim.cx(0, 1);\n", + "println!(\"After CX(0,1) - Bell state with phase:\");\n", + "println!(\"Stabilizers:\\n{}\", sim.stab_tableau());\n", + "\n", + "let m0: SymbolicMeasurementResult = sim.mz(0);\n", + "let m1: SymbolicMeasurementResult = sim.mz(1);\n", + "\n", + "println!(\"Measurement results:\");\n", + "println!(\" M0: {}\", m0);\n", + "println!(\" M1: {}\", m1);\n", + "println!(\"\\nBoth measurements depend on measurement index 0.\");\n", + "println!(\"The flips indicate phase accumulated from the initial X gate.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6975c62c-9504-44f3-b872-6bde94dcfdec", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index bff5631a9..d6e6ef7db 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -54,7 +54,6 @@ nav: - user-guide/graph-api.md - user-guide/qasm-simulation.md - user-guide/noise-model-builders.md - - user-guide/general-noise-factory.md - user-guide/decoders.md - API: - api/api-reference.md diff --git a/python/pecos-rslib/Cargo.toml b/python/pecos-rslib/Cargo.toml index e7dfef649..65983b350 100644 --- a/python/pecos-rslib/Cargo.toml +++ b/python/pecos-rslib/Cargo.toml @@ -26,7 +26,7 @@ wasm = ["pecos/wasm"] [dependencies] # Use the pecos metacrate which includes all simulators and runtimes by default # Enable llvm and wasm features for full Python functionality -pecos = { workspace = true, features = ["llvm", "wasm"] } +pecos = { workspace = true, features = ["llvm", "wasm", "selene"] } pyo3 = { workspace=true, features = ["extension-module", "abi3-py310", "generate-import-lib", "num-complex"] } ndarray.workspace = true diff --git a/uv.lock b/uv.lock index 7f40ab05d..083fbc80b 100644 --- a/uv.lock +++ b/uv.lock @@ -25,17 +25,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.11.0" +version = "4.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, - { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, ] [[package]] @@ -163,15 +162,15 @@ wheels = [ [[package]] name = "beautifulsoup4" -version = "4.14.2" +version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] @@ -1021,59 +1020,59 @@ wheels = [ [[package]] name = "fonttools" -version = "4.60.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/70/03e9d89a053caff6ae46053890eba8e4a5665a7c5638279ed4492e6d4b8b/fonttools-4.60.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9a52f254ce051e196b8fe2af4634c2d2f02c981756c6464dc192f1b6050b4e28", size = 2810747, upload-time = "2025-09-29T21:10:59.653Z" }, - { url = "https://files.pythonhosted.org/packages/6f/41/449ad5aff9670ab0df0f61ee593906b67a36d7e0b4d0cd7fa41ac0325bf5/fonttools-4.60.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7420a2696a44650120cdd269a5d2e56a477e2bfa9d95e86229059beb1c19e15", size = 2346909, upload-time = "2025-09-29T21:11:02.882Z" }, - { url = "https://files.pythonhosted.org/packages/9a/18/e5970aa96c8fad1cb19a9479cc3b7602c0c98d250fcdc06a5da994309c50/fonttools-4.60.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee0c0b3b35b34f782afc673d503167157094a16f442ace7c6c5e0ca80b08f50c", size = 4864572, upload-time = "2025-09-29T21:11:05.096Z" }, - { url = "https://files.pythonhosted.org/packages/ce/20/9b2b4051b6ec6689480787d506b5003f72648f50972a92d04527a456192c/fonttools-4.60.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:282dafa55f9659e8999110bd8ed422ebe1c8aecd0dc396550b038e6c9a08b8ea", size = 4794635, upload-time = "2025-09-29T21:11:08.651Z" }, - { url = "https://files.pythonhosted.org/packages/10/52/c791f57347c1be98f8345e3dca4ac483eb97666dd7c47f3059aeffab8b59/fonttools-4.60.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4ba4bd646e86de16160f0fb72e31c3b9b7d0721c3e5b26b9fa2fc931dfdb2652", size = 4843878, upload-time = "2025-09-29T21:11:10.893Z" }, - { url = "https://files.pythonhosted.org/packages/69/e9/35c24a8d01644cee8c090a22fad34d5b61d1e0a8ecbc9945ad785ebf2e9e/fonttools-4.60.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0b0835ed15dd5b40d726bb61c846a688f5b4ce2208ec68779bc81860adb5851a", size = 4954555, upload-time = "2025-09-29T21:11:13.24Z" }, - { url = "https://files.pythonhosted.org/packages/f7/86/fb1e994971be4bdfe3a307de6373ef69a9df83fb66e3faa9c8114893d4cc/fonttools-4.60.1-cp310-cp310-win32.whl", hash = "sha256:1525796c3ffe27bb6268ed2a1bb0dcf214d561dfaf04728abf01489eb5339dce", size = 2232019, upload-time = "2025-09-29T21:11:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/40/84/62a19e2bd56f0e9fb347486a5b26376bade4bf6bbba64dda2c103bd08c94/fonttools-4.60.1-cp310-cp310-win_amd64.whl", hash = "sha256:268ecda8ca6cb5c4f044b1fb9b3b376e8cd1b361cef275082429dc4174907038", size = 2276803, upload-time = "2025-09-29T21:11:18.152Z" }, - { url = "https://files.pythonhosted.org/packages/ea/85/639aa9bface1537e0fb0f643690672dde0695a5bbbc90736bc571b0b1941/fonttools-4.60.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b4c32e232a71f63a5d00259ca3d88345ce2a43295bb049d21061f338124246f", size = 2831872, upload-time = "2025-09-29T21:11:20.329Z" }, - { url = "https://files.pythonhosted.org/packages/6b/47/3c63158459c95093be9618794acb1067b3f4d30dcc5c3e8114b70e67a092/fonttools-4.60.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3630e86c484263eaac71d117085d509cbcf7b18f677906824e4bace598fb70d2", size = 2356990, upload-time = "2025-09-29T21:11:22.754Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/1934b537c86fcf99f9761823f1fc37a98fbd54568e8e613f29a90fed95a9/fonttools-4.60.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1015318e4fec75dd4943ad5f6a206d9727adf97410d58b7e32ab644a807914", size = 5042189, upload-time = "2025-09-29T21:11:25.061Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d2/9f4e4c4374dd1daa8367784e1bd910f18ba886db1d6b825b12edf6db3edc/fonttools-4.60.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6c58beb17380f7c2ea181ea11e7db8c0ceb474c9dd45f48e71e2cb577d146a1", size = 4978683, upload-time = "2025-09-29T21:11:27.693Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c4/0fb2dfd1ecbe9a07954cc13414713ed1eab17b1c0214ef07fc93df234a47/fonttools-4.60.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec3681a0cb34c255d76dd9d865a55f260164adb9fa02628415cdc2d43ee2c05d", size = 5021372, upload-time = "2025-09-29T21:11:30.257Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d5/495fc7ae2fab20223cc87179a8f50f40f9a6f821f271ba8301ae12bb580f/fonttools-4.60.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4b5c37a5f40e4d733d3bbaaef082149bee5a5ea3156a785ff64d949bd1353fa", size = 5132562, upload-time = "2025-09-29T21:11:32.737Z" }, - { url = "https://files.pythonhosted.org/packages/bc/fa/021dab618526323c744e0206b3f5c8596a2e7ae9aa38db5948a131123e83/fonttools-4.60.1-cp311-cp311-win32.whl", hash = "sha256:398447f3d8c0c786cbf1209711e79080a40761eb44b27cdafffb48f52bcec258", size = 2230288, upload-time = "2025-09-29T21:11:35.015Z" }, - { url = "https://files.pythonhosted.org/packages/bb/78/0e1a6d22b427579ea5c8273e1c07def2f325b977faaf60bb7ddc01456cb1/fonttools-4.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:d066ea419f719ed87bc2c99a4a4bfd77c2e5949cb724588b9dd58f3fd90b92bf", size = 2278184, upload-time = "2025-09-29T21:11:37.434Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" }, - { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" }, - { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb", size = 2825777, upload-time = "2025-09-29T21:12:01.22Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8a/de9cc0540f542963ba5e8f3a1f6ad48fa211badc3177783b9d5cadf79b5d/fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4", size = 2348080, upload-time = "2025-09-29T21:12:03.785Z" }, - { url = "https://files.pythonhosted.org/packages/2d/8b/371ab3cec97ee3fe1126b3406b7abd60c8fec8975fd79a3c75cdea0c3d83/fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c", size = 4903082, upload-time = "2025-09-29T21:12:06.382Z" }, - { url = "https://files.pythonhosted.org/packages/04/05/06b1455e4bc653fcb2117ac3ef5fa3a8a14919b93c60742d04440605d058/fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77", size = 4960125, upload-time = "2025-09-29T21:12:09.314Z" }, - { url = "https://files.pythonhosted.org/packages/8e/37/f3b840fcb2666f6cb97038793606bdd83488dca2d0b0fc542ccc20afa668/fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199", size = 4901454, upload-time = "2025-09-29T21:12:11.931Z" }, - { url = "https://files.pythonhosted.org/packages/fd/9e/eb76f77e82f8d4a46420aadff12cec6237751b0fb9ef1de373186dcffb5f/fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c", size = 5044495, upload-time = "2025-09-29T21:12:15.241Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b3/cede8f8235d42ff7ae891bae8d619d02c8ac9fd0cfc450c5927a6200c70d/fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272", size = 2217028, upload-time = "2025-09-29T21:12:17.96Z" }, - { url = "https://files.pythonhosted.org/packages/75/4d/b022c1577807ce8b31ffe055306ec13a866f2337ecee96e75b24b9b753ea/fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac", size = 2266200, upload-time = "2025-09-29T21:12:20.14Z" }, - { url = "https://files.pythonhosted.org/packages/9a/83/752ca11c1aa9a899b793a130f2e466b79ea0cf7279c8d79c178fc954a07b/fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3", size = 2822830, upload-time = "2025-09-29T21:12:24.406Z" }, - { url = "https://files.pythonhosted.org/packages/57/17/bbeab391100331950a96ce55cfbbff27d781c1b85ebafb4167eae50d9fe3/fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85", size = 2345524, upload-time = "2025-09-29T21:12:26.819Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2e/d4831caa96d85a84dd0da1d9f90d81cec081f551e0ea216df684092c6c97/fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537", size = 4843490, upload-time = "2025-09-29T21:12:29.123Z" }, - { url = "https://files.pythonhosted.org/packages/49/13/5e2ea7c7a101b6fc3941be65307ef8df92cbbfa6ec4804032baf1893b434/fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003", size = 4944184, upload-time = "2025-09-29T21:12:31.414Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2b/cf9603551c525b73fc47c52ee0b82a891579a93d9651ed694e4e2cd08bb8/fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08", size = 4890218, upload-time = "2025-09-29T21:12:33.936Z" }, - { url = "https://files.pythonhosted.org/packages/fd/2f/933d2352422e25f2376aae74f79eaa882a50fb3bfef3c0d4f50501267101/fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99", size = 4999324, upload-time = "2025-09-29T21:12:36.637Z" }, - { url = "https://files.pythonhosted.org/packages/38/99/234594c0391221f66216bc2c886923513b3399a148defaccf81dc3be6560/fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6", size = 2220861, upload-time = "2025-09-29T21:12:39.108Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1d/edb5b23726dde50fc4068e1493e4fc7658eeefcaf75d4c5ffce067d07ae5/fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987", size = 2270934, upload-time = "2025-09-29T21:12:41.339Z" }, - { url = "https://files.pythonhosted.org/packages/fb/da/1392aaa2170adc7071fe7f9cfd181a5684a7afcde605aebddf1fb4d76df5/fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299", size = 2894340, upload-time = "2025-09-29T21:12:43.774Z" }, - { url = "https://files.pythonhosted.org/packages/bf/a7/3b9f16e010d536ce567058b931a20b590d8f3177b2eda09edd92e392375d/fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01", size = 2375073, upload-time = "2025-09-29T21:12:46.437Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b5/e9bcf51980f98e59bb5bb7c382a63c6f6cac0eec5f67de6d8f2322382065/fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801", size = 4849758, upload-time = "2025-09-29T21:12:48.694Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/1d2cf7d1cba82264b2f8385db3f5960e3d8ce756b4dc65b700d2c496f7e9/fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc", size = 5085598, upload-time = "2025-09-29T21:12:51.081Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4d/279e28ba87fb20e0c69baf72b60bbf1c4d873af1476806a7b5f2b7fac1ff/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc", size = 4957603, upload-time = "2025-09-29T21:12:53.423Z" }, - { url = "https://files.pythonhosted.org/packages/78/d4/ff19976305e0c05aa3340c805475abb00224c954d3c65e82c0a69633d55d/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed", size = 4974184, upload-time = "2025-09-29T21:12:55.962Z" }, - { url = "https://files.pythonhosted.org/packages/63/22/8553ff6166f5cd21cfaa115aaacaa0dc73b91c079a8cfd54a482cbc0f4f5/fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259", size = 2282241, upload-time = "2025-09-29T21:12:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cb/fa7b4d148e11d5a72761a22e595344133e83a9507a4c231df972e657579b/fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c", size = 2345760, upload-time = "2025-09-29T21:13:00.375Z" }, - { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" }, +version = "4.61.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/f9/0e84d593c0e12244150280a630999835a64f2852276161b62a0f98318de0/fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7", size = 3561884, upload-time = "2025-11-28T17:05:49.491Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/f3/91bba2721fb173fc68e09d15b6ccf3ad4f83d127fbff579be7e5984888a6/fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33", size = 2850151, upload-time = "2025-11-28T17:04:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8c/a1691dec01038ac7e7bb3ab83300dcc5087b11d8f48640928c02a873eb92/fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0", size = 2389769, upload-time = "2025-11-28T17:04:16.443Z" }, + { url = "https://files.pythonhosted.org/packages/2d/dd/5bb369a44319d92ba25612511eb8ed2a6fa75239979e0388907525626902/fonttools-4.61.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad751319dc532a79bdf628b8439af167181b4210a0cd28a8935ca615d9fdd727", size = 4893189, upload-time = "2025-11-28T17:04:18.398Z" }, + { url = "https://files.pythonhosted.org/packages/5e/02/51373fa8846bd22bb54e5efb30a824b417b058083f775a194a432f21a45f/fonttools-4.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2de14557d113faa5fb519f7f29c3abe4d69c17fe6a5a2595cc8cda7338029219", size = 4854415, upload-time = "2025-11-28T17:04:20.421Z" }, + { url = "https://files.pythonhosted.org/packages/8b/64/9cdbbb804577a7e6191448851c57e6a36eb02aa4bf6a9668b528c968e44e/fonttools-4.61.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:59587bbe455dbdf75354a9dbca1697a35a8903e01fab4248d6b98a17032cee52", size = 4870927, upload-time = "2025-11-28T17:04:22.625Z" }, + { url = "https://files.pythonhosted.org/packages/92/68/e40b22919dc96dc30a70b58fec609ab85112de950bdecfadf8dd478c5a88/fonttools-4.61.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46cb3d9279f758ac0cf671dc3482da877104b65682679f01b246515db03dbb72", size = 4988674, upload-time = "2025-11-28T17:04:24.675Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5c/e857349ce8aedb2451b9448282e86544b2b7f1c8b10ea0fe49b7cb369b72/fonttools-4.61.0-cp310-cp310-win32.whl", hash = "sha256:58b4f1b78dfbfe855bb8a6801b31b8cdcca0e2847ec769ad8e0b0b692832dd3b", size = 1497663, upload-time = "2025-11-28T17:04:26.598Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0c/62961d5fe6f764d6cbc387ef2c001f5f610808c7aded837409836c0b3e7c/fonttools-4.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:68704a8bbe0b61976262b255e90cde593dc0fe3676542d9b4d846bad2a890a76", size = 1546143, upload-time = "2025-11-28T17:04:28.432Z" }, + { url = "https://files.pythonhosted.org/packages/fd/be/5aa89cdddf2863d8afbdc19eb8ec5d8d35d40eeeb8e6cf52c5ff1c2dbd33/fonttools-4.61.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a32a16951cbf113d38f1dd8551b277b6e06e0f6f776fece0f99f746d739e1be3", size = 2847553, upload-time = "2025-11-28T17:04:30.539Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3e/6ff643b07cead1236a534f51291ae2981721cf419135af5b740c002a66dd/fonttools-4.61.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:328a9c227984bebaf69f3ac9062265f8f6acc7ddf2e4e344c63358579af0aa3d", size = 2388298, upload-time = "2025-11-28T17:04:32.161Z" }, + { url = "https://files.pythonhosted.org/packages/c3/15/fca8dfbe7b482e6f240b1aad0ed7c6e2e75e7a28efa3d3a03b570617b5e5/fonttools-4.61.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f0bafc8a3b3749c69cc610e5aa3da832d39c2a37a68f03d18ec9a02ecaac04a", size = 5054133, upload-time = "2025-11-28T17:04:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a2/821c61c691b21fd09e07528a9a499cc2b075ac83ddb644aa16c9875a64bc/fonttools-4.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5ca59b7417d149cf24e4c1933c9f44b2957424fc03536f132346d5242e0ebe5", size = 5031410, upload-time = "2025-11-28T17:04:36.141Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/8b16339e93d03c732c8a23edefe3061b17a5f9107ddc47a3215ecd054cac/fonttools-4.61.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:df8cbce85cf482eb01f4551edca978c719f099c623277bda8332e5dbe7dba09d", size = 5030005, upload-time = "2025-11-28T17:04:38.314Z" }, + { url = "https://files.pythonhosted.org/packages/ac/eb/d4e150427bdaa147755239c931bbce829a88149ade5bfd8a327afe565567/fonttools-4.61.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7fb5b84f48a6a733ca3d7f41aa9551908ccabe8669ffe79586560abcc00a9cfd", size = 5154026, upload-time = "2025-11-28T17:04:40.34Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5f/3dd00ce0dba6759943c707b1830af8c0bcf6f8f1a9fe46cb82e7ac2aaa74/fonttools-4.61.0-cp311-cp311-win32.whl", hash = "sha256:787ef9dfd1ea9fe49573c272412ae5f479d78e671981819538143bec65863865", size = 2276035, upload-time = "2025-11-28T17:04:42.59Z" }, + { url = "https://files.pythonhosted.org/packages/4e/44/798c472f096ddf12955eddb98f4f7c906e7497695d04ce073ddf7161d134/fonttools-4.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:14fafda386377b6131d9e448af42d0926bad47e038de0e5ba1d58c25d621f028", size = 2327290, upload-time = "2025-11-28T17:04:44.57Z" }, + { url = "https://files.pythonhosted.org/packages/00/5d/19e5939f773c7cb05480fe2e881d63870b63ee2b4bdb9a77d55b1d36c7b9/fonttools-4.61.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e24a1565c4e57111ec7f4915f8981ecbb61adf66a55f378fdc00e206059fcfef", size = 2846930, upload-time = "2025-11-28T17:04:46.639Z" }, + { url = "https://files.pythonhosted.org/packages/25/b2/0658faf66f705293bd7e739a4f038302d188d424926be9c59bdad945664b/fonttools-4.61.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2bfacb5351303cae9f072ccf3fc6ecb437a6f359c0606bae4b1ab6715201d87", size = 2383016, upload-time = "2025-11-28T17:04:48.525Z" }, + { url = "https://files.pythonhosted.org/packages/29/a3/1fa90b95b690f0d7541f48850adc40e9019374d896c1b8148d15012b2458/fonttools-4.61.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0bdcf2e29d65c26299cc3d502f4612365e8b90a939f46cd92d037b6cb7bb544a", size = 4949425, upload-time = "2025-11-28T17:04:50.482Z" }, + { url = "https://files.pythonhosted.org/packages/af/00/acf18c00f6c501bd6e05ee930f926186f8a8e268265407065688820f1c94/fonttools-4.61.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6cd0d9051b8ddaf7385f99dd82ec2a058e2b46cf1f1961e68e1ff20fcbb61af", size = 4999632, upload-time = "2025-11-28T17:04:52.508Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e0/19a2b86e54109b1d2ee8743c96a1d297238ae03243897bc5345c0365f34d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e074bc07c31406f45c418e17c1722e83560f181d122c412fa9e815df0ff74810", size = 4939438, upload-time = "2025-11-28T17:04:54.437Z" }, + { url = "https://files.pythonhosted.org/packages/04/35/7b57a5f57d46286360355eff8d6b88c64ab6331107f37a273a71c803798d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a9b78da5d5faa17e63b2404b77feeae105c1b7e75f26020ab7a27b76e02039f", size = 5088960, upload-time = "2025-11-28T17:04:56.348Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0e/6c5023eb2e0fe5d1ababc7e221e44acd3ff668781489cc1937a6f83d620a/fonttools-4.61.0-cp312-cp312-win32.whl", hash = "sha256:9821ed77bb676736b88fa87a737c97b6af06e8109667e625a4f00158540ce044", size = 2264404, upload-time = "2025-11-28T17:04:58.149Z" }, + { url = "https://files.pythonhosted.org/packages/36/0b/63273128c7c5df19b1e4cd92e0a1e6ea5bb74a400c4905054c96ad60a675/fonttools-4.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:0011d640afa61053bc6590f9a3394bd222de7cfde19346588beabac374e9d8ac", size = 2314427, upload-time = "2025-11-28T17:04:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/17/45/334f0d7f181e5473cfb757e1b60f4e60e7fc64f28d406e5d364a952718c0/fonttools-4.61.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba774b8cbd8754f54b8eb58124e8bd45f736b2743325ab1a5229698942b9b433", size = 2841801, upload-time = "2025-11-28T17:05:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/cc/63/97b9c78e1f79bc741d4efe6e51f13872d8edb2b36e1b9fb2bab0d4491bb7/fonttools-4.61.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c84b430616ed73ce46e9cafd0bf0800e366a3e02fb7e1ad7c1e214dbe3862b1f", size = 2379024, upload-time = "2025-11-28T17:05:03.668Z" }, + { url = "https://files.pythonhosted.org/packages/4e/80/c87bc524a90dbeb2a390eea23eae448286983da59b7e02c67fa0ca96a8c5/fonttools-4.61.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2b734d8391afe3c682320840c8191de9bd24e7eb85768dd4dc06ed1b63dbb1b", size = 4923706, upload-time = "2025-11-28T17:05:05.494Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f6/a3b0374811a1de8c3f9207ec88f61ad1bb96f938ed89babae26c065c2e46/fonttools-4.61.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5c5fff72bf31b0e558ed085e4fd7ed96eb85881404ecc39ed2a779e7cf724eb", size = 4979751, upload-time = "2025-11-28T17:05:07.665Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3b/30f63b4308b449091573285f9d27619563a84f399946bca3eadc9554afbe/fonttools-4.61.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:14a290c5c93fcab76b7f451e6a4b7721b712d90b3b5ed6908f1abcf794e90d6d", size = 4921113, upload-time = "2025-11-28T17:05:09.551Z" }, + { url = "https://files.pythonhosted.org/packages/41/6c/58e6e9b7d9d8bf2d7010bd7bb493060b39b02a12d1cda64a8bfb116ce760/fonttools-4.61.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:13e3e20a5463bfeb77b3557d04b30bd6a96a6bb5c15c7b2e7908903e69d437a0", size = 5063183, upload-time = "2025-11-28T17:05:11.677Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e3/52c790ab2b07492df059947a1fd7778e105aac5848c0473029a4d20481a2/fonttools-4.61.0-cp313-cp313-win32.whl", hash = "sha256:6781e7a4bb010be1cd69a29927b0305c86b843395f2613bdabe115f7d6ea7f34", size = 2263159, upload-time = "2025-11-28T17:05:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1f/116013b200fbeba871046554d5d2a45fefa69a05c40e9cdfd0d4fff53edc/fonttools-4.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:c53b47834ae41e8e4829171cc44fec0fdf125545a15f6da41776b926b9645a9a", size = 2313530, upload-time = "2025-11-28T17:05:14.848Z" }, + { url = "https://files.pythonhosted.org/packages/d3/99/59b1e25987787cb714aa9457cee4c9301b7c2153f0b673e2b8679d37669d/fonttools-4.61.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:96dfc9bc1f2302224e48e6ee37e656eddbab810b724b52e9d9c13a57a6abad01", size = 2841429, upload-time = "2025-11-28T17:05:16.671Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/4c1911d4332c8a144bb3b44416e274ccca0e297157c971ea1b3fbb855590/fonttools-4.61.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3b2065d94e5d63aafc2591c8b6ccbdb511001d9619f1bca8ad39b745ebeb5efa", size = 2378987, upload-time = "2025-11-28T17:05:18.69Z" }, + { url = "https://files.pythonhosted.org/packages/24/b0/f442e90fde5d2af2ae0cb54008ab6411edc557ee33b824e13e1d04925ac9/fonttools-4.61.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e0d87e81e4d869549585ba0beb3f033718501c1095004f5e6aef598d13ebc216", size = 4873270, upload-time = "2025-11-28T17:05:20.625Z" }, + { url = "https://files.pythonhosted.org/packages/bb/04/f5d5990e33053c8a59b90b1d7e10ad9b97a73f42c745304da0e709635fab/fonttools-4.61.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cfa2eb9bae650e58f0e8ad53c49d19a844d6034d6b259f30f197238abc1ccee", size = 4968270, upload-time = "2025-11-28T17:05:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/94/9f/2091402e0d27c9c8c4bab5de0e5cd146d9609a2d7d1c666bbb75c0011c1a/fonttools-4.61.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4238120002e68296d55e091411c09eab94e111c8ce64716d17df53fd0eb3bb3d", size = 4919799, upload-time = "2025-11-28T17:05:24.437Z" }, + { url = "https://files.pythonhosted.org/packages/a8/72/86adab22fde710b829f8ffbc8f264df01928e5b7a8f6177fa29979ebf256/fonttools-4.61.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b6ceac262cc62bec01b3bb59abccf41b24ef6580869e306a4e88b7e56bb4bdda", size = 5030966, upload-time = "2025-11-28T17:05:26.115Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a7/7c8e31b003349e845b853f5e0a67b95ff6b052fa4f5224f8b72624f5ac69/fonttools-4.61.0-cp314-cp314-win32.whl", hash = "sha256:adbb4ecee1a779469a77377bbe490565effe8fce6fb2e6f95f064de58f8bac85", size = 2267243, upload-time = "2025-11-28T17:05:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/20/ee/f434fe7749360497c52b7dcbcfdbccdaab0a71c59f19d572576066717122/fonttools-4.61.0-cp314-cp314-win_amd64.whl", hash = "sha256:02bdf8e04d1a70476564b8640380f04bb4ac74edc1fc71f1bacb840b3e398ee9", size = 2318822, upload-time = "2025-11-28T17:05:29.882Z" }, + { url = "https://files.pythonhosted.org/packages/33/b3/c16255320255e5c1863ca2b2599bb61a46e2f566db0bbb9948615a8fe692/fonttools-4.61.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:627216062d90ab0d98215176d8b9562c4dd5b61271d35f130bcd30f6a8aaa33a", size = 2924917, upload-time = "2025-11-28T17:05:31.46Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b8/08067ae21de705a817777c02ef36ab0b953cbe91d8adf134f9c2da75ed6d/fonttools-4.61.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7b446623c9cd5f14a59493818eaa80255eec2468c27d2c01b56e05357c263195", size = 2413576, upload-time = "2025-11-28T17:05:33.343Z" }, + { url = "https://files.pythonhosted.org/packages/42/f1/96ff43f92addce2356780fdc203f2966206f3d22ea20e242c27826fd7442/fonttools-4.61.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70e2a0c0182ee75e493ef33061bfebf140ea57e035481d2f95aa03b66c7a0e05", size = 4877447, upload-time = "2025-11-28T17:05:35.278Z" }, + { url = "https://files.pythonhosted.org/packages/d0/1e/a3d8e51ed9ccfd7385e239ae374b78d258a0fb82d82cab99160a014a45d1/fonttools-4.61.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9064b0f55b947e929ac669af5311ab1f26f750214db6dd9a0c97e091e918f486", size = 5095681, upload-time = "2025-11-28T17:05:37.142Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f6/d256bd6c1065c146a0bdddf1c62f542e08ae5b3405dbf3fcc52be272f674/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5e45a824ce14b90510024d0d39dae51bd4fbb54c42a9334ea8c8cf4d95cbe", size = 4974140, upload-time = "2025-11-28T17:05:39.5Z" }, + { url = "https://files.pythonhosted.org/packages/5d/0c/96633eb4b26f138cc48561c6e0c44b4ea48acea56b20b507d6b14f8e80ce/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e5ca8c62efdec7972dfdfd454415c4db49b89aeaefaaacada432f3b7eea9866", size = 5001741, upload-time = "2025-11-28T17:05:41.424Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/3b536bad3be4f26186f296e749ff17bad3e6d57232c104d752d24b2e265b/fonttools-4.61.0-cp314-cp314t-win32.whl", hash = "sha256:63c7125d31abe3e61d7bb917329b5543c5b3448db95f24081a13aaf064360fc8", size = 2330707, upload-time = "2025-11-28T17:05:43.548Z" }, + { url = "https://files.pythonhosted.org/packages/18/ea/e6b9ac610451ee9f04477c311ad126de971f6112cb579fa391d2a8edb00b/fonttools-4.61.0-cp314-cp314t-win_amd64.whl", hash = "sha256:67d841aa272be5500de7f447c40d1d8452783af33b4c3599899319f6ef9ad3c1", size = 2395950, upload-time = "2025-11-28T17:05:45.638Z" }, + { url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485, upload-time = "2025-11-28T17:05:47.573Z" }, ] [[package]] @@ -1292,7 +1291,7 @@ dependencies = [ { name = "comm" }, { name = "debugpy" }, { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -1335,7 +1334,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.7.0" +version = "9.8.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14'", @@ -1354,9 +1353,9 @@ dependencies = [ { name = "traitlets", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/e6/48c74d54039241a456add616464ea28c6ebf782e4110d419411b83dae06f/ipython-9.7.0.tar.gz", hash = "sha256:5f6de88c905a566c6a9d6c400a8fed54a638e1f7543d17aae2551133216b1e4e", size = 4422115, upload-time = "2025-11-05T12:18:54.646Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/51/a703c030f4928646d390b4971af4938a1b10c9dfce694f0d99a0bb073cb2/ipython-9.8.0.tar.gz", hash = "sha256:8e4ce129a627eb9dd221c41b1d2cdaed4ef7c9da8c17c63f6f578fe231141f83", size = 4424940, upload-time = "2025-12-03T10:18:24.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/aa/62893d6a591d337aa59dcc4c6f6c842f1fe20cd72c8c5c1f980255243252/ipython-9.7.0-py3-none-any.whl", hash = "sha256:bce8ac85eb9521adc94e1845b4c03d88365fd6ac2f4908ec4ed1eb1b0a065f9f", size = 618911, upload-time = "2025-11-05T12:18:52.484Z" }, + { url = "https://files.pythonhosted.org/packages/f1/df/8ee1c5dd1e3308b5d5b2f2dfea323bb2f3827da8d654abb6642051199049/ipython-9.8.0-py3-none-any.whl", hash = "sha256:ebe6d1d58d7d988fbf23ff8ff6d8e1622cfdb194daf4b7b73b792c4ec3b85385", size = 621374, upload-time = "2025-12-03T10:18:22.335Z" }, ] [[package]] @@ -1378,7 +1377,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyterlab-widgets" }, { name = "traitlets" }, { name = "widgetsnbextension" }, @@ -1522,7 +1521,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipykernel" }, { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "prompt-toolkit" }, @@ -2231,7 +2230,7 @@ python = [ [[package]] name = "mkdocstrings-python" -version = "2.0.0" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffe" }, @@ -2239,9 +2238,9 @@ dependencies = [ { name = "mkdocstrings" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/0d/dab7b08ca7e5a38b033cd83565bb0f95f05e8f3df7bc273e793c2ad3576e/mkdocstrings_python-2.0.0.tar.gz", hash = "sha256:4d872290f595221740a304bebca5b3afa4beafe84cc6fd27314d52dc3fbb4676", size = 199113, upload-time = "2025-11-27T16:44:44.894Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/75/d30af27a2906f00eb90143470272376d728521997800f5dce5b340ba35bc/mkdocstrings_python-2.0.1.tar.gz", hash = "sha256:843a562221e6a471fefdd4b45cc6c22d2607ccbad632879234fa9692e9cf7732", size = 199345, upload-time = "2025-12-03T14:26:11.755Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/de/063481352688c3a1468c51c10b6cfb858d5e35dfef8323d9c83c4f2faa03/mkdocstrings_python-2.0.0-py3-none-any.whl", hash = "sha256:1d552dda109d47e4fddecbb1f06f9a86699c1b073e8b166fba89eeef0a0ffec6", size = 104803, upload-time = "2025-11-27T16:44:43.441Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055, upload-time = "2025-12-03T14:26:10.184Z" }, ] [[package]] @@ -3708,150 +3707,150 @@ wheels = [ [[package]] name = "rpds-py" -version = "0.29.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/33/23b3b3419b6a3e0f559c7c0d2ca8fc1b9448382b25245033788785921332/rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359", size = 69359, upload-time = "2025-11-16T14:50:39.532Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/7a/c5b2ff381b74bc742768e8d870f26babac4ef256ba160bdbf8d57af56461/rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113", size = 372385, upload-time = "2025-11-16T14:47:36.287Z" }, - { url = "https://files.pythonhosted.org/packages/28/36/531f1eb4d5bed4a9c150f363a7ec4a98d2dc746151bba5473bc38ee85dec/rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9", size = 362869, upload-time = "2025-11-16T14:47:38.196Z" }, - { url = "https://files.pythonhosted.org/packages/54/df/7e9c0493a2015d9c82807a2d5f023ea9774e27a4c15b33ef1cdb7456138d/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f", size = 391582, upload-time = "2025-11-16T14:47:39.746Z" }, - { url = "https://files.pythonhosted.org/packages/15/38/42a981c3592ef46fbd7e17adbf8730cc5ec87e6aa1770c658c44bbb52960/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545", size = 405685, upload-time = "2025-11-16T14:47:41.472Z" }, - { url = "https://files.pythonhosted.org/packages/12/45/628b8c15856c3849c3f52ec6dac93c046ed5faeed4a435af03b70525fd29/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d", size = 527067, upload-time = "2025-11-16T14:47:43.036Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ba/6b56d09badeabd95098016d72a437d4a0fd82d4672ce92a7607df5d70a42/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae", size = 412532, upload-time = "2025-11-16T14:47:44.484Z" }, - { url = "https://files.pythonhosted.org/packages/f1/39/2f1f3db92888314b50b8f9641f679188bd24b3665a8cb9923b7201ae8011/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756", size = 392736, upload-time = "2025-11-16T14:47:46.053Z" }, - { url = "https://files.pythonhosted.org/packages/60/43/3c3b1dcd827e50f2ae28786d846b8a351080d8a69a3b49bc10ae44cc39b1/rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea", size = 406300, upload-time = "2025-11-16T14:47:47.268Z" }, - { url = "https://files.pythonhosted.org/packages/da/02/bc96021b67f8525e6bcdd68935c4543ada61e1f3dcb067ed037d68b8c6d2/rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2", size = 423641, upload-time = "2025-11-16T14:47:48.878Z" }, - { url = "https://files.pythonhosted.org/packages/38/e9/c435ddb602ced19a80b8277a41371734f33ad3f91cc4ceb4d82596800a3c/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2", size = 574153, upload-time = "2025-11-16T14:47:50.435Z" }, - { url = "https://files.pythonhosted.org/packages/84/82/dc3c32e1f89ecba8a59600d4cd65fe0ad81b6c636ccdbf6cd177fd6a7bac/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b", size = 600304, upload-time = "2025-11-16T14:47:51.599Z" }, - { url = "https://files.pythonhosted.org/packages/35/98/785290e0b7142470735dc1b1f68fb33aae29e5296f062c88396eedf796c8/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a", size = 562211, upload-time = "2025-11-16T14:47:53.094Z" }, - { url = "https://files.pythonhosted.org/packages/30/58/4eeddcb0737c6875f3e30c65dc9d7e7a10dfd5779646a990fa602c6d56c5/rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef", size = 221803, upload-time = "2025-11-16T14:47:54.404Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/b35a8dbdcbeb32505500547cdafaa9f8863e85f8faac50ef34464ec5a256/rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950", size = 235530, upload-time = "2025-11-16T14:47:56.061Z" }, - { url = "https://files.pythonhosted.org/packages/36/ab/7fb95163a53ab122c74a7c42d2d2f012819af2cf3deb43fb0d5acf45cc1a/rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437", size = 372344, upload-time = "2025-11-16T14:47:57.279Z" }, - { url = "https://files.pythonhosted.org/packages/b3/45/f3c30084c03b0d0f918cb4c5ae2c20b0a148b51ba2b3f6456765b629bedd/rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383", size = 363041, upload-time = "2025-11-16T14:47:58.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e9/4d044a1662608c47a87cbb37b999d4d5af54c6d6ebdda93a4d8bbf8b2a10/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c", size = 391775, upload-time = "2025-11-16T14:48:00.197Z" }, - { url = "https://files.pythonhosted.org/packages/50/c9/7616d3ace4e6731aeb6e3cd85123e03aec58e439044e214b9c5c60fd8eb1/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b", size = 405624, upload-time = "2025-11-16T14:48:01.496Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/6d7d6941ca0843609fd2d72c966a438d6f22617baf22d46c3d2156c31350/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311", size = 527894, upload-time = "2025-11-16T14:48:03.167Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f7/aee14dc2db61bb2ae1e3068f134ca9da5f28c586120889a70ff504bb026f/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588", size = 412720, upload-time = "2025-11-16T14:48:04.413Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e2/2293f236e887c0360c2723d90c00d48dee296406994d6271faf1712e94ec/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed", size = 392945, upload-time = "2025-11-16T14:48:06.252Z" }, - { url = "https://files.pythonhosted.org/packages/14/cd/ceea6147acd3bd1fd028d1975228f08ff19d62098078d5ec3eed49703797/rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63", size = 406385, upload-time = "2025-11-16T14:48:07.575Z" }, - { url = "https://files.pythonhosted.org/packages/52/36/fe4dead19e45eb77a0524acfdbf51e6cda597b26fc5b6dddbff55fbbb1a5/rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2", size = 423943, upload-time = "2025-11-16T14:48:10.175Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7b/4551510803b582fa4abbc8645441a2d15aa0c962c3b21ebb380b7e74f6a1/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f", size = 574204, upload-time = "2025-11-16T14:48:11.499Z" }, - { url = "https://files.pythonhosted.org/packages/64/ba/071ccdd7b171e727a6ae079f02c26f75790b41555f12ca8f1151336d2124/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca", size = 600587, upload-time = "2025-11-16T14:48:12.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/09/96983d48c8cf5a1e03c7d9cc1f4b48266adfb858ae48c7c2ce978dbba349/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95", size = 562287, upload-time = "2025-11-16T14:48:14.108Z" }, - { url = "https://files.pythonhosted.org/packages/40/f0/8c01aaedc0fa92156f0391f39ea93b5952bc0ec56b897763858f95da8168/rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4", size = 221394, upload-time = "2025-11-16T14:48:15.374Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/a8b21c54c7d234efdc83dc034a4d7cd9668e3613b6316876a29b49dece71/rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60", size = 235713, upload-time = "2025-11-16T14:48:16.636Z" }, - { url = "https://files.pythonhosted.org/packages/a7/1f/df3c56219523947b1be402fa12e6323fe6d61d883cf35d6cb5d5bb6db9d9/rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c", size = 229157, upload-time = "2025-11-16T14:48:17.891Z" }, - { url = "https://files.pythonhosted.org/packages/3c/50/bc0e6e736d94e420df79be4deb5c9476b63165c87bb8f19ef75d100d21b3/rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954", size = 376000, upload-time = "2025-11-16T14:48:19.141Z" }, - { url = "https://files.pythonhosted.org/packages/3e/3a/46676277160f014ae95f24de53bed0e3b7ea66c235e7de0b9df7bd5d68ba/rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c", size = 360575, upload-time = "2025-11-16T14:48:20.443Z" }, - { url = "https://files.pythonhosted.org/packages/75/ba/411d414ed99ea1afdd185bbabeeaac00624bd1e4b22840b5e9967ade6337/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d", size = 392159, upload-time = "2025-11-16T14:48:22.12Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b1/e18aa3a331f705467a48d0296778dc1fea9d7f6cf675bd261f9a846c7e90/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5", size = 410602, upload-time = "2025-11-16T14:48:23.563Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6c/04f27f0c9f2299274c76612ac9d2c36c5048bb2c6c2e52c38c60bf3868d9/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e", size = 515808, upload-time = "2025-11-16T14:48:24.949Z" }, - { url = "https://files.pythonhosted.org/packages/83/56/a8412aa464fb151f8bc0d91fb0bb888adc9039bd41c1c6ba8d94990d8cf8/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83", size = 416015, upload-time = "2025-11-16T14:48:26.782Z" }, - { url = "https://files.pythonhosted.org/packages/04/4c/f9b8a05faca3d9e0a6397c90d13acb9307c9792b2bff621430c58b1d6e76/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949", size = 395325, upload-time = "2025-11-16T14:48:28.055Z" }, - { url = "https://files.pythonhosted.org/packages/34/60/869f3bfbf8ed7b54f1ad9a5543e0fdffdd40b5a8f587fe300ee7b4f19340/rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181", size = 410160, upload-time = "2025-11-16T14:48:29.338Z" }, - { url = "https://files.pythonhosted.org/packages/91/aa/e5b496334e3aba4fe4c8a80187b89f3c1294c5c36f2a926da74338fa5a73/rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c", size = 425309, upload-time = "2025-11-16T14:48:30.691Z" }, - { url = "https://files.pythonhosted.org/packages/85/68/4e24a34189751ceb6d66b28f18159922828dd84155876551f7ca5b25f14f/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7", size = 574644, upload-time = "2025-11-16T14:48:31.964Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/474a005ea4ea9c3b4f17b6108b6b13cebfc98ebaff11d6e1b193204b3a93/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19", size = 601605, upload-time = "2025-11-16T14:48:33.252Z" }, - { url = "https://files.pythonhosted.org/packages/f4/b1/c56f6a9ab8c5f6bb5c65c4b5f8229167a3a525245b0773f2c0896686b64e/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0", size = 564593, upload-time = "2025-11-16T14:48:34.643Z" }, - { url = "https://files.pythonhosted.org/packages/b3/13/0494cecce4848f68501e0a229432620b4b57022388b071eeff95f3e1e75b/rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7", size = 223853, upload-time = "2025-11-16T14:48:36.419Z" }, - { url = "https://files.pythonhosted.org/packages/1f/6a/51e9aeb444a00cdc520b032a28b07e5f8dc7bc328b57760c53e7f96997b4/rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977", size = 239895, upload-time = "2025-11-16T14:48:37.956Z" }, - { url = "https://files.pythonhosted.org/packages/d1/d4/8bce56cdad1ab873e3f27cb31c6a51d8f384d66b022b820525b879f8bed1/rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7", size = 230321, upload-time = "2025-11-16T14:48:39.71Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d9/c5de60d9d371bbb186c3e9bf75f4fc5665e11117a25a06a6b2e0afb7380e/rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61", size = 375710, upload-time = "2025-11-16T14:48:41.063Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b3/0860cdd012291dc21272895ce107f1e98e335509ba986dd83d72658b82b9/rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154", size = 360582, upload-time = "2025-11-16T14:48:42.423Z" }, - { url = "https://files.pythonhosted.org/packages/92/8a/a18c2f4a61b3407e56175f6aab6deacdf9d360191a3d6f38566e1eaf7266/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014", size = 391172, upload-time = "2025-11-16T14:48:43.75Z" }, - { url = "https://files.pythonhosted.org/packages/fd/49/e93354258508c50abc15cdcd5fcf7ac4117f67bb6233ad7859f75e7372a0/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6", size = 409586, upload-time = "2025-11-16T14:48:45.498Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8d/a27860dae1c19a6bdc901f90c81f0d581df1943355802961a57cdb5b6cd1/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c", size = 516339, upload-time = "2025-11-16T14:48:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ad/a75e603161e79b7110c647163d130872b271c6b28712c803c65d492100f7/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866", size = 416201, upload-time = "2025-11-16T14:48:48.615Z" }, - { url = "https://files.pythonhosted.org/packages/b9/42/555b4ee17508beafac135c8b450816ace5a96194ce97fefc49d58e5652ea/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295", size = 395095, upload-time = "2025-11-16T14:48:50.027Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f0/c90b671b9031e800ec45112be42ea9f027f94f9ac25faaac8770596a16a1/rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b", size = 410077, upload-time = "2025-11-16T14:48:51.515Z" }, - { url = "https://files.pythonhosted.org/packages/3d/80/9af8b640b81fe21e6f718e9dec36c0b5f670332747243130a5490f292245/rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55", size = 424548, upload-time = "2025-11-16T14:48:53.237Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0b/b5647446e991736e6a495ef510e6710df91e880575a586e763baeb0aa770/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd", size = 573661, upload-time = "2025-11-16T14:48:54.769Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b3/1b1c9576839ff583d1428efbf59f9ee70498d8ce6c0b328ac02f1e470879/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea", size = 600937, upload-time = "2025-11-16T14:48:56.247Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7b/b6cfca2f9fee4c4494ce54f7fb1b9f578867495a9aa9fc0d44f5f735c8e0/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22", size = 564496, upload-time = "2025-11-16T14:48:57.691Z" }, - { url = "https://files.pythonhosted.org/packages/b9/fb/ba29ec7f0f06eb801bac5a23057a9ff7670623b5e8013bd59bec4aa09de8/rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7", size = 223126, upload-time = "2025-11-16T14:48:59.058Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6b/0229d3bed4ddaa409e6d90b0ae967ed4380e4bdd0dad6e59b92c17d42457/rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e", size = 239771, upload-time = "2025-11-16T14:49:00.872Z" }, - { url = "https://files.pythonhosted.org/packages/e4/38/d2868f058b164f8efd89754d85d7b1c08b454f5c07ac2e6cc2e9bd4bd05b/rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2", size = 229994, upload-time = "2025-11-16T14:49:02.673Z" }, - { url = "https://files.pythonhosted.org/packages/52/91/5de91c5ec7d41759beec9b251630824dbb8e32d20c3756da1a9a9d309709/rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c", size = 365886, upload-time = "2025-11-16T14:49:04.133Z" }, - { url = "https://files.pythonhosted.org/packages/85/7c/415d8c1b016d5f47ecec5145d9d6d21002d39dce8761b30f6c88810b455a/rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b", size = 355262, upload-time = "2025-11-16T14:49:05.543Z" }, - { url = "https://files.pythonhosted.org/packages/3d/14/bf83e2daa4f980e4dc848aed9299792a8b84af95e12541d9e7562f84a6ef/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0", size = 384826, upload-time = "2025-11-16T14:49:07.301Z" }, - { url = "https://files.pythonhosted.org/packages/33/b8/53330c50a810ae22b4fbba5e6cf961b68b9d72d9bd6780a7c0a79b070857/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4", size = 394234, upload-time = "2025-11-16T14:49:08.782Z" }, - { url = "https://files.pythonhosted.org/packages/cc/32/01e2e9645cef0e584f518cfde4567563e57db2257244632b603f61b40e50/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688", size = 520008, upload-time = "2025-11-16T14:49:10.253Z" }, - { url = "https://files.pythonhosted.org/packages/98/c3/0d1b95a81affae2b10f950782e33a1fd2edd6ce2a479966cac98c9a66f57/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d", size = 409569, upload-time = "2025-11-16T14:49:12.478Z" }, - { url = "https://files.pythonhosted.org/packages/fa/60/aa3b8678f3f009f675b99174fa2754302a7fbfe749162e8043d111de2d88/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee", size = 385188, upload-time = "2025-11-16T14:49:13.88Z" }, - { url = "https://files.pythonhosted.org/packages/92/02/5546c1c8aa89c18d40c1fcffdcc957ba730dee53fb7c3ca3a46f114761d2/rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e", size = 398587, upload-time = "2025-11-16T14:49:15.339Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e0/ad6eeaf47e236eba052fa34c4073078b9e092bd44da6bbb35aaae9580669/rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb", size = 416641, upload-time = "2025-11-16T14:49:16.832Z" }, - { url = "https://files.pythonhosted.org/packages/1a/93/0acedfd50ad9cdd3879c615a6dc8c5f1ce78d2fdf8b87727468bb5bb4077/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967", size = 566683, upload-time = "2025-11-16T14:49:18.342Z" }, - { url = "https://files.pythonhosted.org/packages/62/53/8c64e0f340a9e801459fc6456821abc15b3582cb5dc3932d48705a9d9ac7/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e", size = 592730, upload-time = "2025-11-16T14:49:19.767Z" }, - { url = "https://files.pythonhosted.org/packages/85/ef/3109b6584f8c4b0d2490747c916df833c127ecfa82be04d9a40a376f2090/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a", size = 557361, upload-time = "2025-11-16T14:49:21.574Z" }, - { url = "https://files.pythonhosted.org/packages/ff/3b/61586475e82d57f01da2c16edb9115a618afe00ce86fe1b58936880b15af/rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb", size = 211227, upload-time = "2025-11-16T14:49:23.03Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3a/12dc43f13594a54ea0c9d7e9d43002116557330e3ad45bc56097ddf266e2/rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352", size = 225248, upload-time = "2025-11-16T14:49:24.841Z" }, - { url = "https://files.pythonhosted.org/packages/89/b1/0b1474e7899371d9540d3bbb2a499a3427ae1fc39c998563fe9035a1073b/rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1", size = 363731, upload-time = "2025-11-16T14:49:26.683Z" }, - { url = "https://files.pythonhosted.org/packages/28/12/3b7cf2068d0a334ed1d7b385a9c3c8509f4c2bcba3d4648ea71369de0881/rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8", size = 354343, upload-time = "2025-11-16T14:49:28.24Z" }, - { url = "https://files.pythonhosted.org/packages/eb/73/5afcf8924bc02a749416eda64e17ac9c9b28f825f4737385295a0e99b0c1/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626", size = 385406, upload-time = "2025-11-16T14:49:29.943Z" }, - { url = "https://files.pythonhosted.org/packages/c8/37/5db736730662508535221737a21563591b6f43c77f2e388951c42f143242/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7", size = 396162, upload-time = "2025-11-16T14:49:31.833Z" }, - { url = "https://files.pythonhosted.org/packages/70/0d/491c1017d14f62ce7bac07c32768d209a50ec567d76d9f383b4cfad19b80/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244", size = 517719, upload-time = "2025-11-16T14:49:33.804Z" }, - { url = "https://files.pythonhosted.org/packages/d7/25/b11132afcb17cd5d82db173f0c8dab270ffdfaba43e5ce7a591837ae9649/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17", size = 409498, upload-time = "2025-11-16T14:49:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7d/e6543cedfb2e6403a1845710a5ab0e0ccf8fc288e0b5af9a70bfe2c12053/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32", size = 382743, upload-time = "2025-11-16T14:49:36.704Z" }, - { url = "https://files.pythonhosted.org/packages/75/11/a4ebc9f654293ae9fefb83b2b6be7f3253e85ea42a5db2f77d50ad19aaeb/rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c", size = 400317, upload-time = "2025-11-16T14:49:39.132Z" }, - { url = "https://files.pythonhosted.org/packages/52/18/97677a60a81c7f0e5f64e51fb3f8271c5c8fcabf3a2df18e97af53d7c2bf/rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318", size = 416979, upload-time = "2025-11-16T14:49:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/f0/69/28ab391a9968f6c746b2a2db181eaa4d16afaa859fedc9c2f682d19f7e18/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212", size = 567288, upload-time = "2025-11-16T14:49:42.24Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d3/0c7afdcdb830eee94f5611b64e71354ffe6ac8df82d00c2faf2bfffd1d4e/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94", size = 593157, upload-time = "2025-11-16T14:49:43.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ac/a0fcbc2feed4241cf26d32268c195eb88ddd4bd862adfc9d4b25edfba535/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d", size = 554741, upload-time = "2025-11-16T14:49:45.557Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f1/fcc24137c470df8588674a677f33719d5800ec053aaacd1de8a5d5d84d9e/rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1", size = 215508, upload-time = "2025-11-16T14:49:47.562Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c7/1d169b2045512eac019918fc1021ea07c30e84a4343f9f344e3e0aa8c788/rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b", size = 228125, upload-time = "2025-11-16T14:49:49.064Z" }, - { url = "https://files.pythonhosted.org/packages/be/36/0cec88aaba70ec4a6e381c444b0d916738497d27f0c30406e3d9fcbd3bc2/rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9", size = 221992, upload-time = "2025-11-16T14:49:50.777Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fa/a2e524631717c9c0eb5d90d30f648cfba6b731047821c994acacb618406c/rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10", size = 366425, upload-time = "2025-11-16T14:49:52.691Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a4/6d43ebe0746ff694a30233f63f454aed1677bd50ab7a59ff6b2bb5ac61f2/rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a", size = 355282, upload-time = "2025-11-16T14:49:54.292Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a7/52fd8270e0320b09eaf295766ae81dd175f65394687906709b3e75c71d06/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79", size = 384968, upload-time = "2025-11-16T14:49:55.857Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7d/e6bc526b7a14e1ef80579a52c1d4ad39260a058a51d66c6039035d14db9d/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a", size = 394714, upload-time = "2025-11-16T14:49:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/f0ade3954e7db95c791e7eaf978aa7e08a756d2046e8bdd04d08146ed188/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310", size = 520136, upload-time = "2025-11-16T14:49:59.162Z" }, - { url = "https://files.pythonhosted.org/packages/87/b3/07122ead1b97009715ab9d4082be6d9bd9546099b2b03fae37c3116f72be/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b", size = 409250, upload-time = "2025-11-16T14:50:00.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c6/dcbee61fd1dc892aedcb1b489ba661313101aa82ec84b1a015d4c63ebfda/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808", size = 384940, upload-time = "2025-11-16T14:50:02.312Z" }, - { url = "https://files.pythonhosted.org/packages/47/11/914ecb6f3574cf9bf8b38aced4063e0f787d6e1eb30b181a7efbc6c1da9a/rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761", size = 399392, upload-time = "2025-11-16T14:50:03.829Z" }, - { url = "https://files.pythonhosted.org/packages/f5/fd/2f4bd9433f58f816434bb934313584caa47dbc6f03ce5484df8ac8980561/rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3", size = 416796, upload-time = "2025-11-16T14:50:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/79/a5/449f0281af33efa29d5c71014399d74842342ae908d8cd38260320167692/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9", size = 566843, upload-time = "2025-11-16T14:50:07.243Z" }, - { url = "https://files.pythonhosted.org/packages/ab/32/0a6a1ccee2e37fcb1b7ba9afde762b77182dbb57937352a729c6cd3cf2bb/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8", size = 593956, upload-time = "2025-11-16T14:50:09.029Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3d/eb820f95dce4306f07a495ede02fb61bef36ea201d9137d4fcd5ab94ec1e/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a", size = 557288, upload-time = "2025-11-16T14:50:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f8/b8ff786f40470462a252918e0836e0db903c28e88e3eec66bc4a7856ee5d/rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5", size = 211382, upload-time = "2025-11-16T14:50:12.827Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7f/1a65ae870bc9d0576aebb0c501ea5dccf1ae2178fe2821042150ebd2e707/rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2", size = 225919, upload-time = "2025-11-16T14:50:14.734Z" }, - { url = "https://files.pythonhosted.org/packages/f2/ac/b97e80bf107159e5b9ba9c91df1ab95f69e5e41b435f27bdd737f0d583ac/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d", size = 373963, upload-time = "2025-11-16T14:50:16.205Z" }, - { url = "https://files.pythonhosted.org/packages/40/5a/55e72962d5d29bd912f40c594e68880d3c7a52774b0f75542775f9250712/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3", size = 364644, upload-time = "2025-11-16T14:50:18.22Z" }, - { url = "https://files.pythonhosted.org/packages/99/2a/6b6524d0191b7fc1351c3c0840baac42250515afb48ae40c7ed15499a6a2/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43", size = 393847, upload-time = "2025-11-16T14:50:20.012Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b8/c5692a7df577b3c0c7faed7ac01ee3c608b81750fc5d89f84529229b6873/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf", size = 407281, upload-time = "2025-11-16T14:50:21.64Z" }, - { url = "https://files.pythonhosted.org/packages/f0/57/0546c6f84031b7ea08b76646a8e33e45607cc6bd879ff1917dc077bb881e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe", size = 529213, upload-time = "2025-11-16T14:50:23.219Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c1/01dd5f444233605555bc11fe5fed6a5c18f379f02013870c176c8e630a23/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760", size = 413808, upload-time = "2025-11-16T14:50:25.262Z" }, - { url = "https://files.pythonhosted.org/packages/aa/0a/60f98b06156ea2a7af849fb148e00fbcfdb540909a5174a5ed10c93745c7/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a", size = 394600, upload-time = "2025-11-16T14:50:26.956Z" }, - { url = "https://files.pythonhosted.org/packages/37/f1/dc9312fc9bec040ece08396429f2bd9e0977924ba7a11c5ad7056428465e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0", size = 408634, upload-time = "2025-11-16T14:50:28.989Z" }, - { url = "https://files.pythonhosted.org/packages/ed/41/65024c9fd40c89bb7d604cf73beda4cbdbcebe92d8765345dd65855b6449/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce", size = 426064, upload-time = "2025-11-16T14:50:30.674Z" }, - { url = "https://files.pythonhosted.org/packages/a2/e0/cf95478881fc88ca2fdbf56381d7df36567cccc39a05394beac72182cd62/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec", size = 575871, upload-time = "2025-11-16T14:50:33.428Z" }, - { url = "https://files.pythonhosted.org/packages/ea/c0/df88097e64339a0218b57bd5f9ca49898e4c394db756c67fccc64add850a/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed", size = 601702, upload-time = "2025-11-16T14:50:36.051Z" }, - { url = "https://files.pythonhosted.org/packages/87/f4/09ffb3ebd0cbb9e2c7c9b84d252557ecf434cd71584ee1e32f66013824df/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f", size = 564054, upload-time = "2025-11-16T14:50:37.733Z" }, +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] [[package]] name = "ruff" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, - { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, - { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, - { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, - { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, - { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, - { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, - { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, +version = "0.14.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/5b/dd7406afa6c95e3d8fa9d652b6d6dd17dd4a6bf63cb477014e8ccd3dcd46/ruff-0.14.7.tar.gz", hash = "sha256:3417deb75d23bd14a722b57b0a1435561db65f0ad97435b4cf9f85ffcef34ae5", size = 5727324, upload-time = "2025-11-28T20:55:10.525Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/b1/7ea5647aaf90106f6d102230e5df874613da43d1089864da1553b899ba5e/ruff-0.14.7-py3-none-linux_armv6l.whl", hash = "sha256:b9d5cb5a176c7236892ad7224bc1e63902e4842c460a0b5210701b13e3de4fca", size = 13414475, upload-time = "2025-11-28T20:54:54.569Z" }, + { url = "https://files.pythonhosted.org/packages/af/19/fddb4cd532299db9cdaf0efdc20f5c573ce9952a11cb532d3b859d6d9871/ruff-0.14.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3f64fe375aefaf36ca7d7250292141e39b4cea8250427482ae779a2aa5d90015", size = 13634613, upload-time = "2025-11-28T20:55:17.54Z" }, + { url = "https://files.pythonhosted.org/packages/40/2b/469a66e821d4f3de0440676ed3e04b8e2a1dc7575cf6fa3ba6d55e3c8557/ruff-0.14.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:93e83bd3a9e1a3bda64cb771c0d47cda0e0d148165013ae2d3554d718632d554", size = 12765458, upload-time = "2025-11-28T20:55:26.128Z" }, + { url = "https://files.pythonhosted.org/packages/f1/05/0b001f734fe550bcfde4ce845948ac620ff908ab7241a39a1b39bb3c5f49/ruff-0.14.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3838948e3facc59a6070795de2ae16e5786861850f78d5914a03f12659e88f94", size = 13236412, upload-time = "2025-11-28T20:55:28.602Z" }, + { url = "https://files.pythonhosted.org/packages/11/36/8ed15d243f011b4e5da75cd56d6131c6766f55334d14ba31cce5461f28aa/ruff-0.14.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24c8487194d38b6d71cd0fd17a5b6715cda29f59baca1defe1e3a03240f851d1", size = 13182949, upload-time = "2025-11-28T20:55:33.265Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cf/fcb0b5a195455729834f2a6eadfe2e4519d8ca08c74f6d2b564a4f18f553/ruff-0.14.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79c73db6833f058a4be8ffe4a0913b6d4ad41f6324745179bd2aa09275b01d0b", size = 13816470, upload-time = "2025-11-28T20:55:08.203Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5d/34a4748577ff7a5ed2f2471456740f02e86d1568a18c9faccfc73bd9ca3f/ruff-0.14.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:12eb7014fccff10fc62d15c79d8a6be4d0c2d60fe3f8e4d169a0d2def75f5dad", size = 15289621, upload-time = "2025-11-28T20:55:30.837Z" }, + { url = "https://files.pythonhosted.org/packages/53/53/0a9385f047a858ba133d96f3f8e3c9c66a31cc7c4b445368ef88ebeac209/ruff-0.14.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c623bbdc902de7ff715a93fa3bb377a4e42dd696937bf95669118773dbf0c50", size = 14975817, upload-time = "2025-11-28T20:55:24.107Z" }, + { url = "https://files.pythonhosted.org/packages/a8/d7/2f1c32af54c3b46e7fadbf8006d8b9bcfbea535c316b0bd8813d6fb25e5d/ruff-0.14.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f53accc02ed2d200fa621593cdb3c1ae06aa9b2c3cae70bc96f72f0000ae97a9", size = 14284549, upload-time = "2025-11-28T20:55:06.08Z" }, + { url = "https://files.pythonhosted.org/packages/92/05/434ddd86becd64629c25fb6b4ce7637dd52a45cc4a4415a3008fe61c27b9/ruff-0.14.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:281f0e61a23fcdcffca210591f0f53aafaa15f9025b5b3f9706879aaa8683bc4", size = 14071389, upload-time = "2025-11-28T20:55:35.617Z" }, + { url = "https://files.pythonhosted.org/packages/ff/50/fdf89d4d80f7f9d4f420d26089a79b3bb1538fe44586b148451bc2ba8d9c/ruff-0.14.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:dbbaa5e14148965b91cb090236931182ee522a5fac9bc5575bafc5c07b9f9682", size = 14202679, upload-time = "2025-11-28T20:55:01.472Z" }, + { url = "https://files.pythonhosted.org/packages/77/54/87b34988984555425ce967f08a36df0ebd339bb5d9d0e92a47e41151eafc/ruff-0.14.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1464b6e54880c0fe2f2d6eaefb6db15373331414eddf89d6b903767ae2458143", size = 13147677, upload-time = "2025-11-28T20:55:19.933Z" }, + { url = "https://files.pythonhosted.org/packages/67/29/f55e4d44edfe053918a16a3299e758e1c18eef216b7a7092550d7a9ec51c/ruff-0.14.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f217ed871e4621ea6128460df57b19ce0580606c23aeab50f5de425d05226784", size = 13151392, upload-time = "2025-11-28T20:55:21.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/69/47aae6dbd4f1d9b4f7085f4d9dcc84e04561ee7ad067bf52e0f9b02e3209/ruff-0.14.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6be02e849440ed3602d2eb478ff7ff07d53e3758f7948a2a598829660988619e", size = 13412230, upload-time = "2025-11-28T20:55:12.749Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4b/6e96cb6ba297f2ba502a231cd732ed7c3de98b1a896671b932a5eefa3804/ruff-0.14.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19a0f116ee5e2b468dfe80c41c84e2bbd6b74f7b719bee86c2ecde0a34563bcc", size = 14195397, upload-time = "2025-11-28T20:54:56.896Z" }, + { url = "https://files.pythonhosted.org/packages/69/82/251d5f1aa4dcad30aed491b4657cecd9fb4274214da6960ffec144c260f7/ruff-0.14.7-py3-none-win32.whl", hash = "sha256:e33052c9199b347c8937937163b9b149ef6ab2e4bb37b042e593da2e6f6cccfa", size = 13126751, upload-time = "2025-11-28T20:55:03.47Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b5/d0b7d145963136b564806f6584647af45ab98946660d399ec4da79cae036/ruff-0.14.7-py3-none-win_amd64.whl", hash = "sha256:e17a20ad0d3fad47a326d773a042b924d3ac31c6ca6deb6c72e9e6b5f661a7c6", size = 14531726, upload-time = "2025-11-28T20:54:59.121Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d2/1637f4360ada6a368d3265bf39f2cf737a0aaab15ab520fc005903e883f8/ruff-0.14.7-py3-none-win_arm64.whl", hash = "sha256:be4d653d3bea1b19742fcc6502354e32f65cd61ff2fbdb365803ef2c2aec6228", size = 13609215, upload-time = "2025-11-28T20:55:15.375Z" }, ] [[package]] @@ -4075,15 +4074,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - [[package]] name = "sortedcontainers" version = "2.4.0"