Quantum Portfolio Optimizer: Uma Função Qiskit da Global Data Quantum
As Funções Qiskit são um recurso experimental disponível apenas para usuários dos planos IBM Quantum® Premium, Flex e On-Prem (via IBM Quantum Platform API). Elas estão em status de pré-lançamento e sujeitas a alterações.
Visão geral
O Quantum Portfolio Optimizer é uma Função Qiskit que aborda o problema de otimização dinâmica de portfólio, um problema padrão em finanças que visa rebalancear investimentos periódicos em um conjunto de ativos, maximizando retornos e minimizando riscos. Ao empregar técnicas de otimização quântica de ponta, esta função simplifica o processo para que usuários, sem expertise em computação quântica, possam se beneficiar de suas vantagens na busca de trajetórias de investimento ideais. Ideal para gestores de portfólio, pesquisadores em finanças quantitativas e investidores individuais, esta ferramenta permite o back-testing de estratégias de negociação em otimização de portfólio.
Descrição da função
A função Quantum Portfolio Optimizer usa o algoritmo Variational Quantum Eigensolver (VQE) para resolver um problema de Otimização Binária Irrestrita Quadrática (QUBO), abordando problemas de otimização dinâmica de portfólio. Os usuários precisam apenas fornecer os dados de preço dos ativos e definir a restrição de investimento; em seguida, a função executa o processo de otimização quântica que retorna um conjunto de trajetórias de investimento otimizadas.
O processo consiste em quatro etapas principais. Primeiro, os dados de entrada são mapeados para um problema compatível com quantum, construindo o QUBO do problema de otimização dinâmica de portfólio e transformando-o em um operador quântico (Hamiltoniano de Ising). Em seguida, o problema de entrada e o algoritmo VQE são adaptados para execução no hardware quântico. O algoritmo VQE é então executado no hardware quântico e, por fim, os resultados são pós-processados para fornecer as trajetórias de investimento ideais. O sistema também inclui um pós-processamento ciente de ruído (baseado em SQD) para maximizar a qualidade da saída.
Esta Função Qiskit é baseada no artigo publicado pela Global Data Quantum.
Entrada
Os argumentos de entrada da função estão descritos na tabela a seguir. Os dados dos ativos e outras especificações do problema devem ser fornecidos; além disso, as configurações do VQE podem ser incluídas para personalizar o processo de otimização.
| Nome | Tipo | Descrição | Obrigatório | Padrão | Exemplo |
|---|---|---|---|---|---|
| assets | json | Dicionário com os preços dos ativos | Sim | - | - |
| qubo_settings | json | Configurações do QUBO | Sim | - | Veja os exemplos na tabela abaixo |
| ansatz_settings | json | Configurações do ansatz | Não | None | Veja os exemplos na tabela abaixo. |
| optimizer_settings | json | Configurações do otimizador | Não | None | Veja os exemplos na tabela abaixo. |
| backend | str | Nome do backend QPU | Não | - | "ibm_torino" |
| previous_session_id | list of str | Lista de IDs de sessão para recuperar dados de execuções anteriores(*) | Não | Lista vazia | ["session_id_1", "session_id_2"] |
| apply_postprocess | bool | Aplica pós-processamento SQD ciente de ruído | Não | True | True |
| tags | list of strings | Lista de tags para identificar o experimento | Não | Lista vazia | ["optimization", "quantum_computing"] |
*Para retomar uma execução ou recuperar jobs processados em uma ou mais sessões anteriores, a lista de IDs de sessão deve ser passada no parâmetro previous_session_id. Isso é particularmente útil em casos onde uma tarefa de otimização não foi concluída devido a algum erro no processo e a execução precisa ser finalizada. Para isso, você deve fornecer os mesmos argumentos usados na execução inicial, juntamente com a lista previous_session_id conforme descrito.
O carregamento de dados de sessões anteriores (para retomar uma otimização) pode levar até uma hora.
assets
Os dados devem ser estruturados como um objeto JSON que armazena informações sobre os preços de fechamento de ativos financeiros em datas específicas. O formato é o seguinte:
- Chave primária (string): o nome ou símbolo ticker do ativo financeiro (por exemplo, "8801.T").
- Chave secundária (string): a data no formato AAAA-MM-DD.
- Valor (número): o preço de fechamento do ativo na data especificada. Os preços podem ser inseridos normalizados ou não normalizados.
Observe que todos os dicionários devem ter a mesma chave secundária (datas). Se um ativo específico não tiver uma data que outros possuem, os dados devem ser preenchidos para garantir consistência. Por exemplo, isso pode ser feito usando o último preço de fechamento registrado desse ativo.
Exemplo
{
"8801.T": {
"2023-01-01": 2374.0,
"2023-01-02": 2374.0,
"2023-01-03": 2374.0,
"2023-01-04": 2356.5,
...
},
"AAPL": {
"2023-01-01": 145.2,
"2023-01-02": 146.5,
"2023-01-03": 147.3,
"2023-01-04": 148.1,
...
},
...
}
# Added by doQumentation — required packages for this notebook
!pip install -q pandas qiskit-ibm-catalog
{
"asset_name": {
"date": closing_value,
...
},
...
}
Os dados do ativo devem conter, no mínimo, os preços de fechamento em (nt+1) * dt (veja a seção de entrada qubo_settings) registros de tempo (por exemplo, dias).
qubo_settings
A tabela a seguir descreve as chaves do dicionário qubo_settings. Construa o dicionário especificando o número de passos de tempo nt, o número de qubits de resolução nq e o max_investment - ou altere outros valores padrão.
| Nome | Tipo | Descrição | Obrigatório | Padrão | Exemplo |
|---|---|---|---|---|---|
| nt | int | Número de passos de tempo | Sim | - | 4 |
| nq | int | Número de qubits de resolução | Sim | - | 4 |
| max_investment | float | Número máximo de unidades de moeda investidas em todos os ativos | Sim | - | 10 |
| dt* | int | Janela de tempo considerada em cada passo de tempo. A unidade corresponde aos intervalos de tempo entre as chaves nos dados do ativo | Não | 30 | - |
| risk_aversion | float | Coeficiente de aversão ao risco | Não | 1000 | - |
| transaction_fee | float | Coeficiente de taxa de transação | Não | 0.01 | - |
| restriction_coeff | float | Multiplicador de Lagrange usado para impor a restrição do problema na formulação QUBO | Não | 1 | - |
ansatz_settings
Para modificar as opções padrão, crie um dicionário para o parâmetro ansatz_settings com as chaves a seguir. Por padrão, o ansatz é definido como "real_amplitudes" e ambas as opções extras (veja a tabela a seguir) são definidas como False.
| Nome | Tipo | Descrição | Obrigatório | Padrão |
|---|---|---|---|---|
| ansatz* | str | Ansatz a ser usado | Não | "real_amplitudes" |
| multiple_passmanager** | bool | Habilita a sub-rotina de múltiplos passmanagers (não disponível para o ansatz Tailored) | Não | False |
| dd_enable | bool | Adiciona desacoplamento dinâmico | Não | False |
* Ansatzes disponíveis
real_amplitudescyclicoptimized_real_amplitudestailored(apenas para o backendibm_torino, 7 ativos, 4 passos de tempo e 4 qubits de resolução)
** Se multiple_passmanager for definido como False, a função usa o pass manager padrão do Qiskit com optimization_level=3. Se definido como True, a sub-rotina multiple_passmanager compara três pass managers: o pass manager padrão anterior do Qiskit, um pass manager que mapeia qubits sobre a cadeia de primeiros vizinhos do QPU e os serviços do transpilador de IA. Em seguida, o pass manager com o menor erro cumulativo estimado é selecionado.
optimizer_settings
Este parâmetro é um dicionário com algumas opções ajustáveis do processo de otimização.
| Nome | Tipo | Descrição | Obrigatório | Padrão |
|---|---|---|---|---|
| primitive_options | json | Configurações da primitiva | Não | - |
| optimizer | str | Otimizador clássico selecionado | Não | "differential_evolution" |
| optimizer_options | json | Configuração do otimizador | Não | - |
Atualmente, a única opção de otimizador disponível é "differential_evolution".
Sob as chaves primitive_options e optimizer_options definimos dicionários com os seguintes parâmetros:
primitive_options
| Nome | Tipo | Descrição | Obrigatório | Padrão | Exemplo |
|---|---|---|---|---|---|
| sampler_shots | int | Número de shots do Sampler. | Não | 100000 | - |
| estimator_shots | int | Número de shots do Estimator. | Não | 25000 | - |
| estimator_precision | float | Precisão desejada do valor esperado. Se especificada, a precisão será usada em vez de estimator_shots. | Não | None | 0.015625 · (1 / sqrt(4096)) |
| max_time | int ou str | Tempo máximo que uma sessão de runtime pode permanecer aberta antes de ser encerrada à força. Pode ser fornecido em segundos (int) ou como uma string, como "2h 30m 40s". Deve ser menor que o máximo imposto pelo sistema. | Não | None | "1h 15m" |
optimizer_options
| Nome | Tipo | Descrição | Obrigatório | Padrão |
|---|---|---|---|---|
| num_generations | int | Número de gerações | Não | 20 |
| population_size | int | Tamanho da população | Não | 20 |
| mutation_range | list | Fator de mutação máximo e mínimo | Não | [0, 0.25] |
| recombination | float | Fator de recombinação | Não | 0.4 |
| max_parallel_jobs | int | Número máximo de jobs QPU executados em paralelo | Não | 3 |
| max_batchsize | int | Tamanho máximo do lote | Não | 200 |
-
O número de gerações avaliadas pela evolução diferencial é
num_generations+ 1, pois a população inicial é incluída. -
O número total de circuits é calculado como
(num_generations + 1) * population_size. -
Usar um tamanho de população maior e mais gerações geralmente melhora a qualidade dos resultados de otimização. No entanto, não é recomendado exceder um tamanho de população de 120 e um número de gerações maior que 20 (por exemplo,
120 * 21 = 2520circuits no total), pois isso geraria um número excessivo de circuits, o que pode ser computacionalmente custoso e demorado para processar. -
A função permite retomar otimizações anteriores, e sempre é possível aumentar o número de gerações (fornecendo a mesma entrada, exceto por
previous_session_ide umnum_generationsaumentado).
Garanta o cumprimento dos limites de jobs do Qiskit Runtime.
- Sampler:
sampler_shots <= 10_000_000. - Estimator:
max_batchsize * estimator_shots * observable_size <= 10_000_000(para esta função, todos os termos do observável comutam, portantoobservable_size=1).
Veja o guia Limites de jobs para mais informações.
Saída
A função retorna dois dicionários: o dicionário "result", contendo os melhores resultados de otimização, incluindo a solução ideal e seu custo objetivo mínimo associado; e "metadata", com dados de todos os resultados obtidos durante o processo de otimização, junto com suas respectivas métricas.
O primeiro dicionário foca na solução de melhor desempenho, enquanto o segundo fornece informações detalhadas sobre todas as soluções, incluindo custos objetivo e outras métricas relevantes.
Dicionários de saída:
| Nome | Tipo | Descrição | Exemplo |
|---|---|---|---|
| result | dict[str, dict[str, float]] | Contém a estratégia de investimento ao longo do tempo, com cada registro de tempo mapeado para pesos de investimento específicos por ativo (cada peso é o valor investido normalizado pelo valor total de investimento). | {'time_1': {'asset_1': 0.2, 'asset_2': 0.3, ...\}, ...\} |
| metadata | dict[str, Any] | Dados gerados durante a análise, incluindo soluções, custos e métricas. | Veja os exemplos abaixo |
Descrição do dicionário metadata
| Nome | Tipo | Descrição | Exemplo |
|---|---|---|---|
| session_id | str | Identificador único para a sessão do IBM Quantum. | "d0h30qjvpqf00084fgw0" |
| all_samples_metrics | dict | Dicionário contendo várias métricas para cada amostra pós-processada, como custos ou restrições. | Veja a descrição abaixo |
| sampler_counts | dict[str, int] | Dicionário em que as chaves são representações de bitstring das soluções amostradas e os valores são suas contagens. | {"101010": 3, "111000": 1\} |
| asset_order | list[str] | Lista com a ordem de investimento correspondente dos ativos em cada passo de tempo dentro das estratégias de investimento. | ["Asset_0", "Asset_1", "Asset_3"] |
| QUBO | list[list[float]] | Matriz QUBO do problema. | [[-6.96e-01, 5.81e-01, -1.26e-02, 0.00e+00], ...] |
| resource_summary | dict[str, dict[str, float]] | Resumo dos tempos de uso de CPU e QPU (em segundos) em diferentes etapas do processo. | {'RUNNING: EXECUTING_QPU': {'CPU_TIME': 412.84, 'QPU_TIME': 87.22\}, ...\} |
Descrição do dicionário all_samples_metrics
| Nome | Tipo | Descrição | Exemplo |
|---|---|---|---|
| investment_trajectories | list[list] | Estratégias de investimento derivadas de estados quânticos decodificados. | [[1, 2, 2], [1, 2, 1]] |
| counts | list[int] | Número de vezes que cada trajetória de investimento foi amostrada. O índice corresponde a investment_trajectories. | [5, 3] |
| objective_costs | list[float] | Valor da função objetivo para cada trajetória de investimento, ordenado do menor para o maior. | [0.98, 1.25] |
| sharpe_ratios | list[float] | Desempenho ajustado ao risco (índice de Sharpe) para cada trajetória de investimento. Alinhado por índice. | [1.1, 0.7] |
| returns | list[float] | Retorno esperado para cada trajetória de investimento. Alinhado por índice. | [0.15, 0.10] |
| rest_breaches | list[float] | Desvio máximo de restrição dentro de cada trajetória de investimento. Alinhado por índice. | [0.0, 0.25] |
| transaction_costs | list[float] | Custo de transação estimado associado a cada trajetória de investimento. Alinhado por índice. | [0.01, 0.02] |
Como começar
Autentique-se usando sua chave de API e selecione a Função Qiskit conforme descrito a seguir. (Este trecho assume que você já salvou sua conta no seu ambiente local.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")
Exemplo: Otimização dinâmica de portfólio com sete ativos
Este exemplo demonstra como executar a função de otimização dinâmica de portfólio (DPO) e ajustar suas configurações para desempenho ideal. Ele inclui etapas detalhadas para ajustar os parâmetros e alcançar os resultados desejados.
Este caso envolve sete ativos, quatro passos de tempo e quatro qubits de resolução, resultando em um requisito total de 112 qubits.
1. Leia os ativos incluídos no portfólio.
Se todos os ativos do portfólio estiverem armazenados em uma pasta em um caminho específico, você pode carregá-los em um pandas.DataFrame e convertê-los para um objeto no formato dict usando a seguinte função.
import os
import glob
import pandas as pd
def read_and_join_csv(file_pattern):
"""
Reads multiple CSV files matching the file pattern and combines them into a single DataFrame.
Parameters:
file_pattern (str): The pattern to match CSV files.
Returns:
pd.DataFrame: Combined DataFrame with data from all CSV files.
"""
# Find all files matching the pattern
csv_files = glob.glob(file_pattern)
# Get the base file names without the .csv extension
file_names = [os.path.basename(f).replace(".csv", "") for f in csv_files]
# Read each CSV file into a DataFrame and set the first column as the index
df_list = [pd.read_csv(f).set_index("Unnamed: 0") for f in csv_files]
# Rename columns in each DataFrame to the base file names
for df, name in zip(df_list, file_names):
df.columns = [name]
# Combine all DataFrames into one by merging them side by side
combined_df = pd.concat(df_list, axis=1)
return combined_df
file_pattern = "route/to/folder/with/assets/data/*.csv"
assets = read_and_join_csv(file_pattern).to_dict()
Para este exemplo, usamos os ativos 8801.T, CLF, GBPJPY, ITX.MC, META, TMBMKDE-10Y e XS2239553048. A figura a seguir ilustra os dados usados neste exemplo, mostrando a evolução diária do preço de fechamento dos ativos de 1º de janeiro a 1º de setembro de 2023.
Neste exemplo, para garantir uniformidade entre as datas, preenchemos os dias sem negociação com o preço de fechamento da data disponível anterior. Aplicamos esta etapa porque os ativos selecionados provêm de diferentes mercados com dias de negociação variados, tornando essencial padronizar o conjunto de dados para consistência.
2. Defina o problema.
Defina as especificações do problema configurando os parâmetros no dicionário qubo_settings.
qubo_settings = {
"nt": 4,
"nq": 4,
"dt": 30,
"max_investment": 25,
"risk_aversion": 1000.0,
"transaction_fee": 0.01,
"restriction_coeff": 1.0,
}
3. Defina as configurações do otimizador e do ansatz. (Opcional)
Opcionalmente, defina requisitos específicos para o processo de otimização, incluindo a seleção do otimizador e seus parâmetros, bem como a especificação da primitiva e suas configurações.
Para o Ansatz Tailored, o tamanho de população escolhido foi baseado em experimentos anteriores que mostraram que este valor produz uma otimização estável e eficiente.
No caso do Ansatz Real Amplitudes, voc ê pode seguir uma relação linear entre o population_size e o número de qubits no Circuit. Como regra geral aproximada, é recomendado usar um population_size mínimo de ~ 0.8 * n_qubits para o ansatz real_amplitudes.
Espera-se que o Optimized Real Amplitudes tenha um desempenho de otimização melhor do que o ansatz Real Amplitudes. No entanto, o número de variáveis a otimizar neste ansatz aumenta muito mais rapidamente do que no caso do Real Amplitudes (veja o artigo). Portanto, para problemas grandes, o Optimized Real Amplitudes requer mais execuções de Circuit. O Optimized Real Amplitudes provavelmente será útil para problemas que requerem até 100 qubits, mas é recomendado ter cuidado ao definir os parâmetros population_size. Como exemplo desse aumento de escala em population_size, a tabela anterior mostra que para um problema de 84 qubits, o Optimized Real Amplitudes requer population_size de 120, enquanto para um problema de 56 qubits, um population_size de 40 é suficiente.
optimizer_settings = {
"de_optimizer_settings": {
"num_generations": 20,
"population_size": 90,
"recombination": 0.4,
"max_parallel_jobs": 5,
"max_batchsize": 4,
"mutation_range": [0.0, 0.25],
},
"optimizer": "differential_evolution",
"primitive_settings": {
"estimator_shots": 25_000,
"estimator_precision": None,
"sampler_shots": 100_000,
},
}
Também é possível escolher um ansatz específico. O exemplo a seguir usa o ansatz 'Tailored'.
ansatz_settings = {
"ansatz": "tailored",
"multiple_passmanager": False,
}
4. Execute o problema.
dpo_job = dpo_solver.run(
assets=assets,
qubo_settings=qubo_settings,
optimizer_settings=optimizer_settings,
ansatz_settings=ansatz_settings,
backend_name="<backend name>",
previous_session_id=[],
apply_postprocess=True,
)
5. Recupere os resultados.
Conforme mencionado na seção Saída, a função retorna um dicionário com as trajetórias de investimento ordenadas do menor para o maior de acordo com o valor de sua função objetivo. Este conjunto de resultados permite a identificação da trajetória com o menor custo e suas correspondentes avaliações de investimento. Além disso, fornece a análise de diferentes trajetórias, facilitando a seleção das que melhor se alinham com necessidades ou objetivos específicos. Essa flexibilidade garante que as escolhas possam ser adaptadas para atender a uma variedade de preferências ou cenários. Comece apresentando a estratégia resultante que alcançou o menor custo objetivo encontrado durante o processo.
# Get the results of the job
dpo_result = dpo_job.result()
# Show the solution strategy
dpo_result["result"]
{'time_step_0': {'8801.T': 0.11764705882352941,
'ITX.MC': 0.20588235294117646,
'META': 0.38235294117647056,
'GBPJPY=X': 0.058823529411764705,
'TMBMKDE-10Y': 0.0,
'CLF': 0.058823529411764705,
'XS2239553048': 0.17647058823529413},
'time_step_1': {'8801.T': 0.11428571428571428,
'ITX.MC': 0.14285714285714285,
'META': 0.2,
'GBPJPY=X': 0.02857142857142857,
'TMBMKDE-10Y': 0.42857142857142855,
'CLF': 0.0,
'XS2239553048': 0.08571428571428572},
'time_step_2': {'8801.T': 0.0,
'ITX.MC': 0.09375,
'META': 0.3125,
'GBPJPY=X': 0.34375,
'TMBMKDE-10Y': 0.0,
'CLF': 0.0,
'XS2239553048': 0.25},
'time_step_3': {'8801.T': 0.3939393939393939,
'ITX.MC': 0.09090909090909091,
'META': 0.12121212121212122,
'GBPJPY=X': 0.18181818181818182,
'TMBMKDE-10Y': 0.0,
'CLF': 0.0,
'XS2239553048': 0.21212121212121213}}
Posteriormente, usando os metadados, você pode acessar os resultados de todas as estratégias amostradas. Você pode, assim, analisar mais a fundo as trajetórias alternativas retornadas pelo otimizador. Para isso, leia o dicionário armazenado em dpo_result['metadata']['all_samples_metrics'], que contém não apenas informações adicionais sobre a estratégia ideal, mas também detalhes das outras estratégias candidatas avaliadas durante a otimização.
O exemplo a seguir mostra como ler essas informações usando pandas para extrair métricas-chave associadas à estratégia ideal. Estas incluem Desvio de Restrição, Índice de Sharpe e o retorno de investimento correspondente.
# Convert metadata to a DataFrame
df = pd.DataFrame(dpo_result["metadata"]["all_samples_metrics"])
# Find the minimum objective cost
min_cost = df["objective_costs"].min()
print(f"Minimum Objective Cost Found: {min_cost:.2f}")
# Extract the row with the lowest cost
best_row = df[df["objective_costs"] == min_cost].iloc[0]
# Display the results associated with the best solution
print("Best Solution:")
print(f" - Restriction Deviation: {best_row['rest_breaches']}%")
print(f" - Sharpe Ratio: {best_row['sharpe_ratios']:.2f}")
print(f" - Return: {best_row['returns']}")
Minimum Objective Cost Found: -3.78
Best Solution:
- Restriction Deviation: 40.0
- Sharpe Ratio: 24.82
- Return: 0.46
6. Análise de desempenho
Por fim, analise o desempenho da sua aplicação de otimização. Especificamente, compare seus resultados, obtidos no exemplo anterior, com uma linha de base aleatória para avaliar a eficácia da nossa abordagem. Se o algoritmo quântico produzir demonstravelmente e consistentemente resultados com valores de custo mais baixos, isso indica um processo de otimização eficaz.
A figura apresenta as distribuições de probabilidade dos custos objetivo. Para gerar essas distribuições, pegue a lista de custos objetivo do resultado da função e conte as ocorrências de cada valor de custo (valores arredondados para a segunda casa decimal). Em seguida, atualize a coluna de contagem adequadamente, juntando as contagens de valores arredondados idênticos. Observe que, para melhor comparação visual, as contagens de ocorrências foram normalizadas para que cada distribuição seja exibida entre 0 e 1.
Como mostrado na figura (linha sólida azul), a distribuição de custos para a nossa abordagem com o Variational Quantum Eigensolver (pós-processado com SQD) está concentrada de forma acentuada em valores de custo objetivo mais baixos, indicando bom desempenho de otimização. Em contraste, a linha de base com ruído exibe uma distribuição mais ampla, centrada em torno de valores de custo mais altos. A linha vertical tracejada cinza representa o valor médio da distribuição aleatória, destacando ainda mais a consistência da função em retornar estratégias de investimento otimizadas. Para uma comparação adicional, a linha tracejada preta na figura corresponde à solução obtida com o otimizador Gurobi (versão gratuita). Todos esses resultados são explorados mais detalhadamente nos benchmarks abaixo para o exemplo de "Ativos Mistos" avaliado com o ansatz "Tailored".
Benchmarks
Esta função foi testada em diferentes configurações de qubits de resolução, circuits ansatz e agrupamentos de ativos de vários setores: uma mistura de ativos diferentes (Conjunto 1), derivativos de petróleo (Conjunto 2) e IBEX35 (Conjunto 3). Veja mais detalhes na tabela a seguir.
| Conjunto | Data | Ativos |
|---|---|---|
| Conjunto 1 | 01/01/2023 | 8801.T, CL=F, GBPJPY=X, ITX.MC, META, TMBMKDE-10Y, XS2239553048 |
| Conjunto 2 | 01/06/2023 | CL=F, BZ=F, HO=F, NG=F, XOM, RB=F, 2222.SR |
| Conjunto 3 | 01/11/2022 | ACS.MC, ITX.MC, FER.MC, ELE.MC, SCYR.MC, AENA.MC, AMS.MC |
Duas métricas-chave foram usadas para avaliar a qualidade da solução.
- O custo objetivo, que mede a eficiência da otimização comparando o valor da função de custo de cada experimento com os resultados do Gurobi (versão gratuita).
- O índice de Sharpe, que captura o retorno ajustado ao risco de cada portfólio, oferecendo perspectiva sobre o desempenho financeiro das soluções.
Juntas, essas métricas estabelecem benchmarks dos aspectos computacionais e financeiros dos portfólios gerados pelo quantum.
| Exemplo | Qubits | Ansatz | Profundidade | Uso de Runtime (s) | Uso total (s) | Custo objetivo | Sharpe | Custo objetivo Gurobi | Sharpe Gurobi |
|---|---|---|---|---|---|---|---|---|---|
| Ativos Mistos (Conjunto 1, 4 passos de tempo, 4-bit) | 112 | Tailored | 83 | 12735 | 13095 | -3.78 | 24.82 | -4.25 | 24.71 |
| Ativos Mistos (Conjunto 1, 4 passos de tempo, 4 passos de tempo, 4-bit) | 112 | Real Amplitudes | 359 | 11739 | 11903 | -3.39 | 23.64 | -4.25 | 24.71 |
| Derivativos de Petróleo (Conjunto 2, 4 passos de tempo, 3-bit) | 84 | Optimized Real Amplitudes | 78 | 6180 | 6350 | -3.73 | 19.13 | -4.19 | 21.71 |
| IBEX35 (Conjunto 3, 4 passos de tempo, 2-bit) | 56 | Optimized Real Amplitudes | 96 | 3314 | 3523 | -3.67 | 14.48 | -4.11 | 16.44 |
Os resultados mostram que o otimizador quântico, com ansatzes específicos do problema, identifica efetivamente estratégias de investimento eficientes em vários tipos de portfólio.
Abaixo detalhamos tanto o tamanho da população quanto o número de gerações especificados no dicionário optimizer_options. Todos os outros parâmetros foram definidos com seus valores padrão.
| Exemplo | population_size | num_generations |
|---|---|---|
| Portfólio de Ativos Mistos | 90 | 20 |
| Portfólio de Ativos Mistos | 92 | 20 |
| Portfólio de Derivativos de Petróleo | 120 | 20 |
| Portfólio IBEX35 | 40 | 20 |
O número de gerações foi definido como 20, pois esse valor foi considerado suficiente para atingir a convergência. Além disso, os valores padrão para os parâmetros internos do otimizador foram mantidos inalterados, pois consistentemente forneceram bom desempenho e são geralmente recomendados pela literatura e pelas diretrizes de implementação.
Obter suporte
Se você precisar de ajuda, pode enviar um e-mail para qpo.support@globaldataquantum.com. Na sua mensagem, forneça o ID do job da função.
Próximos passos
- Leia o artigo de pesquisa associado.
- Solicite acesso à função preenchendo este formulário.
- Experimente o tutorial de Otimização Dinâmica de Portfólio.