Skip to main content
IBM Quantum Platform

Modelo de circuito cuántico

qiskit.circuit

El elemento fundamental de la informática cuántica es el circuito cuántico. Se trata de una rutina computacional que puede ejecutarse, disparo a disparo, en una unidad de procesamiento cuántico (QPU). Un circuito actuará sobre una cantidad predefinida de datos cuánticos (en Qiskit, sólo admitimos qubits directamente) con operaciones unitarias (puertas), mediciones y reinicios. Además, un circuito cuántico puede contener operaciones sobre datos clásicos, incluidos cálculos en tiempo real y construcciones de flujo de control, que son ejecutadas por los controladores de la QPU.

Los circuitos se encuentran en un nivel bajo de abstracción cuando se construyen programas cuánticos. Son el constructo que se utiliza para llegar a niveles superiores de abstracción, como las primitivas de la computación cuántica, que acumulan datos de muchos disparos de ejecución de circuitos cuánticos, junto con técnicas avanzadas de mitigación de errores y optimizaciones de medición, en datos clásicos bien tipificados y estadísticas de error.

En Qiskit, los circuitos pueden definirse en uno de dos regímenes :

  • un circuito abstracto, que se define en términos de qubits virtuales y operaciones arbitrarias de alto nivel, como algoritmos encapsulados y puertas definidas por el usuario.
  • un circuito físico, que se define en términos de los qubits hardware de un backend concreto, y contiene sólo operaciones que este backend soporta de forma nativa. Este concepto también se conoce como circuito ISA.

La conversión de un circuito abstracto a un circuito físico se realiza utilizando el paquete de transpilación de Qiskit, cuyo punto de acceso de nivel superior es transpile(). Si defines un circuito, donde pretendes que los índices qubit se refieran a qubits físicos, puedes usar QuantumCircuit.ensure_physical() para reescribir los metadatos del circuito para asegurar que Qiskit reconoce el circuito como un circuito físico, aunque a diferencia de la transpilación, esto no obliga a que las puertas base y las restricciones de acoplamiento de hardware sean respetadas.

En Qiskit, un circuito cuántico está representado por la clase QuantumCircuit clase. A continuación se muestra un ejemplo de circuito cuántico que realiza un estado Greenberger-Horne-Zeilinger (GHZ) de tres qubits definido como:

ψ=(000+111)/2|\psi\rangle = \left( |000\rangle + |111\rangle \right) / \sqrt{2}
from qiskit import QuantumCircuit

# Create a circuit with a register of three qubits
circ = QuantumCircuit(3)
# H gate on qubit 0, putting this qubit in a superposition of |0> + |1>.
circ.h(0)
# A CX (CNOT) gate on control qubit 0 and target qubit 1 generating a Bell state.
circ.cx(0, 1)
# CX (CNOT) gate on control qubit 0 and target qubit 2 resulting in a GHZ state.
circ.cx(0, 2)
# Draw the circuit
circ.draw('mpl')
Diagrama del circuito generado por el código anterior.

Conceptos y definiciones de circuitos

Hay mucha terminología especializada en torno a los circuitos cuánticos. Gran parte de esto es común en la literatura sobre computación cuántica, mientras que otra parte es más específica de los paquetes de software cuántico, y una pequeña cantidad específica de Qiskit. Se trata de una lista alfabética de algunos de los conceptos importantes como referencia rápida, pero no entra en detalles de los conceptos fundamentales. Considere la posibilidad de utilizar la plataforma IBM Quantum Learning si desea empezar desde el principio.

circuito abstracto

Un circuito definido en términos de operaciones matemáticas abstractas y qubits virtuales. Así es como normalmente se piensa en los algoritmos cuánticos: un circuito abstracto puede estar formado por operaciones unitarias completamente arbitrarias, mediciones y potencialmente computación clásica en tiempo real, sin restricciones sobre qué qubits pueden interactuar entre sí.

Convierte un circuito abstracto en un circuito físico utilizando el paquete de transpilación de Qiskit.

ancilla qubit

Un qubit extra que se utiliza para ayudar a implementar operaciones en otros qubits, pero cuyo estado final no es importante para el programa.

circuit

Rutina computacional que define una única ejecución a realizar en una QPU. Puede tratarse de un circuito abstracto o de un circuito físico.

clbit

Abreviatura específica de Qiskit que significa un único bit clásico de datos.

puerta

Operación unitaria sobre uno o varios qubits.

qubit de hardware

La representación de un único qubit en una QPU concreta. Un qubit físico está respaldado por un sistema físico cuántico-mecánico, como los circuitos superconductores; a diferencia de un qubit virtual, tiene restricciones de acoplamiento particulares y sólo pueden aplicarse ciertas puertas a determinados grupos de qubits físicos.

Qiskit no distingue entre qubits lógicos y qubits físicos individuales cuando habla de qubits de hardware. Una QPU puede implementar sus qubits hardware como qubits lógicos, donde cada qubit hardware comprende muchos qubits físicos que son controlados y corregidos de errores de forma opaca a Qiskit por la electrónica de control. Lo más probable, para aplicaciones a corto plazo, es que una QPU exponga directamente sus qubits físicos como qubits de hardware para que Qiskit pueda razonar sobre ellos.

Tanto los qubits físicos como los lógicos tendrán restricciones de acoplamiento entre ellos, sólo permitirán ciertas operaciones cuánticas sobre ellos y tendrán problemas de programación entre ellos. Qiskit abstrae estas preocupaciones en el concepto de qubits de hardware. En los primeros días de la corrección cuántica de errores, determinados backends pueden permitirte acceder a sus recursos de qubits como qubits lógicos de alto nivel o como qubits físicos de bajo nivel a través de Qiskit.

arquitectura del conjunto de instrucciones (ISA)

El modelo abstracto de qué operaciones están disponibles en qué conjuntos de qubits de hardware en una QPU concreta. Por ejemplo, una QPU puede permitir las operaciones X\sqrt X y RZR_Z en todos los qubits hardware individuales, y las operaciones CXCX en determinados pares de qubits hardware.

qubit lógico

Una colección de varios qubits físicos controlados conjuntamente por una QPU (desde la perspectiva del usuario) para aplicar la corrección cuántica de errores en tiempo real. Un qubit lógico es un tipo de qubit de hardware para Qiskit.

measurement

El acto de extraer un bit clásico de un dato a partir de un único estado qubit. Se trata de una operación irreversible, y normalmente destruye el entrelazamiento y la coherencia de fase entre el qubit objetivo y el resto del sistema.

circuito físico

Un circuito definido en términos de qubits de hardware y solo las operaciones cuánticas disponibles en la ISA de una QPU particular. Los circuitos físicos están vinculados a una arquitectura QPU concreta y no funcionarán en otras arquitecturas incompatibles. También se le conoce como circuito ISA.

Normalmente se obtiene un circuito físico utilizando las rutinas de transpilación de Qiskit en un circuito abstracto que has construido.

qubit físico

Un sistema cuántico de dos niveles controlable. Se trata literalmente de un qubit "físico", como un transmón o el estado electrónico de un ion atrapado. Una QPU puede exponer esto directamente como su qubit hardware, o combinar varios qubits físicos en un qubit lógico.

unidad de procesamiento cuántico (QPU)

Análoga a una CPU en informática clásica o una GPU en procesamiento gráfico, una QPU es el hardware que ejecuta operaciones cuánticas sobre datos cuánticos. Siempre se puede esperar que una QPU que utilice el modelo de circuito de computación sea capaz de realizar algún conjunto de puertas y operaciones de medición. Dependiendo de la tecnología de que se trate, también pueden realizar algunos cálculos clásicos en tiempo real, como el flujo de control clásico y cálculos de bit a bit sobre datos clásicos.

Qubit

La unidad básica de la información cuántica.

cálculo clásico en tiempo real

Cualquier cómputo clásico que puede ocurrir dentro de la ejecución de un solo disparo de un circuito, donde los resultados del cómputo clásico pueden afectar a la ejecución posterior del circuito. La cantidad de cálculo clásico en tiempo real disponible con determinadas QPU variará significativamente en función de muchos factores, como la electrónica de control y la tecnología de qubits utilizada. Consulte la documentación de su proveedor de hardware para obtener más información al respecto.

operación unitaria

Una operación reversible sobre un estado cuántico. Todas las puertas cuánticas son operaciones unitarias (por definición).

qubit virtual

