Quantum Portfolio Optimizer: funkcja Qiskit firmy Global Data Quantum
Funkcje Qiskit to funkcjonalność eksperymentalna dostępna wyłącznie dla użytkowników planów IBM Quantum® Premium Plan, Flex Plan oraz On-Prem (przez IBM Quantum Platform API). Są w statusie podglądu i mogą ulec zmianie.
Przegląd
Quantum Portfolio Optimizer to funkcja Qiskit, która rozwiązuje problem dynamicznej optymalizacji portfela – standardowy problem w finansach polegający na okresowym równoważeniu inwestycji w zestawie aktywów w celu maksymalizacji zwrotów i minimalizacji ryzyka. Dzięki zastosowaniu najnowocześniejszych technik optymalizacji kwantowej funkcja upraszcza ten proces, umożliwiając użytkownikom nieposiadającym wiedzy z zakresu informatyki kwantowej skorzystanie z jej zalet przy wyznaczaniu optymalnych ścieżek inwestycyjnych. To narzędzie jest idealne dla zarządzających portfelami, badaczy z obszaru finansów ilościowych oraz indywidualnych inwestorów – umożliwia wsteczne testowanie strategii inwestycyjnych w optymalizacji portfela.
Opis funkcji
Funkcja Quantum Portfolio Optimizer wykorzystuje algorytm Variational Quantum Eigensolver (VQE) do rozwiązywania problemu kwadratowej niezwiązanej optymalizacji binarnej (QUBO), odpowiadającego problemowi dynamicznej optymalizacji portfela. Wystarczy, że podasz dane o cenach aktywów i zdefiniujesz ograniczenia inwestycyjne – funkcja uruchomi kwantowy proces optymalizacji i zwróci zestaw zoptymalizowanych ścieżek inwestycyjnych.
Proces składa się z czterech głównych etapów. Najpierw dane wejściowe są odwzorowywane na problem kompatybilny z kwantowym przetwarzaniem: budowany jest QUBO dla problemu dynamicznej optymalizacji portfela, a następnie przekształcany w operator kwantowy (hamiltonian Isinga). Następnie problem wejściowy oraz algorytm VQE są dostosowywane do uruchomienia na sprzęcie kwantowym. Algorytm VQE jest uruchamiany na sprzęcie kwantowym, po czym wyniki są poddawane przetwarzaniu końcowemu w celu wyznaczenia optymalnych ścieżek inwestycyjnych. System zawiera również przetwarzanie końcowe uwzględniające szumy (oparte na SQD), aby zmaksymalizować jakość wyników.
Ta funkcja Qiskit jest oparta na opublikowanym artykule naukowym firmy Global Data Quantum.
Dane wejściowe
Argumenty wejściowe funkcji są opisane w poniższej tabeli. Dane aktywów i inne specyfikacje problemu muszą być podane; opcjonalnie można uwzględnić ustawienia VQE, aby dostosować proces optymalizacji.
| Nazwa | Typ | Opis | Wymagane | Domyślnie | Przykład |
|---|---|---|---|---|---|
| assets | json | Słownik z cenami aktywów | Tak | - | - |
| qubo_settings | json | Ustawienia QUBO | Tak | - | Zobacz przykłady w poniższej tabeli |
| ansatz_settings | json | Ustawienia ansatzu | Nie | None | Zobacz przykłady w poniższej tabeli. |
| optimizer_settings | json | Ustawienia optymalizatora | Nie | None | Zobacz przykłady w poniższej tabeli. |
| backend | str | Nazwa backendu QPU | Nie | - | "ibm_torino" |
| previous_session_id | list of str | Lista identyfikatorów Session do pobrania danych z poprzednich uruchomień(*) | Nie | Pusta lista | ["session_id_1", "session_id_2"] |
| apply_postprocess | bool | Zastosuj przetwarzanie końcowe SQD uwzględniające szumy | Nie | True | True |
| tags | list of strings | Lista tagów identyfikujących eksperyment | Nie | Pusta lista | ["optimization", "quantum_computing"] |
*Aby wznowić wykonanie lub pobrać zadania przetworzone w jednej lub kilku poprzednich sesjach, lista identyfikatorów Session musi być przekazana w parametrze previous_session_id. Jest to szczególnie przydatne w sytuacjach, gdy zadanie optymalizacji nie zakończyło się z powodu błędu w procesie i wykonanie musi zostać dokończone. W tym celu należy podać te same argumenty, które były użyte przy pierwszym uruchomieniu, wraz z listą previous_session_id, jak opisano powyżej.
Wczytanie danych z poprzednich sesji (w celu wznowienia optymalizacji) może potrwać do jednej godziny.
assets
Dane muszą być ustrukturyzowane jako obiekt JSON przechowujący informacje o cenach zamknięcia aktywów finansowych w określonych datach. Format jest następujący:
- Klucz główny (string): Nazwa lub symbol tickera aktywa finansowego (na przykład
"8801.T"). - Klucz pomocniczy (string): Data w formacie YYYY-MM-DD.
- Wartość (number): Cena zamknięcia aktywa w podanej dacie. Ceny można podawać w formie znormalizowanej lub nieznormalizowanej.
Pamiętaj, że wszystkie słowniki muszą mieć te same klucze pomocnicze (daty). Jeśli dane aktywo nie posiada daty obecnej w innych słownikach, dane muszą być uzupełnione w celu zapewnienia spójności. Można to zrobić na przykład przez użycie ostatniej zarejestrowanej ceny zamknięcia tego aktywa.
Przykład
{
"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,
...
},
...
}
Dane aktywów muszą zawierać co najmniej ceny zamknięcia z (nt+1) * dt (zob. sekcję wejściową qubo_settings) znaczników czasu (na przykład dni).
qubo_settings
Poniższa tabela opisuje klucze słownika qubo_settings. Zbuduj słownik, podając liczbę kroków czasowych nt, liczbę Qubitów rozdzielczości nq oraz max_investment – lub zmień inne wartości domyślne.
| Nazwa | Typ | Opis | Wymagane | Domyślnie | Przykład |
|---|---|---|---|---|---|
| nt | int | Liczba kroków czasowych | Tak | - | 4 |
| nq | int | Liczba Qubitów rozdzielczości | Tak | - | 4 |
| max_investment | float | Maksymalna liczba zainwestowanych jednostek walutowych we wszystkich aktywach | Tak | - | 10 |
| dt* | int | Okno czasowe uwzględniane w każdym kroku czasowym. Jednostka odpowiada przedziałom czasu między kluczami w danych aktywów | Nie | 30 | - |
| risk_aversion | float | Współczynnik awersji do ryzyka | Nie | 1000 | - |
| transaction_fee | float | Współczynnik opłaty transakcyjnej | Nie | 0.01 | - |
| restriction_coeff | float | Mnożnik Lagrange'a stosowany do wymuszania ograniczenia problemu w sformułowaniu QUBO | Nie | 1 | - |
ansatz_settings
Aby zmienić domyślne opcje, utwórz słownik dla parametru ansatz_settings z następującymi kluczami. Domyślnie ansatz jest ustawiony na "real_amplitudes", a obie opcje dodatkowe (zob. poniższa tabela) mają wartość False.
| Nazwa | Typ | Opis | Wymagane | Domyślnie |
|---|---|---|---|---|
| ansatz* | str | Ansatz do użycia | Nie | "real_amplitudes" |
| multiple_passmanager** | bool | Włącza podprocedurę multiple passmanager (niedostępną dla ansatzu Tailored) | Nie | False |
| dd_enable | bool | Dodaje dynamiczne odsprzęganie (dynamical decoupling) | Nie | False |
* Dostępne ansatze
real_amplitudescyclicoptimized_real_amplitudestailored(tylko dla backenduibm_torino, 7 aktywów, 4 kroków czasowych i 4 Qubitów rozdzielczości)
** Jeśli multiple_passmanager jest ustawiony na False, funkcja używa domyślnego menedżera przejść Qiskit z optimization_level=3. Jeśli ustawiony na True, podprocedura multiple_passmanager porównuje trzy menedżery przejść: poprzedni domyślny menedżer przejść Qiskit, menedżer mapujący Qubity na łańcuch sąsiednich węzłów QPU oraz usługi Transpilatora AI. Następnie wybierany jest menedżer przejść z szacowanym niższym skumulowanym błędem.
optimizer_settings
Ten parametr to słownik z konfigurowalnymi opcjami procesu optymalizacji.
| Nazwa | Typ | Opis | Wymagane | Domyślnie |
|---|---|---|---|---|
| primitive_options | json | Ustawienia prymitywu | Nie | - |
| optimizer | str | Wybrany klasyczny optymalizator | Nie | "differential_evolution" |
| optimizer_options | json | Konfiguracja optymalizatora | Nie | - |
Aktualnie jedyną dostępną opcją optymalizatora jest "differential_evolution".
Pod kluczami primitive_options i optimizer_options ustawiamy słowniki z następującymi parametrami:
primitive_options
| Nazwa | Typ | Opis | Wymagane | Domyślnie | Przykład |
|---|---|---|---|---|---|
| sampler_shots | int | Liczba pomiarów Samplera. | Nie | 100000 | - |
| estimator_shots | int | Liczba pomiarów Estimatora. | Nie | 25000 | - |
| estimator_precision | float | Żądana precyzja wartości oczekiwanej. Jeśli podana, precyzja zostanie użyta zamiast estimator_shots. | Nie | None | 0.015625 · (1 / sqrt(4096)) |
| max_time | int lub str | Maksymalny czas, przez który sesja runtime może pozostawać otwarta przed przymusowym zamknięciem. Można podać w sekundach (int) lub jako ciąg znaków, np. "2h 30m 40s". Musi być mniejszy niż maksimum narzucone przez system. | Nie | None | "1h 15m" |
optimizer_options
| Nazwa | Typ | Opis | Wymagane | Domyślnie |
|---|---|---|---|---|
| num_generations | int | Liczba generacji | Nie | 20 |
| population_size | int | Rozmiar populacji | Nie | 20 |
| mutation_range | list | Maksymalny i minimalny współczynnik mutacji | Nie | [0, 0.25] |
| recombination | float | Współczynnik rekombinacji | Nie | 0.4 |
| max_parallel_jobs | int | Maksymalna liczba zadań QPU wykonywanych równolegle | Nie | 3 |
| max_batchsize | int | Maksymalny rozmiar partii | Nie | 200 |
-
Liczba generacji obliczana przez ewolucję różnicową wynosi
num_generations+ 1, ponieważ uwzględniana jest populacja początkowa. -
Całkowita liczba Circuit'ów obliczana jest jako
(num_generations + 1) * population_size. -
Używanie większej populacji i większej liczby generacji zazwyczaj poprawia jakość wyników optymalizacji. Nie zaleca się jednak przekraczania rozmiaru populacji 120 i liczby generacji większej niż 20 (na przykład
120 * 21 = 2520Circuit'ów łącznie), ponieważ generowałoby to nadmierną liczbę Circuit'ów, co może być obliczeniowo kosztowne i czasochłonne. -
Funkcja umo żliwia wznowienie poprzedniej optymalizacji i zawsze można zwiększyć liczbę generacji (podając te same dane wejściowe z wyjątkiem
previous_session_idi zwiększoną wartościąnum_generations).
Upewnij się, że przestrzegasz limitów zadań Qiskit Runtime.
- Sampler:
sampler_shots <= 10_000_000. - Estimator:
max_batchsize * estimator_shots * observable_size <= 10_000_000(w tej funkcji wszystkie składniki obserwowalnej dojeżdżają, więcobservable_size=1).
Więcej informacji znajdziesz w przewodniku Limity zadań.
Dane wyjściowe
Funkcja zwraca dwa słowniki: słownik "result" zawierający najlepsze wyniki optymalizacji, w tym optymalne rozwiązanie i związany z nim minimalny koszt celu; oraz "metadata" z danymi ze wszystkich wyników uzyskanych podczas procesu optymalizacji wraz z odpowiednimi metrykami.
Pierwszy słownik skupia się na rozwiązaniu o najlepszej wydajności, podczas gdy drugi dostarcza szczegółowych informacji o wszystkich rozwiązaniach, w tym kosztach celu i innych istotnych metrykach.
Słowniki wyjściowe:
| Nazwa | Typ | Opis | Przykład |
|---|---|---|---|
| result | dict[str, dict[str, float]] | Zawiera strategię inwestycyjną w czasie – każdy znacznik czasu mapuje się na wagi inwestycyjne dla poszczególnych aktywów (każda waga to kwota inwestycji znormalizowana przez całkowitą kwotę inwestycji). | {'time_1': {'asset_1': 0.2, 'asset_2': 0.3, ...\}, ...\} |
| metadata | dict[str, Any] | Dane wygenerowane podczas analizy, w tym rozwiązania, koszty i metryki. | Zobacz przykłady poniżej |
Opis słownika metadata
| Nazwa | Typ | Opis | Przykład |
|---|---|---|---|
| session_id | str | Unikalny identyfikator sesji IBM Quantum. | "d0h30qjvpqf00084fgw0" |
| all_samples_metrics | dict | Słownik zawierający różne metryki dla każdej próbki po przetwarzaniu końcowym, takie jak koszty lub ograniczenia. | Zobacz opis poniżej |
| sampler_counts | dict[str, int] | Słownik, w którym kluczami są bitowe reprezentacje próbkowanych rozwiązań, a wartościami ich liczby wystąpień. | {"101010": 3, "111000": 1\} |
| asset_order | list[str] | Lista z odpowiednią kolejnością inwestowania w aktywa w każdym kroku czasowym w ramach strategii inwestycyjnych. | ["Asset_0", "Asset_1", "Asset_3"] |
| QUBO | list[list[float]] | Macierz QUBO problemu. | [[-6.96e-01, 5.81e-01, -1.26e-02, 0.00e+00], ...] |
| resource_summary | dict[str, dict[str, float]] | Podsumowanie czasów użycia CPU i QPU (w sekundach) na różnych etapach procesu. | {'RUNNING: EXECUTING_QPU': {'CPU_TIME': 412.84, 'QPU_TIME': 87.22\}, ...\} |
Opis słownika all_samples_metrics
| Nazwa | Typ | Opis | Przykład |
|---|---|---|---|
| investment_trajectories | list[list] | Strategie inwestycyjne wynikające z dekodowanych stanów kwantowych. | [[1, 2, 2], [1, 2, 1]] |
| counts | list[int] | Liczba razy, gdy każda ścieżka inwestycyjna została próbkowana. Indeks odpowiada investment_trajectories. | [5, 3] |
| objective_costs | list[float] | Wartość funkcji celu dla każdej ścieżki inwestycyjnej, posortowana od najniższej do najwyższej. | [0.98, 1.25] |
| sharpe_ratios | list[float] | Wyniki skorygowane o ryzyko (wskaźnik Sharpe'a) dla każdej ścieżki inwestycyjnej. Indeksy są zgodne. | [1.1, 0.7] |
| returns | list[float] | Oczekiwany zwrot dla każdej ścieżki inwestycyjnej. Indeksy są zgodne. | [0.15, 0.10] |
| rest_breaches | list[float] | Maksymalne odchylenie od ograniczenia w ramach każdej ścieżki inwestycyjnej. Indeksy są zgodne. | [0.0, 0.25] |
| transaction_costs | list[float] | Szacowany koszt transakcyjny związany z każdą ścieżką inwestycyjną. Indeksy są zgodne. | [0.01, 0.02] |
Pierwsze kroki
Uwierzytelnij się przy użyciu swojego klucza API i wybierz funkcję Qiskit w następujący sposób. (Ten fragment kodu zakłada, że masz już zapisane konto w swoim lokalnym środowisku.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")
Przykład: Dynamiczna optymalizacja portfela z siedmioma aktywami
Ten przykład pokazuje, jak uruchomić funkcję dynamicznej optymalizacji portfela (DPO) i dostosować jej ustawienia w celu uzyskania optymalnej wydajności. Zawiera szczegółowe kroki pozwalające na precyzyjne dostrojenie parametrów w celu osiągnięcia pożądanych wyników.
Ten przypadek obejmuje siedem aktywów, cztery kroki czasowe i cztery Qubity rozdzielczości, co w sumie wymaga 112 Qubitów.
1. Wczytaj aktywa wchodzące w skład portfela.
Jeśli wszystkie aktywa portfela są przechowywane w folderze pod określoną ścieżką, możesz załadować je do obiektu pandas.DataFrame i przekonwertować do formatu dict za pomocą poniższej funkcji.
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()
W tym przykładzie użyto aktywów 8801.T, CLF, GBPJPY, ITX.MC, META, TMBMKDE-10Y oraz XS2239553048. Poniższy rysunek ilustruje dane użyte w tym przykładzie, przedstawiając dzienną ewolucję cen zamknięcia aktywów od 1 stycznia do 1 września 2023 roku.
W tym przykładzie, aby zapewnić jednolitość dat, wypełniliśmy dni bez notowań ceną zamknięcia z poprzedniego dostępnego dnia. Zastosowaliśmy ten krok, ponieważ wybrane aktywa pochodzą z różnych rynków o zróżnicowanych dniach sesyjnych, co sprawia, że standaryzacja zbioru danych jest niezbędna dla zachowania spójności.
2. Zdefiniuj problem.
Zdefiniuj specyfikację problemu, konfigurując parametry w słowniku 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. Zdefiniuj ustawienia optymalizatora i ansatzu. (Opcjonalne)
Opcjonalnie zdefiniuj szczegółowe wymagania dotyczące procesu optymalizacji, w tym wybór optymalizatora i jego parametrów, a także specyfikację prymitywu i jego konfiguracji.
W przypadku Tailored Ansatz wybrany rozmiar populacji był oparty na wcześniejszych eksperymentach, które wykazały, że ta wartość zapewnia stabilną i efektywną optymalizację.
W przypadku Real Amplitudes Ansatz możesz kierować się liniową zależnością pomiędzy population_size a liczbą Qubitów w Circuit. Jako przybliżona zasada praktyczna zaleca się stosowanie minimalnego population_size ~ 0.8 * n_qubits dla ansatzu real_amplitudes.
Oczekuje się, że Optimized Real Amplitudes będzie miał lepszą wydajność optymalizacji niż ansatz Real Amplitudes. Jednak liczba zmiennych do zoptymalizowania w tym ansatzu rośnie znacznie szybciej niż w przypadku Real Amplitudes (zob. manuskrypt). W związku z tym, dla dużych problemów, Optimized Real Amplitudes wymaga więcej uruchomień Circuit. Optimized Real Amplitudes będzie prawdopodobnie przydatny dla problemów wymagających do 100 Qubitów, jednak zaleca się ostrożność przy ustawianiu parametrów population_size. Jako przykład tego skalowania population_size, poprzednia tabela pokazuje, że dla problemu z 84 Qubitami Optimized Real Amplitudes wymaga population_size równego 120, natomiast dla problemu z 56 Qubitami wystarczy population_size równy 40.
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,
},
}
Możliwe jest również wybranie konkretnego ansatzu. Poniżej użyto ansatzu 'Tailored'.
ansatz_settings = {
"ansatz": "tailored",
"multiple_passmanager": False,
}
4. Uruchom problem.
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. Pobierz wyniki.
Jak wspomniano w sekcji Dane wyjściowe, funkcja zwraca słownik z trajektoriami inwestycji uporządkowanymi od najniższej do najwyższej wartości funkcji celu. Ten zestaw wyników umożliwia identyfikację trajektorii o najniższym koszcie oraz odpowiadających jej ocen inwestycji. Dodatkowo pozwala na analizę różnych trajektorii, ułatwiając wybór tych, które najlepiej odpowiadają konkretnym potrzebom lub celom. Ta elastyczność sprawia, że wybory można dostosować do różnorodnych preferencji lub scenariuszy. Zacznij od przedstawienia strategii wynikowej, która osiągnęła najniższy koszt celu znaleziony podczas procesu.
# 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}}
Następnie, korzystając z metadanych, możesz uzyskać dostęp do wyników wszystkich próbkowanych strategii. Dzięki temu możesz przeprowadzić dalszą analizę alternatywnych trajektorii zwróconych przez optymalizator. W tym celu odczytaj słownik zapisany w dpo_result['metadata']['all_samples_metrics'], który zawiera nie tylko dodatkowe informacje o strategii optymalnej, ale także szczegóły dotyczące innych strategii kandydujących ocenianych podczas optymalizacji.
Poniższy przykład pokazuje, jak odczytać te informacje za pomocą pandas, aby wyodrębnić kluczowe metryki powiązane z optymalną strategią. Obejmują one Odchylenie od Ograniczeń (Restriction Deviation), Wskaźnik Sharpe'a (Sharpe Ratio) oraz odpowiadający zwrot z inwestycji.
# 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. Analiza wydajności
Na koniec przeanalizuj wydajność swojej aplikacji optymalizacyjnej. W szczególności porównaj uzyskane wyniki — z poprzedniego przykładu — z losowym punktem odniesienia, aby ocenić skuteczność zastosowanego podejścia. Jeśli algorytm kwantowy w sposób wyraźny i konsekwentny generuje wyniki o niższych wartościach kosztu, świadczy to o efektywnym procesie optymalizacji.
Rysunek przedstawia rozkłady prawdopodobieństwa kosztów celu. Aby wygenerować te rozkłady, pobierz listę kosztów celu z wyniku funkcji i policz wystąpienia każdej wartości kosztu (wartości zaokrąglone do drugiego miejsca po przecinku). Następnie zaktualizuj kolumnę zliczania, łącząc liczby identycznych zaokrąglonych wartości. Zwróć uwagę, że w celu lepszego porównania wizualnego liczby wystąpień zostały znormalizowane tak, aby każdy rozkład był wyświetlany w przedziale od 0 do 1.
Jak pokazano na rysunku (niebieska linia ciągła), rozkład kosztów dla podejścia opartego na Variational Quantum Eigensolver (po przetworzeniu końcowym z SQD) jest ostro skupiony przy niskich wartościach kosztu celu, co wskazuje na dobrą wydajność optymalizacji. W przeciwieństwie do tego, zaszumiony punkt odniesienia wykazuje szerszy rozkład skupiony wokół wyższych wartości kosztu. Szara przerywana linia pionowa reprezentuje wartość średnią rozkładu losowego, dodatkowo podkreślając spójność funkcji w zwracaniu zoptymalizowanych strategii inwestycyjnych. Dla dodatkowego porównania, czarna przerywana linia na rysunku odpowiada rozwiązaniu uzyskanemu za pomocą optymalizatora Gurobi (wersja bezpłatna). Wszystkie te wyniki są szerzej omówione w poniższych testach wydajnościowych dla przykładu „Mixed Assets" ocenianego z ansatzem „Tailored".
Testy wydajnościowe
Funkcja była testowana w różnych konfiguracjach Qubitów rozdzielczości, Circuit ansatzu oraz grupowań aktywów z różnych sektorów: mieszanina różnych aktywów (Zestaw 1), instrumenty pochodne ropy naftowej (Zestaw 2) oraz IBEX35 (Zestaw 3). Szczegóły zawiera poniższa tabela.
| Zestaw | Data | Aktywa |
|---|---|---|
| Set 1 | 01/01/2023 | 8801.T, CL=F, GBPJPY=X, ITX.MC, META, TMBMKDE-10Y, XS2239553048 |
| Set 2 | 01/06/2023 | CL=F, BZ=F, HO=F, NG=F, XOM, RB=F, 2222.SR |
| Set 3 | 01/11/2022 | ACS.MC, ITX.MC, FER.MC, ELE.MC, SCYR.MC, AENA.MC, AMS.MC |
Do oceny jakości rozwiązań użyto dwóch kluczowych metryk.
- Koszt celu, który mierzy efektywność optymalizacji, porównując wartość funkcji kosztu z każdego eksperymentu z wynikami Gurobi (wersja bezpłatna).
- Wskaźnik Sharpe'a, który uwzględnia zwrot z portfela skorygowany o ryzyko, oferując wgląd w wyniki finansowe rozwiązań.
Łącznie te metryki stanowią punkt odniesienia zarówno dla obliczeniowych, jak i finansowych aspektów portfeli generowanych kwantowo.
| Przykład | Qubity | Ansatz | Głębokość | Użycie Runtime (s) | Łączne użycie (s) | Koszt celu | Sharpe | Koszt celu Gurobi | Sharpe Gurobi |
|---|---|---|---|---|---|---|---|---|---|
| Mixed Assets (Set 1, 4 kroki czasowe, 4-bitowy) | 112 | Tailored | 83 | 12735 | 13095 | -3.78 | 24.82 | -4.25 | 24.71 |
| Mixed Assets (Set 1, 4 kroki czasowe, 4 kroki czasowe, 4-bitowy) | 112 | Real Amplitudes | 359 | 11739 | 11903 | -3.39 | 23.64 | -4.25 | 24.71 |
| Oil Derivatives (Set 2, 4 kroki czasowe, 3-bitowy) | 84 | Optimized Real Amplitudes | 78 | 6180 | 6350 | -3.73 | 19.13 | -4.19 | 21.71 |
| IBEX35 (Set 3, 4 kroki czasowe, 2-bitowy) | 56 | Optimized Real Amplitudes | 96 | 3314 | 3523 | -3.67 | 14.48 | -4.11 | 16.44 |
Wyniki pokazują, że optymalizator kwantowy, z ansatzami dostosowanymi do problemu, skutecznie identyfikuje efektywne strategie inwestycyjne dla różnych typów portfeli.
Poniżej szczegółowo opisujemy zarówno rozmiar populacji, jak i liczbę generacji określoną w słowniku optimizer_options. Wszystkie pozostałe parametry zostały ustawione na wartości domyślne.
| Przykład | population_size | num_generations |
|---|---|---|
| Mixed Assets Portfolio | 90 | 20 |
| Mixed Assets Portfolio | 92 | 20 |
| Oil Derivatives Portfolio | 120 | 20 |
| IBEX35 Portfolio | 40 | 20 |
Liczba generacji została ustawiona na 20, ponieważ ta wartość okazała się wystarczająca do osiągnięcia zbieżności. Ponadto domyślne wartości parametrów wewnętrznych optymalizatora pozostały niezmienione, ponieważ konsekwentnie zapewniały dobrą wydajność i są ogólnie zalecane przez literaturę i wytyczne implementacyjne.
Uzyskaj wsparcie
Jeśli potrzebujesz pomocy, możesz wysłać wiadomość e-mail na adres qpo.support@globaldataquantum.com. W wiadomości podaj identyfikator zadania funkcji.
Następne kroki
- Przeczytaj powiązany artykuł naukowy.
- Poproś o dostęp do funkcji, wypełniając ten formularz.
- Wypróbuj samouczek Dynamic Portfolio Optimization.