modularq 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modularq-1.1.0/AUTHORS +12 -0
- modularq-1.1.0/LICENSE +16 -0
- modularq-1.1.0/PKG-INFO +145 -0
- modularq-1.1.0/README.md +105 -0
- modularq-1.1.0/modularq/__init__.py +1 -0
- modularq-1.1.0/modularq/core.py +533 -0
- modularq-1.1.0/modularq.egg-info/PKG-INFO +145 -0
- modularq-1.1.0/modularq.egg-info/SOURCES.txt +12 -0
- modularq-1.1.0/modularq.egg-info/dependency_links.txt +1 -0
- modularq-1.1.0/modularq.egg-info/requires.txt +6 -0
- modularq-1.1.0/modularq.egg-info/top_level.txt +1 -0
- modularq-1.1.0/pyproject.toml +32 -0
- modularq-1.1.0/setup.cfg +4 -0
- modularq-1.1.0/tests/test_core.py +54 -0
modularq-1.1.0/AUTHORS
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Authors — modularq
|
|
2
|
+
|
|
3
|
+
## Theory & Research
|
|
4
|
+
Christian H. Balfagón
|
|
5
|
+
- Paper: "A Modular Software Stack for Quantum Computing" (2025)
|
|
6
|
+
- Data: https://doi.org/10.5281/zenodo.18066279
|
|
7
|
+
- Contact: cb@balfagonresearch.org
|
|
8
|
+
- ORCID: 0009-0003-0835-5519
|
|
9
|
+
|
|
10
|
+
## Implementation & Product
|
|
11
|
+
Mariano M. Castro
|
|
12
|
+
- Repository: https://github.com/nanocastro79/modularq
|
modularq-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
MIT License (Non-Commercial)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Christian H. Balfagón and Mariano M. Castro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software for academic, research, and non-commercial purposes, subject
|
|
7
|
+
to the following conditions:
|
|
8
|
+
|
|
9
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
10
|
+
all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
2. Commercial use — defined as use in a product or service that generates
|
|
13
|
+
revenue, or use by a for-profit organization — requires a separate
|
|
14
|
+
commercial license. Contact: cb@balfagonresearch.org
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
modularq-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modularq
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Modular Software Stack for Quantum Computing — measurement diagnostics and correction
|
|
5
|
+
Author: Mariano M. Castro
|
|
6
|
+
Author-email: "Christian H. Balfagon" <cb@balfagonresearch.org>
|
|
7
|
+
License: MIT License (Non-Commercial)
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2025 Christian H. Balfagón and Mariano M. Castro
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software for academic, research, and non-commercial purposes, subject
|
|
13
|
+
to the following conditions:
|
|
14
|
+
|
|
15
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
2. Commercial use — defined as use in a product or service that generates
|
|
19
|
+
revenue, or use by a for-profit organization — requires a separate
|
|
20
|
+
commercial license. Contact: cb@balfagonresearch.org
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
23
|
+
|
|
24
|
+
Project-URL: Homepage, https://nanocastro79.github.io/modularq/
|
|
25
|
+
Project-URL: Repository, https://github.com/nanocastro79/modularq
|
|
26
|
+
Keywords: quantum computing,qiskit,measurement,NISQ,Born rule
|
|
27
|
+
Classifier: Programming Language :: Python :: 3
|
|
28
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
29
|
+
Classifier: Intended Audience :: Science/Research
|
|
30
|
+
Requires-Python: >=3.9
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
License-File: AUTHORS
|
|
34
|
+
Requires-Dist: numpy>=1.24
|
|
35
|
+
Requires-Dist: scipy>=1.10
|
|
36
|
+
Provides-Extra: qiskit
|
|
37
|
+
Requires-Dist: qiskit>=1.0; extra == "qiskit"
|
|
38
|
+
Requires-Dist: qiskit-ibm-runtime>=0.20; extra == "qiskit"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# modularq 🔬
|
|
42
|
+
|
|
43
|
+
**Modular Software Stack for Quantum Computing**
|
|
44
|
+
|
|
45
|
+
Una librería Python que implementa diagnóstico y corrección de no-equilibrio modular en mediciones cuánticas, basada en:
|
|
46
|
+
|
|
47
|
+
> Balfagón, C. (2025). *A Modular Software Stack for Quantum Computing: From Born-Rule Equilibrium to Nonequilibrium-Aware Quantum Software*. Universidad de Buenos Aires.
|
|
48
|
+
|
|
49
|
+
[](https://colab.research.google.com/github/nanocastro79/modularq/blob/main/demo_colab.ipynb)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ¿Qué problema resuelve?
|
|
54
|
+
|
|
55
|
+
Las computadoras cuánticas NISQ operan como sistemas abiertos y ruidosos. Las estadísticas de medición pueden desviarse sistemáticamente de las predicciones ideales (Born rule) cuando el proceso de medición opera fuera del equilibrio termodinámico (KMS).
|
|
56
|
+
|
|
57
|
+
**modularq** detecta y corrige estas desviaciones automáticamente, sin modificar el hardware ni los circuitos.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Instalación
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install numpy scipy matplotlib
|
|
65
|
+
# Luego descargá modularq.py desde este repo
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
O en Google Colab:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
!wget https://raw.githubusercontent.com/nanocastro79/modularq/main/modularq.py
|
|
72
|
+
from modularq import ModularAnalyzer
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Uso básico
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import numpy as np
|
|
81
|
+
from modularq import ModularAnalyzer
|
|
82
|
+
|
|
83
|
+
# Matriz de densidad reconstruida (del subsistema medido)
|
|
84
|
+
rho = np.array([[0.52, 0.01], [0.01, 0.48]])
|
|
85
|
+
|
|
86
|
+
# Conteos experimentales
|
|
87
|
+
counts = {"0": 5200, "1": 4800}
|
|
88
|
+
|
|
89
|
+
# Born baseline
|
|
90
|
+
p0 = {"0": 0.5, "1": 0.5}
|
|
91
|
+
|
|
92
|
+
# Analizar
|
|
93
|
+
analyzer = ModularAnalyzer(eps=1e-6, mode="auto")
|
|
94
|
+
result = analyzer.analyze(rho, counts, p0)
|
|
95
|
+
|
|
96
|
+
print(result.summary())
|
|
97
|
+
# → MIS, régimen, probabilidades corregidas, comparación AIC
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Qué hace el stack (capas)
|
|
103
|
+
|
|
104
|
+
| Capa | Función |
|
|
105
|
+
|------|---------|
|
|
106
|
+
| 0 | Hardware cuántico (sin modificar) |
|
|
107
|
+
| 1 | Reconstrucción de estado (tomografía / shadows) |
|
|
108
|
+
| **2** | **Diagnóstico modular: K = -log(ρ), δK_i** ← núcleo |
|
|
109
|
+
| 3 | Clasificación: Born-válido / perturbativo / no-KMS |
|
|
110
|
+
| 4 | Regla de medición efectiva: reweighting exponencial |
|
|
111
|
+
| 5 | Control modular (minimizar imbalance) |
|
|
112
|
+
| 6 | API de software |
|
|
113
|
+
| 7 | Inferencia estadística (AIC/BIC) |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Modular Imbalance Score (MIS)
|
|
118
|
+
|
|
119
|
+
Métrica escalar que indica la validez del Born rule:
|
|
120
|
+
|
|
121
|
+
| MIS | Régimen | Acción |
|
|
122
|
+
|-----|---------|--------|
|
|
123
|
+
| < 0.02 | ✅ KMS-balanced | Ninguna (Born válido) |
|
|
124
|
+
| 0.02 – 0.10 | ⚠️ Perturbativo | Corrección leve |
|
|
125
|
+
| > 0.10 | 🔴 No-KMS | Corrección necesaria |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Reproducibilidad
|
|
130
|
+
|
|
131
|
+
Los experimentos originales están disponibles en Zenodo:
|
|
132
|
+
- DOI: [10.5281/zenodo.18066279](https://doi.org/10.5281/zenodo.18066279)
|
|
133
|
+
- Hardware: IBM Quantum (ibm_marrakesh, ibm_fez, ibm_torino)
|
|
134
|
+
- Circuitos: Bell (2q) y GHZ (3q)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Licencia
|
|
139
|
+
|
|
140
|
+
**Uso académico y no comercial:** libre y gratuito bajo los términos de esta licencia.
|
|
141
|
+
|
|
142
|
+
**Uso comercial:** requiere acuerdo de licencia separado.
|
|
143
|
+
Contacto: cb@balfagonresearch.org
|
|
144
|
+
|
|
145
|
+
© 2025 Christian H. Balfagón and Mariano M. Castro.
|
modularq-1.1.0/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# modularq 🔬
|
|
2
|
+
|
|
3
|
+
**Modular Software Stack for Quantum Computing**
|
|
4
|
+
|
|
5
|
+
Una librería Python que implementa diagnóstico y corrección de no-equilibrio modular en mediciones cuánticas, basada en:
|
|
6
|
+
|
|
7
|
+
> Balfagón, C. (2025). *A Modular Software Stack for Quantum Computing: From Born-Rule Equilibrium to Nonequilibrium-Aware Quantum Software*. Universidad de Buenos Aires.
|
|
8
|
+
|
|
9
|
+
[](https://colab.research.google.com/github/nanocastro79/modularq/blob/main/demo_colab.ipynb)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ¿Qué problema resuelve?
|
|
14
|
+
|
|
15
|
+
Las computadoras cuánticas NISQ operan como sistemas abiertos y ruidosos. Las estadísticas de medición pueden desviarse sistemáticamente de las predicciones ideales (Born rule) cuando el proceso de medición opera fuera del equilibrio termodinámico (KMS).
|
|
16
|
+
|
|
17
|
+
**modularq** detecta y corrige estas desviaciones automáticamente, sin modificar el hardware ni los circuitos.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Instalación
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install numpy scipy matplotlib
|
|
25
|
+
# Luego descargá modularq.py desde este repo
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
O en Google Colab:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
!wget https://raw.githubusercontent.com/nanocastro79/modularq/main/modularq.py
|
|
32
|
+
from modularq import ModularAnalyzer
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Uso básico
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import numpy as np
|
|
41
|
+
from modularq import ModularAnalyzer
|
|
42
|
+
|
|
43
|
+
# Matriz de densidad reconstruida (del subsistema medido)
|
|
44
|
+
rho = np.array([[0.52, 0.01], [0.01, 0.48]])
|
|
45
|
+
|
|
46
|
+
# Conteos experimentales
|
|
47
|
+
counts = {"0": 5200, "1": 4800}
|
|
48
|
+
|
|
49
|
+
# Born baseline
|
|
50
|
+
p0 = {"0": 0.5, "1": 0.5}
|
|
51
|
+
|
|
52
|
+
# Analizar
|
|
53
|
+
analyzer = ModularAnalyzer(eps=1e-6, mode="auto")
|
|
54
|
+
result = analyzer.analyze(rho, counts, p0)
|
|
55
|
+
|
|
56
|
+
print(result.summary())
|
|
57
|
+
# → MIS, régimen, probabilidades corregidas, comparación AIC
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Qué hace el stack (capas)
|
|
63
|
+
|
|
64
|
+
| Capa | Función |
|
|
65
|
+
|------|---------|
|
|
66
|
+
| 0 | Hardware cuántico (sin modificar) |
|
|
67
|
+
| 1 | Reconstrucción de estado (tomografía / shadows) |
|
|
68
|
+
| **2** | **Diagnóstico modular: K = -log(ρ), δK_i** ← núcleo |
|
|
69
|
+
| 3 | Clasificación: Born-válido / perturbativo / no-KMS |
|
|
70
|
+
| 4 | Regla de medición efectiva: reweighting exponencial |
|
|
71
|
+
| 5 | Control modular (minimizar imbalance) |
|
|
72
|
+
| 6 | API de software |
|
|
73
|
+
| 7 | Inferencia estadística (AIC/BIC) |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Modular Imbalance Score (MIS)
|
|
78
|
+
|
|
79
|
+
Métrica escalar que indica la validez del Born rule:
|
|
80
|
+
|
|
81
|
+
| MIS | Régimen | Acción |
|
|
82
|
+
|-----|---------|--------|
|
|
83
|
+
| < 0.02 | ✅ KMS-balanced | Ninguna (Born válido) |
|
|
84
|
+
| 0.02 – 0.10 | ⚠️ Perturbativo | Corrección leve |
|
|
85
|
+
| > 0.10 | 🔴 No-KMS | Corrección necesaria |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Reproducibilidad
|
|
90
|
+
|
|
91
|
+
Los experimentos originales están disponibles en Zenodo:
|
|
92
|
+
- DOI: [10.5281/zenodo.18066279](https://doi.org/10.5281/zenodo.18066279)
|
|
93
|
+
- Hardware: IBM Quantum (ibm_marrakesh, ibm_fez, ibm_torino)
|
|
94
|
+
- Circuitos: Bell (2q) y GHZ (3q)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Licencia
|
|
99
|
+
|
|
100
|
+
**Uso académico y no comercial:** libre y gratuito bajo los términos de esta licencia.
|
|
101
|
+
|
|
102
|
+
**Uso comercial:** requiere acuerdo de licencia separado.
|
|
103
|
+
Contacto: cb@balfagonresearch.org
|
|
104
|
+
|
|
105
|
+
© 2025 Christian H. Balfagón and Mariano M. Castro.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .core import ModularAnalyzer, ModularResult
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"""
|
|
2
|
+
modularq.py — Modular Software Stack for Quantum Systems
|
|
3
|
+
=========================================================
|
|
4
|
+
Implementación basada en:
|
|
5
|
+
Balfagón, C. (2025). "A Modular Software Stack for Quantum Computing"
|
|
6
|
+
Universidad de Buenos Aires.
|
|
7
|
+
|
|
8
|
+
Uso en Google Colab:
|
|
9
|
+
# Clonar repo y hacer:
|
|
10
|
+
from modularq import ModularAnalyzer
|
|
11
|
+
|
|
12
|
+
Autores: C. Balfagón (framework teórico) — Implementación open-source
|
|
13
|
+
|
|
14
|
+
Changelog:
|
|
15
|
+
v1.1 — Fix umbrales MIS contradictorios entre docstring y classify_regime
|
|
16
|
+
— Fix recomendación final unificada (MIS + AIC juntos)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
from scipy.linalg import logm
|
|
21
|
+
from typing import Dict, Tuple, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ─────────────────────────────────────────────
|
|
25
|
+
# CAPA 2: DIAGNÓSTICO MODULAR (núcleo)
|
|
26
|
+
# ─────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
def regularize_state(rho: np.ndarray, eps: float = 1e-6) -> np.ndarray:
|
|
29
|
+
"""
|
|
30
|
+
Regulariza la matriz de densidad para evitar valores singulares.
|
|
31
|
+
Ecuación (5) del paper: rho_eps = (1-eps)*rho + eps*I/d
|
|
32
|
+
|
|
33
|
+
Parámetros
|
|
34
|
+
----------
|
|
35
|
+
rho : np.ndarray
|
|
36
|
+
Matriz de densidad (debe ser cuadrada, hermítica, traza=1)
|
|
37
|
+
eps : float
|
|
38
|
+
Parámetro de regularización (default: 1e-6)
|
|
39
|
+
|
|
40
|
+
Retorna
|
|
41
|
+
-------
|
|
42
|
+
np.ndarray : Matriz de densidad regularizada
|
|
43
|
+
"""
|
|
44
|
+
d = rho.shape[0]
|
|
45
|
+
rho_eps = (1 - eps) * rho + eps * np.eye(d) / d
|
|
46
|
+
return rho_eps
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def modular_generator(rho: np.ndarray, eps: float = 1e-6) -> np.ndarray:
|
|
50
|
+
"""
|
|
51
|
+
Calcula el generador modular K = -log(rho_eps).
|
|
52
|
+
Ecuación (4) del paper.
|
|
53
|
+
|
|
54
|
+
Este es el objeto central del framework: captura cuánto se desvía
|
|
55
|
+
el estado medido del equilibrio (KMS).
|
|
56
|
+
|
|
57
|
+
Parámetros
|
|
58
|
+
----------
|
|
59
|
+
rho : np.ndarray
|
|
60
|
+
Matriz de densidad del subsistema medido
|
|
61
|
+
eps : float
|
|
62
|
+
Parámetro de regularización
|
|
63
|
+
|
|
64
|
+
Retorna
|
|
65
|
+
-------
|
|
66
|
+
np.ndarray : Generador modular K
|
|
67
|
+
"""
|
|
68
|
+
rho_eps = regularize_state(rho, eps)
|
|
69
|
+
K = -logm(rho_eps)
|
|
70
|
+
# Forzar que sea real si la parte imaginaria es despreciable
|
|
71
|
+
if np.max(np.abs(K.imag)) < 1e-10:
|
|
72
|
+
K = K.real
|
|
73
|
+
return K
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def modular_imbalance(
|
|
77
|
+
K: np.ndarray,
|
|
78
|
+
p0: Dict[str, float]
|
|
79
|
+
) -> Dict[str, float]:
|
|
80
|
+
"""
|
|
81
|
+
Calcula el desbalance modular por resultado: deltaK_i.
|
|
82
|
+
Ecuación (6) del paper.
|
|
83
|
+
|
|
84
|
+
deltaK_i = <i|K|i> - sum_j(p0_j * <j|K|j>)
|
|
85
|
+
|
|
86
|
+
Mide cuánto se desvía cada resultado del equilibrio KMS.
|
|
87
|
+
Si todos los deltaK_i son ~0, el Born rule es válido.
|
|
88
|
+
|
|
89
|
+
Parámetros
|
|
90
|
+
----------
|
|
91
|
+
K : np.ndarray
|
|
92
|
+
Generador modular (matriz)
|
|
93
|
+
p0 : dict
|
|
94
|
+
Probabilidades Born baseline {'00': 0.5, '11': 0.5, ...}
|
|
95
|
+
|
|
96
|
+
Retorna
|
|
97
|
+
-------
|
|
98
|
+
dict : Desbalance modular por resultado {'00': deltaK_00, ...}
|
|
99
|
+
"""
|
|
100
|
+
outcomes = list(p0.keys())
|
|
101
|
+
n = len(outcomes)
|
|
102
|
+
d = K.shape[0]
|
|
103
|
+
|
|
104
|
+
if n != d:
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"Dimensión incompatible: p0 tiene {n} outcomes pero K es {d}x{d}. "
|
|
107
|
+
f"Asegurate de que el subsistema medido tenga {int(np.log2(d))} qubits."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Elementos diagonales de K en la base de medición: <i|K|i>
|
|
111
|
+
Ki = {outcomes[i]: K[i, i].real for i in range(n)}
|
|
112
|
+
|
|
113
|
+
# Promedio ponderado por Born: sum_j p0_j * K_jj
|
|
114
|
+
K_bar = sum(p0[outcomes[j]] * Ki[outcomes[j]] for j in range(n))
|
|
115
|
+
|
|
116
|
+
# Desbalance: deltaK_i = K_ii - K_bar
|
|
117
|
+
deltaK = {outcome: Ki[outcome] - K_bar for outcome in outcomes}
|
|
118
|
+
|
|
119
|
+
return deltaK
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def modular_imbalance_score(deltaK: Dict[str, float]) -> float:
|
|
123
|
+
"""
|
|
124
|
+
Calcula el Modular Imbalance Score (MIS).
|
|
125
|
+
Ecuación (10) del paper.
|
|
126
|
+
|
|
127
|
+
MIS = (1/|O|) * sum_i |deltaK_i|
|
|
128
|
+
|
|
129
|
+
Interpretación:
|
|
130
|
+
- MIS < 0.05 → régimen KMS/Born válido (medición confiable)
|
|
131
|
+
- 0.05–0.15 → no-equilibrio perturbativo (posible corrección)
|
|
132
|
+
- MIS > 0.15 → no-KMS fuerte (corrección necesaria)
|
|
133
|
+
|
|
134
|
+
NOTA: estos umbrales son indicativos. La recomendación final
|
|
135
|
+
siempre se determina combinando MIS + comparación AIC.
|
|
136
|
+
|
|
137
|
+
Parámetros
|
|
138
|
+
----------
|
|
139
|
+
deltaK : dict
|
|
140
|
+
Desbalance modular por resultado
|
|
141
|
+
|
|
142
|
+
Retorna
|
|
143
|
+
-------
|
|
144
|
+
float : MIS (adimensional, comparable entre dispositivos)
|
|
145
|
+
"""
|
|
146
|
+
values = list(deltaK.values())
|
|
147
|
+
mis = np.mean(np.abs(values))
|
|
148
|
+
return float(mis)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ─────────────────────────────────────────────
|
|
152
|
+
# CAPA 3: CLASIFICACIÓN DE RÉGIMEN
|
|
153
|
+
# ─────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
def classify_regime(mis: float) -> Tuple[str, str]:
|
|
156
|
+
"""
|
|
157
|
+
Clasifica el régimen de medición según el MIS.
|
|
158
|
+
Capa 3 del stack (Sección 3.4 del paper).
|
|
159
|
+
|
|
160
|
+
IMPORTANTE: esta función solo clasifica el régimen físico.
|
|
161
|
+
La recomendación final (si aplicar corrección o no) se determina
|
|
162
|
+
combinando este resultado con la comparación AIC en summary().
|
|
163
|
+
|
|
164
|
+
Umbrales (consistentes con modular_imbalance_score):
|
|
165
|
+
- MIS < 0.05 → KMS_BALANCED
|
|
166
|
+
- 0.05–0.15 → PERTURBATIVE_NEQ
|
|
167
|
+
- MIS > 0.15 → NON_KMS
|
|
168
|
+
|
|
169
|
+
Parámetros
|
|
170
|
+
----------
|
|
171
|
+
mis : float
|
|
172
|
+
Modular Imbalance Score
|
|
173
|
+
|
|
174
|
+
Retorna
|
|
175
|
+
-------
|
|
176
|
+
tuple : (régimen, descripción)
|
|
177
|
+
"""
|
|
178
|
+
if mis < 0.05:
|
|
179
|
+
return ("KMS_BALANCED",
|
|
180
|
+
"No-equilibrio no detectado (Born posiblemente válido).")
|
|
181
|
+
elif mis < 0.15:
|
|
182
|
+
return ("PERTURBATIVE_NEQ",
|
|
183
|
+
"No-equilibrio perturbativo detectado.")
|
|
184
|
+
else:
|
|
185
|
+
return ("NON_KMS",
|
|
186
|
+
"No-KMS fuerte detectado.")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# ─────────────────────────────────────────────
|
|
190
|
+
# CAPA 4: REGLA DE MEDICIÓN EFECTIVA
|
|
191
|
+
# ─────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
def modular_probabilities(
|
|
194
|
+
p0: Dict[str, float],
|
|
195
|
+
deltaK: Dict[str, float]
|
|
196
|
+
) -> Dict[str, float]:
|
|
197
|
+
"""
|
|
198
|
+
Calcula las probabilidades corregidas por no-equilibrio modular.
|
|
199
|
+
Ecuación (7) del paper.
|
|
200
|
+
|
|
201
|
+
p_i = p0_i * exp(-deltaK_i) / Z
|
|
202
|
+
Z = sum_j p0_j * exp(-deltaK_j)
|
|
203
|
+
|
|
204
|
+
Propiedades garantizadas:
|
|
205
|
+
- Preserva normalización (suma = 1)
|
|
206
|
+
- Preserva positividad
|
|
207
|
+
- Reduce exactamente a Born cuando deltaK_i = 0
|
|
208
|
+
|
|
209
|
+
Parámetros
|
|
210
|
+
----------
|
|
211
|
+
p0 : dict
|
|
212
|
+
Probabilidades Born baseline
|
|
213
|
+
deltaK : dict
|
|
214
|
+
Desbalance modular por resultado
|
|
215
|
+
|
|
216
|
+
Retorna
|
|
217
|
+
-------
|
|
218
|
+
dict : Probabilidades corregidas
|
|
219
|
+
"""
|
|
220
|
+
outcomes = list(p0.keys())
|
|
221
|
+
|
|
222
|
+
# Pesos exponenciales
|
|
223
|
+
weights = {o: p0[o] * np.exp(-deltaK[o]) for o in outcomes}
|
|
224
|
+
|
|
225
|
+
# Factor de normalización
|
|
226
|
+
Z = sum(weights.values())
|
|
227
|
+
|
|
228
|
+
# Probabilidades normalizadas
|
|
229
|
+
p_mod = {o: weights[o] / Z for o in outcomes}
|
|
230
|
+
|
|
231
|
+
return p_mod
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ─────────────────────────────────────────────
|
|
235
|
+
# CAPA 7: INFERENCIA Y COMPARACIÓN DE MODELOS
|
|
236
|
+
# ─────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
def log_likelihood(
|
|
239
|
+
counts: Dict[str, int],
|
|
240
|
+
probs: Dict[str, float],
|
|
241
|
+
eps_clip: float = 1e-12
|
|
242
|
+
) -> float:
|
|
243
|
+
"""
|
|
244
|
+
Calcula la log-verosimilitud multinomial.
|
|
245
|
+
Ecuación (22) del paper.
|
|
246
|
+
|
|
247
|
+
log L = sum_i n_i * log(p_i)
|
|
248
|
+
|
|
249
|
+
Parámetros
|
|
250
|
+
----------
|
|
251
|
+
counts : dict
|
|
252
|
+
Conteos observados por resultado {'00': 5200, '11': 4700, ...}
|
|
253
|
+
probs : dict
|
|
254
|
+
Probabilidades del modelo
|
|
255
|
+
eps_clip : float
|
|
256
|
+
Evita log(0)
|
|
257
|
+
|
|
258
|
+
Retorna
|
|
259
|
+
-------
|
|
260
|
+
float : Log-verosimilitud
|
|
261
|
+
"""
|
|
262
|
+
ll = 0.0
|
|
263
|
+
for outcome, count in counts.items():
|
|
264
|
+
p = max(probs.get(outcome, eps_clip), eps_clip)
|
|
265
|
+
ll += count * np.log(p)
|
|
266
|
+
return float(ll)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def compute_aic(log_l: float, k_params: int) -> float:
|
|
270
|
+
"""
|
|
271
|
+
Criterio de Información de Akaike (AIC).
|
|
272
|
+
Penaliza modelos con más parámetros.
|
|
273
|
+
|
|
274
|
+
AIC = 2k - 2*log(L)
|
|
275
|
+
|
|
276
|
+
Parámetros
|
|
277
|
+
----------
|
|
278
|
+
log_l : float
|
|
279
|
+
Log-verosimilitud del modelo
|
|
280
|
+
k_params : int
|
|
281
|
+
Número de parámetros libres del modelo
|
|
282
|
+
|
|
283
|
+
Retorna
|
|
284
|
+
-------
|
|
285
|
+
float : AIC (menor = mejor modelo)
|
|
286
|
+
"""
|
|
287
|
+
return 2 * k_params - 2 * log_l
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def compute_bic(log_l: float, k_params: int, n_shots: int) -> float:
|
|
291
|
+
"""
|
|
292
|
+
Criterio de Información Bayesiano (BIC).
|
|
293
|
+
|
|
294
|
+
BIC = k*log(N) - 2*log(L)
|
|
295
|
+
"""
|
|
296
|
+
return k_params * np.log(n_shots) - 2 * log_l
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def model_comparison(
|
|
300
|
+
counts: Dict[str, int],
|
|
301
|
+
p_born: Dict[str, float],
|
|
302
|
+
p_modular: Dict[str, float]
|
|
303
|
+
) -> Dict:
|
|
304
|
+
"""
|
|
305
|
+
Compara el modelo Born vs modelo Modular usando AIC y BIC.
|
|
306
|
+
Sección 5.3 del paper.
|
|
307
|
+
|
|
308
|
+
El modelo Born tiene 0 parámetros libres (es la baseline).
|
|
309
|
+
El modelo Modular tiene 1 parámetro libre (epsilon de regularización).
|
|
310
|
+
|
|
311
|
+
Parámetros
|
|
312
|
+
----------
|
|
313
|
+
counts : dict
|
|
314
|
+
Conteos experimentales
|
|
315
|
+
p_born : dict
|
|
316
|
+
Probabilidades Born (sin corrección)
|
|
317
|
+
p_modular : dict
|
|
318
|
+
Probabilidades con corrección modular
|
|
319
|
+
|
|
320
|
+
Retorna
|
|
321
|
+
-------
|
|
322
|
+
dict : Resultados completos de la comparación
|
|
323
|
+
"""
|
|
324
|
+
N = sum(counts.values())
|
|
325
|
+
|
|
326
|
+
ll_born = log_likelihood(counts, p_born)
|
|
327
|
+
ll_mod = log_likelihood(counts, p_modular)
|
|
328
|
+
|
|
329
|
+
aic_born = compute_aic(ll_born, k_params=0)
|
|
330
|
+
aic_mod = compute_aic(ll_mod, k_params=1)
|
|
331
|
+
|
|
332
|
+
bic_born = compute_bic(ll_born, k_params=0, n_shots=N)
|
|
333
|
+
bic_mod = compute_bic(ll_mod, k_params=1, n_shots=N)
|
|
334
|
+
|
|
335
|
+
delta_aic = aic_born - aic_mod # positivo = prefiere modular
|
|
336
|
+
delta_bic = bic_born - bic_mod
|
|
337
|
+
|
|
338
|
+
if delta_aic > 2:
|
|
339
|
+
preference = "MODULAR"
|
|
340
|
+
strength = "fuerte" if delta_aic > 10 else "moderada"
|
|
341
|
+
elif delta_aic < -2:
|
|
342
|
+
preference = "BORN"
|
|
343
|
+
strength = "fuerte" if delta_aic < -10 else "moderada"
|
|
344
|
+
else:
|
|
345
|
+
preference = "EMPATE"
|
|
346
|
+
strength = "evidencia insuficiente"
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
"N_shots": N,
|
|
350
|
+
"logL_born": round(ll_born, 2),
|
|
351
|
+
"logL_modular": round(ll_mod, 2),
|
|
352
|
+
"AIC_born": round(aic_born, 2),
|
|
353
|
+
"AIC_modular": round(aic_mod, 2),
|
|
354
|
+
"BIC_born": round(bic_born, 2),
|
|
355
|
+
"BIC_modular": round(bic_mod, 2),
|
|
356
|
+
"delta_AIC": round(delta_aic, 2),
|
|
357
|
+
"delta_BIC": round(delta_bic, 2),
|
|
358
|
+
"preferred_model": preference,
|
|
359
|
+
"evidence_strength": strength,
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# ─────────────────────────────────────────────
|
|
364
|
+
# CLASE PRINCIPAL: ModularAnalyzer
|
|
365
|
+
# ─────────────────────────────────────────────
|
|
366
|
+
|
|
367
|
+
class ModularAnalyzer:
|
|
368
|
+
"""
|
|
369
|
+
Interfaz principal del Modular Software Stack.
|
|
370
|
+
|
|
371
|
+
Uso básico
|
|
372
|
+
----------
|
|
373
|
+
>>> analyzer = ModularAnalyzer(eps=1e-6)
|
|
374
|
+
>>> result = analyzer.analyze(rho, counts, p0)
|
|
375
|
+
>>> print(result.summary())
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
def __init__(self, eps: float = 1e-6, mode: str = "auto"):
|
|
379
|
+
"""
|
|
380
|
+
Parámetros
|
|
381
|
+
----------
|
|
382
|
+
eps : float
|
|
383
|
+
Parámetro de regularización (default: 1e-6)
|
|
384
|
+
mode : str
|
|
385
|
+
'auto' → aplica corrección solo si AIC lo favorece
|
|
386
|
+
'born' → fuerza Born (sin corrección)
|
|
387
|
+
'modular' → fuerza corrección modular siempre
|
|
388
|
+
"""
|
|
389
|
+
self.eps = eps
|
|
390
|
+
self.mode = mode
|
|
391
|
+
self._results = {}
|
|
392
|
+
|
|
393
|
+
def analyze(
|
|
394
|
+
self,
|
|
395
|
+
rho: np.ndarray,
|
|
396
|
+
counts: Dict[str, int],
|
|
397
|
+
p0: Optional[Dict[str, float]] = None
|
|
398
|
+
) -> "ModularResult":
|
|
399
|
+
"""
|
|
400
|
+
Ejecuta el análisis completo del stack modular.
|
|
401
|
+
|
|
402
|
+
Parámetros
|
|
403
|
+
----------
|
|
404
|
+
rho : np.ndarray
|
|
405
|
+
Matriz de densidad reconstruida del subsistema medido
|
|
406
|
+
counts : dict
|
|
407
|
+
Conteos experimentales {'00': 5200, '11': 4700, ...}
|
|
408
|
+
p0 : dict, opcional
|
|
409
|
+
Born baseline. Si None, se calcula desde rho.
|
|
410
|
+
|
|
411
|
+
Retorna
|
|
412
|
+
-------
|
|
413
|
+
ModularResult : Objeto con todos los resultados
|
|
414
|
+
"""
|
|
415
|
+
outcomes = list(counts.keys())
|
|
416
|
+
N = sum(counts.values())
|
|
417
|
+
|
|
418
|
+
# Si no hay p0, calcularlo desde la diagonal de rho
|
|
419
|
+
if p0 is None:
|
|
420
|
+
diag = np.diag(rho).real
|
|
421
|
+
diag = np.abs(diag) / np.sum(np.abs(diag))
|
|
422
|
+
p0 = {outcomes[i]: diag[i] for i in range(len(outcomes))}
|
|
423
|
+
|
|
424
|
+
# Paso 1: Generador modular
|
|
425
|
+
K = modular_generator(rho, self.eps)
|
|
426
|
+
|
|
427
|
+
# Paso 2: Desbalance modular
|
|
428
|
+
deltaK = modular_imbalance(K, p0)
|
|
429
|
+
|
|
430
|
+
# Paso 3: MIS y clasificación de régimen
|
|
431
|
+
mis = modular_imbalance_score(deltaK)
|
|
432
|
+
regime, regime_desc = classify_regime(mis)
|
|
433
|
+
|
|
434
|
+
# Paso 4: Probabilidades corregidas
|
|
435
|
+
p_mod = modular_probabilities(p0, deltaK)
|
|
436
|
+
|
|
437
|
+
# Paso 5: Comparación de modelos
|
|
438
|
+
comparison = model_comparison(counts, p0, p_mod)
|
|
439
|
+
|
|
440
|
+
# Frecuencias empíricas
|
|
441
|
+
freq_empirical = {o: counts[o] / N for o in outcomes}
|
|
442
|
+
|
|
443
|
+
return ModularResult(
|
|
444
|
+
outcomes=outcomes,
|
|
445
|
+
N_shots=N,
|
|
446
|
+
p0=p0,
|
|
447
|
+
p_modular=p_mod,
|
|
448
|
+
freq_empirical=freq_empirical,
|
|
449
|
+
K=K,
|
|
450
|
+
deltaK=deltaK,
|
|
451
|
+
mis=mis,
|
|
452
|
+
regime=regime,
|
|
453
|
+
regime_description=regime_desc,
|
|
454
|
+
model_comparison=comparison,
|
|
455
|
+
eps=self.eps,
|
|
456
|
+
mode=self.mode,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
class ModularResult:
|
|
461
|
+
"""
|
|
462
|
+
Contenedor de resultados del análisis modular.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
def __init__(self, **kwargs):
|
|
466
|
+
for k, v in kwargs.items():
|
|
467
|
+
setattr(self, k, v)
|
|
468
|
+
|
|
469
|
+
def summary(self) -> str:
|
|
470
|
+
"""Resumen legible de los resultados."""
|
|
471
|
+
|
|
472
|
+
# Recomendación final unificada (MIS + AIC)
|
|
473
|
+
preferred = self.model_comparison['preferred_model']
|
|
474
|
+
strength = self.model_comparison['evidence_strength']
|
|
475
|
+
|
|
476
|
+
if self.mode == "born":
|
|
477
|
+
recomendacion = "→ RECOMENDACIÓN: Born forzado por el usuario."
|
|
478
|
+
elif self.mode == "modular":
|
|
479
|
+
recomendacion = "→ RECOMENDACIÓN: Corrección modular forzada por el usuario."
|
|
480
|
+
elif preferred == "MODULAR":
|
|
481
|
+
recomendacion = f"→ RECOMENDACIÓN: usar probabilidades corregidas (Modular) — evidencia {strength}."
|
|
482
|
+
elif preferred == "BORN":
|
|
483
|
+
recomendacion = f"→ RECOMENDACIÓN: usar probabilidades Born (sin corrección) — evidencia {strength}."
|
|
484
|
+
else:
|
|
485
|
+
recomendacion = "→ RECOMENDACIÓN: evidencia insuficiente para preferir un modelo. Usar Born por defecto."
|
|
486
|
+
|
|
487
|
+
lines = [
|
|
488
|
+
"=" * 55,
|
|
489
|
+
" MODULAR SOFTWARE STACK — Resultados",
|
|
490
|
+
"=" * 55,
|
|
491
|
+
f" Shots totales : {self.N_shots:,}",
|
|
492
|
+
f" Epsilon (reg.) : {self.eps}",
|
|
493
|
+
f" MIS : {self.mis:.4f}",
|
|
494
|
+
f" Régimen físico : {self.regime}",
|
|
495
|
+
f" {self.regime_description}",
|
|
496
|
+
"-" * 55,
|
|
497
|
+
" Probabilidades por resultado:",
|
|
498
|
+
f" {'Outcome':<8} {'Born':>10} {'Modular':>10} {'Empírico':>10}",
|
|
499
|
+
]
|
|
500
|
+
for o in self.outcomes:
|
|
501
|
+
lines.append(
|
|
502
|
+
f" {o:<8} "
|
|
503
|
+
f"{self.p0[o]:>10.4f} "
|
|
504
|
+
f"{self.p_modular[o]:>10.4f} "
|
|
505
|
+
f"{self.freq_empirical[o]:>10.4f}"
|
|
506
|
+
)
|
|
507
|
+
lines += [
|
|
508
|
+
"-" * 55,
|
|
509
|
+
" Comparación de modelos (AIC):",
|
|
510
|
+
f" AIC Born : {self.model_comparison['AIC_born']}",
|
|
511
|
+
f" AIC Modular : {self.model_comparison['AIC_modular']}",
|
|
512
|
+
f" ΔAIC (Born-Mod) : {self.model_comparison['delta_AIC']}",
|
|
513
|
+
f" Modelo preferido : {self.model_comparison['preferred_model']}",
|
|
514
|
+
f" Evidencia : {self.model_comparison['evidence_strength']}",
|
|
515
|
+
"-" * 55,
|
|
516
|
+
f" {recomendacion}",
|
|
517
|
+
"=" * 55,
|
|
518
|
+
]
|
|
519
|
+
return "\n".join(lines)
|
|
520
|
+
|
|
521
|
+
def best_probabilities(self) -> Dict[str, float]:
|
|
522
|
+
"""
|
|
523
|
+
Retorna las mejores probabilidades según el criterio AIC.
|
|
524
|
+
En modo 'auto', elige entre Born y Modular automáticamente.
|
|
525
|
+
"""
|
|
526
|
+
if self.mode == "born":
|
|
527
|
+
return self.p0
|
|
528
|
+
elif self.mode == "modular":
|
|
529
|
+
return self.p_modular
|
|
530
|
+
else: # auto
|
|
531
|
+
if self.model_comparison["preferred_model"] == "MODULAR":
|
|
532
|
+
return self.p_modular
|
|
533
|
+
return self.p0
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modularq
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Modular Software Stack for Quantum Computing — measurement diagnostics and correction
|
|
5
|
+
Author: Mariano M. Castro
|
|
6
|
+
Author-email: "Christian H. Balfagon" <cb@balfagonresearch.org>
|
|
7
|
+
License: MIT License (Non-Commercial)
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2025 Christian H. Balfagón and Mariano M. Castro
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software for academic, research, and non-commercial purposes, subject
|
|
13
|
+
to the following conditions:
|
|
14
|
+
|
|
15
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
2. Commercial use — defined as use in a product or service that generates
|
|
19
|
+
revenue, or use by a for-profit organization — requires a separate
|
|
20
|
+
commercial license. Contact: cb@balfagonresearch.org
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
23
|
+
|
|
24
|
+
Project-URL: Homepage, https://nanocastro79.github.io/modularq/
|
|
25
|
+
Project-URL: Repository, https://github.com/nanocastro79/modularq
|
|
26
|
+
Keywords: quantum computing,qiskit,measurement,NISQ,Born rule
|
|
27
|
+
Classifier: Programming Language :: Python :: 3
|
|
28
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
29
|
+
Classifier: Intended Audience :: Science/Research
|
|
30
|
+
Requires-Python: >=3.9
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
License-File: AUTHORS
|
|
34
|
+
Requires-Dist: numpy>=1.24
|
|
35
|
+
Requires-Dist: scipy>=1.10
|
|
36
|
+
Provides-Extra: qiskit
|
|
37
|
+
Requires-Dist: qiskit>=1.0; extra == "qiskit"
|
|
38
|
+
Requires-Dist: qiskit-ibm-runtime>=0.20; extra == "qiskit"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# modularq 🔬
|
|
42
|
+
|
|
43
|
+
**Modular Software Stack for Quantum Computing**
|
|
44
|
+
|
|
45
|
+
Una librería Python que implementa diagnóstico y corrección de no-equilibrio modular en mediciones cuánticas, basada en:
|
|
46
|
+
|
|
47
|
+
> Balfagón, C. (2025). *A Modular Software Stack for Quantum Computing: From Born-Rule Equilibrium to Nonequilibrium-Aware Quantum Software*. Universidad de Buenos Aires.
|
|
48
|
+
|
|
49
|
+
[](https://colab.research.google.com/github/nanocastro79/modularq/blob/main/demo_colab.ipynb)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ¿Qué problema resuelve?
|
|
54
|
+
|
|
55
|
+
Las computadoras cuánticas NISQ operan como sistemas abiertos y ruidosos. Las estadísticas de medición pueden desviarse sistemáticamente de las predicciones ideales (Born rule) cuando el proceso de medición opera fuera del equilibrio termodinámico (KMS).
|
|
56
|
+
|
|
57
|
+
**modularq** detecta y corrige estas desviaciones automáticamente, sin modificar el hardware ni los circuitos.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Instalación
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install numpy scipy matplotlib
|
|
65
|
+
# Luego descargá modularq.py desde este repo
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
O en Google Colab:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
!wget https://raw.githubusercontent.com/nanocastro79/modularq/main/modularq.py
|
|
72
|
+
from modularq import ModularAnalyzer
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Uso básico
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import numpy as np
|
|
81
|
+
from modularq import ModularAnalyzer
|
|
82
|
+
|
|
83
|
+
# Matriz de densidad reconstruida (del subsistema medido)
|
|
84
|
+
rho = np.array([[0.52, 0.01], [0.01, 0.48]])
|
|
85
|
+
|
|
86
|
+
# Conteos experimentales
|
|
87
|
+
counts = {"0": 5200, "1": 4800}
|
|
88
|
+
|
|
89
|
+
# Born baseline
|
|
90
|
+
p0 = {"0": 0.5, "1": 0.5}
|
|
91
|
+
|
|
92
|
+
# Analizar
|
|
93
|
+
analyzer = ModularAnalyzer(eps=1e-6, mode="auto")
|
|
94
|
+
result = analyzer.analyze(rho, counts, p0)
|
|
95
|
+
|
|
96
|
+
print(result.summary())
|
|
97
|
+
# → MIS, régimen, probabilidades corregidas, comparación AIC
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Qué hace el stack (capas)
|
|
103
|
+
|
|
104
|
+
| Capa | Función |
|
|
105
|
+
|------|---------|
|
|
106
|
+
| 0 | Hardware cuántico (sin modificar) |
|
|
107
|
+
| 1 | Reconstrucción de estado (tomografía / shadows) |
|
|
108
|
+
| **2** | **Diagnóstico modular: K = -log(ρ), δK_i** ← núcleo |
|
|
109
|
+
| 3 | Clasificación: Born-válido / perturbativo / no-KMS |
|
|
110
|
+
| 4 | Regla de medición efectiva: reweighting exponencial |
|
|
111
|
+
| 5 | Control modular (minimizar imbalance) |
|
|
112
|
+
| 6 | API de software |
|
|
113
|
+
| 7 | Inferencia estadística (AIC/BIC) |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Modular Imbalance Score (MIS)
|
|
118
|
+
|
|
119
|
+
Métrica escalar que indica la validez del Born rule:
|
|
120
|
+
|
|
121
|
+
| MIS | Régimen | Acción |
|
|
122
|
+
|-----|---------|--------|
|
|
123
|
+
| < 0.02 | ✅ KMS-balanced | Ninguna (Born válido) |
|
|
124
|
+
| 0.02 – 0.10 | ⚠️ Perturbativo | Corrección leve |
|
|
125
|
+
| > 0.10 | 🔴 No-KMS | Corrección necesaria |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Reproducibilidad
|
|
130
|
+
|
|
131
|
+
Los experimentos originales están disponibles en Zenodo:
|
|
132
|
+
- DOI: [10.5281/zenodo.18066279](https://doi.org/10.5281/zenodo.18066279)
|
|
133
|
+
- Hardware: IBM Quantum (ibm_marrakesh, ibm_fez, ibm_torino)
|
|
134
|
+
- Circuitos: Bell (2q) y GHZ (3q)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Licencia
|
|
139
|
+
|
|
140
|
+
**Uso académico y no comercial:** libre y gratuito bajo los términos de esta licencia.
|
|
141
|
+
|
|
142
|
+
**Uso comercial:** requiere acuerdo de licencia separado.
|
|
143
|
+
Contacto: cb@balfagonresearch.org
|
|
144
|
+
|
|
145
|
+
© 2025 Christian H. Balfagón and Mariano M. Castro.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
AUTHORS
|
|
2
|
+
LICENSE
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
modularq/__init__.py
|
|
6
|
+
modularq/core.py
|
|
7
|
+
modularq.egg-info/PKG-INFO
|
|
8
|
+
modularq.egg-info/SOURCES.txt
|
|
9
|
+
modularq.egg-info/dependency_links.txt
|
|
10
|
+
modularq.egg-info/requires.txt
|
|
11
|
+
modularq.egg-info/top_level.txt
|
|
12
|
+
tests/test_core.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
modularq
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modularq"
|
|
7
|
+
version = "1.1.0"
|
|
8
|
+
description = "Modular Software Stack for Quantum Computing — measurement diagnostics and correction"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {file = "LICENSE"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Christian H. Balfagon", email = "cb@balfagonresearch.org"},
|
|
13
|
+
{name = "Mariano M. Castro"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["quantum computing", "qiskit", "measurement", "NISQ", "Born rule"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
19
|
+
"Intended Audience :: Science/Research"
|
|
20
|
+
]
|
|
21
|
+
requires-python = ">=3.9"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"numpy>=1.24",
|
|
24
|
+
"scipy>=1.10"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
qiskit = ["qiskit>=1.0", "qiskit-ibm-runtime>=0.20"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://nanocastro79.github.io/modularq/"
|
|
32
|
+
Repository = "https://github.com/nanocastro79/modularq"
|
modularq-1.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
from modularq import ModularAnalyzer
|
|
4
|
+
|
|
5
|
+
# Estado |+> ideal
|
|
6
|
+
RHO_IDEAL = np.array([[0.5, 0.5], [0.5, 0.5]])
|
|
7
|
+
|
|
8
|
+
# Estado con ruido (similar al hardware real)
|
|
9
|
+
RHO_NOISY = np.array([[0.505+0.j, 0.494-0.028j],
|
|
10
|
+
[0.494+0.028j, 0.495+0.j ]])
|
|
11
|
+
|
|
12
|
+
def test_mis_ideal_es_cero():
|
|
13
|
+
"""Con rho ideal, el MIS debe ser 0"""
|
|
14
|
+
counts = {'0': 1000, '1': 1000}
|
|
15
|
+
p0 = {'0': 0.5, '1': 0.5}
|
|
16
|
+
result = ModularAnalyzer().analyze(RHO_IDEAL, counts, p0)
|
|
17
|
+
assert result.mis == pytest.approx(0.0, abs=1e-6)
|
|
18
|
+
|
|
19
|
+
def test_regimen_kms_con_rho_ideal():
|
|
20
|
+
"""Con rho ideal el regimen debe ser KMS_BALANCED"""
|
|
21
|
+
counts = {'0': 1000, '1': 1000}
|
|
22
|
+
p0 = {'0': 0.5, '1': 0.5}
|
|
23
|
+
result = ModularAnalyzer().analyze(RHO_IDEAL, counts, p0)
|
|
24
|
+
assert result.regime == 'KMS_BALANCED'
|
|
25
|
+
|
|
26
|
+
def test_mis_positivo_con_ruido():
|
|
27
|
+
"""Con rho ruidosa el MIS debe ser > 0"""
|
|
28
|
+
counts = {'0': 987, '1': 1013}
|
|
29
|
+
p0 = {'0': 0.5, '1': 0.5}
|
|
30
|
+
result = ModularAnalyzer().analyze(RHO_NOISY, counts, p0)
|
|
31
|
+
assert result.mis > 0.0
|
|
32
|
+
|
|
33
|
+
def test_probabilidades_normalizadas():
|
|
34
|
+
"""Las probabilidades corregidas deben sumar 1"""
|
|
35
|
+
counts = {'0': 987, '1': 1013}
|
|
36
|
+
p0 = {'0': 0.5, '1': 0.5}
|
|
37
|
+
result = ModularAnalyzer().analyze(RHO_NOISY, counts, p0)
|
|
38
|
+
total = sum(result.p_modular.values())
|
|
39
|
+
assert total == pytest.approx(1.0, abs=1e-9)
|
|
40
|
+
|
|
41
|
+
def test_summary_contiene_mis():
|
|
42
|
+
"""El summary debe incluir el MIS"""
|
|
43
|
+
counts = {'0': 987, '1': 1013}
|
|
44
|
+
p0 = {'0': 0.5, '1': 0.5}
|
|
45
|
+
result = ModularAnalyzer().analyze(RHO_NOISY, counts, p0)
|
|
46
|
+
assert 'MIS' in result.summary()
|
|
47
|
+
|
|
48
|
+
def test_dimension_incompatible():
|
|
49
|
+
"""Debe lanzar error si p0 y rho tienen dimensiones distintas"""
|
|
50
|
+
rho_3x3 = np.eye(3) / 3
|
|
51
|
+
counts = {'0': 500, '1': 500}
|
|
52
|
+
p0 = {'0': 0.5, '1': 0.5}
|
|
53
|
+
with pytest.raises(ValueError):
|
|
54
|
+
ModularAnalyzer().analyze(rho_3x3, counts, p0)
|