Un qubit matemático abstracto utilizado para construir un circuito abstracto. Suponemos que todas las puertas cuánticas son válidas en todos los qubits virtuales y que todos los qubits virtuales están siempre conectados a cualquier otro qubit virtual.

Al asignar a hardware, los qubits virtuales deben asignarse a qubits de hardware. Esta correspondencia no tiene por qué ser unívoca. Normalmente, será necesario intercambiar un qubit virtual de un qubit hardware a otro en el transcurso de la ejecución de un circuito para satisfacer las restricciones de acoplamiento de la QPU subyacente. No es estrictamente necesario que todos los qubits virtuales utilizados en un circuito se asignen a un qubit físico en cualquier punto dado de un circuito físico; podría ser que un qubit virtual se midiera (colapsando su estado) y luego nunca se volviera a utilizar, por lo que un nuevo qubit virtual podría ocupar su lugar. El paquete de transpilación de Qiskit se encarga de evaluar estas condiciones para asignar un circuito virtual a un circuito físico.


Descripción general de la API de qiskit.circuit

Todos estos objetos se describen con más detalle y en su contexto más amplio en las secciones siguientes. Esta sección proporciona una visión general de los elementos de la API aquí documentados.

La clase principal es QuantumCircuitque tiene su propia página de documentación, incluyendo una sección en profundidad sobre la construcción de circuitos. Los datos cuánticos y los datos clásicos más simples se representan mediante "bits" y "registros":

Dentro de un circuito, cada instrucción completa CircuitInstruction está formada por un Operation (que puede ser un Instruction, un Gate o cualquier otra subclase) y los operandos qubit y clbit. Las clases base principales son las siguientes:

La subclase concreta más habitual de la interfaz mínima y abstracta Operation es la Instruction. Aunque Operation puede incluir objetos matemáticos abstractos, un Instruction es algo que, en teoría, podría ejecutarse directamente en el hardware. A su vez, esta tiene como Gate subclases y ControlledGate , que añaden además unitaridad y una semántica controlada:

  • Instruction, que representa una instrucción basada en hardware
  • Gate, que representa una instrucción de hardware que es unitaria
  • ControlledGate, que representa una puerta con estructura de control.

Qiskit incluye una amplia biblioteca de compuertas y circuitos estándar, que está documentada en qiskit.circuit.library. Muchos de ellos se declaran como Python -object singletons. La maquinaria para ello se describe en detalle en qiskit.circuit.singletoncuyas clases principales son cada una una forma singleton de la jerarquía estándar de puertas de instrucción:

Algunas instrucciones son especialmente especiales porque afectan al flujo de control o al flujo de datos del circuito. Los de más alto nivel son:

  • Barrier, para marcar las partes del circuito que deben optimizarse de forma independiente

  • Delaypara insertar un periodo de espera en tiempo real

  • Measurepara medir un Qubit en a Clbit

  • Resetpara restablecer irreversiblemente un qubit al estado 0\lvert0\rangle

  • Storepara escribir una expresión clásica en tiempo real en una ubicación de almacenamiento

  • ControlFlowOp, que cuenta con subclases específicas:

    • BreakLoopOp, para salir del bucle de contención más cercano
    • ContinueLoopOp, para pasar inmediatamente a la siguiente iteración del bucle contenedor
    • BoxOp, una simple agrupación de instrucciones
    • ForLoopOp, para recorrer un intervalo fijo de valores
    • IfElseOp, para acceder de forma condicional a uno de los dos subcircuitos
    • SwitchCaseOp, para acceder de forma condicional a uno de los muchos subcircuitos
    • WhileLoopOp, para repetir un subcircuito hasta que se desmienta una condición.

Ciertas instrucciones pueden «anotarse» con metadatos, que normalmente están destinados a ser procesados por una pasada del compilador, ya sea de forma local o en un procesamiento posterior del backend. Actualmente, esto se limita a BoxOp. qiskit.circuit.annotationEstas anotaciones están representadas por subclases personalizadas de Annotation, y en se aborda con más detalle la infraestructura de soporte.

Los circuitos pueden incluir expresiones clásicas que se evalúan en tiempo real, mientras la QPU ejecuta una única toma del circuito. Se documentan principalmente en la documentación del módulo de qiskit.circuit.classical. Puede que te interesen especialmente las clases base (que no se exponen desde el directorio qiskit.circuit raíz):

  • Varun lugar de almacenamiento clásico tipificado en un circuito
  • Expruna expresión evaluada en tiempo real
  • Typeel tipo clásico de una expresión.

Además de esta evaluación de expresiones en tiempo real, limitada por las representaciones hardware clásicas de los datos, Qiskit cuenta con el concepto de parametrización "en tiempo de compilación", que se realiza en álgebra simbólica abstracta. Estos se utilizan normalmente para representar ángulos de puerta en algoritmos de alto nivel que podrían querer realizar derivadas numéricas, pero son una parte más antigua de Qiskit que la evaluación en tiempo real, por lo que todavía se utilizan en algunos lugares para hacer parametrización general. Las principales clases relacionadas son:

El módulo qiskit.circuit también expone algunas clases de cálculo que trabajan con circuitos para ayudar a los flujos de trabajo de compilación. Esto incluye:

También existen utilidades para generar circuitos aleatorios:

Por último, el módulo de circuito tiene su propia clase de excepción, para indicar cuando las cosas van mal de manera específica del circuito:


Representación de circuitos en Qiskit

La principal clase de usuario para representar circuitos es QuantumCircuit. Puede tratarse de un circuito abstracto o de un circuito físico. Hay mucha más información sobre la clase QuantumCircuit y la multitud de métodos disponibles en la documentación de la clase.

Internamente, un QuantumCircuit contiene los qubits, los bits clásicos, los parámetros de tiempo de compilación, las variables en tiempo real y otra información de seguimiento sobre los datos sobre los que actúa y cómo está parametrizado. A continuación, contiene una secuencia de CircuitInstructions, que incluyen la operación concreta (puerta, medición, etc.) y sus operandos (los qubits y los bits clásicos).

Bits y registros

Los qubits y los bits clásicos están representados por un tipo base compartido Bit que sólo pretende ser una "etiqueta de tipo"; las clases no tienen otro comportamiento que el de ser objetos inmutables:

Bit

class qiskit.circuit.Bit

GitHub

Bases: object

Implementar un bit genérico.

Nota

Esta clase no puede instanciarse directamente. Su único propósito es permitir la comprobación genérica de tipos para Clbit y Qubit.

Qubit

class qiskit.circuit.Qubit(register=None, index=None)

GitHub

Bases: Bit

Un qubit, que puede compararse entre distintos circuitos.

Clbit

class qiskit.circuit.Clbit(register=None, index=None)

GitHub

Bases: Bit

Un clbit, que puede compararse entre diferentes circuitos.

Qubits y clbits son instanciados por los usuarios sin argumentos, como por ejemplo mediante Qubit(). Los bits se comparan igual si son el mismo objeto Python, o si ambos fueron creados por un registro del mismo nombre y tamaño, y se refieren al mismo índice dentro de ese registro. También existe una etiqueta de tipo especial para qubits "ancilla", pero se utiliza poco en el estado actual de Qiskit:

AncillaQubit

class qiskit.circuit.AncillaQubit

GitHub

Bases: Qubit

Un qubit utilizado como ancilla.

Los bits de una colección del mismo tipo pueden encapsularse en un registro del tipo correspondiente. La funcionalidad base se encuentra en una clase base que no se instancia directamente:

Registrarse

class qiskit.circuit.Register

GitHub

Bases: object

Implementar un registro genérico.

Nota

Esta clase no puede instanciarse directamente. Su único propósito es permitir la comprobación genérica de tipos para ClassicalRegister y QuantumRegister.

Cada uno de los subtipos de bits definidos tiene un registro asociado, que tiene las mismas firmas de constructor, métodos y propiedades que la clase base:

QuantumRegister

class qiskit.circuit.QuantumRegister(size=None, name=None, bits=None)

GitHub

Bases: Register

Implementar un registro.

ClassicalRegister

class qiskit.circuit.ClassicalRegister(size=None, name=None, bits=None)

GitHub

Bases: Register

Implementar un registro.

AncillaRegister

class qiskit.circuit.AncillaRegister(size=None, name=None, bits=None)

GitHub

Bases: QuantumRegister

Implementar un registro ancilla.

