Mejoras en la Gestión del Conocimiento con IA: Lecciones de un Proyecto Real

Young man using cybernetic brain-computer interface with holographic display

Cómo construí un segundo cerebro mantenido por IA: arquitectura, esquema y lecciones aprendidas

¿Cuántas veces has guardado un artículo interesante para no volver a encontrarlo nunca? ¿O has tenido una idea que sabías que conectaba con algo que leíste hace tres semanas, pero no recuerdas dónde?

Ese es el problema que quería resolver: no capturar más, sino conectar mejor. Las herramientas de notas tradicionales acumulan; no piensan. Y los LLMs saben mucho del mundo, pero no saben nada de ti ni de lo que has leído esta semana.

Este post documenta cómo construí un sistema que resuelve exactamente esa tensión: un vault de Obsidian cuyo grafo de conocimiento es mantenido activamente por un agente de IA. No es «IA encima de tus notas» — es un sistema donde el agente es el mantenedor, con un esquema explícito que define cómo opera y que evoluciona con el uso.

Lo llamo LLM Wiki — un término acuñado por Andrej Karpathy, cuyo documento fundacional describe exactamente este patrón y fue el punto de partida de todo lo que sigue.


El patrón: LLM Wiki

La idea central es simple:

captura (inbox/ · mis_notas/)
agente procesa ←── CLAUDE.md (esquema operativo)
sources / entities / concepts / synthesis (wiki mantenida)
Obsidian Graph View (exploración visual)
index.base (índice dinámico por tipo)

Las fuentes externas (artículos, clippings) son inmutables — el agente nunca las modifica. Las lee, extrae lo relevante y genera páginas de síntesis, entidades y conceptos. Las notas propias (ideas, reflexiones, bitácoras de proyecto) se vinculan al grafo existente.

El resultado es una red que crece en coherencia con cada sesión, no solo en volumen.

Stack: Obsidian como interfaz de lectura, escritura y visualización + Claude Code como agente mantenedor.


La pieza clave: CLAUDE.md como esquema operativo

Todo el comportamiento del agente está definido en un único archivo: CLAUDE.md. No es documentación para humanos — es el sistema de instrucciones que el agente carga al inicio de cada sesión y que gobierna cada decisión que toma.

second_brain/
├── CLAUDE.md ← esquema operativo del agente
├── README.md ← guía de uso para el humano
├── index.base ← índice dinámico (Obsidian Bases)
├── log/
│ └── 2026-06.md ← registro mensual de operaciones
├── inbox/ ← punto de captura unificado
├── raw/clippings/ ← fuentes originales — INMUTABLE
├── sources/ ← resúmenes de cada fuente ingerida
├── entities/ ← empresas, personas, productos, tecnologías
├── concepts/ ← ideas conceptuales y marcos de referencia
├── synthesis/ ← ensayos de síntesis multi-fuente
├── projects/ ← proyectos con bitácora
└── mis_notas/ ← notas propias procesadas

El CLAUDE.md define:

  • Los principios que gobiernan cada decisión (mínima fricción al capturar, inferencia conservadora, todo se cita)
  • El frontmatter exacto para cada tipo de nota
  • El orden del barrido (qué carpetas escanear y en qué orden)
  • Los workflows de cada operación (ingesta, barrido, lint, síntesis)
  • Las reglas de inferencia (cuándo crear un enlace en firme vs. marcarlo como candidato)

La clave: cuando algo no funciona, no cambio la conversación — cambio el CLAUDE.md. El esquema se co-evoluciona con el uso. La próxima sesión parte del aprendizaje acumulado.


Frontmatter tipado: la base de todo

Cada nota del vault lleva un bloque YAML con campos semánticos. No es decorativo — es la señal que permite al agente clasificar, buscar y vincular sin ambigüedad.

El campo más importante es tipo:, que determina cómo procesa el agente la nota:

# Una fuente externa procesada
tipo: fuente
origen: "[[2026-06-15-nadella-frontier-ecosystem]]"
fecha: 2026-06-15
tags: [tipo/fuente, ia, token-capital, ecosistema]
relacionado: ["[[token-capital]]", "[[MSFT]]", "[[satya-nadella]]"]
# Un concepto extraído de varias fuentes
tipo: concepto
tags: [tipo/concepto, concepto]
relacionado: ["[[agentes-ia]]", "[[memoria-en-agentes]]", "[[claude-ai]]"]
# Una idea propia
tipo: idea
fecha: 2026-06-07
tags: [tipo/idea, agentes-ia, memoria]
relacionado: ["[[memoria-en-agentes]]", "[[second-brain-agent]]"]
estado: enlazado # nuevo → enlazado → revisado

El campo estado es la señal de tracking: el agente busca notas con estado: nuevo durante el barrido y las procesa. Una vez enlazadas, pasan a estado: enlazado.

El tag estructural para el Graph View

Cada nota lleva además un tag tipo/x como primer elemento de su array. Este tag es exclusivamente visual: colorea el grafo de Obsidian por tipo de nodo.

{ "query": "tag:#tipo/concepto", "color": { "a": 1, "rgb": 3973171 } },
{ "query": "tag:#tipo/entidad", "color": { "a": 1, "rgb": 16744272 } },
{ "query": "tag:#tipo/fuente", "color": { "a": 1, "rgb": 5793266 } },
{ "query": "tag:#tipo/sintesis", "color": { "a": 1, "rgb": 14443650 } }

Las cuatro operaciones del agente

El agente opera mediante cuatro comandos que se pueden combinar (barrido + ingesta, lint + barrido, etc.):

barrido

Escanea el vault buscando contenido nuevo o sin procesar. Orden: inbox/mis_notas/ → resto del vault.

Para cada nota sin frontmatter o con estado: nuevo:

  1. Infiere el tipo (¿idea propia? ¿clipping externo? ¿bitácora?)
  2. Añade el frontmatter mínimo
  3. Busca páginas relacionadas y crea enlaces
  4. Si la relación es dudosa → la marca como «enlace candidato» en lugar de forzarla
  5. Si la nota estaba en inbox/ o en la raíz → la mueve a mis_notas/ tras procesarla

El principio de inferencia conservadora es central: el agente prefiere anotar una relación como «candidato a confirmar» antes que inventar un vínculo. La wiki debe poder confiarse.

ingesta

Procesa fuentes externas (artículos, clippings):

inbox/ o Clippings/ → sources/<slug>.md + entities/ + concepts/
→ raw/clippings/ (archivo histórico inmutable)

