Skip to main content
IBM Quantum Platform

Compilador Quântico Aproximado

qiskit.synthesis.unitary.aqc

Implementação do Approximate Quantum Compiler, conforme descrito no artigo [1].


Interface

A interface pública principal desse módulo é acessada passando unitary_synthesis_method='aqc' para transpile(). Isso trocará o método de síntese para usar AQCSynthesisPlugin. As classes individuais são:

AQC([otimizador, semente] )Uma implementação genérica do Approximate Quantum Compiler.
ApproximateCircuit(num_qubits[, nome] )Uma classe básica que representa um circuito aproximado.
ApproximatingObjective()Uma classe base para a definição de um problema de otimização.
CNOTUnitCircuit(num_qubits, cnots[, tol, name] )Uma classe que representa um circuito aproximado baseado em blocos de unidades CNOT.
CNOTUnitObjective(num_qubits, cnots)Uma classe base para uma definição de problema baseada na unidade CNOT.
DefaultCNOTUnitObjective(num_qubits, cnots)Uma implementação ingênua da função objetiva baseada em unidades CNOT.
FastCNOTUnitObjective(num_qubits, cnots)Implementação da função objetiva e da calculadora de gradiente, que é semelhante ao site DefaultCNOTUnitObjective , mas várias vezes mais rápida.

Detalhes matemáticos

Estamos interessados em compilar um circuito quântico, que formalizamos como a descoberta da melhor representação do circuito em termos de uma sequência de portas ordenadas de uma matriz unitária de destino UU(d)U\in U(d), com algumas restrições adicionais de hardware. Em particular, analisamos as representações que podem ser restringidas em termos de conectividade de hardware, bem como a profundidade da porta, e escolhemos uma base de porta em termos de CNOT e portas de rotação. Lembramos que a combinação de CNOT e portas de rotação é universal em SU(d)SU(d) e, portanto, não limita a compilação.

Para definir adequadamente o que entendemos por “melhor representação do circuito”, definimos a métrica como a norma de Frobenius entre a matriz unitária do circuito compilado VV e a matriz unitária alvo UU, ou seja, VUF\|V - U\|_{\mathrm{F}}. Essa escolha é motivada por considerações de programação matemática e está relacionada a outras formulações que aparecem na literatura. Vamos analisar o problema com mais detalhes.

Seja nn o número de qubits e d=2nd=2^n. Dada uma estrutura CNOT ctct e um vetor de ângulos de rotação θ\theta, o circuito paramétrico forma uma matriz Vct(θ)SU(d)Vct(\theta)\in SU(d). Se nos for dado um circuito-alvo que forma uma matriz USU(d)U\in SU(d), gostaríamos de calcular

argmaxθ1dVct(θ),U\mathrm{argmax}_{\theta}\frac{1}{d}|\langle Vct(\theta),U\rangle|

em que o produto interno é o produto interno de Frobenius. Observe que V,Ud|\langle V,U\rangle|\leq d para todas as unidades UU e VV, de modo que o objetivo tem alcance em [0,1][0,1].

Nossa estratégia é maximizar

1dVct(θ),U\frac{1}{d}\Re \langle Vct(\theta),U\rangle

usando seu gradiente. Agora discutiremos os detalhes específicos por meio de um exemplo.

Embora o intervalo de VctVct seja um subconjunto de SU(d)SU(d) por construção, o circuito de destino pode formar uma matriz unitária geral. Entretanto, para qualquer UU(d)U\in U(d),

exp(2πik/d)det(U)1/dUSU(d) for all k{0,,d1}.\frac{\exp(2\pi i k/d)}{\det(U)^{1/d}}U\in SU(d)\text{ for all }k\in\{0,\ldots,d-1\}.

Portanto, devemos normalizar o circuito de destino por sua fase global e, em seguida, compilar aproximadamente o circuito normalizado. Podemos adicionar a fase global novamente depois.

No algoritmo, deixe UU' denotar a matriz de destino não normalizada e UU a matriz de destino normalizada. Agora que temos UU, fornecemos a função gradiente ao otimizador do método de Nesterov e calculamos θ\theta.