Una forma habitual de instanciar varios bits a la vez es crear un registro, como por ejemplo mediante QuantumRegister("my_qreg", 5). Esto tiene la ventaja de que puedes dar un nombre a esa colección de bits, que aparecerá durante las visualizaciones de circuitos (QuantumCircuit.draw()) y las exportaciones a lenguajes de intercambio (véase qasm2 y qasm3). También puedes pasar un nombre y una lista de bits preconstruidos, pero esto crea un "registro de alias", que están muy mal soportados en hardware.

Los circuitos siguen la pista de los registros, pero los registros en sí no aportan apenas diferencias de comportamiento a los circuitos. La única excepción es que ClassicalRegisterse pueden convertir implícitamente en enteros sin signo para utilizarlos en comparaciones condicionales de operaciones de flujo de control.

Los registros clásicos y los bits eran la forma original de representar datos clásicos en Qiskit, y siguen siendo los más soportados actualmente. A largo plazo, el modelo de datos se está moviendo hacia una representación más completa y fuertemente tipada de un rango de datos clásicos (ver Computación clásica en tiempo real ), pero todavía usarás muy comúnmente bits clásicos en el Qiskit actual.

Contextos de instrucción

El tipo escalar de la QuantumCircuit.data secuencia es el objeto «contexto de instrucción», CircuitInstruction. En esencia, se trata simplemente de una clase de datos que contiene una representación de lo que hay que hacer (su operation) y los datos sobre los que actúa (el qubits y clbitsel ).

Columna « 1 »
Columna « 2 »
CircuitInstruction(operación[, qubits, clbits] )Una única instrucción en un QuantumCircuitcompuesta por operation y varios operandos.

Desde el punto de vista de la programación, esta clase está implementada en Rust y es un identificador creado para acceder a datos internos dentro del espacio de Rust. Las modificaciones realizadas en las instancias de esta clase no se reflejarán en el circuito. En general, no se pueden modificar directamente los contextos de instrucciones que ya se encuentran en el circuito; la QuantumCircuit interfaz está diseñada para almacenar y construir circuitos, mientras que el transpilador, sus pasadas y su representación intermedia DAGCircuit son los elementos en los que debes buscar una interfaz para modificar los circuitos.

Los QuantumCircuit métodos que añaden instrucciones a los circuitos (como append(), y todos los métodos auxiliares de puertas estándar) devuelven un InstructionSet, que es un identificador que hace referencia a varios CircuitInstruction a la vez.

Columna « 1 »
Columna « 2 »
InstructionSet(*[, solicitante_del_recurso] )Recogida de instrucciones y sus contextos.

Esto InstructionSet ya casi no se utiliza en Qiskit. Ofrece un conjunto muy reducido de métodos para realizar modificaciones posteriores a la adición en las instrucciones (que se propagarán al circuito), pero ahora se desaconseja su uso y se recomienda utilizar las alternativas indicadas en dichos métodos.

Operaciones, instrucciones y puertas

Dentro de un CircuitInstruction, la interfaz mínima que debe cumplir cualquier operación es Operation. Se trata de una visión muy general, y solo es aplicable a circuitos abstractos. El objetivo principal de tratar las operaciones como Operation es permitir que se añadan directamente objetos matemáticos arbitrarios (como quantum_info.Operator) a los circuitos abstractos.

Columna « 1 »
Columna « 2 »
Operation()Interfaz de operación cuántica.

InstructionLa mayoría de las operaciones, incluidas todas las realizadas sobre circuitos físicos, son ejemplos de la noción definida de forma más concreta. Esto hace referencia a cualquier instrucción que una QPU pueda ser capaz de ejecutar de forma nativa, como por ejemplo Measure. Instruction no tiene por qué ser unitaria (al igual que Measure no lo es); una instrucción es específicamente unitaria si es una Gate.

Columna « 1 »
Columna « 2 »
Instruction(nombre, num_qubits, num_clbits, params)Instrucción cuántica genérica.

InstructionLas funciones s pueden ser prácticamente arbitrarias, siempre que actúen únicamente sobre Qubits y Clbits, y estén parametrizadas por su params; no deben intentar «cubrir» los registros del circuito exterior ni utilizar parámetros ocultos en su interior. InstructionLos s pueden relacionarse con otros circuitos para proporcionar una descomposición mediante su Instruction.definition atributo, lo que permite una descomposición local y puntual. Esto puede hacerse utilizando cualquier conjunto de operaciones que te resulte más conveniente, siempre y cuando las definiciones de todas las puertas incluidas sigan un orden topológico; es decir, no puedes utilizar una puerta en una definición si su propia definición depende de la puerta principal. Si se considera que el Instruction es totalmente opaco para los optimizadores, su definition puede ser None. Consulta «Creación de instrucciones personalizadas» para obtener más detalles.

La params forma de una instrucción puede ser, técnicamente, arbitraria, pero, en general, conviene intentar ceñirse a parametrizaciones en términos de números reales siempre que sea posible. El propio Qiskit incumple esta regla en muchos casos, y encontrarás todo tipo de tipos inusuales en Instruction.params los campos, pero estos son una fuente molesta de errores, ya que a menudo implican la necesidad de tratar casos especiales en función del tipo. Si tu instrucción está parametrizada en términos de ángulos, podrás utilizar de forma fiable la parametrización en tiempo de compilación en ella, y se integrará bien con QuantumCircuit.assign_parameters().

Aunque Instruction no es necesariamente unitario, su subclase Gate implica unitaridad y añade to_matrix() los métodos y control() a todos los métodos heredados de Instruction.

Columna « 1 »
Columna « 2 »
Gate(nombre, núm_qubits, parámetros[, etiqueta] )Puerta unitaria.

Gate hereda todos los métodos para Instruction y todas las mismas consideraciones sobre su params y definition su campo, salvo, por supuesto, que Gates no puede actuar sobre ningún recurso clásico.

definitionGate Las instancias pueden (y deben) tener una base, pero también se pueden especificar varias descomposiciones diferentes en distintas bases utilizando un EquivalenceLibrary.

GateEn cuanto a la subclasificación, Qiskit también cuenta con una clase especial ControlledGate . Esta clase es la base de muchas puertas de la biblioteca estándar que se controlan (como CXGate), que es donde es más probable que te encuentres con ella:

Columna « 1 »
Columna « 2 »
ControlledGate(nombre, num_qubits, parámetros[,...] )Puerta unitaria controlada.

Cada uno de Instruction, Gate y ControlledGate tiene un tipo singleton correspondiente, creado mediante el mecanismo descrito en qiskit.circuit.singleton. La documentación a nivel de módulo contiene toda la información detallada, junto con descripciones de SingletonInstruction, SingletonGate y SingletonControlledGate. Desde el punto de vista del usuario, apenas hay diferencias dependiendo de si la clase base es un singleton o no; la idea es siempre que se llame to_mutable() primero a si se necesita obtener una copia propia de una instrucción que se pueda modificar de forma segura (no se puede dar por sentado que una instrucción cualquiera sea mutable) y, aunque no se recomienda la inspección directa type de, si fuera necesario, la forma fiable de averiguar el tipo «base» de una instrucción que pudiera ser un singleton es utilizar base_class.

ControlledGate utiliza los mismos mecanismos que las puertas de subclasificación para definir una síntesis fija y diferida para sí misma. Por supuesto, esto no tiene en cuenta el hardware y resulta más difícil de integrar en las rutinas de síntesis del compilador, pero funciona mejor como una solución concreta Instruction que, en teoría, podría ejecutarse de forma nativa en el hardware. En los casos en los que la síntesis y la optimización abstracta son más importantes, Qiskit ofrece una clase componible denominada AnnotatedOperation, que realiza un seguimiento de los «modificadores de puerta» (entre los que ControlModifier se encuentra ) para aplicarlos a la puerta interna base_op.

Columna « 1 »
Columna « 2 »
AnnotatedOperation(operando_base, modificadores)Operación anotada.

Los modificadores disponibles para AnnotatedOperation son:

InverseModifier

class qiskit.circuit.InverseModifier

GitHub

Bases: Modifier

Modificador inverso: especifica que la operación se invierte.

ControlModifier

class qiskit.circuit.ControlModifier(num_ctrl_qubits=0, ctrl_state=None)

GitHub

Bases: Modifier

Modificador de control: especifica que la operación está controlada por num_ctrl_qubits y tiene estado de control ctrl_state.

