Computação Quântica com Django

Curso teórico e prático
Voltar

Material complementar:


Objetivo:

API REST backend desenvolvida com Django REST Framework, Qiskit e integrada à IBM Quantum Platform, utilizando execução de circuitos quânticos em backends reais da IBM. Nesse projeto, não serão utilizadas funcionalidades avançadas, nem arquitetura complexa. Funcionalidades do projeto:

  • Health Check: verifica disponibilidade da API e dos serviços internos;
  • Random Bit: gera bit aleatório quântico (0 ou 1) utilizando superposição quântica em único qubit, retornando também backend utilizado e circuito quântico em formato texto;
  • Coin Flip: realiza simulação de cara/coroa baseada em resultado quântico real, convertendo bit medido para heads ou tails;
  • Random Number: gera nºs aleatórios quânticos com quantidade configurável de bits, criando circuitos com múltiplos qubits, executando-os em hardware quântico da IBM e retornando quantidade de bits utilizada, representação binária gerada, valor decimal correspondente, backend utilizado e circuito quântico em formato texto (ASCII).

Pré-requisitos:

  • Python e Django;
  • VSCode com extensões Python e Django;
  • Conta e token de acesso na IBM Quantum Platform.

Criação do projeto:

  1. Instalar dependências e criar projeto Django DRF:
    
    pip3 install django djangorestframework qiskit qiskit[visualization] qiskit-ibm-runtime python-dotenv --break-system-packages
    
    mkdir api_rest_quantum && cd api_rest_quantum
    django-admin startproject config .
    python manage.py startapp quantum
    
  2. Criar arquivo de variáveis de ambiente 'quantum/.env' para armazenar token de acesso à IBM Quantum Platform:
    
    IBM_QUANTUM_TOKEN=cole_seu_token_aqui
    
  3. Criar arquivo de ignore 'quantum/.gitignore' para evitar commit de variáveis de ambiente:
    
    .env
    __pycache__/
    *.pyc
    venv/
    
  4. Adicionar códigos em 'config/settings.py' para carregar variáveis de ambiente:
    
    # Inserir imports no início do arquivo:
    from pathlib import Path
    import os
    from dotenv import load_dotenv
    
    # Adicionar 'rest_framework' e 'quantum' em INSTALLED_APPS:
    INSTALLED_APPS = [
        ...
        'rest_framework',
        'quantum'
    ]
    
    # Logo após a definição de BASE_DIR, adicione:
    load_dotenv(BASE_DIR / ".env")
    
  5. Executar migrations: python manage.py migrate
  6. Executar servidor: python manage.py runserver
  7. Inserir rotas globais em 'config/urls.py':
    
    from django.contrib import admin
    from django.urls import include, path
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/', include('quantum.urls')),
    ]
    

Criar conexão IBM Quantum:

  1. Criar provedor de conexão com IBM Quantum Platform em 'quantum/ibm_quantum.py':
    
    import os
    from qiskit_ibm_runtime import QiskitRuntimeService
    
    class IBMQuantumProvider:
        @staticmethod
        def get_service():
            return QiskitRuntimeService(
                channel="ibm_quantum_platform",
                token=os.getenv("IBM_QUANTUM_TOKEN")
            )
    
        @staticmethod
        def get_backend():
            service = IBMQuantumProvider.get_service()
            return service.least_busy(
                simulator=False,
                operational=True
            )
    

