Circuitos

Los circuitos cuánticos pueden construirse usando la clase Circuit. Se pueden añadir puertas secuencialmente para definir el circuito:

Inicialización

from qilisdk.digital import Circuit

# Create a 3-qubit circuit
circuit = Circuit(nqubits=3)

Añadir puertas

from qilisdk.digital import H, CNOT

circuit.add(H(0))  # Hadamard on qubit 0
circuit.add(CNOT(0, 1))  # CNOT: control 0 → target 2
circuit.draw()

También se pueden añadir o insertar varias puertas a la vez:

from qilisdk.digital import X, RX
import numpy as np

circuit.add([X(0), RX(1, theta=np.pi / 2)])
circuit.insert([H(2), CNOT(2, 1)], index=1)
circuit.insert(X(0), 0)

Los circuitos pueden concatenarse al final o al principio, y el operador + refleja esos comportamientos:

left = Circuit(2)
left.add(H(0))

right = Circuit(2)
right.add(CNOT(0, 1))

left.append(right)
# Equivalent: left = left + right

extra = X(1)
# Equivalent to left.insert(extra, index=0)
left = extra + left

Generación de circuitos aleatorios

También se puede inicializar un circuito aleatorio con un número especificado de puertas usando el método random():

from qilisdk.digital import Circuit, X, H, CNOT
c = Circuit.random(
    nqubits=3,
    single_qubit_gates=[X, H],
    two_qubit_gates=[CNOT],
    ngates=10,
)
circuit.draw()

Circuitos parametrizados

Los circuitos pueden incluir puertas parametrizadas. Su adición es similar a la de las puertas regulares:

from qilisdk.digital import Circuit, RX
import numpy as np

circuit = Circuit(1)
circuit.add(RX(0, theta=np.pi))

Se puede obtener el parámetro actual usando:

print("Initial Parameters:", circuit.get_parameters())

Salida:

Initial Parameters: {'RX(0)_theta_0': 3.141592653589793}

También se puede obtener una lista que contenga solo los valores actuales de los parámetros:

print("Initial parameter values:", circuit.get_parameter_values())

Salida:

Initial parameter values: [3.141592653589793]

Para actualizar parámetros por sus claves:

circuit.set_parameters({"RX(0)_theta_0": 2 * np.pi})

Para actualizar todos los parámetros con nuevos valores:

circuit.set_parameter_values([2 * np.pi])

Advertencia

El orden de los parámetros en la lista pasada a set_parameter_values debe coincidir con el orden en que se añadieron las puertas al circuito.

Visualización

Use draw() para renderizar un circuito con Matplotlib. Por defecto, el renderizador aplica el estilo integrado de la librería (incluida la fuente predeterminada incluida, si está disponible). Se pueden omitir todos los valores predeterminados pasando un CircuitStyle personalizado, que limita el estilo a la llamada específica sin modificar la configuración global de Matplotlib.

from qilisdk.digital import Circuit, H, CNOT

circuit = Circuit(3)
circuit.add(H(0))
circuit.add(CNOT(0, 2))

# Render in a window
circuit.draw()

Salida

Ejemplo de circuito digital renderizado con Circuit.draw

Circuito de ejemplo producido por circuit.draw().

The plot can also be saved to a file by providing a filepath to the draw method:

# Save as SVG (use .png, .pdf, etc. as needed)
circuit.draw(filepath="my_circuit.svg")

Estilo personalizado con CircuitStyle

Cree un objeto de estilo para controlar el tema, las fuentes, el espaciado, el DPI, las etiquetas y más. Pasar este objeto a draw() anula los valores predeterminados de la librería para esa llamada. También se puede cambiar si el orden del dibujo sigue el orden de adición o si compacta las capas al máximo cambiando el parámetro layout a «normal» (predeterminado) o «compact» respectivamente.

from qilisdk.digital import Circuit, H, CNOT
from qilisdk.utils.visualization import CircuitStyle, light, dark

circuit = Circuit(3)
circuit.add(H(0))
circuit.add(CNOT(0, 2))

# Example 1: dark theme, larger text, higher DPI
style_dark = CircuitStyle(
    theme=dark,
    dpi=200,
    fontsize=12,
    title="Teleportation (fragment)",
)
circuit.draw(style=style_dark)

# Example 2: use a system font family and bypass the bundled font
style_font = CircuitStyle(
    theme=light,
    fontfname=None,                         # do not force a specific TTF file
    fontfamily=["DejaVu Sans", "Arial"],    # fallback list
    fontsize=11,
)
circuit.draw(style=style_font, filepath="circuit_custom_font.png")

# Example 3: adjust layout spacing
compact = CircuitStyle(
    theme=dark,
    wire_sep=0.45,      # vertical distance between wires (inches)
    layer_sep=0.45,     # horizontal distance between layers (inches)
    gate_margin=0.10,   # side margin inside each layer cell (inches)
    label_pad=0.08,     # left padding for wire labels (inches)
    layout="compact",   # compresses the circuit whenever possible
    title="Compact layout",
)
circuit.draw(style=compact)

Nota

Los campos de CircuitStyle se corresponden directamente con la configuración de diseño y fuentes del renderizador. En particular, se puede cambiar la fuente de dos maneras: (1) proporcionar un archivo de fuente específico mediante fontfname="/ruta/a/MiFuente.ttf"; o (2) establecer fontfname=None y elegir una lista de familias con fontfamily=[...] para usar fuentes del sistema. Ambos enfoques solo afectan a la llamada de dibujo actual.

Utilidades de parámetros

Los circuitos recopilan los parámetros simbólicos aportados por cada puerta. Más allá de los ejemplos rápidos anteriores, se pueden consultar nombres, valores actuales y límites, o actualizarlos de forma selectiva:

import numpy as np

from qilisdk.core.variables import Parameter
from qilisdk.digital import Circuit, RX, RZ

circuit = Circuit(nqubits=2)
theta = Parameter("theta", value=np.pi / 4, bounds=(0.0, np.pi))

circuit.add(RX(0, theta=theta))
circuit.add(RZ(1, phi=np.pi / 2))

print(circuit.get_parameter_names())   # ['RX(0)_theta_0', 'RZ(1)_phi_1']
print(circuit.get_parameters())        # {'RX(0)_theta_0': 0.785..., 'RZ(1)_phi_1': 1.570...}
print(circuit.get_parameter_bounds())  # {'RX(0)_theta_0': (0.0, 3.1415...), 'RZ(1)_phi_1': (None, None)}

circuit.set_parameter_bounds({"RX(0)_theta_0": (0.1, np.pi / 2)})
circuit.set_parameters({"RX(0)_theta_0": np.pi / 3})
circuit.set_parameter_values([np.pi / 3, np.pi / 2])

Estos auxiliares facilitan la integración del circuito en bucles de optimización clásica manteniendo sincronizados los metadatos de los parámetros.

Nota

Las puertas y circuitos parametrizados exponen la información de los parámetros a través de get_parameter_names, get_parameters, get_parameter_values, y los métodos set_* correspondientes.