Parámetros

  • num_ctrl_qubits (int)
  • ctrl_state (int |str | None)

PowerModifier

class qiskit.circuit.PowerModifier(power)

GitHub

Bases: Modifier

Modificador de potencia: especifica que la operación se eleva a la potencia power.

Parámetros

power (float)

Para obtener información sobre cómo crear puertas e instrucciones personalizadas, incluyendo cómo construir objetos únicos y puertas paramétricas reutilizables mediante subclases, consulte Crear instrucciones personalizadas más abajo. La biblioteca de circuitos Qiskit en qiskit.circuit.library contiene muchas puertas y circuitos predefinidos que puedes utilizar.

Instrucciones especiales integradas

Qiskit contiene algunas Instruction clases que, en cierto modo, son «especiales». Por lo general, estos elementos requieren un tratamiento especial en el código del circuito, en el transpilador o en los modelos de hardware. En general, se trata de instrucciones con las que quizá ya estés familiarizado.

Las mediciones en Qiskit son de una sola Qubit en una única Clbit. Estos son los dos a los que se aplica la instrucción. Las medidas están en la base computacional.

Medida

class qiskit.circuit.Measure(label=None)

GitHub

Bases: SingletonInstruction

Medición cuántica en la base computacional.

Parámetros

label - etiqueta de cadena opcional para esta instrucción.

En relación con las mediciones, existe una Reset que no produce datos clásicos, sino que ordena al hardware que devuelva el qubit al estado 0\lvert0\rangle. Se supone que esto ocurre de forma incoherente y colapsa cualquier entrelazamiento.

Restablecer

class qiskit.circuit.Reset(label=None)

GitHub

Bases: SingletonInstruction

Restablecer incoherentemente un qubit al estado 0\lvert0\rangle.

Parámetros

label - etiqueta de cadena opcional de esta instrucción.

Se puede indicar al hardware que aplique un periodo de inactividad en tiempo real a un qubit determinado. Un circuito programado (véase qiskit.transpiler) incluirá explícitamente todos los tiempos de inactividad de los qubits en términos de este Delay. BoxOp también puede tener asociada una duración explícita en su BoxOp.duration campo.

Retardo

class qiskit.circuit.Delay(duration, unit=None)

GitHub

Bases: Instruction

No hace nada y sólo retrasa/espera/espera durante un tiempo especificado.

Parámetros

  • duración - la duración de la duración. Si se trata de un Exprdebe ser una expresión constante de tipo Duration y el parámetro unit debe omitirse (o DEBE ser "expr" si se especifica).
  • unidad - la unidad de la duración, si duration es un valor numérico. Debe ser "dt", una unidad de segundos con prefijo SI, o "expr".

Eleva

CircuitError - Se especificó una expresión duration con un tipo resuelto que no está basado en el tiempo o se especificó incorrectamente unit .

Las duraciones de los retardos pueden especificarse con tiempos constantes concretos o con "expresiones de duración" de resolución retardada construidas a partir de objetos expr.Stretch objetos. Para más información, véase Programación con resolución retardada.

La instrucción Barrier puede abarcar un número arbitrario de qubits y clbits, y es un no-op en hardware. Durante la transpilación y la optimización, sin embargo, bloquea cualquier optimización que "cruce" la barrera; es decir, en:

from qiskit.circuit import QuantumCircuit

qc = QuantumCircuit(1)
qc.x(0)
qc.barrier()
qc.x(0)

está prohibido que el optimizador anule las dos instrucciones XX.

Barrera

class qiskit.circuit.Barrier(num_qubits, label=None)

GitHub

Bases: Instruction

Una directiva para la compilación de circuitos para separar las piezas de un circuito de modo que cualquier optimización o reescritura se limite a actuar sólo entre las barreras.

Esto también aparecerá en las visualizaciones como un marcador visual.

Parámetros

  • num_qubits (int) – el número de qubits de la barrera.
  • label (str | None) – la etiqueta opcional de esta barrera.

La instrucción Store es particularmente especial, ya que permite escribir el resultado de una expresión de cálculo clásico en tiempo real (an expr.Expr) en una variable local clásica (a expr.Var). No hace falta Qubit ni Clbit pero tiene un operando explícito lvalue y rvalue.

Por ejemplo, para determinar la paridad de una cadena de bits cr y almacenarla en otro registro creg, la instrucción Store se puede utilizar de la siguiente manera:

parity = expr.lift(cr[0])
for i in range(1,n):
    parity = expr.bit_xor(cr[i], parity)
qc.store(creg[0], parity)

Tienda

class qiskit.circuit.Store(lvalue, rvalue)

GitHub

Bases: Instruction

Almacenamiento manual de algún valor clásico en una posición de memoria clásica.

Esta es una primitiva de bajo nivel del manejo de expresiones clásicas (similar a como Measure es una primitiva para la medición cuántica), y no es seguro para la subclase.

Parámetros

  • lvalue (expr.Expr) – la posición de memoria en la que se almacena.
  • rvalue (expr.Expr) – el resultado de la expresión que se almacena.
lvalue

Obtener el valor l Expr que se está almacenando.

rvalue

Obtener el nodo r-value Expr que se está escribiendo en el nodo l-value.

Cálculo clásico en tiempo real

Consulte también

qiskit.circuit.classical

Documentación a nivel de módulo sobre el funcionamiento de los sistemas de variables, expresiones y tipos, los objetos utilizados para representarlos y las operaciones clásicas disponibles.

Trabajar con datos clásicos mecanografiados en tiempo real

El sitio QuantumCircuit métodos para trabajar con estas variables en el contexto de un circuito único.

Qiskit tiene un soporte rudimentario de bajo nivel para representar cálculos clásicos en tiempo real, que ocurren durante la ejecución de la QPU y afectan a los resultados. El soporte de hardware para estos conceptos también es relativamente reciente, así que ten en cuenta que tendrás que trabajar en estrecha colaboración con la documentación de tu proveedor de hardware para sacar el máximo partido de cualquier cálculo clásico en tiempo real.

Estos cálculos en tiempo real están representados por el sistema de expresiones y tipos en qiskit.circuit.classical. A alto nivel, todas las expresiones en tiempo real se representan mediante un nodo Expr que forma parte de un "árbol" de expresiones, al que se asocia una dirección Type bien definida en cada nivel. Consulte la documentación a nivel de módulo para obtener más información sobre las representaciones internas de estas clases.

El resultado de un análisis en tiempo Expr real se puede utilizar directamente en determinados casos. Actualmente, esto se limita a las condiciones de IfElseOp y WhileLoopOp, y al objetivo de SwitchCaseOp. El resultado también se puede almacenar en una ubicación de almacenamiento clásica tipada, utilizando la Store instrucción (o su QuantumCircuit.store() constructor), respaldada por un expr.Var nodo.

Un circuito puede contener almacenes clásicos manuales, representados internamente por el Var nodo del Expr árbol. Tienen un tipo clásico adjunto (como cualquier otra expresión). Pueden declararse e inicializarse en cada ejecución del circuito (add_var()), o ser entradas al circuito (add_input()).

Parametrización en tiempo de compilación

Varias instancias paramétricas Instruction de Qiskit pueden parametrizarse de forma que su resolución se realice en tiempo de compilación. Se caracterizan por el uso de las Parameter clases y ParameterExpression .

Columna « 1 »
Columna « 2 »
Parameter(nombre[, uuid] )Un parámetro simbólico en tiempo de compilación.
ParameterExpression(nombre_map, expr)Una expresión de parámetro.

La principal diferencia con respecto a las expr.Var variables utilizadas en la computación clásica en tiempo real es que ParameterExpression es una representación simbólica de una expresión matemática. La semántica de la expresión es la de las matemáticas habituales sobre los números reales continuos (y, en casos concretos, sobre los números complejos). Por el contrario, Var es un identificador de una variable almacenada en un ordenador clásico, como un valor de coma flotante o un entero de ancho fijo, que siempre son discretos.

En otras palabras, puedes esperar ParameterExpression que realice simplificaciones simbólicas que sean válidas en matemáticas, como simplificar « (x+yx)/y1(x + y - x) / y \to 1 ». Una simplificación de este tipo no es válida en la aritmética de coma flotante, y expr.Expr no la llevará a cabo.