Por ejemplo, al ingerir el artículo de Satya Nadella sobre ecosistemas de IA, el agente genera automáticamente:

## Tesis central
La ventaja competitiva no está en elegir el mejor modelo, sino en construir
un bucle de aprendizaje donde el capital humano y el token capital se
componen mutuamente.
## Relaciones con el vault
- [[token-capital]] — concepto central introducido aquí
- [[memoria-en-agentes]] — el "knowledge base" institucional es exactamente
el problema que resuelve un agente de memoria

lint

Mantenimiento periódico. Revisa: enlaces candidatos pendientes, páginas huérfanas, contradicciones entre páginas, conceptos sin página propia. Al final, propone síntesis para temas con ≥ 3 fuentes.

síntesis: <tema>

Cuando un tema alcanza masa crítica, el agente propone antes de crear:

Propuesta de síntesis: "El estado del arte de agentes IA (2026)"
- Fuentes: [[tipos-de-agentes-claude]], [[loop-engineering-omarsar0]], [[graph-tech-yahoo]]
- Tesis: el trabajo del ingeniero sube un nivel — de diseñar agentes a diseñar
los loops que los promptean
- Estructura tentativa: taxonomía → anatomía del loop → economía → casos de uso
¿Procedo?

El agente propone; el humano aprueba. Sin aprobación, no crea nada.


Las 5 mejoras que surgieron del uso real

La arquitectura inicial fue sencilla. Estos cinco problemas emergieron rápido:

1. inbox/ como punto de captura unificado

Problema: las notas aterrizaban en la raíz, en mis_notas/, en Clippings/… el agente buscaba por todo el vault en cada sesión.

Solución: inbox/ que el agente escanea primero. Las notas procesadas se mueven a mis_notas/ automáticamente.

2. Pipeline de clippings simplificado

Problema: el flujo original tenía dos pasos intermedios innecesarios y fricción para el usuario.

Solución: el agente procesa directamente desde donde llega el contenido a sources/. raw/clippings/ es el archivo histórico, pero ya no es un paso obligatorio.

3. Rotación mensual del log

Problema: log.md crecía indefinidamente.

Solución: archivos mensuales en log/YYYY-MM.md. log.md pasa a ser un índice de punteros.

4. index.mdindex.base (Obsidian Bases)

Problema: el agente mantenía el índice a mano — fuente de errores y trabajo innecesario.

Solución: 6 vistas dinámicas (Fuentes, Conceptos, Entidades, Proyectos, Síntesis, Ideas) que consultan el frontmatter automáticamente. El agente ya no toca el índice.

5. synthesis/ para síntesis multi-fuente

Problema: concepts/ tiene páginas de un solo concepto. Cuando acumulas 3+ fuentes sobre un tema, merece un ensayo que cruce perspectivas.

Solución: carpeta synthesis/ con tipo: sintesis y color propio en el Graph View.


Lo que escala bien y lo que no

Escala bien: index.base dinámico, rotación mensual del log, obsidian search indexado, inbox/ como captura acotada.

Lo que se rompe primero:

UmbralProblemaFix
~200 notasPáginas de concepto con listas de menciones inmanejablesBacklinks dinámicos vía Obsidian Bases
~300 notasBarrido full-vault lentoConfiar solo en inbox/ + estado: nuevo
~500 notasCoherencia global degradaSesiones temáticas acotadas en lugar de operaciones globales

El riesgo estructural: los LLMs tienen contexto finito. A partir de cierto volumen, la coherencia se mantiene localmente (en la sesión) pero no globalmente. La respuesta es acotar el scope de cada sesión, no intentar procesar todo el vault de una vez.


Lecciones clave

Después de varias semanas usando este sistema, estas son las conclusiones que llevaría a cualquier implementación similar:

  1. El esquema es el producto. La calidad del segundo cerebro depende casi enteramente del CLAUDE.md, no del modelo ni de la herramienta. Un esquema claro convierte cualquier LLM capaz en un mantenedor confiable.
  2. La inferencia conservadora es más valiosa que la exhaustiva. Un enlace marcado como «candidato a confirmar» vale más que diez enlaces inventados. La confianza en el sistema depende de que sus afirmaciones sean verificables.
  3. El esquema debe co-evolucionar con el uso. Cuando algo produce un resultado inesperado, el insight va al CLAUDE.md, no solo a la conversación. Así la próxima sesión parte del aprendizaje acumulado.
  4. La mínima fricción al capturar es no negociable. Si el sistema requiere esfuerzo para añadir notas, dejarás de añadirlas. El agente debe poder inferir razonablemente y enlazar después.
  5. Dos vaults especializados > un vault generalista. Mezclar contenido técnico con recetas o películas degrada la calidad de los enlaces — el agente pierde contexto semántico coherente.
  6. El dominio importa. El agente hace mejores inferencias cuando el vault tiene coherencia semántica. Una sola carpeta bien definida supera a diez carpetas mezcladas.

Preguntas frecuentes

¿Necesito saber programar para montar esto?
No. El único «código» que escribes es el CLAUDE.md en texto plano. Claude Code maneja la interacción con Obsidian vía su CLI oficial.

¿Cuánto tiempo lleva cada sesión de mantenimiento?
Un barrido con 2-3 notas nuevas tarda menos de un minuto. Una sesión de lint completa, dependiendo del volumen, entre 5 y 15 minutos. La cadencia habitual es un barrido al capturar algo nuevo y un lint mensual.

¿Qué pasa si el agente enlaza algo incorrectamente?
El campo estado: enlazado no es irreversible. Los enlaces candidatos están marcados explícitamente para revisión humana. El lint periódico detecta inconsistencias. Y el CLAUDE.md puede ajustarse para que el agente sea más o menos conservador.

¿Es seguro tener el vault en GitHub público?
No directamente. raw/clippings/ contiene copias de artículos (copyright), y mis_notas/ tiene contenido personal. La recomendación es un repositorio separado tipo «template» con solo la estructura, el CLAUDE.md y ejemplos anonimizados.

¿Funciona con otros LLMs además de Claude?
El esquema en CLAUDE.md es agnóstico al modelo — cualquier LLM suficientemente capaz puede seguirlo. Claude Code es la herramienta de ejecución, pero el patrón es portable.

