1from typing import Literal
2from collections.abc import Callable
3import numpy as np
4
5from qlauncher.base import Problem, Algorithm, Backend, Result
6from qlauncher.exceptions import DependencyError
7from qlauncher.routines.orca.backends import OrcaBackend
8
9try:
10 from ptseries.algorithms.binary_solvers import BinaryBosonicSolver
11except ImportError as e:
12 raise DependencyError(e, install_hint='orca', private=True) from e
13
14
[docs]
15class BBS(Algorithm):
16 """
17 Binary Bosonic Solver algorithm class.
18
19 This class represents the Binary Bosonic Solver (BBS) algorithm. BBS is a quantum-inspired algorithm that
20 solves optimization problems by mapping them onto a binary bosonic system. It uses a training process
21 to find the optimal solution.
22
23 ### Attributes:
24
25 - algorithm_format ('qubo', 'fn'), optional): If the algorithm input is a function or a qubo matrix. Defaults to 'qubo'.
26 - input_state (list[int] | None, optional): Photonic circuit input state provided to the ORCA computer. If None defaults to [1,0,1,0,1...]. Defaults to None.
27 - n_samples (int, optional): Number of samples. Defaults to 100.
28 - gradient_mode (str, optional): Gradient mode. Defaults to "parameter-shift".
29 - gradient_delta (float, optional): Gradient Delta. Defaults to np.pi/6.
30 - sampling_factor (int, optional): Number of times quantum samples are passed through the classical flipping layer. Defaults to 1.
31 - learning_rate (float, optional): Learning rate of the algorithm. Defaults to 5e-2.
32 - learning_rate_flip (float, optional): Bit flip learning rate. Defaults to 1e-1.
33 - updates (int, optional): Number of epochs. Defaults to 100.
34
35 """
36 _algorithm_format = 'qubo'
37
38 def __init__(self, algorithm_format: Literal['qubo', 'fn'] = 'qubo',
39 input_state: list[int] | None = None,
40 n_samples: int = 100,
41 gradient_mode: str = "parameter-shift",
42 gradient_delta: float = np.pi / 6,
43 sampling_factor: int = 1,
44 learning_rate: float = 5e-2,
45 learning_rate_flip: float = 1e-1,
46 updates: int = 100):
47 super().__init__()
48 self._algorithm_format = algorithm_format
49 self.bbs_params = {
50 'n_samples': n_samples,
51 'gradient_mode': gradient_mode,
52 'gradient_delta': gradient_delta,
53 'sampling_factor': sampling_factor,
54 }
55 self.training_params = {
56 'learning_rate': learning_rate,
57 'learning_rate_flip': learning_rate_flip,
58 'updates': updates,
59 }
60 self.input_state = input_state
61
[docs]
62 def run(self, problem: Problem, backend: Backend, formatter: Callable[[Problem], np.ndarray]) -> Result:
63
64 if not isinstance(backend, OrcaBackend):
65 raise ValueError(f'{backend.__class__} is not supported by BBS algorithm, use OrcaBackend instead')
66 objective = formatter(problem)
67
68 # TODO: use offset somehow
69 if not callable(objective):
70 objective, offset = objective
71
72 if self.input_state is None:
73 if not callable(objective):
74 self.input_state = [(i + 1) % 2 for i in range(len(objective))]
75 else:
76 raise ValueError('input_state needs to be provided if objective is a function (callable)')
77
78 tbi = backend.get_tbi()
79 bbs = BinaryBosonicSolver(pb_dim=len(self.input_state),
80 objective=objective,
81 input_state=self.input_state,
82 tbi=tbi,
83 **self.bbs_params)
84
85 bbs.solve(**self.training_params)
86
87 return self.construct_results(bbs)
88
[docs]
89 def get_bitstring(self, result: list[float]) -> str:
90 return ''.join(map(str, map(int, result)))
91
[docs]
92 def construct_results(self, solver: BinaryBosonicSolver) -> Result:
93 # TODO: add support for distribution (probably with different logger)
94 best_bitstring = ''.join(
95 map(str, map(int, solver.config_min_encountered)))
96 best_energy = solver.E_min_encountered
97 most_common_bitstring = None
98 most_common_bitstring_energy = None
99 distribution = None
100 energy = None
101 num_of_samples = solver.n_samples
102 average_energy = None
103 energy_std = None
104 #! Todo: instead of None attach relevant info from 'results'
105 # results fail to pickle correctly btw
106 return Result(best_bitstring, best_energy, most_common_bitstring,
107 most_common_bitstring_energy, distribution, energy,
108 num_of_samples, average_energy, energy_std, None)