La parte de "tiempo de compilación" de estos parámetros significa que normalmente querrá "asignar" valores a los parámetros antes de enviar el circuito para su ejecución. Estos parámetros se pueden utilizar normalmente en cualquier lugar que espere un ángulo matemático (como los parámetros de una puerta de rotación), con la advertencia de que el hardware normalmente requerirá que se asignen a un valor de tipo clásico adecuado antes de la ejecución. Puede realizar esta tarea utilizando QuantumCircuit.assign_parameters().

Es posible que quieras utilizar muchos parámetros que estén relacionados entre sí. ParameterVectorPara que te resulte más fácil (y para que no tengas que inventarte muchos nombres), puedes utilizar el constructor de conveniencia. ParameterVectorElementTodos los elementos del vector son instancias válidas Parameter de una subclase especial.

Columna « 1 »
Columna « 2 »
ParameterVector(nombre[, longitud, uuid] )Un contenedor que alberga muchos objetos relacionados Parameter .
ParameterVectorElement(vector, índice[, uuid] )Un elemento de un ParameterVector.

Control de flujo en circuitos

InstructionEn QuantumCircuit, el flujo de control clásico se representa mediante s específicos, que son subclases de ControlFlowOp.

Columna « 1 »
Columna « 2 »
ControlFlowOp(nombre, num_qubits, num_clbits,...)Clase abstracta para encapsular todas las operaciones de flujo de control.

Para mayor comodidad, hay una frozenset instancia que contiene los Instruction.name atributos de cada una de las operaciones de flujo de control.

qiskit.circuit.CONTROL_FLOW_OP_NAMES

Conjunto de los nombres de instrucción de las operaciones de flujo de control conocidas de Qiskit.

La función get_control_flow_name_mapping() permite acceder a las clases de operación de flujo de control asociadas a cada nombre.

get_control_flow_name_mapping

qiskit.circuit.get_control_flow_name_mapping()

GitHub

Devuelve un diccionario que asigna los nombres de las operaciones de flujo de control a sus clases correspondientes."

Ejemplos

from qiskit.circuit import get_control_flow_name_mapping

ctrl_flow_name_map = get_control_flow_name_mapping()
if_else_object = ctrl_flow_name_map["if_else"]

print(if_else_object)
<class 'qiskit.circuit.controlflow.if_else.IfElseOp'>

Todas estas operaciones de flujo de control (IfElseOp, WhileLoopOp, SwitchCaseOp, ForLoopOp, y BoxOp) tienen un estado específico que define las condiciones y estrategias de ramificación, pero contienen en su blocks propiedad todos los diferentes bloques de subcircuitos a los que se puede acceder.

Columna « 1 »
Columna « 2 »
IfElseOp(condición, cuerpo_verdadero[, cuerpo_falso,...] )Una operación de circuito que ejecuta un programa (true_body) si una condición (condition) evaluada es verdadera, y opcionalmente evalúa otro programa (false_body) en caso contrario.
WhileLoopOp(condición, cuerpo[, etiqueta] )Una operación de circuito que ejecuta repetidamente un subcircuito (body) hasta que una condición (condition) se evalúa como Falso.
SwitchCaseOp(objetivo, casos, *[, etiqueta] )Operación de circuito que ejecuta un bloque de circuito concreto basándose en la correspondencia de un target dado con una lista ordenada de values.
ForLoopOp(conjunto de índices, parámetro de bucle, cuerpo[,...] )Una operación de circuito que ejecuta repetidamente un subcircuito (body) parametrizado por un parámetro loop_parameter a través del conjunto de valores enteros proporcionados en indexset.
BoxOp(cuerpo[, duración, unidad, etiqueta, anotaciones] )Una "caja" de alcance de operaciones en un circuito que se tratan atómicamente en el contexto mayor.

El SwitchCaseOp también interpreta un valor especial:

qiskit.circuit.CASE_DEFAULT

Valor predeterminado: <default case>

Un objeto especial que representa el caso "por defecto" de una sentencia switch. Si lo utiliza como objetivo de caso, debe ser el último caso y coincidirá con todo lo que no haya coincidido ya. Cuando se utiliza la interfaz del constructor de QuantumCircuit.switch()también se puede acceder a él como atributo DEFAULT del objeto constructor de casos vinculado.

Además de las operaciones de flujo de control de la estructura de bloques, también hay dos instrucciones especiales que afectan al flujo de control cuando se encuentran dentro de bucles. Corresponden a los usos típicos de las sentencias break y continue en los lenguajes de programación clásicos.

Columna « 1 »
Columna « 2 »
BreakLoopOp(num_qubits, num_clbits[, label] )Una operación de circuito que, cuando se encuentra, salta al final del bucle envolvente más cercano.
ContinueLoopOp(num_qubits, num_clbits[, label] )Operación de circuito que, cuando se encuentra, se desplaza a la siguiente iteración del bucle adyacente más cercano.
Nota

Las representaciones de las clases están documentadas aquí, pero tenga en cuenta que la construcción manual de estas clases es una operación de bajo nivel que no esperamos que los usuarios necesiten hacer con frecuencia.

Los usuarios deben leer Añadir flujo de control a los circuitos para conocer los flujos de trabajo recomendados para construir circuitos habilitados para flujo de control.

Dado que ControlFlowOp las subclases también Instruction son subclases, esto significa que la forma en que se almacenan en CircuitInstruction las instancias hace que se «apliquen» a una secuencia de qubits y clbits en sus qubits atributos y clbits . Esto puede dar lugar a problemas sutiles de coherencia de datos: los Qubit objetos y Clbit utilizados dentro de los bloques de subcircuitos de las operaciones de flujo de control no serán necesariamente idénticos a los objetos correspondientes en el CircuitInstruction. Cualquier código que utilice operaciones de flujo de control en Qiskit debe tener esto en cuenta; dentro de un subcircuito, debes tratar subcircuit.qubits[i] como si fuera realmente outer_instruction.qubits[i], y así sucesivamente. Puedes crear una tabla de consulta sencilla para esto de la siguiente manera:

cf_instruction: CircuitInstruction = ...
cf_operation: ControlFlowOp = cf_instruction.operation
for block in blocks:
    # Mappings of "inner" qubits/clbits to the outer ones.
    qubit_map = dict(zip(block.qubits, cf_instruction.qubits))
    clbit_map = dict(zip(block.clbits, cf_instruction.clbits))

    # ... do something with `block` ...

Recuerde que tendrá que propagar esta información si recurre a subbloques de operaciones de flujo de control.

Todos los bloques de subcircuitos de un ControlFlowOp deben contener el mismo número de Qubits y Clbits, que hagan referencia a los mismos bits externos en el mismo orden, de modo que funcione el zip bucle que aparece en el bloque de código anterior. No es necesario que los objetos del circuito Bit interno sean, literalmente, los mismos objetos. {x: x}Al utilizar la interfaz del generador de flujo de control (que, no nos cansaremos de repetir, es muy recomendable para los usuarios), los generadores se encargarán de que los objetos «bit» internos sean idénticos a los objetos «bit» externos; el qubit_map del bloque de código anterior siempre será una asignación, pero si estás utilizando los bloques, debes estar preparado para el caso de que se requiera la asignación.

Cualquier ClassicalRegister«s» que se utilice en un subcircuito de flujo de control también debe estar presente en todos los bloques que lo contengan ( es decir, en cualquier operación de flujo de control que lo contenga y en el circuito más externo), y todos los bloques del mismo ControlFlowOp deben contener los mismos registros. Una vez más, la interfaz del generador se encargará de que así sea (o generará un error inmediato si no puede hacerlo).

Cuando se utiliza la construcción de bajo nivel, los bloques QuantumCircuit deben cerrarse manualmente sobre cualquier variable de computación clásica en tiempo real de ámbito externo que utilicen. Esto se marca por estar en el iter_captured_vars() iterador de ese bloque. Las bibliotecas que construyan estos bloques manualmente tendrán que rastrear estas capturas cuando construyan bloques de circuitos de flujo de control y añadirlas al bloque utilizando add_capture() (o el argumento del constructor captures ), pero el código de usuario utilizará normalmente la interfaz del constructor de flujo de control, que se encarga de ello automáticamente.

Consulte la documentación de construcción con flujo de control para obtener más información sobre cómo construir circuitos con flujo de control.

Anotaciones locales de instrucciones

Consulte también

qiskit.circuit.annotation

La discusión a nivel de módulo del marco de anotaciones, incluyendo cómo definir anotaciones personalizadas, y cómo el sistema interactúa con el compilador y con la serialización a otros formatos.