¿Qué diferencia hay entre sources/ y synthesis/?
sources/ es un resumen de una fuente externa con sus relaciones. synthesis/ es un ensayo que cruza varias fuentes sobre un mismo tema, con tesis propia. Una synthesis/ necesita ≥ 3 fuentes y aprobación explícita del usuario antes de crearse.


Referencias y recursos


¿Y tú qué sistema usas?

Este vault lleva pocas semanas rodando y ya está cambiando cómo capturo y conecto ideas. Pero seguro que hay enfoques que no he considerado.

¿Tienes un sistema de notas conectado? ¿Has probado a darle mantenimiento con un agente? Cuéntamelo en los comentarios — me interesa especialmente saber cómo resuelves el problema del volumen y la coherencia a largo plazo.

Y si quieres replicar esta arquitectura o tienes dudas sobre alguna parte del sistema, escríbeme directamente.


Este post es parte de una serie sobre el proyecto second-brain-agent — la construcción en curso de un sistema de conocimiento personal mantenido por IA.Cómo construí un segundo cerebro mantenido por IA: arquitectura, esquema y lecciones aprendidas

Llevo años con el mismo problema: capturo artículos, ideas y notas, pero la colección crece sin conectarse. Los sistemas de notas acumulan, no piensan. Y los LLMs saben mucho del mundo, pero no saben nada de ti.

Este post documenta cómo resolví esa tensión construyendo lo que llamo un LLM Wiki: un vault de Obsidian cuyo grafo de conocimiento es mantenido activamente por un agente de IA. No es un sistema de notas con IA encima — es un sistema donde el agente es el mantenedor, con un schema explícito que define exactamente cómo opera

Distribución Cuántica de Claves: Protocolo BB84

Objetivo

La Distribución Cuántica de Claves (QKD : Quantum Key Distribution) es un método por el que dos partes, que comparten un canal de comunicación no seguro pueden construir, negociar y acordar una clave secreta.

En este artículo vamos a analizar este procedimiento, sin entrar en la propia generación de la clave.

Describiremos como funciona el protocolo BB84, desarrollado por Charles Bennett y Gilles Brassard en 1984.

Leer más »

Algoritmo de Deutsch

A través de este algoritmo podremos entender por qúe la computación cuántica puede ofrecer mejoras en el tiempo de ejecución de carácter exponencial frente a la computación clásica.

Su nombre se debe a David Deutsch, profesor de la Universidad de Oxford.

¿Y como vamos a conseguir eso objetivo?

Empecemos imaginando que tenemos una función básica, f , que tiene como dominio Dom f = \{0,1\} y de igual manera tiene como imagen Im f = \{0,1\} . Es esta una función muy básica: puede dar siempre el mismo valor, ya sea 0 ó 1, a cualquier valor de entrada, o por el contrario puede ofrecer una valor diferente segun sea el valor de x. Llamaremos constantes al primer tipo de función, y balanceadas al segundo.

El problema al que nos enfrentamos se reduce a averiguar, dada una función f, de que tipo es.

Si disponemos de un ordenador clásico: ¿cuantas llamadas a la función hemos de hacer?. Es clara la respuesta : 2 veces.

Duetsch nos mostró que con un ordenador, o circuito, cuántico basta con hacer una sola llamada.

Veámoslo….

Partimos con un circuito cuántico tal cual mostramos en la figura 1.

Figura 1: esquema del circuito cuántico necesario para el Algoritmo de Deutsch

Tenemos aquí un circuito con dos qubits , el primero de los cuales llamaremos qubit de entrada, inicializado a |0\rangle y qubit de referencia al segundo, inicializado a |1\rangle , y dos qubits de salida, siendo uno de ellos el que nos indica el resultado del algortimo. A los dos primeros se les aplica una puerta de Hadamard, que ya hemos estudiado en una entrada anterior y que básicamente aplica una superposición al qubit correspondiente. El circuito en sí, que denominamos U_{f} aplica al segundo quibit una suma exclusica, XOR, entre el qubit de control y el resultado de aplicar la función f al qubit de entrada.

Entremos en el detalle, calculando cada unos de los 4 estados que hemos llamado \psi_{0} ,\psi_{1}, \psi_{2}  \ y\ \psi_{3} .

El primero es inmediato, pues se refiere a los qubits inicializados. Luego \psi_{0} = |01\rangle. Recordemos que utilizamos la convención de indicar a la izquierda el qubit con la posición superior.

Tras aplicar la puerta Hadamard a los dos qubits nos encontramos con :

|\psi_{1}\rangle = H^{\otimes 2} |01\rangle = \left (\frac {|0\rangle + |1\rangle}{\sqrt{2}} \right ) \left (\frac {|0\rangle - |1\rangle}{\sqrt{2}} \right ) vemos como la puerta de Hadamard provoca el paralelismo, al convertir un quibit de valor 0 ó 1 en una superposición.

Pasamos a calcular \psi_{2}, para ello vamos a calcular de manera genérica U_{f} |x\rangle \left (\frac {|0\rangle - |1\rangle}{\sqrt{2}} \right ) , que equivale a dejar el primer termino tal cual y aplicar la suma exclusiva al segundo término U_{f} |x \rangle \left (\frac {|0\rangle - |1\rangle}{\sqrt{2}} \right ) = |x \rangle \left (\frac {|0\oplus{f(x)}\rangle - |1\oplus{f(x)}\rangle}{\sqrt{2}} \right ) y para continuar vamos a considerar dos escenarios separados :

si

  • si f(x)=0 \rightarrow |x\rangle \left (\frac {|0\oplus{f(x)}\rangle - |1\oplus{f(x)}\rangle}{\sqrt{2}} \right )  = |x\rangle \left (\frac {|0\rangle - |1\rangle}{\sqrt{2}} \right )
  • si f(x)=1 \rightarrow |x\rangle \left (\frac {|0\oplus{f(x)}\rangle - |1\oplus{f(x)}\rangle}{\sqrt{2}} \right ) = |x\rangle \left (\frac {|1\rangle - |0\rangle}{\sqrt{2}} \right )

Luego podemos simplificar agrupando los dos términos tal que (-1)^{f(x)} |x\rangle \left (\frac {|0\rangle - |1\rangle}{\sqrt{2}} \right )

Estos calculos previos nos permiten avanzar con el cálculo de \psi_{2} :

\psi_{2}= \frac {\left ((-1)^{f(0)}|0\rangle + (-1)^{f(1)}|1\rangle \right )}{\sqrt{2}} \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right ), que de igual manera que en el paso previo vamos a dividir en varios escenarios, tal que :

