IQuantumProcessor
hace que sea muy fácil crear sus propios simuladores e integrarlos en sus proyectos Q #.
Esta publicación es la primera de una serie en esta interfaz. Comenzaremos implementando un simulador reversible como primer ejemplo, que ampliaremos en futuras publicaciones de blog. Simulador reversible puede simular programas cuánticos que consisten solamente en operaciones clásicas:
X
, CNOT
,CCNOT
(Puerta Toffoli) u operaciones X controladas aleatoriamente. Dado que un simulador reversible puede representar un estado cuántico asignando un valor booleano a cada qubit, incluso puede ejecutar programas cuánticos de miles de qubits. Este simulador es muy útil para probar operaciones cuánticas que evalúan funciones booleanas.
Implementación del simulador en C #
Esta publicación de blog destaca los fragmentos de código básicos. El código fuente completo se puede encontrar en el repositorio de muestras de Microsoft QDK.
Empiezas a escribir tu propio simulador ampliando la clase
QuantumProcessorBase
:
class ReversibleSimulatorProcessor : QuantumProcessorBase {
private IDictionary<Qubit, bool> simulationValues = new Dictionary<Qubit, bool>();
// (intrinsic operations)...
}
El diccionario que almacenará el valor de simulación actual para cada qubit en el programa ya se ha agregado a la clase. Los estados cuánticos clásicos | 0⟩ y | 1⟩ se representan como valores booleanos falso y verdadero, respectivamente. Los simuladores definen operaciones internas en un programa Q #.
QuantumProcessorBase
contiene un método para cada operación interna que puede anular para definir su comportamiento en el nuevo simulador. Tenga en cuenta que los métodos no implementados generarán una excepción predeterminada. Esto le ayudará a identificar los casos en los que la operación aún debe implementarse e informará al usuario del simulador que el simulador no admite la operación en línea. Por ejemplo, un simulador reversible no se puede utilizar para simular programas cuánticos que contienen operaciones no clásicas como H o T.
Comencemos por inicializar falsamente los qubits recién asignados anulando el método OnAllocateQubits. Asimismo, los qubits se eliminan del diccionario cuando se publican. En este caso, se llama a OnReleaseQubits.
public override void OnAllocateQubits(IQArray qubits) {
foreach (var qubit in qubits) {
simulationValues[qubit] = false;
}
}
public override void OnReleaseQubits(IQArray qubits) {
foreach (var qubit in qubits) {
simulationValues.Remove(qubit);
}
}
Con estas dos operaciones, se garantiza que los valores de simulación están disponibles cuando la operación se aplica a algún qubit, y que no quedan valores de simulación en el diccionario para qubits que no están en la región actual.
Como siguiente paso, implementemos las acciones de operaciones clásicas. En este caso, dos métodos son suficientes: se llama al método X cuando se simula la operación X, y se llama al método ControlledX cuando se simula la operación X, que también incluye CNOT y CCNOT. La operación X invierte el valor de simulación del qubit, mientras que en el caso de una operación X controlada arbitrariamente, el qubit objetivo se invierte si y solo si todos los qubits de control se establecen en verdadero.
public override void X(Qubit qubit) {
simulationValues[qubit] = !simulationValues[qubit];
}
public override void ControlledX(IQArray controls, Qubit qubit) {
simulationValues[qubit] ^= And(controls);
}
Finalmente, medir un qubit en el programa Q # devuelve un resultado (Uno o Cero), que se puede calcular en función del valor de simulación actual del qubit. También implementamos un método Reset que se llama cuando se llama a una operación de reinicio en un programa Q #. Este último no elimina el qubit de la región actual, pero restablece el valor de simulación a su valor inicial, que es falso.
public override Result M(Qubit qubit) {
return simulationValues[qubit] ? Result.One : Result.Zero;
}
public override void Reset(Qubit qubit) {
simulationValues[qubit] = false;
}
El ReversibleSimulatorProcessor se puede utilizar como un simulador creando una instancia de QuantumProcessorDispatcher. El estilo de diseño recomendado es proporcionar una clase personalizada para el simulador:
public class ReversibleSimulator : QuantumProcessorDispatcher {
public ReversibleSimulator() : base(new ReversibleSimulatorProcessor()) {}
}
Usando el nuevo simulador
Probemos un nuevo simulador en la siguiente operación Q #:
operation ApplyMajority(a : Qubit, b : Qubit, c : Qubit, f : Qubit) : Unit {
within {
CNOT(b, a);
CNOT(b, c);
} apply {
CCNOT(a, c, f);
CNOT(b, f);
}
}
También escribimos una operación que realiza una operación mayoritaria al proporcionar tres entradas booleanas:
operation RunMajority(a : Bool, b : Bool, c : Bool) : Bool {
using ((qa, qb, qc, f) = (Qubit(), Qubit(), Qubit(), Qubit())) {
within {
ApplyPauliFromBitString(PauliX, true, [a, b, c], [qa, qb, qc]);
} apply {
ApplyMajority(qa, qb, qc, f);
}
return MResetZ(f) == One;
}
}
Finalmente, puede juntarlo todo invocando la operación Q # con un nuevo simulador en el programa host de C #. El siguiente ejemplo evalúa la operación principal para todos los diferentes destinos de entrada y muestra todos los resultados de la simulación:
public static void Main(string[] args) {
var sim = new ReversibleSimulator();
var bits = new[] {false, true};
foreach (var a in bits) {
foreach (var b in bits) {
foreach (var c in bits) {
var f = RunMajority.Run(sim, a, b, c).Result;
Console.WriteLine($"Majority({a,5}, {b,5}, {c,5}) = {f,5}");
}
}
}
}
¿Listo para escribir tu propio simulador?
Esperamos que esta publicación te inspire a crear tu propio simulador. En los siguientes pasos, analizaremos cómo mejorar el rendimiento de la implementación actual (por ejemplo, al no utilizar un diccionario para almacenar valores de simulación), cómo convertirlo en un proyecto Q # independiente, cómo proporcionar acciones personalizadas para operaciones no intrínsecas y cómo proporcionar operaciones de diagnóstico que ayuda a la depuración.