Ciertas instrucciones de circuito pueden «anotarse» con anotaciones locales a la instrucción. A partir de la versión 2.1.0 de Qiskit, esto se limita a BoxOp. Todas las anotaciones son subclases de un objeto base que define una interfaz, pero suelen representar análisis y comandos totalmente personalizados.

Columna « 1 »
Columna « 2 »
Annotation(*args, **kwargs)Una anotación arbitraria para las instrucciones.

Investigación de las relaciones de conmutación

Si dos operaciones de un circuito conmutan, podemos intercambiar el orden en que se aplican. Esto puede permitir optimizaciones y simplificaciones, por ejemplo, si permite fusionar o cancelar compuertas:

     ┌─────────┐     ┌─────────┐               ┌─────────┐
q_0: ┤ Rz(0.5) ├──■──┤ Rz(1.2) ├──■──     q_0: ┤ Rz(1.7) ├
     └─────────┘┌─┴─┐└──┬───┬──┘┌─┴─┐  =       └──┬───┬──┘
q_1: ───────────┤ X ├───┤ X ├───┤ X ├     q_1: ───┤ X ├───
                └───┘   └───┘   └───┘             └───┘

La aplicación de estas optimizaciones forma parte del transpilador, pero las herramientas para analizar las conmutaciones están disponibles en el CommutationChecker.

Columna « 1 »
Columna « 2 »
CommutationChecker([...])Comprobar conmutaciones de dos operaciones.

Programación de resolución diferida

Por lo general, el código generado por el compilador de Qiskit no se puede ejecutar directamente en una QPU. En primer lugar, es probable que pase por algún compilador a nivel de impulsos específico del fabricante, que convierte las puertas lógicas y las mediciones en señales destinadas a la electrónica de control de la QPU. Aunque Qiskit Target puede representar algunas de las restricciones temporales que tendrán estos pulsos, por lo general no es del todo exhaustivo. Esto es especialmente cierto cuando se trata de circuitos dinámicos (operaciones de avance); los retrasos provocados por los componentes clásicos suelen depender de detalles de bajo nivel posteriores a la optimización de los compiladores de backend, y Qiskit no puede conocerlos.

En estas situaciones, el usuario puede seguir controlando la programación relativa de los impulsos, por ejemplo para el desacoplamiento dinámico, utilizando duraciones de "estiramiento". Se construyen mediante QuantumCircuit.add_stretch()e interactúan con el sistema de expresión clásico descrito en qiskit.circuit.classicalaunque no son mutables en tiempo real.

Por ejemplo, podemos añadir tramos y cajas para crear un sistema en el que se apliquen dos secuencias separadas de desacoplamiento dinámico al mismo qubit, mientras que otro par de qubits sufre un retraso de duración desconocida. Las dos secuencias deben tener la misma longitud, aunque internamente los impulsos DD concretos tengan longitudes diferentes.

from qiskit import QuantumCircuit
from qiskit.circuit.classical import expr

qc = QuantumCircuit(3, 3)
# This sets up three duration "degrees of freedom" that will
# be resolved later by a backend compiler.
a = qc.add_stretch("a")
b = qc.add_stretch("b")
c = qc.add_stretch("c")

# This set of operations involves feed-forward operations that
# Qiskit cannot know the length of.
with qc.box():
    qc.h(1)
    qc.cx(1, 2)
    qc.measure([1, 2], [1, 2])
    with qc.if_test(expr.equal(qc.clbits[1], qc.clbits[2])):
        qc.h(1)

# While that stuff is happening to qubits (1, 2), we want
# qubit 0 to do two different DD sequences.  The two DD
# sequences are fixed to be the same length as each other,
# even though they're both internally stretchy.
with qc.box(duration=a):
    # Textbook NMRish XX DD.
    qc.delay(b, 0)
    qc.x(0)
    qc.delay(expr.mul(2, b), 0)
    qc.x(0)
    qc.delay(b, 0)
with qc.box(duration=a):
    # XY4-like DD.
    for _ in range(2):
        qc.delay(c, 0)
        qc.y(0)
        qc.delay(expr.mul(2, c), 0)
        qc.x(0)
        qc.delay(c, 0)

Creación de instrucciones personalizadas

Si deseas crear instrucciones o puertas sencillas y puntuales que se vayan a añadir a un circuito, y los bloques solo se utilizan con fines de visualización o agrupación, la forma más sencilla de crear una instrucción o puerta personalizada es simplemente definirla como un QuantumCircuit, y luego utilizar su to_instruction() método o to_gate() según corresponda. Los resultados pueden introducirse directamente en QuantumCircuit.append() el circuito más grande. Estos métodos crearán instancias base Instruction o Gate cuyas definition propiedad sea el circuito tal y como se ha proporcionado, lo que significa que el transpilador podrá acceder a él automáticamente, al igual que otras funciones de Qiskit que intenten descomponer circuitos.

Tenga en cuenta que las instrucciones independientes y las puertas sólo deben actuar sobre qubits y clbits; las instrucciones que necesiten utilizar un flujo de control complejo tendrán que alinearse en QuantumCircuit utilizando compose().

Creación de subclases de instrucción

InstructionLas clases base, Gate y ControlledGate están diseñadas para que sea seguro crear subclases a partir de ellas, y cuentan con puntos de enganche que las subclases deben implementar. Si tu puerta personalizada no tiene parámetros ni estado, quizá también te interese derivarla de la clase singleton correspondiente en qiskit.circuit.singleton, como por ejemplo SingletonGate. Consulta la documentación en qiskit.circuit.singleton para conocer otros métodos y puntos de enganche del mecanismo de singleton.

Por lo general, las subclases deben definir un constructor por defecto que llame al constructor :class`super` con los argumentos correctos para tu instrucción. Está permitido incluir estado adicional en la clase, pero tus subclases se integrarán de forma más fiable con el resto del entorno de Qiskit si solo dependes de tus Instruction.params, y estos parámetros son exclusivamente ángulos de puerta.

Las subclases de Instruction (o una de sus subclases) deben implementar el método privado Instruction._define() , que rellena de forma diferida la caché oculta _definition que respalda el método público definition .

_define

Instruction._define()

GitHub

Rellena el campo «cached» _definition de este Instruction.

Las subclases deben implementar este método para permitir la construcción diferida de su atributo público definition . Una subclase puede utilizar su params en el momento de la llamada. El método debería asignar _definition un QuantumCircuit valor a y no devolver ningún valor.

En las subclases de ControlledGate, el _define() método debe implementar la descomposición únicamente para el estado de control «todo unos». El ControlledGate.definition sistema se encargará de adaptarlo para que se ajuste al estado real de control.

Si la subclase utiliza la maquinaria singleton, tenga en cuenta que _define() será llamado inmediatamente después de que la sentencia class-body haya sido ejecutada, para producir el objeto de definición del objeto singleton canónico. Esto significa que su definición sólo debe utilizar puertas que ya estén definidas; si está escribiendo una biblioteca con muchas puertas singleton, tendrá que ordenar sus archivos e importaciones para asegurarse de que esto es posible.

Es probable que las subclases de Gate también quieran sobrescribir el método de instancia del protocolo de matrices de NumPy, __array__. Gate.to_matrix()Lo utiliza, y tiene la siguiente firma:

__array__

object.__array__(dtype=None, copy=None)

Devuelve una matriz de NumPy que representa la puerta lógica. Esto puede utilizar el campo de params la puerta y puede dar por hecho que se trata de valores numéricos (suponiendo que la subclase así lo espere) y no de parámetros de tiempo de compilación.

Para una mayor eficiencia, el array devuelto debe tener por defecto un dtype de complex.

Si tu subclase personalizada tiene representaciones naturales de sus formas controladas o inversas, quizá también te interese sobrescribir los inverse() métodos y control() .

A modo de ejemplo, veamos cómo definir una puerta personalizada RxzR_{xz}; es decir, una rotación de un solo ángulo alrededor del eje XZXZ. RZXGateEn esencia, esto es lo que pasaría si los qubits estuvieran al revés, así que escribiremos nuestra definición en función de eso. Somos paramétricos, por lo que no podemos ser un singleton, pero somos unitarios, por lo que deberíamos ser un Gate:

import math
import numpy as np
from qiskit.circuit import Gate, QuantumCircuit