|\psi_{2} \rangle = \left\{ \begin{aligned} &  +\left ( \frac {\left (|0\rangle + |1\rangle \right )}{\sqrt{2}} \right ) \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right )  \quad si f(0)=f(1)=0 \\ &   - \left ( \frac {\left (|0\rangle + |1\rangle \right )}{\sqrt{2}} \right ) \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right )  \quad si f(0)=f(1)=1 \\ & + \left ( \frac {\left (|0\rangle - |1\rangle \right )}{\sqrt{2}} \right ) \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right ) \quad si f(0)=0 \ y \ f(1)=1 \\ & - \left (\frac {\left (|0\rangle - |1\rangle \right )}{\sqrt{2}} \right ) \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right ) \quad si f(0)=1 \ y \  f(1)=0 \end{aligned} \right.

que se puede simplificar tal que :

|\psi_{2} \rangle = \left\{ \begin{aligned} &  \pm \left ( \frac {\left (|0\rangle + |1\rangle \right )}{\sqrt{2}} \right ) \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right )  \quad si f(0)=f(1) \\ & \pm \left ( \frac {\left (|0\rangle - |1\rangle \right )}{\sqrt{2}} \right ) \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right ) \quad si f(0) \neq f(1) \end{aligned} \right.

Finalmente aplicamos la puerta de Hadamard de salida al primer qubit , y tendremos :

|\psi_{3} \rangle = \left\{ \begin{aligned} &  \pm |0\rangle \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right )  \quad si f(0)=f(1) \\ & \pm |1\rangle \left ( \frac { |0\rangle - |1\rangle }{\sqrt{2}} \right ) \quad si f(0) \neq f(1) \end{aligned} \right.

…que a su vez podemos simplificar :

\psi_{3} =  \pm | f(0) \oplus f(1) \rangle \left ( \frac {|0\rangle - |1\rangle}{\sqrt{2}} \right )

¿Y que nos indica esa expresión?: que podemos medir el primer qubit de ese estado cuántico resultante, si es cero sabremos que la función es constante, y balanceada si el valor es 1.

Con un solo ciclo de ejecución de llamada a la función, con el estado de dos qubits |01 \rangle, podremos averiguarde que tipo es, ofreciendo por tanto una gran ventaja en complejidad de computación sobre un ordenador de computación clásica que necesitará dos llamadas.

Este algoritmo se puede extender a funciones de manera Dom f = \{0,1\}^{n} que veremos en una siguiente entrada en el blog.

He preparado este trabajo leyendo el capítulo 1.4.3 del libro Quantum Computation and Quantum Informacion de Michael A. Nielsen & Isaac L. Chuang y el capítulo 7.2 del Introduction to Classical and Quantum Computing de Thomas G. Wong ,

La puerta de Hadamard

¿Qué es una puerta Hadamard?

La puerta de Hadamard es un operador de computación cuántica, muy simple a la vez que, probablemente, de los más usados. Su utilidad reside en su capacidad de convertir un qubit de un estado, tal que |0\rangle o |1\rangle, en una superposición de los mismos. Veamos los detalles.

La puerta de Hadamard se nombra con H y se define de forma matricial tal que H= \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}

\huge { \text {Puerta de Hadamard }  H= \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} }

Y efectivamente, si aplicamos una puerta Hadamard a |0\rangle queda tal que:

\dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} |0\rangle = \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} = \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 \\ 1 \end{pmatrix} = \dfrac{1}{ \sqrt{2}} (|0\rangle + |1\rangle)

…de igual manera si aplicamos Hadamard a un qubit |1\rangle tendremos:

\dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} |1\rangle = \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 0 \\ 1 \end{pmatrix} = \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 \\ -1 \end{pmatrix} = \dfrac{1}{ \sqrt{2}} (|0\rangle - |1\rangle)

Apliquemos Hadamard sobre estados en otras bases:

