Crie Seu Próprio Simulador Q # - Parte 1

Simuladores são um recurso particularmente versátil do QDK. Eles permitem que você execute várias tarefas em um programa Q # sem alterá-lo. Essas tarefas incluem simulação de estado completo , estimativa de recursos ou simulação de rastreamento . A nova interface IQuantumProcessortorna muito fácil criar seus próprios simuladores e integrá-los aos seus projetos Q #.



Esta postagem é a primeira de uma série sobre essa interface. Começaremos implementando um simulador reversível como primeiro exemplo, que expandiremos em postagens futuras no blog. Simulador reversível pode simular programas quânticos que consistem apenas em operações clássicas: X, CNOT,CCNOT(Toffoli gate) ou operações X controladas aleatoriamente. Como um simulador reversível pode representar um estado quântico atribuindo um valor booleano a cada qubit, ele pode até mesmo executar programas quânticos de milhares de qubits. Este simulador é muito útil para testar operações quânticas que avaliam funções booleanas.







Implementação do simulador em C #



Esta postagem do blog destaca os trechos de código básicos. O código-fonte completo pode ser encontrado no repositório de Amostras Microsoft QDK.


Você começa a escrever seu próprio simulador estendendo a classe QuantumProcessorBase:



class ReversibleSimulatorProcessor : QuantumProcessorBase {
    private IDictionary<Qubit, bool> simulationValues = new Dictionary<Qubit, bool>();

    //       (intrinsic operations)...
}


Um dicionário que armazenará o valor da simulação atual para cada qubit no programa já foi adicionado à classe. Os estados quânticos clássicos | 0⟩ e | 1⟩ são representados como valores booleanos falso e verdadeiro, respectivamente. Simuladores definem operações internas em um programa Q #. QuantumProcessorBasecontém um método para cada operação interna que você pode substituir para definir seu comportamento no novo simulador. Observe que os métodos não implementados irão lançar uma exceção padrão. Isso o ajudará a identificar os casos em que a operação ainda precisa ser implementada e informará ao usuário do simulador que a operação em linha não é suportada pelo simulador. Por exemplo, um simulador reversível não pode ser usado para simular programas quânticos que contêm operações não clássicas, como H ou T.



Vamos começar inicializando erroneamente os qubits recém-alocados, substituindo o método OnAllocateQubits. Da mesma forma, os qubits são removidos do dicionário quando são liberados. Nesse caso, OnReleaseQubits é chamado.



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);
    }
}


Com essas duas operações, é garantido que os valores de simulação estão disponíveis quando a operação é aplicada a algum qubit, e que não há valores de simulação deixados no dicionário para qubits que não estão na região atual.



Como uma próxima etapa, vamos implementar as ações de operações clássicas. Neste caso, dois métodos são suficientes: o método X é chamado quando a operação X é simulada, e o método ControlledX é chamado quando a operação X é simulada, que também inclui CNOT e CCNOT. A operação X inverte o valor de simulação do qubit, enquanto no caso de uma operação X controlada arbitrariamente, o qubit alvo é invertido se e somente se todos os qubits de controle forem configurados como verdadeiros.



public override void X(Qubit qubit) {
    simulationValues[qubit] = !simulationValues[qubit];
}

public override void ControlledX(IQArray controls, Qubit qubit) {
    simulationValues[qubit] ^= And(controls);
}


Finalmente, medir um qubit no programa Q # retorna um resultado (Um ou Zero), que pode ser calculado com base no valor de simulação atual do qubit. Também implementamos um método Reset que é chamado quando uma operação de reset é chamada em um programa Q #. O último não remove o qubit da região atual, mas redefine o valor da simulação de volta ao seu valor inicial, que é falso.



public override Result M(Qubit qubit) {
    return simulationValues[qubit] ? Result.One : Result.Zero;
}

public override void Reset(Qubit qubit) {
    simulationValues[qubit] = false;
}


O ReversibleSimulatorProcessor pode ser usado como um simulador instanciando o QuantumProcessorDispatcher. O estilo de design recomendado é fornecer uma classe personalizada para o simulador:



public class ReversibleSimulator : QuantumProcessorDispatcher {
    public ReversibleSimulator() : base(new ReversibleSimulatorProcessor()) {}
}


Usando o novo simulador



Vamos tentar um novo simulador na seguinte operação 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);
    }
}


Também escrevemos uma operação que realiza uma operação majoritária, fornecendo três 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, você pode juntar tudo invocando a operação Q # com um novo simulador no programa host C #. O exemplo a seguir avalia a operação principal para todos os destinos de entrada diferentes e exibe todos os resultados da simulação:



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}");
            }
        }
    }
}


Pronto para escrever seu próprio simulador?



Esperamos que este post inspire você a criar seu próprio simulador. Nas próximas etapas, discutiremos como melhorar o desempenho da implementação atual (por exemplo, não usando um dicionário para armazenar valores de simulação), como transformá-lo em um projeto Q # autônomo, como fornecer ações personalizadas para operações não intrínsecas e como fornecer operações de diagnóstico que ajudar na depuração.



All Articles