class RXZGate(Gate):
    def __init__(self, theta):
        # Initialize with our name, number of qubits and parameters.
        super().__init__("rxz", 2, [theta])

    def _define(self):
        # Our base definition is an RZXGate, applied "backwards".
        defn = QuantumCircuit(2)
        defn.rzx(1, 0)
        self._definition = defn

    def inverse(self, annotated = False):
        # We have an efficient representation of our inverse,
        # so we'll override this method.
        return RXZGate(-self.params[0])

    def power(self, exponent: float):
        # Also we have an efficient representation of power.
        return RXZGate(exponent * self.params[0])

    def __array__(self, dtype=None, copy=None):
        if copy is False:
            raise ValueError("unable to avoid copy while creating an array as requested")
        cos = math.cos(0.5 * self.params[0])
        isin = 1j * math.sin(0.5 * self.params[0])
        return np.array([
            [cos, -isin, 0, 0],
            [-isin, cos, 0, 0],
            [0, 0, cos, isin],
            [0, 0, isin, cos],
        ], dtype=dtype)

En este ejemplo, hemos definido una definición básica en términos de RZXGate, pero para permitir descomposiciones más rápidas en una serie de bases, quizá nos interese añadir algunas equivalencias más a SessionEquivalenceLibrary. Ten en cuenta que la BasisTranslator búsqueda de traducciones analizará todas las equivalencias posibles en todas las profundidades posibles, por lo que, si se proporciona una equivalencia en términos de (por ejemplo) XGate , automáticamente también estarán disponibles las descomposiciones en términos de RXGate .

Añadamos una equivalencia en términos de HH, CXCX y RzR_z para un parámetro simbólico arbitrario:

from qiskit.circuit import SessionEquivalenceLibrary, Parameter

theta = Parameter("theta")

equiv = QuantumCircuit(2)
equiv.h(0)
equiv.cx(1, 0)
equiv.rz(theta, 0)
equiv.cx(1, 0)
equiv.h(0)

SessionEquivalenceLibrary.add_equivalence(RZXGate(theta), equiv)

A partir de ese momento, mientras dure la sesión del intérprete Python, los traductores como BasisTranslator encontrarán nuestra nueva definición en sus búsquedas.


Trabajar con objetos a nivel de circuito

Conversión de circuitos abstractos en circuitos físicos

Un resumen QuantumCircuit no puede ejecutarse de forma fiable en hardware. Es posible que pueda utilizar algunos de los simuladores de alto nivel enlazados en Simulación de circuitos para obtener resultados rápidos para circuitos a pequeña escala, pero para ejecutar circuitos a escala de servicios públicos, tendrá que utilizar hardware real, lo que implica compilar a un circuito físico.

La función de alto nivel que permite hacer esto es transpile(); toma como argumentos un circuito abstracto y un hardware backend o target, y devuelve un circuito físico. Para tener más acceso y control sobre las etapas de las ejecuciones que se llevarán a cabo, utiliza generate_preset_pass_manager() para crear primero un StagedPassManager , que luego podrás modificar.

La maquinaria completa de transpilación y compilación se describe en detalle en la documentación del módulo qiskit.transpiler y los detalles de todos los pases incorporados en Qiskit están disponibles en qiskit.transpiler.passes.

Simulación de circuitos

Aunque no forma parte de la qiskit.circuit interfaz, una de las necesidades más comunes es obtener resultados rápidos de simulación para QuantumCircuit objetos. Esta sección proporciona un punto de partida rápido a otros lugares de la documentación para encontrar la información pertinente.

En el caso de los circuitos unitarios, puedes simular los efectos sobre el estado « 00\lvert0\dotsm0\rangle » pasando el objeto QuantumCircuit directamente al constructor Statevector por defecto. Del mismo modo, puedes obtener una matriz unitaria que represente el circuito como operador pasándola al constructor Operator por defecto. Si dispones de un circuito físico, quizá te interese pasarlo al Operator.from_circuit() método para aplicar las transformaciones del QuantumCircuit.layout y volver a mapearlo al espacio de qubits «abstracto».

Para una experiencia de simulación más parecida a la del backend, existen implementaciones respaldadas por simulador de todas las interfaces de hardware de Qiskit. En particular, podría interesarle:

Definición de relaciones de equivalencia

Una tarea habitual a la hora de mapear circuitos abstractos a hardware físico y optimizar el resultado consiste en encontrar relaciones de equivalencia que asignen una puerta lógica a un conjunto de bases diferente. Qiskit almacena esta información en una clase de base de datos llamada EquivalenceLibrary.

Columna « 1 »
Columna « 2 »
EquivalenceLibrary( [base] )Una biblioteca que proporciona un mapeo unidireccional de Gates a sus implementaciones equivalentes como QuantumCircuits.

Qiskit incluye un amplio conjunto de relaciones de equivalencia predefinidas para todas sus puertas estándar. Esta biblioteca base se llama StandardEquivalenceLibraryy debe tratarse como inmutable.

qiskit.circuit.StandardEquivalenceLibrary

Un EquivalenceLibrary que almacena todas las relaciones entre las puertas estándar integradas en Qiskit. SessionEquivalenceLibrarybaseNo debes modificar esto, sino que debes crear el tuyo propio EquivalenceLibrary utilizando este como modelo, o bien modificar el estado global.

Qiskit también define un objeto de estado global compartido, SessionEquivalenceLibrary, que constituye las equivalencias predeterminadas que se utilizan en diversos lugares de Qiskit, sobre todo en la BasisTranslator etapa del transpilador. BasisTranslatorNo dudes en añadir tus propias equivalencias utilizando su add_equivalence() método, ya que las instancias predeterminadas del las detectarán automáticamente.

qiskit.circuit.SessionEquivalenceLibrary

La instancia predeterminada de EquivalenceLibrary, que será la utilizada por la mayoría de los objetos de Qiskit si no se especifica manualmente ninguna biblioteca. No dudes en añadir equivalencias a esto utilizando add_equivalence(). Heredan todas las reglas integradas de StandardEquivalenceLibrary.

Aplicar el efecto Pauli a un circuito

Hay dos tipos principales de ruido cuando se ejecutan circuitos cuánticos. El primero es el ruido estocástico, o incoherente, que se debe principalmente a la interacción no deseada entre el procesador cuántico y el entorno externo en el que reside. El segundo se conoce como error coherente, y estos errores surgen debido al control imperfecto de un sistema cuántico. Puede tratarse de términos no deseados en el Hamiltoniano de un sistema, es decir, una evolución unitaria incorrecta, o de errores derivados de un control temporal incorrecto del sistema cuántico, lo que incluye cosas como formas de pulso incorrectas para las puertas.

El giro de Pauli es una técnica de supresión de errores cuánticos que utiliza la aleatorización para transformar los errores coherentes en errores estocásticos combinando los resultados de muchos circuitos aleatorios, pero lógicamente equivalentes. Qiskit proporciona una función para aplicar el giro de Pauli a un circuito dado para puertas estándar de dos qubits. Para más detalles, consulte la documentación de la función que figura a continuación:

pauli_twirl_2q_gates

qiskit.circuit.pauli_twirl_2q_gates(circuit, twirling_gate=None, seed=None, num_twirls=None, target=None)

GitHub

Crea copias de un circuito dado con Pauli twirling aplicado alrededor de puertas de dos qubits especificados.

Si está ejecutando esta función con la intención de girar un circuito para ejecutarlo en hardware, esta puede no ser la forma más eficiente de realizar el giro. Especialmente si el proveedor de hardware ha implementado la primitives interfaz de ejecución con SamplerV2 y EstimatorV2 lo más probable es que esta no sea la mejor manera de aplicar el giro a su circuito y usted querrá referirse a la implementación de SamplerV2 y/o EstimatorV2 para el proveedor de hardware especificado.

Si se pretende que esta función se ejecute después transpile() de o PassManager.run() , se puede utilizar el argumento opcional target para que las puertas de Pauli de 1 qubit insertadas se sinteticen de forma que sean compatibles con el valor dado de Target , de modo que los circuitos de salida sigan siendo compatibles.