H |+\rangle = \dfrac{1}{ \sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \dfrac{1}{ \sqrt{2}} (|0\rangle + |1\rangle) = \dfrac{1}{2} \begin{pmatrix} 2  \\ 0 \end{pmatrix} = \begin{pmatrix} 1 \\ 0 \end{pmatrix} = |0\rangle

como no podía ser de otra manera, ya que H es unitaria y reversible.

Es muy útil hacer una pequeña transformación y escribir H de manera genérica para un x \in \{0,1\} tal que H|x\rangle = \dfrac {1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} |x\rangle = \dfrac{1}{\sqrt{2}}(|0\rangle + (-1)^{x}|1\rangle) , efectivamente

H|x\rangle  = \dfrac{1}{\sqrt{2}}(|0\rangle + (-1)^{x}|1\rangle) = \left\{ \begin{aligned} & \text{si x=0 } H|0\rangle = \dfrac{1}{\sqrt{2}}(|0\rangle + |1\rangle)  \\ & \text{si x=1 } H|1\rangle = \dfrac{1}{\sqrt{2}}(|0\rangle - |1\rangle) \end{aligned}  \right.

y podemos ir un paso más allá y escribir la expresión tal que :

H |x \rangle  = \dfrac{1}{\sqrt{2}}\left(|0\rangle + (-1)^{x}|1\rangle\right) = \dfrac{1}{\sqrt{2}} \left(\sum\limits_{y=0}^{1}(-1)^{xy}|y\rangle\right) \text{para } x\in \{0,1\}

Hadamard sobre multiples qubits..

Hadamard es una puerta que opera sobre un sólo qubit, pero en circuitos multiqubits no es infrecuente aplicar simultáneamente esta puerta sobre todos los qubits. En algunos textos se le denomina tensor Hadamard o H^{\otimes n} . Hagamos un caso práctico de aplicación de este tensor a un estado cuántico genérico | \psi \rangle = |x_{1} x_{2}x_{3}..x_{n}\rangle con x_{i} \in \{0,1\} \forall i \in \{1,2..,n\}

H^{\otimes n}| \psi \rangle = H^{\otimes n} |x_{1} x_{2}x_{3}..x_{n}\rangle = H |x_{1}\rangle \otimes H |x_{2} \rangle \otimes H |x_{3} \rangle \otimes ..H|x_{n}\rangle

..es importante ver que se está aplicando una puerta a cada uno de los estados simples y multiplicando tensorialmento esos n resultados. Utilizando la expresión genérica que usamos en el apartado anterior podemos continuar con:

H |x_{1} \rangle \otimes H |x_{2}\rangle  \otimes H |x_{3}\rangle \otimes \cdots H|x_{n}\rangle = \dfrac{1}{\sqrt{2}}(|0\rangle + (-1)^{x_{1}}|1\rangle) \otimes \dfrac{1}{\sqrt{2}}(|0\rangle + (-1)^{x_{2}}|1\rangle)\cdots\otimes \dfrac{1}{\sqrt{2}}(|0\rangle + (-1)^{x_{n}}|1\rangle) =  \dfrac{1}{\sqrt{2}}\left(\sum\limits_{y_{1}=0}^{1}(-1)^{x_{1}y_{1}}|y_{1}\rangle\right) \otimes \left(\sum\limits_{y_{2}=0}^{1}(-1)^{x_{1}y_{2}}|y_{2}\rangle\right) \cdots \otimes \left(\sum\limits_{y_{n}=0}^{1}(-1)^{x_{n}y_{n}}|y_{n}\rangle\right)

..que se puede simplificar con \dfrac{1}{\sqrt{2^{n}}} \sum\limits_{y\in\{0,1\}^{n}}(-1)^{x_{1}y_{1}+x_{2}y_{2}\cdots x_{n}y_{n}}|y\rangle = \dfrac{1}{\sqrt{2^{n}}} \left(\sum\limits_{y\in\{0,1\}^{n}}(-1)^{\sum\limits_{i=1}^{n}x_{i}y_{i}}|y\rangle  \right)

La manera intuitiva de llegar a esta última expresión es viendo como cada componente |0\rangle de cuaquier estado de múltiples qubits lleva siempre signo positivo, ya que todo producto x_iy_i con x_i=0 será cero, y el componte |1\rangle lleva un signo tal que (-1)^{x_{i}} , que se suman para el estado final de multiples qubits.

Veamos un caso particular con n=3..

H^{\otimes n}| \psi \rangle = H^{\otimes 3} |x_{1} x_{2}x_{3} \rangle= H |x_{1}\rangle \otimes H |x_{2} \rangle \otimes H |x_{3} \rangle = \dfrac{1}{ \sqrt{2^{3}}} \left ( \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} |x_{1} \rangle \otimes \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} |x_{2} \rangle \otimes \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} |x_{3} \rangle \right) = \dfrac{1}{ \sqrt{2^{3}}} \bigg ( \Big ((-1)^{x_{1}*0}|0\rangle + (-1)^{x_{1}*1}|1\rangle \Big ) \Big ((-1)^{x_{2}*0}|0\rangle + (-1)^{x_{2}*1}|1\rangle \Big ) \Big ((-1)^{x_{3}*0}|0\rangle + (-1)^{x_{3}*1}|1\rangle \Big ) \bigg ) = \dfrac{1}{ \sqrt{2^{3}}} \Big ( (-1)^{(x_{1}*0+x_{2}*0+x_{3}*0)} |000\rangle + (-1)^{(x_{1}*0+x_{2}*0+x_{3}*1)} |001\rangle + (-1)^{(x_{1}*0+x_{2}*1+x_{3}*0)} |010\rangle + (-1)^{(x_{1}*0+x_{2}*1+x_{3}*1)} |011\rangle + (-1)^{(x_{1}*1+x_{2}*0+x_{3}*0)} |100\rangle + (-1)^{(x_{1}*1+x_{2}*0+x_{3}*1)} |101\rangle + (-1)^{(x_{1}*1+x_{2}*1+x_{3}*0)} |110\rangle + (-1)^{(x_{1}*1+x_{2}*1+x_{3}*1)} |111\rangle \Big ) = \dfrac{1}{ \sqrt{2^{3}}} \Big (|000\rangle + (-1)^{(x_{3})} |001\rangle + (-1)^{(x_{2})} |010\rangle + (-1)^{(x_{2}+x_{3})} |011\rangle + (-1)^{(x_{1})} |100\rangle +(-1)^{(x_{1}+x_{3})} |101\rangle + (-1)^{(x_{1}+x_{2})} |110\rangle + (-1)^{(x_{1}+x_{2}+x_{3})} |111\rangle \Big )

Hemos visto la definición de la puerta de Hadamard, su utilidad para provocar una superposición desde un estado de una sola base, y hemos trabajado un poco de álgebra para generar una expresión genérica del tensor Hadamard sobre múltiples qubits, que será muy útil en la demostración del algoritmo de Deutsch-Jozsa que veremos más adelante.

Por cierto, esta puerta debe su nombre al matemático francés Jacques Hadamard.

Tratando de entender la Física Cuántica: Principio de No Clonación

Uno de los pilares sobre los que se apoya la física cuántica es el principio de no clonación o no replicabilidad.

¿Qué significa eso?: que no podemos copiar el estado de un qubit, o dicho de otra manera que no podemos replicar su estado cuántico en otro qubit. Copiar bits en la computación «clásica» es una operación más que cotidiana, forma parte intrínseca de las operaciones, y no nos podemos imaginar un escenario en el que no podamos hacerlo.

«The no-cloning theorem is one of the key results in quantum mechanics that reflects the underlying non-classical nature of quantum information.»

Fuente: Deutsch, D., Ekert, A., & Jozsa, R. (1996). Quantum privacy amplification and the security of quantum cryptography over noisy channels. Proceedings of the Royal Society of London. Series A: Mathematical, Physical and Engineering Sciences, 452(1954), 1799-1824.

¿Y como podemos demostrar una cosa así?, pues con un poquito de álgebra, espacios vectoriales y productos escalares.

Vamos a ello1

En primer lugar pensaremos en un operador lineal U aplicable a dos qubits tal que replica el valor de uno de ellos en el otro. Lo podemos expresar en lenguaje matemático tal que :

U|\phi 0\rangle = | \phi \phi \rangle

que indica que aplicamos el operador sobre |\phi \phi \rangle y obtenemos un par de qubits iguales. Habríamos replicado \phi en el qubit con valor inicial 0 . La dimensión del operador U es 4×4, ya que aplica a dos qubits.

Podemos desarrollar el término de la izquierda tal que :

