diff --git a/.github/workflows/build_linux_2_17.yml b/.github/workflows/build_linux_2_17.yml index 42fb72ece93244220fd6ea79f4bfc094151d65e4..678d477a04d211bbfb021c9933da2ce2429396be 100644 --- a/.github/workflows/build_linux_2_17.yml +++ b/.github/workflows/build_linux_2_17.yml @@ -15,8 +15,8 @@ jobs: include: - os: ubuntu-latest # 测试通过 arch: x86_64 - - os: ubuntu-latest # 测试通过 - arch: i686 +# - os: ubuntu-latest # 测试通过 +# arch: i686 runs-on: ${{ matrix.os }} diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index 2a08f09adc4c70102736d4bd400d21864339a4e0..bb7a280a56b2fb9f2c9fe610956c8a214b00902e 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -15,7 +15,7 @@ jobs: include: - os: macos-latest # 测试通过 arch: arm64 - - os: macos-12 # 测试通过 + - os: macos-13 # 测试通过 arch: x86_64 runs-on: ${{ matrix.os }} @@ -42,12 +42,8 @@ jobs: - name: Build wheels with cibuildwheel env: - CIBW_ARCHS_LINUX: ${{ matrix.arch }} - CIBW_ARCHS_WINDOWS: ${{ matrix.arch }} CIBW_ARCHS_MACOS: ${{ matrix.arch }} -# CIBW_BUILD: cp31-* CIBW_ENVIRONMENT_WINDOWS: "CFLAGS=/openmp" - CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10" CIBW_BUILD_VERBOSITY: 1 MACOSX_DEPLOYMENT_TARGET: 12.0 CIBW_MACOSX_DEPLOYMENT_TARGET: 12.0 diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index 8a8e8bc571db60436ba297d16cf4d8ccabda4f36..f8b2d68b6a7e5e6525f191b761d5d6e4126744ee 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -7,50 +7,61 @@ on: branches: [ main ] jobs: - build_wheels_windows_x86: - environment: release - strategy: - fail-fast: false - matrix: - include: - - os: windows-latest - arch: x86 -# - os: windows-latest # 不支持,不折腾了 -# arch: ARM64 - - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install cibuildwheel - run: pip install -U cibuildwheel delocate numpy - - - name: Build wheels with cibuildwheel - shell: cmd - env: - CIBW_ENVIRONMENT_WINDOWS: > - PATH="C:\\mingw32\\bin;$PATH" - CC="C:/mingw32/bin/gcc.exe" - LD="C:/mingw32/bin/ld.exe" - CIBW_ARCHS_WINDOWS: ${{ matrix.arch }} -# CIBW_BUILD: cp31?-* - CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10" - CIBW_BUILD_VERBOSITY: 1 - run: | - where gcc - gcc --version - where ld - ld --version - python -m cibuildwheel --output-dir wheelhouse --archs ${{ matrix.arch }} - - - name: Upload built wheels - uses: actions/upload-artifact@v4 - with: - name: wheels-${{ matrix.os }}-${{ matrix.arch }} - path: wheelhouse/*.whl - +# build_wheels_windows_x86: +# environment: release +# strategy: +# fail-fast: false +# matrix: +# include: +# - os: windows-latest +# arch: x86 +## - os: windows-latest # 不支持,不折腾了 +## arch: ARM64 +# +# runs-on: ${{ matrix.os }} +# +# steps: +# - name: Checkout source code +# uses: actions/checkout@v4 +# +# - name: Set up Python +# uses: actions/setup-python@v5 +# id: setpy +# with: +# python-version: | +# 3.10.11 +# 3.11.9 +# 3.12.9 +# 3.13.2 +# architecture: 'x86' +# - run: echo '${{ steps.setpy.outputs }}' +# +# - name: Install cibuildwheel +# run: pip install -U cibuildwheel delocate +# +# - name: Build wheels with cibuildwheel +# shell: cmd +# env: +# CIBW_ENVIRONMENT_WINDOWS: > +# PATH="C:\\mingw32\\bin;$PATH" +# CC="C:/mingw32/bin/gcc.exe" +# LD="C:/mingw32/bin/ld.exe" +# CIBW_ARCHS_WINDOWS: ${{ matrix.arch }} +# CIBW_PROJECT_REQUIRES_PYTHON: '>=3.10' +# CIBW_BUILD_VERBOSITY: 1 +# CIBW_PYTHON_HOME: ${{ steps.setpy.outputs.python-path }} +# run: | +# where gcc +# gcc --version +# where ld +# ld --version +# python -m cibuildwheel --output-dir wheelhouse --archs ${{ matrix.arch }} +# +# - name: Upload built wheels +# uses: actions/upload-artifact@v4 +# with: +# name: wheels-${{ matrix.os }}-${{ matrix.arch }} +# path: wheelhouse/*.whl build_wheels_windows_amd64: environment: release @@ -71,15 +82,26 @@ jobs: - name: Checkout source code uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + id: setpy + with: + python-version: | + 3.10.11 + 3.11.9 + 3.12.9 + 3.13.2 + - run: echo '${{ steps.setpy.outputs }}' + - name: Install cibuildwheel - run: pip install -U cibuildwheel delocate numpy + run: pip install -U cibuildwheel delocate - name: Build wheels with cibuildwheel env: CIBW_ARCHS_WINDOWS: ${{ matrix.arch }} -# CIBW_BUILD: cp31?-* - CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10" + CIBW_PROJECT_REQUIRES_PYTHON: '>=3.10' CIBW_BUILD_VERBOSITY: 1 + CIBW_PYTHON_HOME: ${{ steps.setpy.outputs.python-path }} run: cibuildwheel --output-dir wheelhouse - name: Upload built wheels diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 587dc46f34b872f8365d8d38c7c9c72b262018ac..663002ad1824f6dd951cbaed0e39c8d5d86ef94a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,6 @@ repos: hooks: - id: pylint name: pylint - entry: poetry run pylint + entry: pylint language: system types: [python] diff --git a/cqlib/VERSION.txt b/cqlib/VERSION.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb174d58a5349fd5df9dfd2014d74225d1756780 --- /dev/null +++ b/cqlib/VERSION.txt @@ -0,0 +1 @@ +1.2.1 \ No newline at end of file diff --git a/cqlib/__init__.py b/cqlib/__init__.py index c3e0aa5f1f42d7445ecf718e79f364016d25637e..909f2a71730c0fc4d85a33ce773f08e87596af30 100644 --- a/cqlib/__init__.py +++ b/cqlib/__init__.py @@ -14,7 +14,7 @@ """cqlib""" from cqlib.circuits import Circuit, Barrier, Qubit, Measure, Parameter, Instruction, \ - InstructionData, gates, CouplingQubit, Pulse, PulseParameter, Waveform + InstructionData, gates from cqlib.circuits import dag_to_circuit, circuit_to_dag from cqlib.quantum_platform import BasePlatform, TianYanPlatform, GuoDunPlatform, QuantumLanguage diff --git a/cqlib/_version.py b/cqlib/_version.py index 14d2bf1c73c8e7e27b556721505cdb54d827e7fa..09cafa4b52b5feeda3c3f1dbed5202eecf8f75b1 100644 --- a/cqlib/_version.py +++ b/cqlib/_version.py @@ -13,7 +13,7 @@ """Version number""" - +import os import sys if sys.version_info < (3, 10, 0): # pragma: no cover @@ -23,7 +23,11 @@ if sys.version_info < (3, 10, 0): # pragma: no cover "You can download the latest version of Python at https://www.python.org/downloads/\n" ) +ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) +with open(os.path.join(ROOT_DIR, "VERSION.txt")) as version_file: + VERSION = version_file.read().strip() + version: str __version__: str -__version__ = version = '1.2.1' +__version__ = version = VERSION diff --git a/cqlib/circuits/__init__.py b/cqlib/circuits/__init__.py index 97541c4c317066088274f2b9f4f03967f4ab0ea6..4a60287927c79d2c27fd974f85b959f6cb2810d8 100644 --- a/cqlib/circuits/__init__.py +++ b/cqlib/circuits/__init__.py @@ -15,15 +15,11 @@ from .barrier import Barrier from .circuit import Circuit -from .coupling_qubit import CouplingQubit from .instruction_data import InstructionData from .measure import Measure from .parameter import Parameter from .qubit import Qubit from .instruction import Instruction from .dag import dag_to_circuit, circuit_to_dag -from .pulse.pulse_parameter import PulseParameter -from .pulse.waveform import Waveform -from .pulse.pulse import Pulse from .commutation import circuit_commutator, check_commutation, commute diff --git a/cqlib/circuits/bit.py b/cqlib/circuits/bit.py new file mode 100644 index 0000000000000000000000000000000000000000..30a8c85314aad34916aa88b36381c23f833fe5cf --- /dev/null +++ b/cqlib/circuits/bit.py @@ -0,0 +1,70 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" quantum bit base class""" + +from __future__ import annotations + + +class Bit: + """Quantum bit.""" + __slots__ = ["_index", '_initialized', '_hash'] + + def __init__(self, index: int): + """ + Initialize a new Qubit instance. + + Args: + index: logical index of the qubit + """ + if index < 0: + raise ValueError("Index must be non-negative.") + if not hasattr(self, '_initialized'): + self._index = index + self._hash = None + self._initialized = True + + @property + def index(self) -> int: + """Returns the logical index of the qubit.""" + return self._index + + def __repr__(self): + return f"{self.__class__.__name__}({self.index})" + + def __str__(self): + return f'Bit{self.index}' + + def __copy__(self): + """ + Returns a reference to the same qubit instance since qubits + should be unique. + """ + return self + + def __deepcopy__(self, memo=None): + return self + + def __eq__(self, other: Bit) -> bool: + """Check equality with another qubit based on the index.""" + return (isinstance(other, self.__class__) and + self.index == other.index) + + def __hash__(self) -> int: + """ + Return the hash based on the qubit's index, used for collections + that depend on hashable items. + """ + if self._hash is None: + self._hash = hash(f"{self.__class__.__name__}({self._index})") + return self._hash diff --git a/cqlib/circuits/circuit.py b/cqlib/circuits/circuit.py index 75f0e525caf867f5266031ac748acecd8c043f65..a9a6920f55528d7e3e180d2e27492a9e14c47e77 100644 --- a/cqlib/circuits/circuit.py +++ b/cqlib/circuits/circuit.py @@ -27,9 +27,6 @@ from cqlib.circuits.instruction_data import InstructionData from cqlib.circuits.measure import Measure from cqlib.circuits.parameter import Parameter from cqlib.circuits.qubit import Qubit -from cqlib.circuits.coupling_qubit import CouplingQubit -from cqlib.circuits.pulse.pulse import Pulse -from cqlib.circuits.pulse.pulse_parameter import PulseParameter # Type Alias Definition Qubits = Union[Qubit, int, Sequence[Union[Qubit, int]]] @@ -51,7 +48,7 @@ class Circuit: """ self._qubits: dict[str, Qubit] = self._initialize_qubits(qubits) self._parameters: dict[Parameter, Optional[float]] = self._initialize_parameters(parameters) - self._instruction_sequence: list[InstructionData] = [] + self._circuit_data: list[InstructionData] = [] @staticmethod def _initialize_parameters( @@ -111,13 +108,17 @@ class Circuit: """Return number of qubits.""" return len(self._qubits) + def circuit_data(self): + """Circuit data""" + return self._circuit_data + def qubits_path(self) -> dict[Qubit, list[InstructionData]]: """ Constructs a path of operations for each qubit. """ path = defaultdict(list) max_depths = defaultdict(int) - for op in self._instruction_sequence: + for op in self._circuit_data: instruction = op.instruction qubits = op.qubits if instruction.num_qubits != len(qubits): @@ -196,7 +197,7 @@ class Circuit: list[InstructionData]: The sequence of instructions added to the circuit, each represented by an InstructionData object. """ - return self._instruction_sequence + return self._circuit_data def insert(self, instruction: Instruction, qubits: Qubits, index: int = None): """ @@ -210,14 +211,14 @@ class Circuit: """ instruction, qubits = self._prepare_instruction(instruction, qubits) if index is None: - index = len(self._instruction_sequence) + index = len(self._circuit_data) elif not isinstance(index, int): raise TypeError("Index must be an integer or None.") - elif index < 0 or index > len(self._instruction_sequence): + elif index < 0 or index > len(self._circuit_data): raise ValueError(f"Index {index} out of bounds for instructions list of length " - f"{len(self._instruction_sequence)}.") + f"{len(self._circuit_data)}.") - self._instruction_sequence.insert( + self._circuit_data.insert( index, InstructionData(instruction=instruction, qubits=qubits) ) @@ -231,7 +232,7 @@ class Circuit: qubits (Qubits): The qubit(s) to which the instruction is applied. """ instruction, qubits = self._prepare_instruction(instruction, qubits) - self._instruction_sequence.append(InstructionData(instruction=instruction, qubits=qubits)) + self._circuit_data.append(InstructionData(instruction=instruction, qubits=qubits)) def append_instruction_data(self, instruction_data: InstructionData): """ @@ -240,7 +241,7 @@ class Circuit: Args: instruction_data (InstructionData): The instruction_data to be appended. """ - self._instruction_sequence.append(instruction_data) + self._circuit_data.append(instruction_data) def _prepare_instruction(self, instruction: Instruction, qubits: Qubits): """ @@ -266,8 +267,8 @@ class Circuit: for q in qubits: if isinstance(q, int): q = Qubit(q) - if not isinstance(q, (Qubit, CouplingQubit)): - raise TypeError("Qubit must be an instance of QuBit CouplingQubit or an integer.") + if not isinstance(q, Qubit): + raise TypeError(f"{q} must be an instance of QuBit or an integer.") if str(q) not in self._qubits: raise ValueError(f"{q} not found in circuit.") qs.append(q) @@ -281,7 +282,7 @@ class Circuit: for param in instruction.params: if isinstance(param, str): param = Parameter(param) - if not isinstance(param, (Parameter, PulseParameter, float, int)): + if not isinstance(param, (Parameter, float, int)): raise TypeError("Parameter must be a float, int, str, or an instance " "of Parameter or PulseParameter.") if isinstance(param, Parameter): @@ -616,18 +617,6 @@ class Circuit: """ self.append(Barrier(len(qubits)), list(qubits)) - def pulse(self, qubit: Qubit | CouplingQubit, pulse_param: PulseParameter): - """ - - Args: - qubit (Qubit or CouplingQubit): The qubits to measure, can be a single - qubit or a list of qubits. - pulse_param (PulseParameter): - """ - if not isinstance(qubit, (Qubit, CouplingQubit)): - raise TypeError("pulse") - self.append(Pulse(pulse_param), [qubit]) - def measure(self, qubits: Qubits): """ Measures the specified qubits and collapses their quantum state into classical bits. @@ -646,14 +635,12 @@ class Circuit: Measures all qubits in the circuit that have not yet been measured. """ measured_qubits = set() - for ins in self._instruction_sequence: + for ins in self._circuit_data: if isinstance(ins.instruction, Measure): for qubit in ins.qubits: measured_qubits.add(qubit) for _, qubit in self._qubits.items(): - if isinstance(qubit, CouplingQubit): - continue if qubit not in measured_qubits: self.append(Measure(), qubit) @@ -739,7 +726,7 @@ class Circuit: """ Generates a qcis string of all instructions in the circuit. """ - return self._export_circuit_str(self._instruction_sequence, True, self._parameters) + return self._export_circuit_str(self._circuit_data, True, self._parameters) def as_str(self, qcis_compliant: bool = False): """ @@ -750,7 +737,7 @@ class Circuit: if False, it will retain the original format with composite gates. """ params = self._parameters - return self._export_circuit_str(self._instruction_sequence, qcis_compliant, params) + return self._export_circuit_str(self._circuit_data, qcis_compliant, params) @classmethod def _export_circuit_str( @@ -794,69 +781,91 @@ class Circuit: ops.append(' '.join(line)) return '\n'.join(ops) - @staticmethod - def load(qcis: str) -> Circuit: + @classmethod + def load(cls, qcis: str) -> Circuit: """ - Loads a quantum circuit from a formatted string. + Loads a quantum circuit from a QCIS string. - Args: - qcis (str): A string containing quantum circuit instructions, where each line - represents a circuit operation. The format for each line is - "GATE QUBITS [PARAMETERS]", e.g., "H Q0 Q1", "CX Q0 Q1", "RZ Q0 0.5". + Args: + qcis (str): A string containing quantum circuit instructions, where each line + represents a circuit operation. The format for each line is + "GATE QUBITS [PARAMETERS]", e.g., "H Q0 Q1", "CX Q0 Q1", "RZ Q0 0.5". - Returns: - Circuit: A quantum circuit object constructed based on the input string. + Returns: + Circuit: A quantum circuit object constructed based on the input string. - Raises: - ValueError: If the input string is improperly formatted or contains - unknown gate operations. + Raises: + ValueError: If the input string is improperly formatted or contains + unknown gate operations. - Example: - >>> circuit_description = "H Q0\\nCX Q0 Q1\\nM Q0" - >>> c = Circuit.load(circuit_description) - >>> print(c) - Circuit with 3 instructions - """ - circuit = Circuit(0) + Example: + >>> circuit_description = "H Q0\\nCX Q0 Q1\\nM Q0" + >>> c = Circuit.load(circuit_description) + >>> print(c) + Circuit with 3 instructions + """ + circuit = cls(qubits=[]) line_pattern = re.compile(r'^([A-Z][A-Z0-9]*)\s+((?:Q[0-9]+\s*)+)' r'((?:[+-]?(?:\d*\.\d+|\d+)(?:[Ee][+-]?\d+)?\s*)*)$') for line in qcis.split('\n'): - line = line.strip() + # Delete comments `#` `//`. + line = re.sub(r'(#|//).*', '', line).strip() if not line: continue - if line.startswith('PLS ') or line.startswith('PULSE '): - pls, coupling_qubit = Pulse.load(line) - if str(coupling_qubit) not in circuit.qubits: - circuit.add_qubit(coupling_qubit) - circuit.append(instruction=pls, qubits=coupling_qubit) - continue match = line_pattern.match(line) if not match: - raise ValueError(f'Invalid line format: {line}') - + raise ValueError(f'Invalid instruction format: {line}') gate, qubits_str, params_str = match.groups() - qubits = [circuit._qubits.setdefault(str(Qubit(int(q[1:]))), Qubit(int(q[1:]))) - for q in qubits_str.split()] + qubits = circuit._parse_qubits_str(qubits_str) params = [float(p) for p in params_str.split()] if params_str else [] + circuit._process_instruction(gate, qubits, params) + return circuit - if gate == 'M': - for qubit in qubits: - circuit.append_instruction_data( - InstructionData(instruction=Measure(), qubits=[qubit])) - continue - if gate == 'B': - circuit.append_instruction_data( - InstructionData(instruction=Barrier(len(qubits)), qubits=qubits)) - continue + def _parse_qubits_str(self, qubits_str: str) -> list[Qubit]: + """ + Parses and initializes standard qubit identifiers from QCIS instruction. + + Converts space-separated qubit tokens (Q-prefixed) into Qubit objects, + creating new instances only when encountering previously unseen qubits. + + Args: + qubits_str(str): Raw qubit specification segment from QCIS line. + + Returns: + list[Qubit]: Initialized Qubit objects in order of appearance. + """ + qubits = [] + for q_str in qubits_str.split(): + if q_str.startswith('Q'): + qubit = self._qubits.setdefault(q_str, Qubit(int(q_str[1:]))) + qubits.append(qubit) + else: + raise ValueError(f"Invalid qubit format: {q_str}") + return qubits - if not hasattr(gates, gate): - raise ValueError(f'Unknown gate: {gate}') + def _process_instruction(self, gate: str, qubits: list, params: list): + """ + Core dispatcher for converting parsed components into circuit operations. + + Args: + gate: Uppercase gate identifier (e.g., 'H', 'CX', 'RZ') + qubits: Qubit targets from prior parsing stage + params: Numerical parameters for parameterized gates + """ + if gate == 'M': + for qubit in qubits: + self.append_instruction_data( + InstructionData(instruction=Measure(), qubits=[qubit])) + elif gate == 'B': + self.append_instruction_data( + InstructionData(instruction=Barrier(len(qubits)), qubits=qubits)) + elif hasattr(gates, gate): data = InstructionData(instruction=getattr(gates, gate)(*params, label=None), qubits=qubits) - circuit.append_instruction_data(data) - - return circuit + self.append_instruction_data(data) + else: + raise ValueError(f"Unsupported gate: {gate}") def __str__(self): return self.as_str() @@ -931,6 +940,6 @@ class Circuit: Copy the circuit. """ circuit = Circuit(qubits=self.qubits, parameters=self.parameters) - for item in self._instruction_sequence: + for item in self._circuit_data: circuit.append(item.instruction.copy(), qubits=item.qubits) return circuit diff --git a/cqlib/circuits/commutation/circuit_commutate.py b/cqlib/circuits/commutation/circuit_commutate.py index 186cfc3ee2f699fab2e53ff0dcd2d087c18b7b66..5da33e87faaa00608807b2504b72896fb32fef0c 100644 --- a/cqlib/circuits/commutation/circuit_commutate.py +++ b/cqlib/circuits/commutation/circuit_commutate.py @@ -13,7 +13,7 @@ """ Quantum circuit commutation checker """ -import networkx as nx +from rustworkx.rustworkx import topological_sort from cqlib.circuits.circuit import Circuit from cqlib.circuits.instruction_data import InstructionData @@ -24,8 +24,11 @@ from .commutation import check_commutation from ._gates_commutations import gates_commutations -def circuit_commutator(circuit: Circuit, use_cache: bool = True, cache_commutations: dict = None) \ - -> list[tuple[InstructionData, InstructionData]]: +def circuit_commutator( + circuit: Circuit, + use_cache: bool = True, + cache_commutations: dict = None +) -> list[tuple[InstructionData, InstructionData]]: """ Check whether there is a commutation relationship between consecutive instructions in the circuit. @@ -42,8 +45,9 @@ def circuit_commutator(circuit: Circuit, use_cache: bool = True, cache_commutati cache_commutations = gates_commutations dag = circuit_to_dag(circuit) data = [] - for node in nx.topological_sort(dag): - for next_node in dag[node]: + for node_idx in topological_sort(dag): + node = dag.get_node_data(node_idx) + for next_node in dag.successors(node_idx): if use_cache: res = query_commute(node, next_node, cache_commutations) if res is not None: diff --git a/cqlib/circuits/dag.py b/cqlib/circuits/dag.py index f25c93db66121b72a868ac16579efbeccf32c6e1..9484d33f2f26f2fe042ff7101deffd76a1dc43ac 100644 --- a/cqlib/circuits/dag.py +++ b/cqlib/circuits/dag.py @@ -13,14 +13,15 @@ """Circuits as Directed Acyclic Graphs.""" -import networkx as nx - +# pylint: disable=E0611 +from rustworkx import PyDiGraph, topological_sort, DAGHasCycle, NodeIndices from cqlib.circuits.circuit import Circuit from cqlib.circuits.instruction_data import InstructionData from cqlib.circuits.parameter import Parameter +from cqlib.exceptions import CqlibError -def circuit_to_dag(circuit: Circuit) -> nx.DiGraph: +def circuit_to_dag(circuit: Circuit) -> PyDiGraph: """ Convert a quantum circuit into a Directed Acyclic Graph (DAG). @@ -35,22 +36,24 @@ def circuit_to_dag(circuit: Circuit) -> nx.DiGraph: Returns: nx.DiGraph: The directed acyclic graph representation of the circuit. """ - dag = nx.DiGraph() + dag = PyDiGraph(check_cycle=True) qubit_last_nodes = {} - + node_ids = {} for op in circuit.instruction_sequence: if not isinstance(op, InstructionData): raise TypeError(f"{op} must be instance of InstructionData") - dag.add_node(op, label=str(op)) + node_id = dag.add_node(op) + node_ids[op] = node_id for qubit in op.qubits: if qubit in qubit_last_nodes: - dag.add_edge(qubit_last_nodes[qubit], op) + last_node = qubit_last_nodes[qubit] + dag.add_edge(node_ids[last_node], node_id, f'{last_node}-{op}') qubit_last_nodes[qubit] = op return dag -def dag_to_circuit(dag: nx.DiGraph) -> Circuit: +def dag_to_circuit(dag: PyDiGraph) -> Circuit: """ Converts a Directed Acyclic Graph (DAG) back into a quantum circuit. The DAG is expected to have nodes representing quantum operations @@ -63,15 +66,17 @@ def dag_to_circuit(dag: nx.DiGraph) -> Circuit: Returns: Circuit: A quantum circuit reconstructed from the DAG. """ - if not nx.is_directed_acyclic_graph(dag): - raise ValueError("The provided graph must be acyclic to form a valid quantum circuit.") - + try: + topological_order = topological_sort(dag) + except DAGHasCycle as e: + raise CqlibError("The provided graph must be acyclic to form a valid" + " quantum circuit.") from e circuit = Circuit(0) - topological_order: list[InstructionData] = list(nx.topological_sort(dag)) - for node in topological_order: + for index in topological_order: + node = dag.get_node_data(index) if not isinstance(node, InstructionData): - raise ValueError(f"{node} in the DAG must be instance of InstructionData") + raise CqlibError(f"{node} in the DAG must be instance of InstructionData") for qubit in node.qubits: if qubit not in circuit.qubits: circuit.add_qubit(qubit) @@ -81,3 +86,34 @@ def dag_to_circuit(dag: nx.DiGraph) -> Circuit: circuit.append_instruction_data(node) return circuit + + +def topological_layers(graph: PyDiGraph) -> list[list[NodeIndices | int]]: + """ + Perform topological sorting to decompose a directed acyclic graph + (DAG) into layers of nodes. + + This function implements a Kahn's algorithm-based approach to determine + the hierarchical layers of nodes in a DAG. Each layer contains nodes that + can be processed simultaneously in dependency resolution scenarios. + + Args: + graph (PyDiGraph): The input directed acyclic graph represented as + a `PyDiGraph` object from the `rustworkx` library. + + Returns: + list[list[NodeIndices]]: A list of lists where each inner list represents + a layer of node IDs. + """ + node_in_degree = {node: graph.in_degree(node) for node in graph.node_indices()} + front_layer = [node_id for node_id, in_degree in node_in_degree.items() if in_degree == 0] + + while front_layer: + yield front_layer + next_layer = [] + for node_id in front_layer: + for successor in graph.successor_indices(node_id): + node_in_degree[successor] -= 1 + if node_in_degree[successor] == 0: + next_layer.append(successor) + front_layer = next_layer diff --git a/cqlib/circuits/gates/gate.py b/cqlib/circuits/gates/gate.py index 9ee30b07c705ca92eeb7cb7d035b26569706dc96..36c54459de24065b0f157ac60e7067bde01b9c5b 100644 --- a/cqlib/circuits/gates/gate.py +++ b/cqlib/circuits/gates/gate.py @@ -66,11 +66,13 @@ class ControlledGate(Gate): This class represents a quantum gate that has control qubits in addition to target qubits. """ - # pylint: disable=useless-parent-delegation + # pylint: disable=useless-parent-delegation,too-many-arguments,too-many-positional-arguments def __init__( self, name: str, num_qubits: int, + control_index: list[int], + base_gate: Gate, params: List[Union[Parameter, float, complex]], label: Optional[str] = None ): @@ -80,10 +82,17 @@ class ControlledGate(Gate): Args: name (str): The name of the controlled gate. num_qubits (int): The number of qubits the gate acts on. + control_index (list[int]): A list of indices specifying the control qubits + for the gate. Each index represents a qubit within the range of + `[0, num_qubits - 1]` that acts as a control for the gate operation. + base_gate (Gate): Gate object to be controlled. params (list[Parameter | float | complex]): A list of parameters for the gate. label (str | None, optional): An optional label for the gate. Defaults to None. """ super().__init__(name, num_qubits, params, label) + self.control_index = control_index + self.base_gate = base_gate + assert all(map(lambda s: s < self.num_qubits, self.control_index)) def __array__(self, dtype=None): """ diff --git a/cqlib/circuits/gates/rx.py b/cqlib/circuits/gates/rx.py index 30ee91d6e2ce8b4d21400a55a39abaf17b2701f0..3834942cdb7bd5750a7b9258551034b251e2886b 100644 --- a/cqlib/circuits/gates/rx.py +++ b/cqlib/circuits/gates/rx.py @@ -76,7 +76,14 @@ class CRX(ControlledGate): theta (float | Parameter): The rotation angle in radians around the X-axis. label (str | None, optional): An optional label for the CRX gate. Defaults to None. """ - super().__init__('CRX', 2, [theta], label=label) + super().__init__( + 'CRX', + 2, + control_index=[0], + base_gate=RX(theta), + params=[theta], + label=label + ) def __array__(self, dtype=np.complex128): """ diff --git a/cqlib/circuits/gates/ry.py b/cqlib/circuits/gates/ry.py index ed5707076f178ab03fcfc3f40cbfd28af094a787..e2a02ffc020dcf8f31f2b267fa8d5ac48622c3de 100644 --- a/cqlib/circuits/gates/ry.py +++ b/cqlib/circuits/gates/ry.py @@ -75,7 +75,14 @@ class CRY(ControlledGate): theta (float | Parameter): The rotation angle in radians around the Y-axis. label (str | None, optional): An optional label for the CRY gate. Defaults to None. """ - super().__init__('CRY', 2, [theta], label=label) + super().__init__( + 'CRY', + 2, + control_index=[0], + base_gate=RY(theta), + params=[theta], + label=label + ) def __array__(self, dtype=np.complex128): """ diff --git a/cqlib/circuits/gates/rz.py b/cqlib/circuits/gates/rz.py index 5769a5f97a2329122e61f26aa4f603e2c62310ca..bc05321b0c3fb521dfbffa3c5acbd9c53156c7b0 100644 --- a/cqlib/circuits/gates/rz.py +++ b/cqlib/circuits/gates/rz.py @@ -75,7 +75,14 @@ class CRZ(ControlledGate): theta (float | Parameter): The rotation angle in radians around the Z-axis. label (str | None, optional): An optional label for the CRZ gate. Defaults to None. """ - super().__init__('CRZ', 2, [theta], label=label) + super().__init__( + 'CRZ', + 2, + control_index=[0], + base_gate=RZ(theta), + params=[theta], + label=label + ) def __array__(self, dtype=np.complex128): """ diff --git a/cqlib/circuits/gates/x.py b/cqlib/circuits/gates/x.py index fed8c1301239534211db48defc3db9f6e6dd7883..e14df5c40e76997cca0080937cd38523b0dceab7 100644 --- a/cqlib/circuits/gates/x.py +++ b/cqlib/circuits/gates/x.py @@ -89,7 +89,14 @@ class CX(ControlledGate): Args: label (str | None, optional): An optional label for the CX gate. Defaults to None. """ - super().__init__('CX', 2, [], label=label) + super().__init__( + 'CX', + 2, + control_index=[0], + base_gate=X(), + params=[], + label=label + ) def __array__(self, dtype=np.complex128): """ @@ -165,7 +172,8 @@ class CCX(ControlledGate): label (str | None, optional): An optional label for the CCX gate. Defaults to None. """ - super().__init__('CCX', 3, [], label=label) + super().__init__('CCX', 3, control_index=[0, 1], + base_gate=X(), params=[], label=label) def __array__(self, dtype=np.complex128): """ diff --git a/cqlib/circuits/gates/y.py b/cqlib/circuits/gates/y.py index e07778f462abec51927ee4cb1c8e162a1f68a35a..ccb1cf19f2d85a6db14d2cd0726bdc0874ca4669 100644 --- a/cqlib/circuits/gates/y.py +++ b/cqlib/circuits/gates/y.py @@ -79,7 +79,8 @@ class CY(ControlledGate): Args: label (str | None, optional): An optional label for the CY gate. Defaults to None. """ - super().__init__('CY', 2, [], label=label) + super().__init__('CY', 2, control_index=[0], + base_gate=Y(), params=[], label=label) def __array__(self, dtype: np.dtype = np.complex128): """ diff --git a/cqlib/circuits/gates/z.py b/cqlib/circuits/gates/z.py index ee71a3c39fa068e980e231a67a1c0801f9d111bf..a5018716ae46259bf631f3a2deb2b311e5988c52 100644 --- a/cqlib/circuits/gates/z.py +++ b/cqlib/circuits/gates/z.py @@ -77,7 +77,8 @@ class CZ(ControlledGate): Args: label (str | None, optional): An optional label for the CZ gate. Defaults to None. """ - super().__init__('CZ', 2, [], label=label) + super().__init__('CZ', 2, control_index=[0], + base_gate=Z(), params=[], label=label) def __array__(self, dtype=np.complex128): """ diff --git a/cqlib/circuits/instruction.py b/cqlib/circuits/instruction.py index 2bfacf8ce295c36301c4d922fb38ac0d6069c744..34b31b86f73b2721f0cad2904694ee90d4441c10 100644 --- a/cqlib/circuits/instruction.py +++ b/cqlib/circuits/instruction.py @@ -17,7 +17,6 @@ from __future__ import annotations from copy import copy from typing import List, Optional, Union, Sequence -from cqlib.circuits.pulse.pulse_parameter import PulseParameter from .parameter import Parameter @@ -31,7 +30,7 @@ class Instruction: self, name: str, num_qubits: int, - params: Optional[List[Union[Parameter, 'PulseParameter', float, complex]]] = None, + params: Optional[List[Union[Parameter, float, complex]]] = None, label: Optional[str] = None ): """ diff --git a/cqlib/circuits/instruction_data.py b/cqlib/circuits/instruction_data.py index 0a875789c380260c3a5b6f4067f7ad11aefe533e..bf9804ffa6cfe1ff8326a7342a7fbc5c14dbefbb 100644 --- a/cqlib/circuits/instruction_data.py +++ b/cqlib/circuits/instruction_data.py @@ -15,7 +15,7 @@ from typing import NamedTuple, Sequence -from cqlib.circuits.qubit import Qubit +from cqlib.circuits.bit import Bit from cqlib.circuits.instruction import Instruction @@ -25,7 +25,7 @@ class InstructionData(NamedTuple): instruction and the qubits it affects. """ instruction: Instruction - qubits: Sequence[Qubit] + qubits: Sequence[Bit] def __repr__(self): s = [self.instruction.name, " ".join(map(str, self.qubits))] diff --git a/cqlib/circuits/pulse/pulse.py b/cqlib/circuits/pulse/pulse.py deleted file mode 100644 index a142d23b53f27a90517b296d37752a43397da007..0000000000000000000000000000000000000000 --- a/cqlib/circuits/pulse/pulse.py +++ /dev/null @@ -1,103 +0,0 @@ -# This code is part of cqlib. -# -# Copyright (C) 2024 China Telecom Quantum Group, QuantumCTek Co., Ltd., -# Center for Excellence in Quantum Information and Quantum Physics. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Pulse operation command""" -import re -from typing import Optional - -from cqlib.circuits.coupling_qubit import CouplingQubit -from cqlib.circuits.instruction import Instruction -from cqlib.circuits.qubit import Qubit - -from .pulse_parameter import PulseParameter -from .waveform import Waveform - - -# pylint: disable=too-few-public-methods -class Pulse(Instruction): - """ - A class representing a quantum pulse instruction within a quantum circuit. - - The Pulse class allows for fine-grained control over quantum operations by applying - specific pulses to qubits. This class can be used to construct instructions that - directly manipulate qubit states through customized pulse sequences. It supports - the highest level of control for quantum operations, including single-qubit gates, - multi-qubit gates, and other operations. - """ - - def __init__( - self, - param: PulseParameter, - label: Optional[str] = None - ): - """ - Initialize a new Pulse instruction. - - Args: - param (PulseParameter): The parameters defining the pulse, such as waveform, - start time, length, amplitude, etc. - label (Optional[str]): An optional label for the pulse instruction. Default is None. - """ - super().__init__("PLS", 1, [param], label=label) - - @staticmethod - def load(line: str) -> ('Pulse', Qubit): - """ - load Pulse gate - Parameters - ---------- - line - - Returns - ------- - - """ - tokens = line.strip().split() - if len(tokens) < 9: - raise ValueError(f"Invalid instruction, expected at least 9 tokens, got {tokens}") - instruction, qubit, waveform, t_start, length, amplitude, frequency, phase, \ - drag_alpha, *other_params = tokens - if instruction not in ('PLS', 'PULSE'): - raise ValueError(f"Unknown instruction '{instruction}'") - - qubit_obj = Pulse._parse_qubit(qubit) - param = PulseParameter( - waveform=Waveform(int(waveform)), - t_start=int(t_start), - length=int(length), - amplitude=int(amplitude), - frequency=int(frequency), - phase=int(phase), - drag_alpha=int(drag_alpha), - others=[Pulse._parse_param(param) for param in other_params], - ) - - return Pulse(param=param), qubit_obj - - @staticmethod - def _parse_qubit(qubit: str) -> Qubit: - if not re.match(r'^[GQ]\d+$', qubit): - raise ValueError(f"Invalid qubit identifier '{qubit}'") - if qubit[0] == 'G': - return CouplingQubit(int(qubit[1:])) - if qubit[0] == 'Q': - return Qubit(int(qubit[1:])) - raise ValueError(f"Unknown qubit type '{qubit[0]}'") - - @staticmethod - def _parse_param(param: str) -> float: - try: - num = float(param) - return int(num) if num.is_integer() else num - except ValueError as exc: - raise ValueError(f"Invalid parameter '{param}'") from exc diff --git a/cqlib/circuits/pulse/pulse_parameter.py b/cqlib/circuits/pulse/pulse_parameter.py deleted file mode 100644 index 8683a5ace0e6fa57892903db055b0e286944f1da..0000000000000000000000000000000000000000 --- a/cqlib/circuits/pulse/pulse_parameter.py +++ /dev/null @@ -1,83 +0,0 @@ -# This code is part of cqlib. -# -# Copyright (C) 2024 China Telecom Quantum Group, QuantumCTek Co., Ltd., -# Center for Excellence in Quantum Information and Quantum Physics. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Pulse Parameter""" - -from .waveform import Waveform - - -# pylint: disable=too-many-instance-attributes, too-many-arguments, too-few-public-methods -class PulseParameter: - """A class representing the parameters of a pulse in a quantum control sequence.""" - - def __init__( - self, - waveform: Waveform, - t_start: int, - length: int = 0, - amplitude: int = 0, - frequency: int = 0, - phase: int = 0, - drag_alpha: int = 0, - others: list[float] = None - ): - """ - Initialize a pulse parameter. - - if waveform is Numerical waveform. Each point in the waveform is defined by a numerical - value provided by the user through the 'other parameters' list. This waveform type allows - for precise control over each sample point in the pulse. - Example usage: PULSE C02 -1 256 0 0 0 0 0 200 100 300 400 … 23466. The pulse starts at - time 256 and has the numerical waveform values 200, 100, 300, 400, … 23466. - For numerical waveforms, the 'length', 'amplitude', 'frequency', and 'phase' parameters - are not relevant. - - Args: - waveform (Waveform): The waveform type or identifier associated with the pulse. - t_start (int): The start time of the pulse relative to the control waveform sequence - start point. A t_start < 0 indicates that the pulse start time should be aligned - with the end time of the previous pulse or gate operation, enabling sequential - concatenation. A t_start >= 0 sets the pulse start time to an absolute value, - allowing the pulse to be inserted freely in the sequence, independent of previous - gate operations. - length (int, optional): The duration of the pulse in DAC sampling cycles. This value - might be irrelevant for certain waveform types, in which case it can be set to any - value. Default is 0. - amplitude (int, optional): The amplitude of the pulse, in DAC code values. The maximum - amplitude is 32768. For some waveform types, this parameter may be irrelevant and - can be set to any value. Default is 0. - frequency (int, optional): The sideband mixing frequency of the pulse in Hz. This value - may be irrelevant for certain waveform types. Default is 0. - phase (int, optional): The sideband mixing phase of the pulse in radians. This value may - be irrelevant for certain waveform types. Default is 0. - drag_alpha (int, optional): The DRAG correction coefficient, used to minimize leakage - errors during pulse execution. This value may be irrelevant for certain waveform - types. Default is 0. - others (list[int], optional): Additional parameters needed to generate the waveform, - specific to the waveform type. For numeric waveforms, this could be a sequence of - values defining each sample point of the waveform. For other waveform types, these - parameters might not be used. Default is an empty list. - """ - self.waveform = waveform - self.t_start = t_start - self.length = length - self.amplitude = amplitude - self.frequency = frequency - self.phase = phase - self.drag_alpha = drag_alpha - self.others = others or [] - - def __str__(self): - return (f"{self.waveform.value} {self.t_start} {self.length} {self.amplitude} " - f"{self.frequency} {self.phase} {self.drag_alpha} " - f"{' '.join(map(str, self.others))}") diff --git a/cqlib/circuits/qubit.py b/cqlib/circuits/qubit.py index 7e4fff0d7aa1256167365aa695d712577adc1606..879efa2f46cac43009698724895474b44560b1d6 100644 --- a/cqlib/circuits/qubit.py +++ b/cqlib/circuits/qubit.py @@ -17,11 +17,13 @@ from __future__ import annotations import weakref +from .bit import Bit -class Qubit: + +class Qubit(Bit): """Quantum bit.""" - __slots__ = ["_index", '_initialized', '_hash', '__weakref__'] - _cache = weakref.WeakValueDictionary[int, 'QuBit']() + __slots__ = ["__weakref__"] + _cache = weakref.WeakValueDictionary[int, 'Qubit']() def __new__(cls, index: int) -> Qubit: """ @@ -36,58 +38,15 @@ class Qubit: """ if index < 0: raise ValueError("Qubit index must be non-negative.") + inst = cls._cache.get(index) if inst is None: inst = super().__new__(cls) inst._index = index inst._hash = None + inst._initialized = True cls._cache[index] = inst return inst - def __init__(self, index: int): - """ - Initialize a new Qubit instance. - - Args: - index: logical index of the qubit - """ - if not hasattr(self, '_initialized'): - self._index = index - self._hash = None - self._initialized = True - - @property - def index(self) -> int: - """Returns the logical index of the qubit.""" - return self._index - - def __repr__(self): - return f"{self.__class__.__name__}({self.index})" - def __str__(self): return f'Q{self.index}' - - def __copy__(self): - """ - Returns a reference to the same qubit instance since qubits - should be unique. - """ - return self - - def __deepcopy__(self, memodict=None): - return self - - def __eq__(self, other: Qubit) -> bool: - """Check equality with another qubit based on the index.""" - if isinstance(other, Qubit): - return self.index == other.index - return False - - def __hash__(self) -> int: - """ - Return the hash based on the qubit's index, used for collections - that depend on hashable items. - """ - if self._hash is None: - self._hash = hash(f"{self.__class__.__name__}({self._index})") - return self._hash diff --git a/cqlib/circuits/pulse/waveform.py b/cqlib/pulse/__init__.py similarity index 46% rename from cqlib/circuits/pulse/waveform.py rename to cqlib/pulse/__init__.py index ac833aa831e15038f8498d9ec34aeaf89fbf27f9..d634487aa7260abefd941b444bd29e57c3d9d0c5 100644 --- a/cqlib/circuits/pulse/waveform.py +++ b/cqlib/pulse/__init__.py @@ -1,6 +1,6 @@ # This code is part of cqlib. # -# Copyright (C) 2024 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., # Center for Excellence in Quantum Information and Quantum Physics. # # This code is licensed under the Apache License, Version 2.0. You may @@ -11,25 +11,24 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Waveform""" +""" +Pulse module based on the pulse instruction in the QCIS instruction. +""" -from enum import Enum +from .coupler_qubit import CouplerQubit +from .waveform import Waveform, WaveformType, CosineWaveform, \ + FlattopWaveform, SlepianWaveform, NumericWaveform +from .g import G +from .pz import PZ +from .pz0 import PZ0 +from .pxy import PXY +from .pulse_circuit import PulseCircuit - -class Waveform(Enum): - """ - An enumeration representing different types of waveforms that can be used - in quantum pulse sequences. - - 0: Numerical waveform. - 1: Flattop waveform. - 2: Cosine waveform. - - Note: - ---- - Additional waveform types may be added in the future, but currently only the three types - listed above are available. - """ - NUMERICAL = 0 - FLATTOP = 1 - COSINE = 2 +__all__ = [ + 'CouplerQubit', + 'Waveform', 'WaveformType', + 'CosineWaveform', 'FlattopWaveform', + 'SlepianWaveform', 'NumericWaveform', + 'G', 'PXY', 'PZ', 'PZ0', + 'PulseCircuit', +] diff --git a/cqlib/pulse/base_pulse.py b/cqlib/pulse/base_pulse.py new file mode 100644 index 0000000000000000000000000000000000000000..dda4e2e7993dd3a72770d7b6607e3f64b90992bf --- /dev/null +++ b/cqlib/pulse/base_pulse.py @@ -0,0 +1,55 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Base Class for QCIS Pulse instruction +""" + +from abc import ABC, abstractmethod + +from cqlib.circuits.instruction import Instruction + +from .waveform import Waveform + + +class BasePulse(Instruction, ABC): + """ + Abstract base class for Quantum Control Instruction System (QCIS) pulse commands. + """ + + def __init__( + self, + name: str, + waveform: Waveform = None, + label: str = None + ): + """Initialize base pulse instance + + Args: + name (str): Pulse type identifier (e.g. PZ/PZ0/PXY/G) + waveform (Waveform): Waveform parameter container storing physical values. + label (str): Optional operational label for experimental tracking + """ + self._waveform = waveform + if waveform: + params = waveform.data + else: + params = [] + super().__init__(name=name, num_qubits=1, params=params, label=label) + self.validate() + + @abstractmethod + def validate(self): + """ + Abstract template method for parameter validation + """ diff --git a/cqlib/circuits/coupling_qubit.py b/cqlib/pulse/coupler_qubit.py similarity index 68% rename from cqlib/circuits/coupling_qubit.py rename to cqlib/pulse/coupler_qubit.py index 92762e1c124ad134ad130dac1cd95e2d6cdcd937..cc8e6852b83cabad7d2d2590ff7754b8c9e1483f 100644 --- a/cqlib/circuits/coupling_qubit.py +++ b/cqlib/pulse/coupler_qubit.py @@ -1,6 +1,6 @@ # This code is part of cqlib. # -# Copyright (C) 2024 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., # Center for Excellence in Quantum Information and Quantum Physics. # # This code is licensed under the Apache License, Version 2.0. You may @@ -11,27 +11,28 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Coupling Qubit""" +"""Coupler Qubit""" from __future__ import annotations import weakref -from .qubit import Qubit +from cqlib.circuits.bit import Bit -class CouplingQubit(Qubit): +class CouplerQubit(Bit): """A class representing a coupled qubit in a quantum system.""" - _cache = weakref.WeakValueDictionary[int, 'CouplingQubit']() + _cache = weakref.WeakValueDictionary[int, 'CouplerQubit']() - def __new__(cls, index: int) -> CouplingQubit: + def __new__(cls, index: int) -> CouplerQubit: if index < 0: - raise ValueError("Qubit index must be non-negative.") + raise ValueError("CouplerQubit index must be non-negative.") inst = cls._cache.get(index) if inst is None: - inst = super().__new__(cls, index) + inst = super().__new__(cls) inst._index = index + inst._initialized = True inst._hash = None cls._cache[index] = inst return inst diff --git a/cqlib/pulse/g.py b/cqlib/pulse/g.py new file mode 100644 index 0000000000000000000000000000000000000000..f0cb626fc0323585ff49b7b34a15ad2308721e92 --- /dev/null +++ b/cqlib/pulse/g.py @@ -0,0 +1,52 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" G: Adjust and control the coupling function of the coupling qubit. """ +from cqlib.exceptions import CqlibError +from .base_pulse import BasePulse + + +class G(BasePulse): + """ + Coupling strength controller for coupling qubits via sequential DC(Direct Current) pulses. + Applies tunable interaction between adjacent qubits by setting duration (DAC cycles) + and coupling amplitude (Hz). Exclusively operates on coupling qubits. + + Parameters: + - length: Coupling activation duration in DAC sampling cycles (1 cycle=0.5ns) + - coupling_strength: Interaction magnitude in Hertz (Hz) + + Example: + `G G107 100 -3E6` sets 3MHz coupling on G107 for 50ns (100 cycles). + """ + + def __init__(self, length: int, coupling_strength: int, label: str = None): + self.length = length + self.coupling_strength = coupling_strength + super().__init__('G', waveform=None, label=label) + self.params = [length, coupling_strength] + + def validate(self): + """ + Validate pulse parameters for G-type coupling operations. + + Ensures: + - Duration (length) is within valid range [0, 1e5] DAC cycles + - Coupling strength meets implementation constraints + """ + if self.length < 0 or self.length > 1e5: + raise CqlibError(f"Invalid duration {self.length}: " + f"Must be 0-100,000 DAC cycles (1 cycle=0.5ns)") + + def __str__(self): + return f'{self.__class__.__name__}({self.length},{self.coupling_strength})' diff --git a/cqlib/pulse/pulse_circuit.py b/cqlib/pulse/pulse_circuit.py new file mode 100644 index 0000000000000000000000000000000000000000..02fbac5b965e43c483170edc38130e0aabdd1934 --- /dev/null +++ b/cqlib/pulse/pulse_circuit.py @@ -0,0 +1,335 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +A specialized quantum circuit class designed for pulse-level control +of qubits and coupler qubits. +""" +import re +from collections.abc import Sequence + +from cqlib.circuits.circuit import Circuit, Qubits +from cqlib.circuits.instruction import Instruction +from cqlib.circuits.instruction_data import InstructionData +from cqlib.circuits.parameter import Parameter +from cqlib.circuits.qubit import Qubit +from cqlib.exceptions import CqlibError + +from .coupler_qubit import CouplerQubit +from .g import G +from .pxy import PXY +from .pz import PZ +from .pz0 import PZ0 +from .waveform import Waveform +from .base_pulse import BasePulse + +CouplerQubits = int | CouplerQubit | Sequence[int | CouplerQubit] + +PULSE_GATES = ['PZ', 'PZ0', 'PXY', 'G'] + + +class PulseCircuit(Circuit): + """ + Circuit with pulse support. + """ + + def __init__( + self, + qubits: Qubits, + coupler_qubits: CouplerQubits, + parameters: Sequence[Parameter | str] | None = None + ) -> None: + """ + Initialize PulseCircuit. + + Args: + qubits(Qubits): Specification for standard qubits. + coupler_qubits(CouplerQubits): Specification for coupler qubits. + parameters(Sequence[Parameter | str] | None): Circuit parameters. + """ + super().__init__(qubits, parameters) + self._coupler_qubits = self._initialize_coupler_qubits(coupler_qubits) + + @property + def coupler_qubits(self) -> list[CouplerQubit]: + """ + A list of initialized coupler qubit objects in the circuit. + """ + return list(self._coupler_qubits.values()) + + def append_pulse(self, instruction: Instruction, qubit: CouplerQubit | Qubit): + """ + Appends a pulse instruction to the circuit. + + Args: + instruction (Instruction): Pulse instruction to add. + qubit (Qubit | CouplerQubit): Target qubit or coupler qubit. + """ + if isinstance(qubit, CouplerQubit) and str(qubit) not in self._coupler_qubits: + raise CqlibError(f"Coupler qubit {qubit} is not registered in the circuit. " + f"Available coupler qubits: {self.coupler_qubits}") + if isinstance(qubit, Qubit) and str(qubit) not in self._qubits: + raise CqlibError(f"Qubit {qubit} is not registered in the circuit. " + f"Available qubits: {self.qubits}") + self._circuit_data.append(InstructionData(instruction=instruction, qubits=[qubit])) + + def g(self, couple_qubit: CouplerQubit | int, length: int, coupling_strength: int): + """ + Applies a G-pulse (coupling pulse) to a coupler qubit. + + Args: + couple_qubit(CouplerQubit | int): Target coupler qubit or its index. + length(int): Duration of the pulse in time steps. + coupling_strength(int): Strength of the coupling interaction. + """ + if isinstance(couple_qubit, int): + couple_qubit = CouplerQubit(couple_qubit) + self.append_pulse(G(length, coupling_strength), couple_qubit) + + def pxy(self, qubit: Qubit | int, waveform: Waveform): + """ + Applies a PXY-pulse (XY control pulse) to a standard qubit. + + Args: + qubit(Qubit | int): Target qubit or its index. + waveform(Waveform): Waveform object defining the pulse shape. + """ + if isinstance(qubit, int): + qubit = Qubit(qubit) + self.append_pulse(PXY(waveform=waveform), qubit) + + def pz(self, qubit: CouplerQubit | Qubit, waveform: Waveform): + """ + Applies a PZ-pulse (Z control pulse) to a qubit or coupler qubit. + + Args: + qubit(CouplerQubit | Qubit): Target CouplerQubit or Qubit. + waveform (Waveform): Waveform object defining the pulse shape. + """ + self.append_pulse(PZ(waveform=waveform), qubit) + + def pz0(self, qubit: CouplerQubit | Qubit, waveform: Waveform): + """ + Applies a PZ0-pulse (parallel) to a qubit or coupler qubit. + + Args: + qubit(CouplerQubit | Qubit): Target CouplerQubit or Qubit. + waveform (Waveform): Waveform object defining the pulse shape. + """ + self.append_pulse(PZ0(waveform=waveform), qubit) + + def add_coupler_qubits(self, couple_qubits: CouplerQubits): + """ + Adds a coupler qubit(or list) to the circuit, ensuring it does not already exist. + + Args: + couple_qubits (CouplerQubits): The coupler qubits to add, specified as an + integer index or a Qubit object. + """ + if not isinstance(couple_qubits, Sequence): + couple_qubits = [couple_qubits] + + for qubit in couple_qubits: + if isinstance(qubit, int): + qubit = CouplerQubit(qubit) + elif not isinstance(qubit, CouplerQubit): + raise TypeError(f"{qubit} must be an int or CouplerQubit instance.") + if str(qubit) in self._qubits: + raise ValueError(f"CouplerQubit {qubit} already exists in the circuit.") + self._coupler_qubits[str(qubit)] = qubit + + @staticmethod + def _initialize_coupler_qubits(coupler_qubits: CouplerQubits) -> dict[str, CouplerQubit]: + """ + Helper function to initialize coupler_qubits. + + Args: + coupler_qubits (CouplerQubits): Input coupler qubits specification. + + Returns: + dict[int, CouplerQubit]: Dictionary of CouplerQubit objects. + """ + if isinstance(coupler_qubits, int): + if coupler_qubits < 0: + raise ValueError("Number of coupler qubits must be non-negative.") + return {str(Qubit(i)): CouplerQubit(i) for i in range(coupler_qubits)} + if isinstance(coupler_qubits, CouplerQubit): + return {str(coupler_qubits): coupler_qubits} + if not isinstance(coupler_qubits, (list, tuple)): + raise ValueError("Invalid coupler_qubits input. Expected int, CouplerQubit," + " or list/tuple of these.") + + qs = {} + for qubit in coupler_qubits: + if isinstance(qubit, int): + qubit = CouplerQubit(qubit) + elif not isinstance(qubit, CouplerQubit): + raise TypeError("CouplerQubit must be an int or CouplerQubit instance.") + if qubit.index in qs: + raise ValueError(f"Duplicate qubit detected: {qubit}") + qs[str(qubit)] = qubit + return qs + + def to_qasm2(self) -> str: + """ + Generate a QASM 2.0 string representation of the circuit. + + Note: + Circuits containing coupler qubits cannot be exported to QASM 2.0 format, + as the standard does not support coupler qubit operations. + + Returns: + str: QASM 2.0 compliant code representing the circuit + + Raises: + CqlibError: If the circuit contains coupler qubits, as they are not supported + in QASM 2.0. + """ + if self._coupler_qubits: + raise CqlibError(f"QASM 2.0 export not supported for circuits with coupler qubits. " + f"Found coupler(s): {self.coupler_qubits}") + return super().to_qasm2() + + @classmethod + def load(cls, qcis: str) -> 'PulseCircuit': + """ + Loads quantum circuit with pulse-level instructions from QCIS string. + + Extends base Circuit functionality with support for: + - Coupler qubits (G-prefixed identifiers) + - Pulse waveforms (PXY, PZ, PZ0) + - Hybrid circuits mixing gates and pulses + + Args: + qcis: String containing hybrid quantum instructions. + + Returns: + PulseCircuit: Circuit with pulse capabilities initialized. + """ + circuit = cls(qubits=[], coupler_qubits=[]) + pattern = re.compile(r'^([A-Z][A-Z0-9]*)\s+((?:[QG][0-9]+\s*)+)' + r'((?:[+-]?(?:\d*\.\d+|\d+)(?:[Ee][+-]?\d+)?\s*)*)$') + for line in qcis.split('\n'): + line = re.sub(r'(#|//).*', '', line).strip() + if not line: + continue + match = pattern.match(line) + if not match: + raise ValueError(f'Invalid instruction format: {line}') + gate, qubits_str, params_str = match.groups() + qubits, coupler_qubits = cls._parse_pulse_qubits_str(circuit, qubits_str) + params = [float(p) for p in params_str.split()] if params_str else [] + if gate in PULSE_GATES: + cls._process_pulse_instruction(circuit, gate, qubits, coupler_qubits, params) + else: + cls._process_instruction(circuit, gate, qubits, params) + + return circuit + + def _parse_pulse_qubits_str( + self, + qubits_str: str + ) -> tuple[list[Qubit], list[CouplerQubit]]: + """ + Parses and categorizes qubit identifiers from QCIS instruction string. + + Processes space-separated qubit tokens distinguishing between: + - Standard qubits (Q-prefixed) + - Coupler qubits (G-prefixed) + + Args: + qubits_str: Raw qubit specification from QCIS instruction line. + + Returns: + tuple: Contains two lists respectively holding: + [0] Parsed standard Qubit objects + [1] Parsed CouplerQubit objects + """ + qubits, coupler_qubits = [], [] + for q_str in qubits_str.split(): + if q_str.startswith('Q'): + qubit = self._qubits.setdefault(q_str, Qubit(int(q_str[1:]))) + qubits.append(qubit) + elif q_str.startswith('G'): + coupler_qubit = self._coupler_qubits.setdefault(q_str, CouplerQubit(int(q_str[1:]))) + coupler_qubits.append(coupler_qubit) + else: + raise ValueError(f"Invalid qubit format: {q_str}") + return qubits, coupler_qubits + + def _process_pulse_instruction( + self, + gate: str, + qubits: list[Qubit], + coupler_qubits: list[CouplerQubit], + params: list[float | int] + ): + """ + Processes pulse-specific quantum instructions from parsed components. + + Handles three categories of pulse operations: + 1. G-type coupling pulses + 2. PXY parametric XY control pulses + 3. General pulse operations (PZ/PZ0 and dynamically resolved instructions) + + Args: + gate(str): Uppercase gate identifier (e.g., 'G', 'PXY', 'PZ') + qubits( list[Qubit]): Standard qubit targets parsed from instruction + coupler_qubits(list[CouplerQubit]): Coupler qubit targets parsed from instruction + params(list[float | int]): Numerical parameters following the qubit specification + """ + if gate == 'G': + if len(params) != 2: + raise CqlibError("G gate requires exactly 2 parameters (length, coupling_strength)") + length, coupling_strength = params + if length != int(length): + raise CqlibError( + f"G pulse length parameter must be integer value, " + f"received {type(length).__name__} {length}" + ) + if coupling_strength != int(coupling_strength): + raise CqlibError( + f"G pulse coupling_strength parameter requires integer value, " + f"got {type(coupling_strength).__name__} {coupling_strength}" + ) + self._circuit_data.append( + InstructionData(instruction=G(int(length), int(coupling_strength)), + qubits=coupler_qubits) + ) + return + # Create waveform for pulse operations + waveform = Waveform.load(params, gate) + if gate == 'PXY': + if not qubits: + raise CqlibError("PXY pulse requires valid qubit targets") + self._circuit_data.append( + InstructionData(instruction=PXY(waveform), qubits=qubits) + ) + return + # For PZ/PZ0, validate mutual exclusivity of qubit types + if bool(qubits) == bool(coupler_qubits): + raise CqlibError("Must provide exactly one of qubits or coupler_qubits for PZ/PZ0") + qubits = qubits if len(qubits) > 0 else coupler_qubits + + # Dynamic gate resolution + try: + # pylint: disable=import-outside-toplevel, cyclic-import + from cqlib import pulse + instruction_class = getattr(pulse, gate) + except AttributeError as ex: + raise CqlibError(f"Unknown pulse instruction: {gate}") from ex + if not issubclass(instruction_class, BasePulse): + raise CqlibError(f"{gate} is not a valid pulse instruction") + self._circuit_data.append( + InstructionData(instruction=instruction_class(waveform=waveform), qubits=qubits) + ) diff --git a/cqlib/pulse/pxy.py b/cqlib/pulse/pxy.py new file mode 100644 index 0000000000000000000000000000000000000000..e0944a09accd37cf94d893bd35e4c01314990da8 --- /dev/null +++ b/cqlib/pulse/pxy.py @@ -0,0 +1,48 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Sequential AC(Alternating Current) pulse controller for data qubit XY channels. +""" +from cqlib.exceptions import CqlibError +from .base_pulse import BasePulse +from .waveform import Waveform, NumericWaveform + + +class PXY(BasePulse): + """ + Pulse controller for data qubit XY channels. + + For PXY pulses, the numerical list of data pulses is in the form of + [i0, i1, ..., in, q0, q1, ..., qn]. The first half describes the + pulse values of I channel, and the second half describes the + pulse values of Q channel. The length of its list is >=6 and + must be even. + """ + + def __init__(self, waveform: Waveform, label: str = None): + """ + Initialize XY channel pulse with waveform parameters + """ + super().__init__('PXY', waveform=waveform, label=label) + + def validate(self): + """ + Verify PXY-specific waveform constraints. + """ + waveform = self._waveform + if waveform.phase is None or waveform.drag_alpha is None: + raise CqlibError('PXY must have phase and drag_alpha params') + if isinstance(waveform, NumericWaveform) \ + and (len(waveform.data_list) < 6 or len(waveform.data_list) % 2 == 1): + raise CqlibError('The length of its list must >=6 and must be even.') diff --git a/cqlib/pulse/pz.py b/cqlib/pulse/pz.py new file mode 100644 index 0000000000000000000000000000000000000000..d2e277d2be7a1e9ea6675a43b2ad204d8db698b3 --- /dev/null +++ b/cqlib/pulse/pz.py @@ -0,0 +1,32 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Sequential DC pulse controller for data/coupling qubit Z channels. """ +from cqlib.exceptions import CqlibError +from .base_pulse import BasePulse +from .waveform import Waveform + + +class PZ(BasePulse): + """ + Sequential DC pulse controller for data/coupling qubit Z channels. + """ + + def __init__(self, waveform: Waveform, label: str = None): + """Initialize Z-axis DC pulse with Stark shift parameters""" + super().__init__('PZ', waveform=waveform, label=label) + + def validate(self): + p = self._waveform + if p.phase is not None or p.drag_alpha is not None: + raise CqlibError('PZ must not have phase and drag_alpha params') diff --git a/cqlib/pulse/pz0.py b/cqlib/pulse/pz0.py new file mode 100644 index 0000000000000000000000000000000000000000..20a8bd73e564643dba142e1a1bbf1653b883dbe9 --- /dev/null +++ b/cqlib/pulse/pz0.py @@ -0,0 +1,43 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + + +"""Parallel DC pulse controller for data/coupling qubit Z channels.""" +from cqlib.exceptions import CqlibError +from .base_pulse import BasePulse +from .waveform import Waveform + + +class PZ0(BasePulse): + """ + Parallel DC pulse controller for data/coupling qubit Z channels. + + Enables synchronized multi-channel DC pulse generation for simultaneous + frequency adjustments. + """ + + def __init__(self, waveform: Waveform, label: str = None): + """ + Initialize parallel Z-axis DC pulse controller. + + Args: + waveform (Waveform): Waveform. + label (str, optional): Operational identifier for batch processing + """ + super().__init__('PZ0', waveform, label=label) + + def validate(self): + """ Verify parallel DC pulse constraints. """ + p = self._waveform + if p.phase is not None or p.drag_alpha is not None: + raise CqlibError('PZ0 must not have phase and drag_alpha params') diff --git a/cqlib/pulse/waveform.py b/cqlib/pulse/waveform.py new file mode 100644 index 0000000000000000000000000000000000000000..9ecd8f60503bd26fe6b35c755acd2eb1852d35ad --- /dev/null +++ b/cqlib/pulse/waveform.py @@ -0,0 +1,268 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Waveform + +This module defines waveform type enumerations and parameter classes +for generating quantum pulse sequences. It provides a standardized +interface to configure and validate waveform parameters across different +waveform types. +""" +from __future__ import annotations +from dataclasses import dataclass +from enum import IntEnum, unique +from math import pi + +from cqlib.exceptions import CqlibError + + +@unique +class WaveformType(IntEnum): + """ + Enumeration of supported waveform types for quantum pulse sequences. + + Members: + COSINE (0): Cosine-shaped waveform. + FLATTOP (1): Flat-top waveform with customizable edge length. + SLEPIAN (2): Slepian tapering waveform defined by time-bandwidth + product parameters. + NUMERIC (3): Arbitrary numeric waveform specified via sample array. + """ + COSINE = 0 + FLATTOP = 1 + SLEPIAN = 2 + NUMERIC = 3 + + +@dataclass +class Waveform: + """ + Base dataclass for waveform parameter validation and string representation. + + Attributes: + waveform (WaveformType): Enumerated waveform type. + length (int): Total waveform duration (1-1e5). + amplitude (float): Normalized signal amplitude (0.0-1.0). + phase (float, optional): Phase shift for PXY modulations. + drag_alpha (float, optional): Drag coefficient for PXY modulations. + """ + waveform: WaveformType + length: int + amplitude: float + phase: float = None # PXY only + drag_alpha: float = None # PXY only + + @staticmethod + def create( + w_type: WaveformType, + length: int, + amplitude: float, + phase: float = None, + drag_alpha: float = None, + **kwargs + ) -> 'Waveform': + """ + Factory method to create waveform parameter objects based on the specified type. + + Instantiates a concrete waveform parameter class according to the given waveform type, + validates parameters, and returns the initialized object. + + Args: + w_type(WaveformType): Enumerated waveform type identifier. + length(int): Total duration of the waveform in samples (1-1e5). + amplitude: Normalized signal amplitude (0.0-1.0). + phase (float, optional): Phase shift for PXY. Must be in (-π, π] range. + drag_alpha (float, optional): DRAG correction coefficient for PXY. Must be positive. + **kwargs: Type-specific parameters for advanced waveform configurations: + - FLATTOP: edge (int) + - SLEPIAN: thf, thi, lam2, lam3 (float) + - NUMERIC: samples (list[float]) + """ + param_classes = { + WaveformType.COSINE: CosineWaveform, + WaveformType.FLATTOP: FlattopWaveform, + WaveformType.SLEPIAN: SlepianWaveform, + WaveformType.NUMERIC: NumericWaveform + } + + if w_type not in param_classes: + raise ValueError(f"Unsupported waveform type: {w_type}") + + return param_classes[w_type]( + waveform=w_type, + length=length, + amplitude=amplitude, + phase=phase, + drag_alpha=drag_alpha, + **kwargs + ) + + def validate(self): + """Performs basic parameter validation.""" + if not isinstance(self.length, int) or self.length <= 0 or self.length > 1e5: + raise CqlibError(f"Invalid length: {self.length}. Must be in [0, 1e5]") + if not 0.0 <= self.amplitude <= 1.0: + raise CqlibError(f"Invalid amplitude: {self.amplitude}. Must be in [0, 1]") + if self.phase is not None and (self.phase <= -pi or self.phase >= pi): + raise CqlibError("The phase value must be within (-pi, pi].") + if self.drag_alpha is not None and self.drag_alpha < 0: + raise CqlibError("The drag_alpha value must be positive float.") + + def __str__(self): + return ' '.join(map(str, self.data)) + + @property + def data(self) -> list[float]: + """Data list""" + ps = [self.waveform.value, self.length, self.amplitude] + if self.phase is not None and self.drag_alpha is not None: + ps.extend([self.phase, self.drag_alpha]) + return ps + + @classmethod + def load(cls, waveform: str | list[float | int], gate: str) -> Waveform: + """ + Constructs Waveform from serialized data. + + Args: + waveform (str | list[float | int]): Input data as either: + - Space-separated string of parameters + - List of numeric values + gate (str): Target gate operation (PXY/PZ/etc.) determining + parameter interpretation + + Returns: + Waveform: Instantiated waveform object + """ + if isinstance(waveform, str): + waveform = map(float, waveform.split()) + ps = waveform + try: + waveform_type = WaveformType(ps[0]) + except ValueError as e: + raise CqlibError(f"Invalid waveform type value: {ps[0]}") from e + length, amplitude = ps[1:3] + ps = ps[3:] + if gate == 'PXY': + phase, drag_alpha = ps[:2] + ps = ps[2:] + else: + phase, drag_alpha = None, None + kwargs = { + 'w_type': waveform_type, + 'length': int(length), + 'amplitude': amplitude, + 'phase': phase, + 'drag_alpha': drag_alpha + } + + if waveform_type == WaveformType.FLATTOP: + edge = ps[0] + if edge != int(edge): + raise CqlibError('') + kwargs['edge'] = int(edge) + else: + ps = [int(p) if p == int(p) else p for p in ps] + if waveform_type == WaveformType.SLEPIAN: + kwargs.update({ + 'thf': ps[0], + 'thi': ps[1], + 'lam2': ps[2], + 'lam3': ps[3], + }) + elif waveform_type == WaveformType.NUMERIC: + kwargs['data_list'] = ps + return Waveform.create(**kwargs) + + +@dataclass +class CosineWaveform(Waveform): + """Configuration parameters for cosine-shaped waveforms.""" + + def __post_init__(self): + self.validate() + + +@dataclass +class FlattopWaveform(Waveform): + """ + Specialized parameters for flat-top waveforms with edge control. + + Additional Attributes: + edge (int): Length of rising/falling edges (must be < total length/2). + """ + edge: int = None + + def __post_init__(self): + self.validate() + if self.edge < 0 or not isinstance(self.edge, int): + raise CqlibError(f"Flattop waveform edge parameter must be a positive integer, " + f"got {type(self.edge).__name__} {self.edge}") + if 2 * self.edge >= self.length: + raise CqlibError(f"Edge ({self.edge}) too large for length {self.length}") + + @property + def data(self) -> list[float]: + ps = super().data + ps.append(self.edge) + return ps + + +@dataclass +class SlepianWaveform(Waveform): + """ + Specialized parameters for slepian waveforms + + Additional Attributes: + - thf: + - thi: + - lam2: + - lam3: + """ + thf: float = None + thi: float = None + lam2: float = None + lam3: float = None + + @property + def data(self) -> list[float]: + ps = super().data + ps.extend([self.thf, self.thi, self.lam2, self.lam3]) + return ps + + +@dataclass +class NumericWaveform(Waveform): + """ + Specialized parameters for numeric waveforms. + + Additional Attributes: + - params: List of floats, Values must be in [0.0, 1.0], + Minimum length of 3 samples; + """ + data_list: list[float] = None + + def __post_init__(self): + super().validate() + if any(not (0.0 <= x <= 1.0) for x in self.data_list): + raise CqlibError("All data values must be in [0,1]") + if len(self.data_list) < 3: + raise CqlibError("The numerical list of data pulses is in the form" + " of [d0, d1, ..., dn], and the list length must be >= 3") + + @property + def data(self) -> list[float]: + ps = super().data + ps.extend(self.data_list) + return ps diff --git a/cqlib/simulator/statevector_simulator.py b/cqlib/simulator/statevector_simulator.py index 8ee99183e19076b1397cb8b4b6663dc95f47e553..97539c368eedf61d11fc38d7bf972e404a60a88b 100644 --- a/cqlib/simulator/statevector_simulator.py +++ b/cqlib/simulator/statevector_simulator.py @@ -42,8 +42,8 @@ from cqlib.circuits.circuit import Circuit from cqlib.circuits.parameter import Parameter from cqlib.exceptions import CqlibError from cqlib.simulator.mergy import Gate, merge_gate -from cqlib.simulator.statevector_simulator_c import get_measure, get_probs, \ - get_sample, get_state, free_memory + +from cqlib.simulator.statevector_simulator_c import get_state, get_probs, get_measure, get_sample gate_name_map = { "H": 72, @@ -80,33 +80,6 @@ gate_name_map = { } -class CGate(ctypes.Structure): - """ - CGate class represents a quantum gate structure in C, which is used to interface - with the low-level C library for quantum gate simulations. - - """ - _fields_ = [ - ("gate_id", ctypes.c_size_t), - ("qubits", ctypes.POINTER(ctypes.c_uint32)), - ("qubits_len", ctypes.c_size_t), - ("theta", ctypes.POINTER(ctypes.c_double)), - ("mat", ctypes.c_void_p), - ] - - def __init__(self, gate: Gate): - """ - Initializes the CGate object by converting a Python Gate object to the C structure. - """ - super().__init__() - self.gate_id = gate_name_map[gate.name] - # NOTE: may raise key Err, but this is a feature - self.qubits = (len(gate.qubits) * ctypes.c_uint32)(*gate.qubits) - self.qubits_len = len(gate.qubits) - self.theta = (len(gate.theta) * ctypes.c_double)(*gate.theta) - self.mat = gate.mat.ctypes.data_as(ctypes.c_void_p) - - class StatevectorSimulator: """ StatevectorSimulator is a quantum circuit simulator that simulates quantum circuits @@ -170,16 +143,12 @@ class StatevectorSimulator: param = float(param.value(params=self.circuit.parameters_value)) ps.append(param) name = item.instruction.name - # if name in ['CRX', 'CRY', 'CRZ', 'CCX', 'SWAP']: - # name = 'fgate' - # if self.is_fusion: - # raise ValueError(f"gate {item.instruction.name} not support fusion.") self.gates.append(Gate( name=name, qubits=qubits, theta=ps, - mat=np.array(item.instruction) + mat=np.asarray(item.instruction) )) def _check_fusion(self): @@ -224,9 +193,9 @@ class StatevectorSimulator: """ self._check_fusion() gates_list = self.get_gates() - state_array, state_ptr_capsule = get_state(self.nq, 2 ** self.nq, gates_list, self.omp_threads) + state_list, state_ptr_capsule = get_state(self.nq, 2 ** self.nq, gates_list, self.omp_threads) self.state_ptr_capsule = state_ptr_capsule - state = {np.binary_repr(i, width=self.nq): val for i, val in enumerate(state_array)} + state = {np.binary_repr(i, width=self.nq): val for i, val in enumerate(state_list)} return state def probs(self) -> dict[str, np.float64]: @@ -307,6 +276,7 @@ class StatevectorSimulator: # Call get_sample from the C extension module samples_array, samples_ptr_capsule = get_sample( shots, + len(self.measure_qubits), self.measure_ptr_capsule, self.omp_threads, self.nq, @@ -323,10 +293,7 @@ class StatevectorSimulator: if is_raw_data: return samples_array - samples_array = samples_array.astype(np.uint64) mq_len = len(self.measure_qubits) - # powers_of_two = 2 ** np.arange(mq_len)[::-1] - # samples_int = samples_array.dot(powers_of_two).astype(np.uint64) counts = Counter(samples_array) # Convert counts to binary strings result = { diff --git a/cqlib/visualization/__init__.py b/cqlib/visualization/__init__.py index 2bc382bcb1d9bfb6d0f2259e419d2e68ac6a597d..361407ee088b2fc5e92b582656e70f669aca21f7 100644 --- a/cqlib/visualization/__init__.py +++ b/cqlib/visualization/__init__.py @@ -15,6 +15,7 @@ visualization module defines methods to visualize qcis circuits and plotting experiment results. """ -from .circuit import draw_circuit +# from .circuit import draw_circuit +from .circuit import draw_text from .gplot import draw_gplot from .result import draw_histogram diff --git a/cqlib/circuits/pulse/__init__.py b/cqlib/visualization/circuit/__init__.py similarity index 73% rename from cqlib/circuits/pulse/__init__.py rename to cqlib/visualization/circuit/__init__.py index 70f7be408e080b47b0d4bda06dd9f79f4cb3604d..fe04304bb9ad33946b5fdaade526dcdfb5e1f40c 100644 --- a/cqlib/circuits/pulse/__init__.py +++ b/cqlib/visualization/circuit/__init__.py @@ -1,6 +1,6 @@ # This code is part of cqlib. # -# Copyright (C) 2024 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., # Center for Excellence in Quantum Information and Quantum Physics. # # This code is licensed under the Apache License, Version 2.0. You may @@ -11,4 +11,13 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Pulse""" +""" +Quantum circuit visualization +""" + +from .text import draw_text, TextDrawer + +__all__ = [ + 'draw_text', + 'TextDrawer' +] diff --git a/cqlib/visualization/circuit/base.py b/cqlib/visualization/circuit/base.py new file mode 100644 index 0000000000000000000000000000000000000000..3a90389d548d3ae245e442f4bb194ab45c65485b --- /dev/null +++ b/cqlib/visualization/circuit/base.py @@ -0,0 +1,169 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Quantum circuit visualization +""" + +import logging +from abc import ABC, abstractmethod +from enum import Enum +from typing import Iterator + +from cqlib.circuits.bit import Bit +from cqlib.circuits.qubit import Qubit +from cqlib.circuits.circuit import Circuit +from cqlib.circuits.dag import circuit_to_dag, topological_layers +from cqlib.circuits.instruction_data import InstructionData +from cqlib.exceptions import CqlibError + +logger = logging.getLogger('cqlib.vis') + + +class BoxChar(str, Enum): + """ + Unicode box-drawing characters collection for circuit visualization + """ + TOP = '╵' + BOTTOM = '╷' + LEFT = '╴' + RIGHT = '╶' + TOP_BOTTOM = '│' + LEFT_RIGHT = '─' + + TOP_LEFT = '┘' + TOP_RIGHT = '└' + BOTTOM_LEFT = '┐' + BOTTOM_RIGHT = '┌' + + TOP_BOTTOM_LEFT = '┤' + TOP_BOTTOM_RIGHT = '├' + TOP_LEFT_RIGHT = '┴' + BOTTOM_LEFT_RIGHT = '┬' + TOP_BOTTOM_LEFT_RIGHT = '┼' + + DOT = '■' + CONNECT = 'X' + LEFT_ARROW = '«' + RIGHT_ARROW = '»' + + +class BaseDrawer(ABC): + """ + Abstract Quantum circuit Drawer. + """ + + def __init__(self, circuit: Circuit, qubit_order: list[int | Bit] = None): + self.circuit = circuit + self.qubit_order = qubit_order + self.sorted_qubits: list[Bit] = [] + self.qubit_mapping: dict[Bit, int] = {} + + self.order_qubits() + + def order_qubits(self): + """ + Determines the display order of qubits and initializes related attributes. + Uses specified qubit_order if provided (supplementing with remaining circuit qubits), + otherwise sorts qubits by their indices. Initializes: + - sorted_qubits: List of qubits in display order + - qubit_mapping: Dictionary mapping qubits to display line numbers + """ + if self.qubit_order is None: + sorted_qubits = sorted(self.circuit.qubits, key=lambda s: s.index) + else: + sorted_qubits = [q if isinstance(q, Qubit) else Qubit(q) for q in self.qubit_order] + for qubit in self.circuit.qubits: + if qubit not in sorted_qubits: + sorted_qubits.append(qubit) + self.sorted_qubits = sorted_qubits + self.qubit_mapping = {q: i * 2 + 1 for i, q in enumerate(sorted_qubits)} + + def generate_moment(self) -> Iterator[list[InstructionData]]: + """ + Generates an iterator of topological layers (moments) from the circuit DAG. + Each moment contains non-overlapping operations that can be executed in parallel. + + Yields: + list[InstructionData]: A moment containing parallel-compatible operations + + Raises: + CqlibError: If qubit conflicts are detected within a topological layer + """ + dag_circuit = circuit_to_dag(self.circuit) + for layer in topological_layers(dag_circuit): + moment = [] + used_qubits = set() + for node_id in layer: + node: InstructionData = dag_circuit.get_node_data(node_id) + if any(q in used_qubits for q in node.qubits): + raise CqlibError(f"Qubit conflict in topological layer: {node.qubits}") + used_qubits.update(node.qubits) + moment.append(node) + yield moment + + @staticmethod + def moment_to_columns(moment: list[InstructionData]) -> list[list[InstructionData]]: + """ + Organizes a moment into display columns by analyzing qubit ranges. + Operations with overlapping qubit ranges are placed in separate columns. + + Args: + moment (list[InstructionData]): A collection of operations from one topological layer + + Returns: + list[list[InstructionData]]: Column-based structure for visualization, + where each sublist represents a display column + """ + + qubit_ranges = [] + qubit_min_to_ins = {} + for ins in moment: + qubits_index = [q.index for q in ins.qubits] + qubit_min_to_ins[min(qubits_index)] = ins + qubit_ranges.append([min(qubits_index), max(qubits_index)]) + + moment_columns = [] + for qubit_range in qubit_ranges: + min_index, max_index = qubit_range + for column in moment_columns: + for item in column: + mi, ma = item + if max_index >= mi and min_index <= ma: + break + else: + column.append(qubit_range) + break + else: + moment_columns.append([qubit_range]) + return [[qubit_min_to_ins[r[0]] for r in col] for col in moment_columns] + + def qubit_line_no(self, qubit: Bit) -> int: + """ + Retrieves the display line number for a given qubit in the visualization + + Args: + qubit (Qubit): Target qubit to query + + Returns: + int: Odd-numbered line position corresponding to the qubit's display order + """ + return self.qubit_mapping[qubit] + + @abstractmethod + def drawer(self) -> str: + """ start draw lines""" + + @abstractmethod + def draw_column(self, column: list[InstructionData]) -> list[list[str]]: + """ draw column lines""" diff --git a/cqlib/visualization/circuit/text.py b/cqlib/visualization/circuit/text.py new file mode 100644 index 0000000000000000000000000000000000000000..5bbaeec6c68750cf782200fa4d614e1cbe7eecdd --- /dev/null +++ b/cqlib/visualization/circuit/text.py @@ -0,0 +1,295 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Text-based quantum circuit visualization module + +Provides ASCII/Unicode art representation of quantum circuits using box-drawing characters. +""" +import copy +import logging +import shutil + +from cqlib.circuits.circuit import Circuit +from cqlib.circuits.gates import SWAP, CZ +from cqlib.circuits.barrier import Barrier +from cqlib.circuits.gates.gate import ControlledGate +from cqlib.circuits.instruction_data import InstructionData +from cqlib.circuits.qubit import Qubit + +from .base import BaseDrawer, BoxChar + +logger = logging.getLogger('cqlib.vis') + + +class TextDrawer(BaseDrawer): + """ + Renders quantum circuits as text diagrams using box-drawing characters + """ + MIN_LINE_WIDTH = 10 + DEFAULT_LINE_WIDTH = 30 + + def __init__( + self, + circuit: Circuit, + qubit_order: list[int | Qubit] = None, + line_width: int = None, + ): + """ + Initialization Parameters + + Args: + circuit (Circuit): Quantum circuit to visualize + qubit_order (list[int | Qubit]): Custom ordering of qubits (optional) + line_width (int): Force specific output width (default: auto-detect terminal width) + """ + super().__init__(circuit, qubit_order) + self.line_width = line_width + + def drawer(self) -> str: + """ + Generates the complete text-based circuit diagram. + + Returns: + str: Full circuit representation characters. + """ + max_line_width = self.get_line_width() + lines = self.make_lines() + data = [] + lines_count = len(self.sorted_qubits) * 2 + 1 + start_qubits = copy.deepcopy(lines[0]) + current_data = [lines[0][i] for i in range(lines_count)] + current_width = 0 + for moment in lines[1:]: + + moment_len = len(''.join(moment[0])) + if moment_len + current_width > max_line_width: + data.append(current_data) + for d in current_data: + d.append(BoxChar.RIGHT_ARROW) + current_data = [] + for i in range(lines_count): + s = [BoxChar.LEFT_ARROW] + s.extend(start_qubits[i]) + current_data.append(s) + current_width = 0 + for i, line in enumerate(moment): + current_data[i].extend(line) + current_width += moment_len + data.append(current_data) + t = [] + for lines in data: + for line in lines: + t.extend(line) + t.append('\n') + t.append('\n') + return ''.join(t) + + # pylint: disable=too-many-locals + def make_lines(self) -> list[list[list[str]]]: + """ + Constructs the circuit visualization line structure. + + Builds a 3-level nested list representing the circuit layout: + - Outer list: Moments (vertical slices of parallel operations) + - Middle list: Text lines per moment (including qubit labels and connections) + - Inner list: Character sequences for each line segment + + Returns: + List of moments, each containing lists of text lines with box characters. + Structure details: + - Even indices: Spacer lines between qubits + - Odd indices: Qubit lines with labels and gate symbols + - First element: Initial qubit label headers + - Subsequent elements: Circuit moments with gate representations + """ + lines = [] + qubit_len = max(len(str(q.index)) for q in self.sorted_qubits) + empty_line = ' ' * (qubit_len + 6) + lines_count = len(self.sorted_qubits) * 2 + 1 + # qubits + start_lines = [] + for qubit in self.sorted_qubits: + start_lines.extend([ + [empty_line], + [''.join([' ', f'Q{qubit.index}'.rjust(qubit_len + 1), + ': ', BoxChar.LEFT_RIGHT * 2])] + ]) + start_lines.append([empty_line]) + lines.append(start_lines) + + # instructions + for moment in self.generate_moment(): + # before moment, add one symbol + moment_lines = [[BoxChar.LEFT_RIGHT if i % 2 == 1 else ' '] for i in range(lines_count)] + + # drawer one moment, maybe multi columns + columns = self.moment_to_columns(moment) + for column in columns: + column_lines = self.draw_column(column) + for line_i, line in enumerate(column_lines): + moment_lines[line_i].extend(line) + + # after moment, add one symbol + for i, line in enumerate(moment_lines): + line.append(BoxChar.LEFT_RIGHT if i % 2 == 1 else ' ') + + # mark many column as one moment + col_len = len(columns) + if col_len > 1: + s = BoxChar.LEFT_RIGHT * (len(''.join(moment_lines[0])) - 2) + moment_lines[0] = [BoxChar.BOTTOM_RIGHT, s, BoxChar.BOTTOM_LEFT] + moment_lines[-1] = [BoxChar.TOP_RIGHT, s, BoxChar.TOP_LEFT] + lines.append(moment_lines) + return lines + + def draw_column(self, column: list[InstructionData]): + """ + Processes a vertical column of parallel operations + + Args: + column: Group of non-overlapping operations from the same moment + + Returns: + list: Formatted lines ready for insertion into main drawing + """ + max_width = 1 + + # container + lines = [[] for _ in range(len(self.sorted_qubits) * 2 + 1)] + # draw every InstructionData + for ins in column: + match len(ins.qubits): + case 1: + lines = self.draw_single_gate(ins, lines) + case _: + lines = self.draw_multi_gate(ins, lines) + # calculate max width + for line in lines: + for s in line: + max_width = max(max_width, len(s)) + # fit max width + empty_line = ' ' * max_width + left_right_line = BoxChar.LEFT_RIGHT * max_width + for i, line in enumerate(lines): + if i % 2 == 0: + if line: + lines[i] = [line[0].center(max_width)] + else: + lines[i].append(empty_line) + else: + if line: + lines[i] = [line[0].center(max_width, BoxChar.LEFT_RIGHT)] + else: + lines[i].append(left_right_line) + + return lines + + def draw_single_gate(self, ins: InstructionData, lines: list[list[str]]): + """ + Handles single-qubit gate visualization + + Args: + ins: Gate instruction to render + lines: Current drawing lines state + + Returns: + Updated lines with gate symbol placed on target qubit line + """ + lines[self.qubit_line_no(ins.qubits[0])].append(str(ins.instruction)) + return lines + + def draw_multi_gate(self, ins: InstructionData, lines: list[list[str]]): + """ + Visualizes multi-qubit operations with vertical connections + + Special cases: + - SWAP: Uses 'X' connection symbols + - CZ: Uses solid dots + - Barriers: Vertical lines spanning involved qubits + - Controlled gates: Differentiates control/target qubits + + Args: + ins: Multi-qubit instruction to render + lines: Current drawing lines state + + Returns: + Updated lines with gate symbols and connection lines + """ + if isinstance(ins.instruction, SWAP): + lines[self.qubit_line_no(ins.qubits[0])].append(BoxChar.CONNECT) + lines[self.qubit_line_no(ins.qubits[1])].append(BoxChar.CONNECT) + elif isinstance(ins.instruction, CZ): + lines[self.qubit_line_no(ins.qubits[0])].append(BoxChar.DOT) + lines[self.qubit_line_no(ins.qubits[1])].append(BoxChar.DOT) + elif isinstance(ins.instruction, Barrier): + for qubit in ins.qubits: + idx = self.qubit_line_no(qubit) + if idx not in (1, len(lines) - 1): + lines[idx - 1].append(BoxChar.TOP_BOTTOM) + lines[idx].append(BoxChar.TOP_BOTTOM) + # Barrier, No vertical connections required + return lines + elif isinstance(ins.instruction, ControlledGate): + for index, qubit in enumerate(ins.qubits): + s = BoxChar.DOT if index in ins.instruction.control_index \ + else str(ins.instruction.base_gate) + lines[self.qubit_line_no(qubit)].append(s) + + # add connect vertical line + qubit_index = [self.qubit_line_no(q) for q in ins.qubits] + min_index, max_index = min(qubit_index), max(qubit_index) + for idx in range(min_index + 1, max_index): + lines[idx].append(BoxChar.TOP_BOTTOM_LEFT_RIGHT if idx % 2 == 1 + else BoxChar.TOP_BOTTOM) + + return lines + + def get_line_width(self): + """ + Determines the effective display width for circuit rendering. + + Priority order for width determination: + 1. User-specified line_width (if > MIN_LINE_WIDTH) + 2. Current terminal width + 3. DEFAULT_LINE_WIDTH fallback + + Returns: + int: display width in characters. + """ + if self.line_width and self.line_width > self.MIN_LINE_WIDTH: + return self.line_width + # try to get terminal size + width, _ = shutil.get_terminal_size() + if not width: + width = self.DEFAULT_LINE_WIDTH + return width + + +def draw_text( + circuit: Circuit, + qubit_order: list[int | Qubit] = None, + line_width: int = None, +): + """ + Quick-access function for text circuit visualization. + + Args: + circuit (Circuit): Quantum circuit to visualize + qubit_order (list[int | Qubit]): Optional custom qubit arrangement + line_width (int): + + Returns: + str: Ready-to-print circuit diagram + """ + return TextDrawer(circuit, qubit_order, line_width).drawer() diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index cf19c40ffd52e39ef5215d848ea9520d1807236e..0000000000000000000000000000000000000000 --- a/poetry.lock +++ /dev/null @@ -1,1281 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. - -[[package]] -name = "antlr4-python3-runtime" -version = "4.13.1" -description = "ANTLR 4.13.1 runtime for Python 3" -optional = false -python-versions = "*" -files = [ - {file = "antlr4-python3-runtime-4.13.1.tar.gz", hash = "sha256:3cd282f5ea7cfb841537fe01f143350fdb1c0b1ce7981443a2fa8513fddb6d1a"}, - {file = "antlr4_python3_runtime-4.13.1-py3-none-any.whl", hash = "sha256:78ec57aad12c97ac039ca27403ad61cb98aaec8a3f9bb8144f889aa0fa28b943"}, -] - -[[package]] -name = "astroid" -version = "3.2.4" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, - {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "certifi" -version = "2024.7.4" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "contourpy" -version = "1.2.1" -description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false -python-versions = ">=3.9" -files = [ - {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, - {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, - {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, - {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, - {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, - {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, - {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, - {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, - {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, - {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, - {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, -] - -[package.dependencies] -numpy = ">=1.20" - -[package.extras] -bokeh = ["bokeh", "selenium"] -docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] -test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] - -[[package]] -name = "cycler" -version = "0.12.1" -description = "Composable style cycles" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, - {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, -] - -[package.extras] -docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] -tests = ["pytest", "pytest-cov", "pytest-xdist"] - -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "distlib" -version = "0.3.8" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "execnet" -version = "2.1.1" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "filelock" -version = "3.15.4" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "fonttools" -version = "4.53.1" -description = "Tools to manipulate font files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, - {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, - {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, - {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, - {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, - {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, - {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, - {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, - {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, - {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, - {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, - {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, - {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, - {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, -] - -[package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] -graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "pycairo", "scipy"] -lxml = ["lxml (>=4.0)"] -pathops = ["skia-pathops (>=0.5.0)"] -plot = ["matplotlib"] -repacker = ["uharfbuzz (>=0.23.0)"] -symfont = ["sympy"] -type1 = ["xattr"] -ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0)"] -woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] - -[[package]] -name = "identify" -version = "2.6.0" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.7" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "jinja2" -version = "3.1.4" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "kiwisolver" -version = "1.4.5" -description = "A fast implementation of the Cassowary constraint solver" -optional = false -python-versions = ">=3.7" -files = [ - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, - {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, -] - -[[package]] -name = "markupsafe" -version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "matplotlib" -version = "3.9.1" -description = "Python plotting package" -optional = false -python-versions = ">=3.9" -files = [ - {file = "matplotlib-3.9.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ccd6270066feb9a9d8e0705aa027f1ff39f354c72a87efe8fa07632f30fc6bb"}, - {file = "matplotlib-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:591d3a88903a30a6d23b040c1e44d1afdd0d778758d07110eb7596f811f31842"}, - {file = "matplotlib-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd2a59ff4b83d33bca3b5ec58203cc65985367812cb8c257f3e101632be86d92"}, - {file = "matplotlib-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc001516ffcf1a221beb51198b194d9230199d6842c540108e4ce109ac05cc0"}, - {file = "matplotlib-3.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:83c6a792f1465d174c86d06f3ae85a8fe36e6f5964633ae8106312ec0921fdf5"}, - {file = "matplotlib-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:421851f4f57350bcf0811edd754a708d2275533e84f52f6760b740766c6747a7"}, - {file = "matplotlib-3.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b3fce58971b465e01b5c538f9d44915640c20ec5ff31346e963c9e1cd66fa812"}, - {file = "matplotlib-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a973c53ad0668c53e0ed76b27d2eeeae8799836fd0d0caaa4ecc66bf4e6676c0"}, - {file = "matplotlib-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cd5acf8f3ef43f7532c2f230249720f5dc5dd40ecafaf1c60ac8200d46d7eb"}, - {file = "matplotlib-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab38a4f3772523179b2f772103d8030215b318fef6360cb40558f585bf3d017f"}, - {file = "matplotlib-3.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2315837485ca6188a4b632c5199900e28d33b481eb083663f6a44cfc8987ded3"}, - {file = "matplotlib-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0c977c5c382f6696caf0bd277ef4f936da7e2aa202ff66cad5f0ac1428ee15b"}, - {file = "matplotlib-3.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:565d572efea2b94f264dd86ef27919515aa6d629252a169b42ce5f570db7f37b"}, - {file = "matplotlib-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d397fd8ccc64af2ec0af1f0efc3bacd745ebfb9d507f3f552e8adb689ed730a"}, - {file = "matplotlib-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26040c8f5121cd1ad712abffcd4b5222a8aec3a0fe40bc8542c94331deb8780d"}, - {file = "matplotlib-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12cb1837cffaac087ad6b44399d5e22b78c729de3cdae4629e252067b705e2b"}, - {file = "matplotlib-3.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0e835c6988edc3d2d08794f73c323cc62483e13df0194719ecb0723b564e0b5c"}, - {file = "matplotlib-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:44a21d922f78ce40435cb35b43dd7d573cf2a30138d5c4b709d19f00e3907fd7"}, - {file = "matplotlib-3.9.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0c584210c755ae921283d21d01f03a49ef46d1afa184134dd0f95b0202ee6f03"}, - {file = "matplotlib-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11fed08f34fa682c2b792942f8902e7aefeed400da71f9e5816bea40a7ce28fe"}, - {file = "matplotlib-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0000354e32efcfd86bda75729716b92f5c2edd5b947200be9881f0a671565c33"}, - {file = "matplotlib-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db17fea0ae3aceb8e9ac69c7e3051bae0b3d083bfec932240f9bf5d0197a049"}, - {file = "matplotlib-3.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:208cbce658b72bf6a8e675058fbbf59f67814057ae78165d8a2f87c45b48d0ff"}, - {file = "matplotlib-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:dc23f48ab630474264276be156d0d7710ac6c5a09648ccdf49fef9200d8cbe80"}, - {file = "matplotlib-3.9.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3fda72d4d472e2ccd1be0e9ccb6bf0d2eaf635e7f8f51d737ed7e465ac020cb3"}, - {file = "matplotlib-3.9.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:84b3ba8429935a444f1fdc80ed930babbe06725bcf09fbeb5c8757a2cd74af04"}, - {file = "matplotlib-3.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b918770bf3e07845408716e5bbda17eadfc3fcbd9307dc67f37d6cf834bb3d98"}, - {file = "matplotlib-3.9.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f1f2e5d29e9435c97ad4c36fb6668e89aee13d48c75893e25cef064675038ac9"}, - {file = "matplotlib-3.9.1.tar.gz", hash = "sha256:de06b19b8db95dd33d0dc17c926c7c9ebed9f572074b6fac4f65068a6814d010"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -kiwisolver = ">=1.3.1" -numpy = ">=1.23" -packaging = ">=20.0" -pillow = ">=8" -pyparsing = ">=2.3.1" -python-dateutil = ">=2.7" - -[package.extras] -dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -description = "Python library for arbitrary-precision floating-point arithmetic" -optional = false -python-versions = "*" -files = [ - {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, - {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, -] - -[package.extras] -develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] -docs = ["sphinx"] -gmpy = ["gmpy2 (>=2.1.0a4)"] -tests = ["pytest (>=4.6)"] - -[[package]] -name = "networkx" -version = "3.3" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.10" -files = [ - {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, - {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, -] - -[package.extras] -default = ["matplotlib (>=3.6)", "numpy (>=1.23)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["myst-nb (>=1.0)", "numpydoc (>=1.7)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=2.0)", "pygraphviz (>=1.12)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "numpy" -version = "1.26.4" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, -] - -[[package]] -name = "openqasm3" -version = "1.0.0" -description = "Reference OpenQASM AST in Python" -optional = false -python-versions = "*" -files = [ - {file = "openqasm3-1.0.0-py3-none-any.whl", hash = "sha256:d4371737b4a49b0d56248ed3d30766a94000bccfb43303ec9c7ead351a1b6cc3"}, - {file = "openqasm3-1.0.0.tar.gz", hash = "sha256:3f2bb1cca855cff114e046bac22d59adbf9b754cac6398961aa6d22588fb688e"}, -] - -[package.extras] -all = ["antlr4-python3-runtime (>=4.7,<4.14)", "importlib-metadata", "pytest (>=6.0)", "pyyaml"] -parser = ["antlr4-python3-runtime (>=4.7,<4.14)", "importlib-metadata"] -tests = ["pytest (>=6.0)", "pyyaml"] - -[[package]] -name = "packaging" -version = "24.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, -] - -[[package]] -name = "pillow" -version = "10.4.0" -description = "Python Imaging Library (Fork)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, - {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, - {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, - {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, - {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, - {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, - {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, - {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, - {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, - {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] -fpx = ["olefile"] -mic = ["olefile"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] -xmp = ["defusedxml"] - -[[package]] -name = "platformdirs" -version = "4.2.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "3.8.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, - {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pylint" -version = "3.2.6" -description = "python code static checker" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "pylint-3.2.6-py3-none-any.whl", hash = "sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f"}, - {file = "pylint-3.2.6.tar.gz", hash = "sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3"}, -] - -[package.dependencies] -astroid = ">=3.2.4,<=3.3.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pyparsing" -version = "3.1.2" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pytest" -version = "8.3.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-html" -version = "4.1.1" -description = "pytest plugin for generating HTML reports" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_html-4.1.1-py3-none-any.whl", hash = "sha256:c8152cea03bd4e9bee6d525573b67bbc6622967b72b9628dda0ea3e2a0b5dd71"}, - {file = "pytest_html-4.1.1.tar.gz", hash = "sha256:70a01e8ae5800f4a074b56a4cb1025c8f4f9b038bba5fe31e3c98eb996686f07"}, -] - -[package.dependencies] -jinja2 = ">=3.0.0" -pytest = ">=7.0.0" -pytest-metadata = ">=2.0.0" - -[package.extras] -docs = ["pip-tools (>=6.13.0)"] -test = ["assertpy (>=1.1)", "beautifulsoup4 (>=4.11.1)", "black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "pytest-mock (>=3.7.0)", "pytest-rerunfailures (>=11.1.2)", "pytest-xdist (>=2.4.0)", "selenium (>=4.3.0)", "tox (>=3.24.5)"] - -[[package]] -name = "pytest-metadata" -version = "3.1.1" -description = "pytest plugin for test session metadata" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, - {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, -] - -[package.dependencies] -pytest = ">=7.0.0" - -[package.extras] -test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>=3.24.5)"] - -[[package]] -name = "pytest-xdist" -version = "3.6.1" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, -] - -[package.dependencies] -execnet = ">=2.1" -pytest = ">=7.0.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "requests" -version = "2.32.3" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sympy" -version = "1.13.1" -description = "Computer algebra system (CAS) in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, - {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, -] - -[package.dependencies] -mpmath = ">=1.1.0,<1.4" - -[package.extras] -dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] - -[[package]] -name = "tabulate" -version = "0.9.0" -description = "Pretty-print tabular data" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] - -[package.extras] -widechars = ["wcwidth"] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tomlkit" -version = "0.13.0" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, - {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "urllib3" -version = "2.2.2" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "virtualenv" -version = "20.26.3" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "135acdd759591b012f60b10d646e4389e66174bba22072fb5bbf5fee794c169c" diff --git a/pyproject.toml b/pyproject.toml index 23a477c90f9af6a17c490f0f1856bbb538143cb3..7646509b18ec0f63332142b82c1281cb220ec606 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [project] name = "cqlib" -version = "1.2.1" description = "China Quantum Library" authors = [ { name = "China Telecom Quantum Group Quantum Computing Team", email = "tianyan@chinatelecom.cn" }, @@ -30,27 +29,11 @@ keywords = [ "quantum", "sdk", ] +dynamic = ["version", "dependencies",] -dependencies = [ - "matplotlib>=3.7.5", - "networkx>=3.0", - "numpy>=2.1.2", - "requests>=2.31.0", - "sympy>=1.12.0", - "tabulate>=0.9.0", - "openqasm3[parser]>=1.0.0", - "antlr4-python3-runtime>=4.13.2" -] - -[project.optional-dependencies] -dev = [ - "pytest>=8.0.2", - "pylint>=3.2.2", - "pre-commit>=3.7.1", - "pytest-xdist>=3.6.1", - "pytest-html>=4.1.1", - "antlr4-tools>=0.2.1", -] +[tool.setuptools.dynamic] +version = { file = "cqlib/VERSION.txt" } +dependencies = {file = "requirements.txt" } [project.urls] Homepage = "https://qc.zdxlz.com/" @@ -59,7 +42,7 @@ Repository = "https://gitee.com/cq-lib/cqlib" Issues = "https://gitee.com/cq-lib/cqlib/issues" [build-system] -requires = ["setuptools>=61.0", "wheel", 'delocate', "poetry-core", 'numpy==2.1.2', 'pipx'] +requires = ["setuptools>=61.0", "wheel", 'delocate', 'pipx'] build-backend = "setuptools.build_meta" [tool.setuptools] @@ -68,6 +51,7 @@ packages = ["cqlib"] [tool.cibuildwheel] before-all = "uname -a" skip = "pp* cp36-* cp37-* cp38-* cp39-* *musllinux*" +test-command = "python -c \"import cqlib.simulator.statevector_simulator_c\"" [tool.cibuildwheel.linux] repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel} && pipx run abi3audit --strict --report {wheel}" @@ -77,9 +61,13 @@ environment = "MACOSX_DEPLOYMENT_TARGET=12" repair-wheel-command = "delocate-wheel -w {dest_dir} -v {wheel} && pipx run abi3audit --strict --report {wheel}" [tool.cibuildwheel.windows] -repair-wheel-command = ["pip install delvewheel", +environment = { CC="gcc", CXX="g++" } +repair-wheel-command = [ + "pip install delvewheel", 'delvewheel repair -w {dest_dir} {wheel}', - "pipx run abi3audit --strict --report {wheel}"] +# windows 上不使用 abi3,而是每个 Python 版本打包一个 wheel 文件 +# "pipx run abi3audit --strict --report {wheel}" +] [tool.setuptools.package-data] diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000000000000000000000000000000000000..2fdfbc8b383203dfc25536175228ccbbbf44a889 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,10 @@ +pytest>=8.0.2 +pylint>=3.2.2 +pre-commit>=3.7.1 +pytest-xdist>=3.6.1 +pytest-html>=4.1.1 +antlr4-tools>=0.2.1 +delocate +poetry-core +pipx +psutil \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..07f6331e3f9b83acd366d8a5dca11fec1647ab58 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +matplotlib >= 3.7.5 +networkx>=3.0 +numpy>=2.1.2 +requests>=2.31.0 +sympy>=1.12.0 +tabulate>=0.9.0 +openqasm3[parser]>=1.0.0 +antlr4-python3-runtime>=4.13.2 +rustworkx>=0.16.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 67b4d5883e2e340429d551ea3e4faea19e3d4ca9..61672a1af815b38d49ca91e992ae69885785f01d 100644 --- a/setup.py +++ b/setup.py @@ -3,44 +3,54 @@ import platform import sys import sysconfig -import numpy - -from setuptools import setup, Extension, find_packages -# from distutils.command.build_ext import build_ext as build_ext_orig +from setuptools import setup, Extension, find_packages, find_namespace_packages from setuptools.command.build_ext import build_ext as build_ext_orig python_paths = sysconfig.get_paths() -python_lib_dir = sysconfig.get_config_var('LIBDIR') +print(f'python_paths: {python_paths}') + +library_dirs = [] +py_limited_api = True if sys.platform == 'win32': - architecture = platform.machine() - cibw_arch = os.environ.get('CIBW_ARCHS_WINDOWS') - if cibw_arch == 'AMD64': - p = r"C:\Users\runneradmin\.nuget\packages\python\3.10.11\tools\libs" - elif cibw_arch == 'arm64': - raise SystemError("当前系统是 ARM64 架构,暂不支持") - elif cibw_arch == 'x86': - p = r"C:\Users\runneradmin\.nuget\packages\pythonx86\3.10.11\tools" + v = sys.version_info + if v.minor == 10: + version = '3.10.11' + elif v.minor == 11: + version = '3.11.9' + elif v.minor == 12: + version = '3.12.9' + elif v.minor == 13: + version = '3.13.2' else: - raise SystemError(f"未知架构: {architecture}") + raise ValueError('不知道的 Python 版本') + arch = os.getenv('CIBW_ARCHS_WINDOWS', 'AMD64') + print(f'arch: {arch}') + if arch == 'AMD64': + arch = 'x64' + python_lib = os.path.join(r'C:\hostedtoolcache\windows\Python', version, arch, 'libs') + print(f'python_lib: {python_lib} {os.path.exists(python_lib)} {os.walk(python_lib)}') os.environ['CC'] = 'gcc' os.environ['LD'] = 'ld' - extra_compile_args = ['-fopenmp'] - extra_link_args = [ - '-fopenmp', - '-lgomp', - '-lpython310', - f"-L{p}", - ] - + extra_compile_args = ['-O3', '-fopenmp', '-static'] + extra_link_args = ['-static', '-fopenmp', '-Wl,-Bstatic', '-lgomp'] + library_dirs = [python_lib] + py_limited_api = False elif sys.platform == 'darwin': os.environ['CC'] = 'gcc-14' os.environ['MACOSX_DEPLOYMENT_TARGET'] = '12.0' libomp_prefix = os.environ.get('LIBOMP_PREFIX', '/opt/homebrew/opt/libomp') - extra_compile_args = ['-fopenmp', f'-I{libomp_prefix}/include'] - extra_link_args = [f'-L{libomp_prefix}/lib', '-fopenmp', - f'-Wl,-rpath,{libomp_prefix}/lib'] + extra_compile_args = [ + '-fopenmp', + f'-I{libomp_prefix}/include' + ] + extra_link_args = [ + f'-L{libomp_prefix}/lib', + '-fopenmp', + '-lomp', + f'-Wl,-rpath,{libomp_prefix}/lib' + ] if platform.machine() == 'arm64': # 针对 ARM 架构(Apple Silicon) extra_compile_args.append('-march=armv8-a') @@ -67,25 +77,25 @@ ext_modules = [ include_dirs=[ 'cqlib/simulator/c_fusion/include', python_paths['include'], - numpy.get_include() ], - library_dirs=[python_paths['platlib']], # 链接库文件所在目录 + library_dirs=[python_paths['platlib']] + library_dirs, # 链接库文件所在目录 sources=[ 'cqlib/simulator/c_fusion/src/c_fusion.c', 'cqlib/simulator/c_fusion/src/state_vector.c', ], - define_macros=[('Py_LIMITED_API', '0x030A0000')], - py_limited_api=True, - # extra_compile_args=extra_compile_args, - extra_compile_args=extra_compile_args + ['-DPy_LIMITED_API=0x030A0000'], + define_macros=[('Py_LIMITED_API', '0x030A0000')] if py_limited_api else [], + py_limited_api=py_limited_api, + extra_compile_args=extra_compile_args + (['-DPy_LIMITED_API=0x030A0000'] if py_limited_api else []), extra_link_args=extra_link_args, ), ] +print(f"find_namespace_packages: {find_namespace_packages(include=['cqlib', 'cqlib.*'])}") +print(f"find_packages: {find_packages(include=['cqlib', 'cqlib.*'])}") setup( name='cqlib', ext_modules=ext_modules, - packages=find_packages(include=['cqlib', 'cqlib.*']), + packages=find_namespace_packages(include=['cqlib', 'cqlib.*']), package_data={ 'cqlib': ['*'], }, @@ -94,7 +104,6 @@ setup( exclude_package_data={ 'cqlib.simulator': ['*.c', '*.h'], }, - py_limited_api='3.10', cmdclass={'build_ext': CustomBuildExt}, - options={"bdist_wheel": {"py_limited_api": "cp310"}}, + options={"bdist_wheel": {"py_limited_api": "cp310"} if py_limited_api else {}}, ) diff --git a/tests/circuit/__init__.py b/tests/circuit/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c32c814b80e89ddb81b1dac29fc75336112c257c 100644 --- a/tests/circuit/__init__.py +++ b/tests/circuit/__init__.py @@ -0,0 +1,12 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. diff --git a/tests/circuit/test_circuit.py b/tests/circuit/test_circuit.py index 06c8ecd0d092d0eaf6c1119521bed9e3beb5f08d..205b1e4baacbd5be22eb709156f18c86eeac51c6 100644 --- a/tests/circuit/test_circuit.py +++ b/tests/circuit/test_circuit.py @@ -17,8 +17,7 @@ Test Circuit """ import pytest -from cqlib.circuits import Circuit, Qubit, gates, Parameter, CouplingQubit, \ - PulseParameter, Waveform +from cqlib.circuits import Circuit, Qubit, gates, Parameter def test_basic_circuit(): @@ -258,51 +257,13 @@ def test_load_circuit(): def test_load_circuit_params(): """test load circuit with param, but will fail""" qcis = "H Q1\nRX Q0 theta\nRX Q1 2.2\nM Q0\nM Q1" - with pytest.raises(ValueError): + with pytest.raises(ValueError) as exec_info: Circuit.load(qcis) - - -def test_pulse(): - """test load circuit with param, but will fail""" - q0 = Qubit(0) - q6 = Qubit(6) - g0 = CouplingQubit(0) - circuit = Circuit([q0, q6, g0]) - - circuit.h(q0) - circuit.h(q6) - - pp = PulseParameter( - waveform=Waveform.FLATTOP, - t_start=-1, - length=100, - amplitude=0, - frequency=0, - phase=0, - drag_alpha=0, - others=[4] - ) - circuit.pulse(qubit=g0, pulse_param=pp) - circuit.measure_all() - target = """H Q0 -H Q6 -PLS G0 1 -1 100 0 0 0 0 4 -M Q0 -M Q6""" - assert circuit.qcis == target - - -def test_load_pulse(): - qcis = """H Q0 -H Q6 -PLS G0 1 -1 100 0 0 0 0 4 -M Q0 -M Q6""" - circuit = Circuit.load(qcis) - assert circuit.qcis == qcis + assert 'Invalid instruction format: RX Q0 theta' in exec_info.value.args[0] def test_load_e(): + """ test load Scientific notation """ qcis = """ Y2M Q0 RZ Q0 3.4641020674186507e-07 @@ -322,6 +283,7 @@ X2P Q2""" def test_add(): + """ test parameters add """ qcis = "H Q0\nCZ Q0 Q1" c1 = Circuit(2, parameters=['gamma']) c1.h(0) @@ -341,7 +303,9 @@ def test_add(): assert c1.qcis == '\n'.join([qcis, qcis]) assert c2.qcis == qcis + def test_copy(): + """ test copy circuit """ phi = Parameter('phi') theta = Parameter('theta') circuit = Circuit(2, [phi, theta]) diff --git a/tests/circuit/test_commutation.py b/tests/circuit/test_commutation.py index da0414c4c3de4441d00d6aa03d128610c32e289b..d87ba55eac8e8bf046bea5c50f422e4baa7e1bd8 100644 --- a/tests/circuit/test_commutation.py +++ b/tests/circuit/test_commutation.py @@ -153,8 +153,10 @@ M Q1 M Q2""" c1 = Circuit.load(qcis) data = circuit_commutator(c1) - res = '[(CZ Q0 Q1, CZ Q1 Q2), (H Q2, H Q2), (CZ Q0 Q1, CZ Q1 Q2), (CZ Q0 Q1, CZ Q1 Q2)]' - assert str(data) == res + res = ['(CZ Q0 Q1, CZ Q1 Q2)', '(H Q2, H Q2)', '(CZ Q0 Q1, CZ Q1 Q2)', '(CZ Q0 Q1, CZ Q1 Q2)'] + assert len(data) == len(res) + for item in data: + assert str(item) in res # pylint: disable=redefined-outer-name diff --git a/tests/circuit/test_dag.py b/tests/circuit/test_dag.py new file mode 100644 index 0000000000000000000000000000000000000000..fda2af3048cf0e1c195a3e802ef8ec15c1a2d6a4 --- /dev/null +++ b/tests/circuit/test_dag.py @@ -0,0 +1,92 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test DAG Circuit """ +import pytest +# pylint: disable=E0611 +from rustworkx import PyDiGraph + +from cqlib.circuits.circuit import Circuit +from cqlib.circuits.instruction_data import InstructionData +from cqlib.circuits.gates import H, CX +from cqlib.circuits.qubit import Qubit +from cqlib.circuits.dag import circuit_to_dag, dag_to_circuit +from cqlib.exceptions import CqlibError + + +class TestCircuitToDAG: + """test Circuit object to dag""" + + def test_basic(self): + """ basic """ + circuit = Circuit(0) + dag = circuit_to_dag(circuit) + assert len(dag.nodes()) == 0 + + circuit = Circuit(2) + circuit.h(0) + circuit.h(1) + circuit.cx(0, 1) + dag = circuit_to_dag(circuit) + assert str(dag.nodes()) == '[H Q0, H Q1, CX Q0 Q1]' + assert str(dag.edges()) == "['H Q0-CX Q0 Q1', 'H Q1-CX Q0 Q1']" + + def test_same_ins(self): + """ test same instruction """ + circuit = Circuit(3) + circuit.h(0) + circuit.h(1) + circuit.h(0) + circuit.h(1) + circuit.cx(0, 1) + circuit.measure_all() + dag = circuit_to_dag(circuit) + assert len(dag.nodes()) == 8 + assert len(dag.edges()) == 6 + assert str(dag.nodes()) == '[H Q0, H Q1, H Q0, H Q1, CX Q0 Q1, M Q0, M Q1, M Q2]' + assert str(dag.edges()) == ("['H Q0-H Q0', 'H Q1-H Q1', 'H Q0-CX Q0 Q1', " + "'H Q1-CX Q0 Q1', 'CX Q0 Q1-M Q0', 'CX Q0 Q1-M Q1']") + + +class TestDAGToCircuit: + """test dag to Circuit object """ + + def test_basic(self): + """ basic """ + dag = PyDiGraph(check_cycle=True) + h0 = dag.add_node(InstructionData(H(), [Qubit(0)])) + h1 = dag.add_node(InstructionData(H(), [Qubit(1)])) + cx_0_1 = dag.add_node(InstructionData(CX(), [Qubit(1), Qubit(0)])) + dag.add_edge(h0, cx_0_1, edge='e1') + dag.add_edge(h1, cx_0_1, edge='e2') + + circuit = dag_to_circuit(dag) + + assert len(circuit.qubits) == 2 + assert set(circuit.qubits) == {Qubit(0), Qubit(1)} + assert len(circuit.circuit_data()) == 3 + assert circuit.as_str() == 'H Q1\nH Q0\nCX Q1 Q0' + + def test_cycle_error(self): + """ + test cycle graph error + """ + dag = PyDiGraph() + h0 = dag.add_node(InstructionData(H(), [Qubit(0)])) + cx_0_1 = dag.add_node(InstructionData(CX(), [Qubit(1), Qubit(0)])) + dag.add_edge(h0, cx_0_1, edge='e1') + dag.add_edge(cx_0_1, h0, edge='e2') + + with pytest.raises(CqlibError) as exc_info: + dag_to_circuit(dag) + assert "The provided graph must be acyclic to form a valid" in str(exc_info.value) diff --git a/tests/circuit/test_qubit.py b/tests/circuit/test_qubit.py index 3eb515722667507af7a36cfc67f0714de9cf0dce..25ea7b4c0c3f12942b22fb249f72c72fd5e17475 100644 --- a/tests/circuit/test_qubit.py +++ b/tests/circuit/test_qubit.py @@ -15,7 +15,7 @@ Test qubits in the circuit """ -from cqlib.circuits import Circuit, Qubit, CouplingQubit +from cqlib.circuits import Circuit, Qubit def test_number_of_qubit(): @@ -56,17 +56,3 @@ def test_qubit_singleton(): circuit = Circuit(1) assert q0 is q0_1 assert q0 is circuit.qubits[0] - - -def test_couple_qubit(): - """test couple Qubit""" - coupling_bit_0 = CouplingQubit(0) - coupling_bit_1 = CouplingQubit(0) - assert coupling_bit_0 is coupling_bit_1 - assert str(coupling_bit_0) == 'G0' - assert repr(coupling_bit_0) == 'CouplingQubit(0)' - qbit_0 = Qubit(0) - qbit_1 = Qubit(0) - assert qbit_0 is qbit_1 - assert repr(qbit_0) == 'Qubit(0)' - assert qbit_0 is not coupling_bit_0 diff --git a/tests/pulse/test_instruction.py b/tests/pulse/test_instruction.py new file mode 100644 index 0000000000000000000000000000000000000000..0fe0221a700afe8dc925dd1b55a0edf312009bee --- /dev/null +++ b/tests/pulse/test_instruction.py @@ -0,0 +1,128 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Tests for pulse instruction""" + +import pytest + +from cqlib.exceptions import CqlibError +from cqlib.pulse import WaveformType, Waveform, PZ, PXY, PZ0, G + +# pylint: disable=missing-function-docstring + +class TestPXY: + """Test cases for the PXY pulse class.""" + + def test_cosine(self): + waveform = Waveform.create(WaveformType.COSINE, length=10, amplitude=0.1) + with pytest.raises(CqlibError) as exec_info: + PXY(waveform=waveform) + assert "PXY must have phase and drag_alpha params" in exec_info.value.args[0] + waveform = Waveform.create(WaveformType.COSINE, length=10, amplitude=0.1, + phase=2, drag_alpha=0.2) + pxy = PXY(waveform=waveform) + assert str(pxy) == 'PXY(0,10,0.1,2,0.2)' + + def test_flattop(self): + waveform = Waveform.create(WaveformType.FLATTOP, length=10, amplitude=0.1, edge=1, + phase=2, drag_alpha=0.2) + pxy = PXY(waveform=waveform) + assert str(pxy) == 'PXY(1,10,0.1,2,0.2,1)' + + def test_slepian(self): + waveform = Waveform.create(WaveformType.SLEPIAN, length=10, amplitude=0.1, phase=2, + drag_alpha=0.2, thf=0.1, thi=0.2, lam2=0.3, lam3=1) + pxy = PXY(waveform=waveform) + assert str(pxy) == 'PXY(2,10,0.1,2,0.2,0.1,0.2,0.3,1)' + + def test_numeric(self): + waveform = Waveform.create(WaveformType.NUMERIC, length=10, amplitude=0.1, phase=2, + drag_alpha=0.2, params=[0.1, 0.2, 0.3]) + with pytest.raises(CqlibError) as exec_info: + PXY(waveform=waveform) + assert 'The length of its list must >=6 and must be even.' in exec_info.value.args[0] + waveform = Waveform.create(WaveformType.NUMERIC, length=10, amplitude=0.1, phase=2, + drag_alpha=0.2, params=[0.1, 0.2, 0.3, 0.11, 0.22, 0.33]) + pxy = PXY(waveform=waveform) + assert str(pxy) == 'PXY(3,10,0.1,2,0.2,0.1,0.2,0.3,0.11,0.22,0.33)' + + +class TestPZ: + """Test cases for the PZ pulse class.""" + + def test_cosine(self): + waveform = Waveform.create(WaveformType.COSINE, length=10, amplitude=0.1) + pz = PZ(waveform=waveform) + assert str(pz) == 'PZ(0,10,0.1)' + + def test_flattop(self): + waveform = Waveform.create(WaveformType.FLATTOP, length=10, amplitude=0.1, edge=1) + pz = PZ(waveform=waveform) + assert str(pz) == 'PZ(1,10,0.1,1)' + + def test_slepian(self): + waveform = Waveform.create(WaveformType.SLEPIAN, length=10, amplitude=0.1, + thf=0.1, thi=0.2, lam2=0.3, lam3=1) + pz = PZ(waveform=waveform) + assert str(pz) == 'PZ(2,10,0.1,0.1,0.2,0.3,1)' + + def test_numeric(self): + waveform = Waveform.create(WaveformType.NUMERIC, length=10, amplitude=0.1, + params=[0.1, 0.2, 0.3]) + pz = PZ(waveform=waveform) + assert str(pz) == 'PZ(3,10,0.1,0.1,0.2,0.3)' + + with pytest.raises(CqlibError) as exec_info: + Waveform.create(WaveformType.NUMERIC, length=10, amplitude=0.1, params=[0.1, 0.2]) + assert ('The numerical list of data pulses is in the form of [d0, d1, ..., dn]' + ', and the list length must be >= 3') in exec_info.value.args[0] + + +class TestPZ0: + """Test cases for the PZ0 pulse class.""" + + def test_cosine(self): + waveform = Waveform.create(WaveformType.COSINE, length=10, amplitude=0.1) + pz0 = PZ0(waveform=waveform) + assert str(pz0) == 'PZ0(0,10,0.1)' + + def test_flattop(self): + waveform = Waveform.create(WaveformType.FLATTOP, length=10, amplitude=0.1, edge=1) + pz = PZ0(waveform=waveform) + assert str(pz) == 'PZ0(1,10,0.1,1)' + + def test_slepian(self): + waveform = Waveform.create(WaveformType.SLEPIAN, length=10, amplitude=0.1, + thf=0.1, thi=0.2, lam2=0.3, lam3=1) + pz0 = PZ0(waveform=waveform) + assert str(pz0) == 'PZ0(2,10,0.1,0.1,0.2,0.3,1)' + + def test_numeric(self): + waveform = Waveform.create(WaveformType.NUMERIC, length=10, amplitude=0.1, + params=[0.1, 0.2, 0.3]) + pz0 = PZ0(waveform=waveform) + assert str(pz0) == 'PZ0(3,10,0.1,0.1,0.2,0.3)' + + +class TestG: + """Test cases for the G (global coupling) pulse class.""" + + def test_g(self): + g = G(length=100, coupling_strength=-3) + assert str(g) == 'G(100,-3)' + + def test_g1(self): + with pytest.raises(TypeError) as exec_info: + # pylint: disable=unexpected-keyword-arg,no-value-for-parameter + G(waveform=100) + assert "got an unexpected keyword argument 'waveform'" in exec_info.value.args[0] diff --git a/tests/pulse/test_pulse_circuit.py b/tests/pulse/test_pulse_circuit.py new file mode 100644 index 0000000000000000000000000000000000000000..ea0d4e4de175c64aab680b03b21df635ca5a0c59 --- /dev/null +++ b/tests/pulse/test_pulse_circuit.py @@ -0,0 +1,58 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" testcase for pulse circuit """ + +from cqlib.circuits.qubit import Qubit +from cqlib.pulse import PulseCircuit, CouplerQubit, Waveform, WaveformType + + +def test_coupler_qubit(): + """ test coupler qubits""" + cq = CouplerQubit(1) + assert cq.index == 1 + assert str(cq) == 'G1' + + +def test_pulse_circuit(): + """ test base pulse circuit """ + pc = PulseCircuit(qubits=[0], coupler_qubits=[0]) + assert pc.qubits == [Qubit(0)] + assert pc.coupler_qubits == [CouplerQubit(0)] + + +def test_base_qcis(): + """ test base qcis """ + pc = PulseCircuit(qubits=[0], coupler_qubits=[0]) + pc.h(0) + pc.measure(0) + assert pc.qcis == 'H Q0\nM Q0' + + pc = PulseCircuit(qubits=[0], coupler_qubits=[0]) + waveform = Waveform.create(WaveformType.COSINE, length=100, amplitude=0.2, + phase=1.1, drag_alpha=0.1) + pc.pxy(0, waveform) + pc.pz(CouplerQubit(0), Waveform.create(WaveformType.COSINE, length=100, amplitude=0.2)) + pc.measure(0) + assert pc.qcis == 'PXY Q0 0 100 0.2 1.1 0.1\nPZ G0 0 100 0.2\nM Q0' + + +def test_load_pulse(): + """ test load pulse qcis """ + qcis = """PXY Q0 0 100 0.2 1.1 0.1 +PZ G0 0 100 0.2 +PZ0 Q1 1 100 0.2 10 +G G0 10 1000 +M Q0""" + qc = PulseCircuit.load(qcis) + assert qc.as_str() == qcis diff --git a/tests/pulse/test_waveform.py b/tests/pulse/test_waveform.py new file mode 100644 index 0000000000000000000000000000000000000000..16f2dfcec756815e923b0f75d97cf7d364d1819d --- /dev/null +++ b/tests/pulse/test_waveform.py @@ -0,0 +1,63 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""test waveform""" + +# pylint: disable=no-member + +from cqlib.pulse import Waveform, WaveformType + + +def test_cosine(): + """test cosine waveform""" + length = 10 + amplitude = 1 + cos = Waveform.create(WaveformType.COSINE, length=length, amplitude=amplitude) + assert cos.data == [WaveformType.COSINE, length, amplitude] + assert str(cos) == f'{WaveformType.COSINE.value} {length} {amplitude}' + + +def test_flattop(): + """test flattop waveform""" + length = 10 + amplitude = 1 + edge = 1 + waveform = Waveform.create(WaveformType.FLATTOP, length=length, amplitude=amplitude, edge=edge) + assert waveform.data == [WaveformType.FLATTOP, length, amplitude, edge] + assert str(waveform) == f'{WaveformType.FLATTOP.value} {length} {amplitude} {edge}' + + +def test_slepian(): + """test slepian waveform""" + length = 10 + amplitude = 1 + thf, thi, lam2, lam3 = 0.1, 0.2, 0.3, 0.4 + waveform = Waveform.create(WaveformType.SLEPIAN, length=length, amplitude=amplitude, + thf=thf, thi=thi, lam2=lam2, lam3=lam3) + assert waveform.data == [WaveformType.SLEPIAN, length, amplitude, thf, thi, lam2, lam3] + assert str(waveform) == (f'{WaveformType.SLEPIAN.value} {length} ' + f'{amplitude} {thf} {thi} {lam2} {lam3}') + + +def test_numeric(): + """test numeric waveform""" + length = 10 + amplitude = 1 + params = 0.1, 0.2, 0.3, 0.4 + waveform = Waveform.create(WaveformType.NUMERIC, length=length, amplitude=amplitude, + params=params) + d = [WaveformType.NUMERIC.value, length, amplitude] + d.extend(params) + assert waveform.data == d + assert str(waveform) == (f'{WaveformType.NUMERIC.value} {length} ' + f'{amplitude} {" ".join(map(str, params))}') diff --git a/tests/simulator/test_statevector_sim.py b/tests/simulator/test_statevector_sim.py index 17eec02cd51610c678117f2d5d475a733ea0819d..7c4dee1c13d42497f620de8e336eda6598f068a3 100644 --- a/tests/simulator/test_statevector_sim.py +++ b/tests/simulator/test_statevector_sim.py @@ -12,10 +12,12 @@ # that they have been altered from the originals. """Test statevector simulator""" - +import gc import math +import os import numpy as np +import psutil from cqlib import Circuit, Parameter from cqlib.simulator import StatevectorSimulator @@ -697,3 +699,39 @@ def test_u(): '11': 0.67208884 - 0.02346986j } all_close(state, s1) + + +def test_memory(): + """test memory recycling""" + process = psutil.Process(os.getpid()) + initial_mem = process.memory_info().rss + + def run_simulation(num_qubits): + c = Circuit(num_qubits) + for i in range(num_qubits): + c.h(i) + c.measure_all() + + sim = StatevectorSimulator(c) + state = sim.statevector() + return sim, state + + for i in range(10, 21, 2): + print(f'\nqubits: {i}') + sim, state = run_simulation(i) + peak_mem = process.memory_info().rss + del sim + del state + gc.collect() + final_mem = process.memory_info().rss + + used_mem = peak_mem - initial_mem + remaining_mem = final_mem - initial_mem + print(f"initial: {initial_mem / 1024 / 1024:.2f} MB") + print(f"peak: {peak_mem / 1024 / 1024:.2f} MB (+{used_mem / 1024 / 1024:.2f} MB)") + print(f"released: {final_mem / 1024 / 1024:.2f} MB (remain: {remaining_mem / 1024 / 1024:.2f} MB)") + + # Allow a maximum of 10 MB of temporary residual data + max_allowed_leak = 10 * 1024 * 1024 + assert remaining_mem < max_allowed_leak, \ + f"Memory leak detected. Residual memory: {remaining_mem / 1024 / 1024:.2f} MB"