Para adicionar a fase global novamente, podemos formar o circuito de controle como

Vct(θ),UVct(θ),UVct(θ).\frac{\langle Vct(\theta),U'\rangle}{|\langle Vct(\theta),U'\rangle|}Vct(\theta).

Observe que, embora tenhamos otimizado usando o método de Nesterov no artigo, isso foi feito por causa de suas garantias de convergência, não por sua velocidade na prática. É muito mais rápido usar o L-BFGS, que é usado como otimizador padrão nessa implementação.

Um uso básico do algoritmo AQC deve consistir nas seguintes etapas:

# Define a target circuit as a unitary matrix
unitary = ...

# Define a number of qubits for the algorithm, at least 3 qubits
num_qubits = round(math.log2(unitary.shape[0]))

# Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for
# a linear layout.
layout = options.get("layout") or "spin"

# Choose a connectivity type, e.g. ``full`` for full connectivity between qubits.
connectivity = options.get("connectivity") or "full"

# Define a targeted depth of the approximate circuit in the number of CNOT units.
depth = int(options.get("depth") or 0)

# Generate a network made of CNOT units
cnots = make_cnot_network(
    num_qubits=num_qubits,
    network_layout=layout,
    connectivity_type=connectivity,
    depth=depth
)

# Create an optimizer to be used by AQC
optimizer = partial(scipy.optimize.minimize, method="L-BFGS-B")

# Create an instance
aqc = AQC(optimizer)

# Create a template circuit that will approximate our target circuit
approximate_circuit = CNOTUnitCircuit(num_qubits=num_qubits, cnots=cnots)

# Create an objective that defines our optimization problem
approximating_objective = DefaultCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)

# Run optimization process to compile the unitary
aqc.compile_unitary(
    target_matrix=unitary,
    approximate_circuit=approximate_circuit,
    approximating_objective=approximating_objective
)

Agora, approximate_circuit é um circuito que aproxima o unitário de destino até certo ponto e pode ser usado no lugar da matriz original.

Isso usa uma função auxiliar, make_cnot_network.

make_cnot_network

qiskit.synthesis.unitary.aqc.make_cnot_network(num_qubits, network_layout='spin', connectivity_type='full', depth=0)

GitHub

Gera uma rede que consiste em blocos de construção, cada um contendo um portão CNOT e possivelmente alguns de um único qubit. Essa rede modela um operador quântico em questão. Observe que cada bloco de construção tem 2 entradas e saídas correspondentes a um par de qubits. O que realmente retornamos aqui é uma cadeia de índices de pares de qubits compartilhados por cada bloco de construção em uma linha.

Parâmetros

  • num_qubits (int) – número de qubits.
  • network_layout (str) – tipo de geometria de rede, {"sequ", "spin", "cart", "cyclic_spin", "cyclic_line"}.
  • connectivity_type (str) – tipo de conectividade entre qubits, {"full", "line", "star"}.
  • depth (int) – profundidade da rede CNOT, ou seja, o número de camadas, em que cada camada consiste em um único bloco CNOT; o valor padrão será selecionado, se L <= 0.

Retorna

Uma matriz de tamanho (2, N) que define as camadas em cnot-network, onde N

é igual a L ou definido por um tipo concreto de rede.

Aumentos

ValueError - se for passado um tipo não suportado de layout de rede CNOT, número de qubits ou combinação de parâmetros.

Tipo de retorno

ndarray

É possível tirar proveito da versão acelerada da função objetiva. Ele implementa o mesmo algoritmo matemático que o padrão DefaultCNOTUnitObjective , mas é executado várias vezes mais rápido. A instanciação da classe de função objetiva acelerada é semelhante ao caso padrão:

# Crie um objetivo que defina nosso problema de otimização approximating_objective = FastCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)

O restante do código no exemplo acima não é alterado.

Referências

[1] : Liam Madden, Andrea Simonetto, Best Approximate Quantum Compiling Problems (Problemas de compilação quântica com melhor aproximação).

arXiv:2106.05649

Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.