Parámetros

  • circuit (QuantumCircuit) – El circuito para girar
  • twirling_gate (None | str |Gate |list[str] | list[Gate]) – La puerta de giro tiene como valor predeterminado «None», lo que significa que se girarán todas las puertas predeterminadas: CXGate, CZGate, ECRGate, y iSwapGate. Si se proporciona, puede ser una puerta única o una lista de puertas, ya sea como un objeto de puerta o como su nombre en forma de cadena. Actualmente solo se admiten los nombres «cx», «cz», «ecr» e «iswap». Si se proporciona un objeto «gate» fuera de las puertas predeterminadas, debe tener definida una matriz en su to_matrix método para que la puerta pueda girarse. Si no se puede calcular una configuración de giro válida, esa puerta en concreto se ignorará sin aviso y no se le aplicará ningún giro.
  • seed (int | None) – Una semilla entera para el generador de números aleatorios utilizado internamente por esta función. Si se especifica, debe estar comprendido entre 0 y 18.446.744.073.709.551.615.
  • num_twirls (int | None) – El número de circuitos giratorios a construir. El valor predeterminado es None y devolverá un único circuito. Si es un número entero se devolverá una lista de circuitos con num_twirls circuitos.
  • target (Target | None) – Si se especifica una Target instancia que se utilizará para ejecutar la descomposición de un solo qubit como parte del «Pauli twirling», con el fin de optimizar y asignar las puertas de Pauli añadidas al circuito al objetivo especificado.

Devuelve

Una copia del circuito dado con Pauli twirling aplicado a cada instancia de la puerta twirling especificada.

Tipo de retorno

QuantumCircuit | Lista QuantumCircuit ]


Excepciones

Casi todas las funciones y métodos del circuito lanzarán un CircuitError cuando se encuentren con un error particular del uso de Qiskit (a diferencia de los problemas de tipado o indexación, que normalmente mostrarán el correspondiente error estándar de Python ).

CircuitError

exception qiskit.circuit.CircuitError(*message)

GitHub

Bases: QiskitError

Clase base para los errores que se producen al procesar un circuito.

Establece el mensaje de error.


Convenciones de circuitos

Cuando se construyen circuitos a partir de objetos abstractos y matrices más concretas, existen varias convenciones posibles en torno al etiquetado y ordenación de bits, y a cómo se realiza el producto tensorial abstracto en el álgebra matricial concreta.

Las convenciones de Qiskit son:

  • en las representaciones de cadenas de bits, los bits se etiquetan con el bit situado más a la derecha de la cadena denominado 00 y el bit situado más a la izquierda de la cadena de bits nn denominado n1n - 1.
  • cuando se utilizan números enteros como índices de especificadores de bits en funciones de construcción de circuitos, el número entero se trata como un índice en QuantumCircuit.qubits (o clbits).
  • al dibujar circuitos, ponemos los bits de menor índice en la parte superior.
  • en representaciones vectoriales de estado, realizamos el producto tensorial abstracto como el producto de Kronecker, y ordenamos los argumentos a este de tal manera que la amplitud del estado de base computacional x\lvert x\rangle, donde xx es la cadena de bits interpretada como un entero, se encuentra en la ubicación statevector[x].
  • al controlar una puerta, el qubit o qubits de control se colocan en primer lugar en la lista de argumentos, por ejemplo, en la llamada qc.cx(0, 1), el qubit 0 será el control y el qubit 1 será el objetivo. Del mismo modo, en la llamada manual qc.append(CXGate(), [0, 1]), el qubit 0 será el control y el qubit 1 será el objetivo.

Ilustremos estas convenciones con algunos ejemplos.

Etiquetado de bits

Toma el circuito:

from qiskit import QuantumCircuit

qc = QuantumCircuit(5, 5)
qc.x(0)
qc.x(1)
qc.x(4)
qc.measure(range(5), range(5))

Esto invierte los estados de los qubits 0, 1 y 4 de 0\lvert0\rangle a 1\lvert1\rangle, luego mide todos los qubits nn en el correspondiente clbit nn utilizando la base computacional ( ZZ ). Si se simula sin ruido, la cadena de bits de salida de este circuito será 1001110011 cada vez; los qubits 0, 1 y 4 se voltean, y los valores "uno" de la cadena de bits están en los dígitos cero, primero y cuarto por la derecha.

En Qiskit, escribiríamos el estado del qubit inmediatamente antes de la medición en abreviatura ket-notation como 10011\lvert10011\rangle. Nótese que la etiqueta ket coincide con la cadena de bits clásica, y tiene el valor numérico binario de 19.

Si dibujamos este circuito, veremos que Qiskit coloca el qubit zeroth en la parte superior del dibujo del circuito:

qc.draw("mpl")
Diagrama del circuito generado por el código anterior.

Representaciones matriciales

Los vectores de estado se definen según la convención de que, para un sistema de dos niveles, la relación entre la representación abstracta y la representación matricial es tal que

α0+β1(αβ)\alpha\lvert0\rangle + \beta\lvert1\rangle \leftrightarrow \begin{pmatrix} \alpha \\ \beta \end{pmatrix}

donde α\alpha y β\beta son números complejos. Almacenamos el vector de estado como 1D Numpy ndarray con datos sv = [alpha, beta], es decir sv[0] == alpha y sv[1] == beta; nótese que los índices en el vector estado coinciden con las etiquetas ket.

Construimos el producto tensorial de dos estados qubit en álgebra matricial utilizando el producto de Kronecker, con el qubit 0 a la derecha y el qubit 1 a la izquierda, de forma que el estado base ZZ x\lvert x\rangle (donde xx es la interpretación entera de la cadena de bits) tiene su término distinto de cero en el vector de estado sv en sv[x]:

import numpy
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

state_0 = [1, 0]  # defined representation of |0>
state_1 = [0, 1]  # defined representation of |1>

# Circuit that creates basis state |10011>, where
# binary 10011 has the decimal value 19.
qc = QuantumCircuit(5)
qc.x(0)
qc.x(1)
qc.x(4)
qiskit_sv = Statevector(qc)

# List index 'n' corresponds to qubit 'n'.
individual_states = [
    state_1,
    state_1,
    state_0,
    state_0,
    state_1,
]
# Start from a scalar.
manual_sv = [1]
for qubit_state in individual_states:
    # Each new qubit goes "on the left".
    manual_sv = numpy.kron(qubit_state, manual_sv)

# Now `qiskit_sv` and `manual_sv` are the same, and:
assert manual_sv[19] == 1
assert qiskit_sv[19] == 1

Esto se refleja en la representación matricial de los operadores y se ajusta a las convenciones sobre el orden de los bits para los operadores controlados. Por ejemplo, la forma matricial de CXGate es:

import numpy
from qiskit.circuit.library import CXGate

numpy.array(CXGate())
array(CX)=(1000000100100100)\operatorname{array}(CX) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix}

Esto puede ser diferente a otras representaciones matriciales que hayas visto para CXCX, pero recuerda que la elección de la representación matricial es convencional, y esta forma coincide con las convenciones de Qiskit de que los qubits de control van primero y el producto tensorial se representa de forma que haya una correspondencia entre el índice de la "amplitud uno" y el valor de la cadena de bits de un estado.

En el caso de que haya varios controles para una puerta, como por ejemplo en CCXGate, el ctrl_state argumento se interpreta como el valor de la cadena de bits de los qubits de control, utilizando las mismas convenciones de numeración con base cero. CCXGatePor ejemplo, dado que el valor por defecto ctrl_state es la cadena de bits compuesta exclusivamente por unos, podemos observar que la forma matricial de CCXGate con ctrl_state = 1 es la misma que si tomáramos el estado de control compuesto exclusivamente por unos, pero invirtiéramos el valor del qubit de control con mayor índice al entrar y al salir de la puerta:

from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator

# Build the natural representation of `CCX` with the
# control qubits being `[0, 1]`, relative to the
# bitstring state "01", such that qubit 0 must be in |1>
# and qubit 1 must be in |0>.  The target qubit is 2.
ccx_natural = QuantumCircuit(3)
ccx_natural.ccx(0, 1, 2, ctrl_state=1)

# Build the same circuit in terms of the all-ones CCX.
# Note that we flip _qubit 1_, because that's the one
# that differs from the all-ones state.
ccx_relative = QuantumCircuit(3)
ccx_relative.x(1)
ccx_relative.ccx(0, 1, 2)
ccx_relative.x(1)

assert Operator(ccx_relative) == Operator(ccx_natural)

En ambos casos, la forma matricial de CCXGate en ctrl_state = 1 es:

array(CCX(ctrl_state=1))=(1000000000000100001000000001000000001000010000000000001000000001)\operatorname{array}\bigl(CCX(\text{ctrl\_state}=1)\bigr) = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{pmatrix}
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.