\begin{pmatrix} u_{11} & u_{12} & u_{13} & u_{14} \\ u_{21} & u_{22} & u_{23} & u_{24} \\ u_{31} & u_{32} & u_{33} & u_{34} \\ u_{41} & u_{42} & u_{43} & u_{44} \end{pmatrix}  \begin{pmatrix} \alpha \\ \beta \end{pmatrix} \otimes \begin{pmatrix} 1 \\ 0 \end{pmatrix}  =  \begin{pmatrix} u_{11} & u_{12} & u_{13} & u_{14} \\ u_{21} & u_{22} & u_{23} & u_{24} \\ u_{31} & u_{32} & u_{33} & u_{34} \\ u_{41} & u_{42} & u_{43} & u_{44} \end{pmatrix} \begin{pmatrix} \alpha \begin{pmatrix} 1 \\  0  \end{pmatrix}  \\ \beta \begin{pmatrix} 1 \\ 0  \end{pmatrix} \end{pmatrix} = \begin{pmatrix} u_{11} & u_{12} & u_{13} & u_{14} \\ u_{21} & u_{22} & u_{23} & u_{24} \\ u_{31} & u_{32} & u_{33} & u_{34} \\ u_{41} & u_{42} & u_{43} & u_{44} \end{pmatrix} \begin{pmatrix} \alpha \\ 0 \\  \beta \\ 0  \end{pmatrix} = \begin{pmatrix} u_{11} \alpha  + u_{13} \beta \\ u_{21} \alpha  + u_{23} \beta \\ u_{31} \alpha + u_{33} \beta \\ u_{41} \alpha  + u_{43} \beta \end{pmatrix}

y si desarrollamos el término de la derecha:

| \phi \phi \rangle = \begin{pmatrix} \alpha \\ \beta \end{pmatrix} \otimes \begin{pmatrix} \alpha \\ \beta \end{pmatrix} = \begin{pmatrix} \alpha \begin{pmatrix} \alpha \\  \beta  \end{pmatrix}  \\ \beta \begin{pmatrix} \alpha \\ \beta  \end{pmatrix} \end{pmatrix} = \begin{pmatrix} \alpha^2  \\ \alpha \beta \\  \alpha \beta \\ \beta^2 \end{pmatrix}

Igualando ambas expresiones obtenemos estas cuatro ecuaciones :

u_{11} \alpha  + u_{13} \beta = \alpha^2

u_{21} \alpha  + u_{23} \beta = \alpha \beta

u_{31} \alpha  + u_{33} \beta = \alpha \beta

u_{41} \alpha  + u_{43} \beta = \beta^2

de aquí podemos conseguir muchas soluciones, pero que siempre serán función de \alpha y \beta . Sólo como ejemplo :

u_{11}=\alpha , u_{13} = 0 , u_{21} = 0 , u_{23} = \alpha , u_{31} = \beta , u_{33} = 0 , u_{41} = 0 , u_{43} = \beta

Podemos observar como la matriz U tiene componentes función del qubit a replicar, luego no existe un operador U que replique el estado cuántico de un qubit en otro.

  1. Demostración extraída del libro Introduction to Classical and Quantum Computing de Thomas G. Wong. Capítulo 4.4.4 No-Cloning Theorem ↩︎

Resumen de puertas y qubits comunes en quantum computing

Me he preparado esta tabla con la definición de las puertas y qubits más frecuentes. Iré actualizanda conforme avanzo en el estudio de esta teoría.

IdentityI|0\rangle = |0\rangle
I|1\rangle = |1\rangle
I = \begin{pmatrix}1&0\\0&1\end{pmatrix}
Pauli XX|j\rangle = |1 \oplus j\rangle \;  \text{para j=\{0,1\}}
X|0\rangle = |1 \rangle
X|1\rangle = |0\rangle
X = \begin{pmatrix}0&1\\1&0\end{pmatrix}
Pauli YY|j\rangle = (-i)^{j}|1\oplus j\rangle  \;  \text{para j=\{0,1\}}
Y|0\rangle = i|0\rangle
Y|1\rangle = -i|1\rangle
Y = \begin{pmatrix}0&{-i}\\{i}&0\end{pmatrix}
Pauli ZZ|j\rangle = (-1)^j |j\rangle \;  \text{para j=\{0,1\}}
Z|0\rangle = |0\rangle
Z|1\rangle = -|1\rangle
Z = \begin{pmatrix}1&0\\0&-1\end{pmatrix}
Phase SS|0\rangle = |0\rangle
S|1\rangle = i|1\rangle
S = \begin{pmatrix}1&0\\0&i\end{pmatrix}
TT|0\rangle = |0\rangle
T|1\rangle = e^{i\frac{\pi}{4}}|1\rangle
T = \begin{pmatrix}1&0\\0&e^{i\frac{\pi}{4}}\end{pmatrix}
H
Hadamard
H|0\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle+|1\rangle\right)
H|1\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle-|1\rangle\right)
H = \frac{1}{\sqrt{2}}\begin{pmatrix}1&1\\1&-1\end{pmatrix}
|+\rangle
|-\rangle
|+\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle+|1\rangle\right)
|-\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle-|1\rangle\right)
|i\rangle
|-i\rangle
|i\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle+i|1\rangle\right)
|-i\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle-i|1\rangle\right)
|\phi^{+}\rangle
|\phi^{-}\rangle
|\phi^{+}\rangle = \frac{1}{\sqrt{2}}\left(|00\rangle+|11\rangle\right)
|\phi^{-}\rangle = \frac{1}{\sqrt{2}}\left(|00\rangle-|11\rangle\right)
|\psi^{+}\rangle
|\psi^{-}\rangle
|\psi^{+}\rangle = \frac{1}{\sqrt{2}}\left(|01\rangle+|10\rangle\right)
|\psi^{-}\rangle = \frac{1}{\sqrt{2}}\left(|01\rangle-|10\rangle\right)

Trabajando con Datetime y Time

Photo by Pixabay on Pexels.com

Comenzamos…

Dejamos ya importadas las librerías que vamos a necesitar:

import datetime
import time
import numpy as np
from datetime import datetime,timedelta
from zoneinfo import ZoneInfo, available_timezones
import calendar
%matplotlib inline
import pandas as pd
import sys

A raiz de la actualización del código que soporta al @elgalloaurora he estado jugando y probando con el tratamiento y gestión de horas con sus diferentes versiones “naive” y “no naive” y el cambio entre UTC, referenciadas a diferentes timezones y en modo segundos desde un determinado epoch. He escrito este pqueño doc para que me sirva de memoria para futuros trabajos

Vemos cosas básicas, como :

Horal actual local