Criar funcionalidades quânticas:

  1. Criar classes serializadoras em 'quantum/serializers.py':
    
    from rest_framework import serializers
    
    class RandomBitResponseSerializer(serializers.Serializer):
        bit = serializers.IntegerField()
        backend = serializers.CharField()
        circuit = serializers.CharField()
    
    class HealthResponseSerializer(serializers.Serializer):
        status = serializers.CharField()
    
    class CoinFlipResponseSerializer(serializers.Serializer):
        result = serializers.CharField()
        backend = serializers.CharField()
        circuit = serializers.CharField()
    
    class RandomNumberResponseSerializer(serializers.Serializer):
        bits = serializers.IntegerField()
        binary = serializers.CharField()
        decimal = serializers.IntegerField()
        backend = serializers.CharField()
        circuit = serializers.CharField()
    
  2. Criar serviços quânticos em 'quantum/services.py':
    
    import os
    from qiskit import QuantumCircuit, transpile
    from qiskit_ibm_runtime import SamplerV2
    from .ibm_quantum import IBMQuantumProvider
    
    class QuantumService:
        @staticmethod
        def health():
            return {"status": "quantum-service-ok"}
        
        @staticmethod
        def test_ibm_connection():
            backend = IBMQuantumProvider.get_backend()
    
            return {
                "connected": True,
                "backend": backend.name
            }
    
        @staticmethod
        def generate_random_bit():
            backend = IBMQuantumProvider.get_backend()
    
            circuit = QuantumCircuit(1)
            circuit.h(0)
            circuit.measure_all()
    
            circuit_ascii = circuit.draw(output="text").single_string()
            
            transpiled_circuit = transpile(circuit, backend=backend)
            sampler = SamplerV2(mode=backend)
            job = sampler.run([transpiled_circuit], shots=1)
    
            result = job.result()
            bitstrings = result[0].data.meas.get_bitstrings()
            bit = int(bitstrings[0])
    
            return {
                "bit": bit,
                "backend": backend.name,
                "circuit": circuit_ascii
            }
        
        @staticmethod
        def coin_flip():
            random_result = QuantumService.generate_random_bit()
    
            return {
                "result": (
                    "heads"
                    if random_result["bit"] == 0
                    else "tails"
                ),
                "backend": random_result["backend"],
                "circuit": random_result["circuit"]
            }
        
        @staticmethod
        def generate_random_number(bits):
            if bits < 1 or bits > 16:
                raise ValueError("Bits devem ser entre 1 e 16")
    
            backend = IBMQuantumProvider.get_backend()
            circuit = QuantumCircuit(bits)
    
            for i in range(bits):
                circuit.h(i)
    
            circuit.measure_all()
    
            circuit_ascii = circuit.draw(output="text").single_string()
    
            transpiled_circuit = transpile(circuit, backend=backend)
            sampler = SamplerV2(mode=backend)
            job = sampler.run([transpiled_circuit], shots=1)
    
            result = job.result()
            bitstrings = result[0].data.meas.get_bitstrings()
            binary = bitstrings[0]
            decimal = int(binary, 2)
    
            return {
                "bits": bits,
                "binary": binary,
                "decimal": decimal,
                "backend": backend.name,
                "circuit": circuit_ascii
            }
    
  3. Criar visões para os endpoints quânticos:
    
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from .serializers import HealthResponseSerializer, RandomBitResponseSerializer, CoinFlipResponseSerializer, RandomNumberResponseSerializer
    from .services import QuantumService
    
    class HealthCheckView(APIView):
        def get(self, request):
            data = QuantumService.health()
            serializer = HealthResponseSerializer(instance=data)
            return Response(serializer.data)
    
    class RandomBitView(APIView):
        def get(self, request):
            data = QuantumService.generate_random_bit()
            serializer = RandomBitResponseSerializer(instance=data)
            return Response(serializer.data)
        
    class CoinFlipView(APIView):
        def get(self, request):
            data = QuantumService.coin_flip()
            serializer = CoinFlipResponseSerializer(instance=data)
            return Response(serializer.data)
        
    class RandomNumberView(APIView):
        def get(self, request):
            bits = int(request.query_params.get("bits", 8))
            data = QuantumService.generate_random_number(bits)
            serializer = RandomNumberResponseSerializer(instance=data)
            return Response(serializer.data)
    
  4. Criar arquivo de rotas locais 'quantum/urls.py':
    
    from django.urls import path
    from .views import HealthCheckView, RandomBitView, CoinFlipView, RandomNumberView
    
    urlpatterns = [
        path(
            "health/",
            HealthCheckView.as_view(),
            name="health-check"
        ),
        path(
            "quantum/random-bit/",
            RandomBitView.as_view(),
            name="random-bit"
        ),
        path(
            "quantum/coin-flip/",
            CoinFlipView.as_view(),
            name="coin-flip"
        ),
        path(
            "quantum/random-number/",
            RandomNumberView.as_view(),
            name="random-number"
        )
    ]
    

Testar funcionalidades:

  1. Executar servidor: python manage.py runserver
  2. Testar endpoint 'GET /api/health' (retornará 'ok'): http://localhost:8000/api/health
  3. Testar endpoint 'GET /api/quantum/random-bit' (retornará bit aleatório): http://localhost:8000/api/quantum/random-bit
  4. Testar endpoint 'GET /api/quantum/coin-flip' (retornará resultado de moeda lançada): http://localhost:8000/api/quantum/coin-flip
  5. Testar endpoint 'GET /api/quantum/random-number' (retornará nº aleatório com nº de bits especificado): http://localhost:8000/api/quantum/random-number?bits=8
  6. Testar endpoint 'GET /api/quantum/random-number' (retornará erro de validação para limite de bits): http://localhost:8000/api/quantum/random-number?bits=20

Elaborado por Mateus Schwede
ubsocial.github.io