datetime.now(ZoneInfo('Europe/Madrid'))
datetime.datetime(2024, 5, 26, 11, 50, 47, 425942, tzinfo=zoneinfo.ZoneInfo(key='Europe/Madrid'))
datetime.now(ZoneInfo('US/Hawaii'))
datetime.datetime(2024, 5, 25, 23, 50, 47, 666428, tzinfo=zoneinfo.ZoneInfo(key='US/Hawaii'))

y aquí se puede encontrar todas las zonas disponibles:

available_timezones()
{'Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 .....
 'US/Samoa',
 'UTC',
 'Universal',
 'W-SU',
 'WET',
 'Zulu',
 'build/etc/localtime'}

Otra manera de conseguir la hora actual en otra zona horaría es con astimezone

datetime.now(ZoneInfo('Europe/Madrid')).astimezone(ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 10, 50, 49, 27417, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

o directamente:

datetime.now().astimezone(ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 10, 50, 49, 474078, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

Como caso especial tenemos la hora UTC, muy útil pues es la que nos devuelven, y aceptan, muchas librerías:

datetime.now(ZoneInfo('UTC'))
datetime.datetime(2024, 5, 26, 9, 50, 50, 453552, tzinfo=zoneinfo.ZoneInfo(key='UTC'))

Hora naive y no naive:

fecha = datetime(year=2024,month=5,day=1,hour=10,minute=10)

Esa hora no tiene asociao ningún timezone, y lo podemos confirmar llamando a la función tzinfo:

fecha.tzinfo

Sin embargo esta fecha sí está asociada, en particular a Lisboa:

fecha_lisboa = datetime(year=2024,month=5,day=1,hour=10,minute=10,tzinfo=ZoneInfo(key='Europe/Lisbon'))

en efecto:

fecha_lisboa.tzinfo
zoneinfo.ZoneInfo(key='Europe/Lisbon')

Y que impacto tiene esa diferencia?: por ejemplo, que no puedes restarlas. No puedes calcular la diferencia en tiempo entre una fecha naive y no naive

fecha-fecha_lisboa
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In[114], line 1
----&gt; 1 fecha-fecha_lisboa


TypeError: can't subtract offset-naive and offset-aware datetimes

Sin embargo esto si funciona al estar comparando dos naives :

fecha_1 = datetime(year=2024,month=5,day=1,hour=10,minute=10,tzinfo= ZoneInfo('Europe/Lisbon'))
fecha_2 = datetime(year=2024,month=5,day=1,hour=11,minute=10,tzinfo= ZoneInfo('Europe/Madrid'))

qué resultado dará la resta anterior?

(fecha_1-fecha_2).seconds
0

efectivamente, la 11:10 hora local en Madrid corresponden con las 11:10 en Lisboa

Horas medidas en segundos

Este resultado nos da los segundos desde un epoch en particular. Y qué es eso de un epoch?..es una referencia, tal como se define en la documentacion oficial de la librería time : > The epoch is the point where the time starts, the return value of time.gmtime(0). It is January 1, 1970, 00:00:00 (UTC) on all platforms.

time.time()
1716717063.091218

y como conseguimos esa referencia?. La conseguimos con la ordent:

time.gmtime(0)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)

que podemos convertir a formato ´datetime`

datetime.fromtimestamp(time.mktime(time.gmtime(0)))
datetime.datetime(1970, 1, 1, 0, 0)

ese time.time() nos da un resultado en hora UTC. Para comprobarlo vamos a usar la función gmtime

time.gmtime(time.time())
time.struct_time(tm_year=2024, tm_mon=5, tm_mday=26, tm_hour=9, tm_min=51, tm_sec=5, tm_wday=6, tm_yday=147, tm_isdst=0)

y para conseguirla en hora actual:

time.localtime(time.time())
time.struct_time(tm_year=2024, tm_mon=5, tm_mday=26, tm_hour=11, tm_min=51, tm_sec=5, tm_wday=6, tm_yday=147, tm_isdst=1)

Y podemos conseguir esa hora en formato datetime tal como lo veíamos anteriormente

datetime.fromtimestamp(time.time()) #ojo...en local, aunque no naive
datetime.datetime(2024, 5, 26, 11, 51, 7, 303250)
datetime.fromtimestamp(time.time()).replace(tzinfo=ZoneInfo('Europe/Madrid'))
datetime.datetime(2024, 5, 26, 11, 51, 7, 536789, tzinfo=zoneinfo.ZoneInfo(key='Europe/Madrid'))

ojo, que replace tan solo cambia la timezone pero no recalcula la hora de acuerdo a ese cambio.

datetime.fromtimestamp(time.time()).replace(tzinfo=ZoneInfo('Europe/Madrid')).replace(tzinfo=ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 11, 51, 7, 950463, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

y para generar una hora en segundos desde una estructurada:

time.time()
1716717068.888389
time.gmtime(time.time())
time.struct_time(tm_year=2024, tm_mon=5, tm_mday=26, tm_hour=9, tm_min=51, tm_sec=9, tm_wday=6, tm_yday=147, tm_isdst=0)
calendar.timegm(time.gmtime(time.time()))
1716717069

que es dentro de una hora (los 3600 segundos sumados)

Y para pasar a un formato no naive desde segundos:

datetime.fromtimestamp(time.time()+3600,tz=ZoneInfo('Europe/Madrid'))
datetime.datetime(2024, 5, 26, 12, 51, 10, 228910, tzinfo=zoneinfo.ZoneInfo(key='Europe/Madrid'))
datetime.fromtimestamp(time.time(),tz=ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 10, 51, 10, 435502, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

Así como resumen, y replicando el cuadro de la documentación de la librería:

  • Cambio de segundos desde epoch a struc_time en UTC : gmtime()

  • Cambio de segundos desde epoch a struct_time en LocatTime : localtime()

  • Cambio de struct_time en UTC a segundos : calendar.timegm()

  • Cambio de struct_time local time a segundos: mktime()

    y añado dos más:

  • Cambo de datetime a time_struct : datetime.timetuple()

  • Cambio de time_struct a datetime : datetime.fromtimestamp(mktime(time_struc))


¿Cómo tuitear desde Python?

¿Para que vale eso?

Desde mi cuenta de twitter @walyt publico diariamente diversos gráficos con información relevante del Mercado Eléctrico: precios del spot del día, como se comporta la generación, evolución precios, previsiones energía solar etc…Estos tuits los publico de manera «automatizada» por medio de scripts en Python residentes en una máquina virtual en Google Cloud. Por medio de un crontab planifico a lo largo del día esa publicación de información. Así aseguras una publicación de manera regular, y que la información de base está siempre disponible.

Me di cuenta el fin de semana pasado de que hacía varios días que no salía ningun nuevo tuit. Alarma!. Tras chequear y cacharrear un poco me di cuenta del algún cambio en la API de twitter, que hacía que el procedimiento que estaba usando no era válido. Hasta este fin de semana no he tenido unas horas para arreglarlo. Y parece que ya lo tengo. Para ayudar a quien le pase algo parecido o para quien necesite este código he preparado este post.

Qué necesito?

Lo primero que se necesita es obvio: una cuenta de twitter!!!

Venga, que me imagino que la tenéis. Lo segundo es dar de alta esa cuenta en el portal de desarrollo. Nos encontramos aquí tres modalidades :

  • Free: que te permite acceso gratuito, aunque con alguna limitación, a las APIs de Twitter. Te permite publicar hasta 1.500 tuits al mes, y realizar 50 llamadas (queries) al día. Esta es la modalidad con la que estoy subscrito.
  • Basic : con un coste de 100$/mes, dos entornos de aplicación, descarga de 10.000 tuis al mes, 1.667 peticiones cada 24 horas…
  • Pro : con un coste de 5.000€/mes se multiplican, obviamente, las capacidades: 3 entornos de aplicaciones, puedes descargar hasta un millón de tuits al mes, publicar 100 tuits cada 15 min ó 10.000 al mes. Además de muchos endpoints para la gestión total de una cuenta

…y dar de alta la APP

A continuación daremos de alta la entorno y una aplicación, simplemente hay que añadir una breve descripción…Así me aparece:

y solo queda ir a la pestaña de «Keys and tokens», en la que deberemos generar cinco claves:

  • Bearer Token que identifica a la App
  • API Key y API Secret, que tal como indica : «Think of these as the user name and password that represents your App when making API requests. While your Secret will remain permanently hidden, you can always view the last 6 characters of your API Key
  • Access Token and Access Secret, que de igaul manera describe como : «An Access Token and Secret are user-specific credentials used to authenticate OAuth 1.0a API requests. They specify the Twitter account the request is made on behalf of.«

copia, de manera segura, esas claves y ya estaríamos listos para empezar con Python

Un poco de código

#importamos la librería
import tweepy

#definimos las variables que nos identifican junto con nuestra app
api_key = 'tus valores'
api_key_secret = 'tus valores'
access_token = 'tus valores'
access_token_secret = 'tus valores'

bearer_token = 'tus valores'

#creamos un cliente tweepy
cliente = tweepy.Client(bearer_token=bearer_token,
consumer_key=api_key,
consumer_secret=api_key_secret,
access_token=access_token,
access_token_secret=access_token_secret)

#llamamos a la query de twitter para publicarlo
cliente.create_tweet(text='Hola a todos!')

Y ya debería tener el tuit en el timeline.

Si, como es mi caso, quiere publicar el tuit con un gráfico se deberá subir previamente ese gráfico con el API v1, recoger el id, y añadir ese media_id en la llamada a la query. Queda el código tal que así :

#importamos la librería
import tweepy

#definimos las variables que nos identifican junto con nuestra app
api_key = 'tus valores'
api_key_secret = 'tus valores'
access_token = 'tus valores'
access_token_secret = 'tus valores'
bearer_token = 'tus valores'

#hacemos una llamada al apiv1 para subir el gráfico
auth = tweepy.OAuth1UserHandler(api_key, api_key_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
media = api.media_upload(filename='media_horaria.png')
media_id = media.media_id

#creamos un cliente tweepy
cliente = tweepy.Client(bearer_token=bearer_token,
consumer_key=api_key,
consumer_secret=api_key_secret,
access_token=access_token,
access_token_secret=access_token_secret)

#hacemos la llamada para publicar, pero en esta ocasión con media
cliente.create_tweet(text='otra prueba, pero esta vez con gráfico',media_ids=[media_id])

Próximos pasos

Me he quedado con dos acciones :

  • No usar una librería externa y hacer llamadas directamente al API de twitter
  • Buscar un modo de subir el fichero con la v2 y no con la v1. Sospecho que en cualquier momento pueden meter alguna nueva limitación a la v1, y tener que rehacer los scripts de nuevo
  • ..y una tercera : tengo que añadir logs a los scripts para que me alerten si ha habido algún problema. Y así evitar estos 10 días sin publicar nada

Gracias a todos por los comentarios en twitter y los ánimos para seguir con estas tareas.

Descarga de datos del mercado eléctrico.

Jugar con los datos abiertos que se proporcionan en las diferentes fuentes de información de este mercado es una buena manera de intentar entenderlo. En mi cuenta de twitter publico diariamente gráficos generados con datos descargados desde estas fuentes. Es un mercado ciertamente complejo, con una regulación extensa, y con mucho ruido mediático. Todo esto facilita la opacidad y dificultad de entender este mercado, que por otra parte se lleva, y más con la subida de precios de estos tiempos, una parte material del presupuesto de servicios básicos de las familias. Mi intención, publicando estas líneas de código (por otra parte extremadamente simple) es ayudar un poco en hacerlo más transparente.

¿Como lo he organizado?

He preparado un notebook de Jupyter mostrando una serie de funciones, en Python, para facilitar la descarga de datos desde fuentes relacionadas con el mercado eléctrico:

He preparado cinco funciones:

  • catalogo_esios(token): que permite bajar el catálogo completo de la api de esios, para, posteriorment, buscar de manera fácil los identificadores que necesitamos para nuestro trabajo
  • download_esios : que permite bajar datos de cualquier indentificador, entre dos fechas determinadas, para posteriormente trabajar sobre ellos.
  • download_ree : idem que el caso anterir pero sobre apidatos de REE
  • download_gas : que nos permite bajar desde https://mibgas.es el precio de gas GDAES del día siguiente
  • downlaod_gas_rd: que nos permite bajar desde https://mibgas.es el precio de gas correspondiente al mecanismo de compensación del RD10/2022

A modo de caso práctico, he añadido un ejemplo de descarga de datos junto con un gráfico para mostrarlos visualmente. He procurado elegir tipos diferentes de gráficos para cada gráfico por si es de ayuda.

Aquí pueden encontrar el notebook.

Me alegraré mucho si alguien lo encuentra de utilidad…