openjij
Framework for the Ising model and QUBO.
Loading...
Searching...
No Matches
integer_polynomial_sa_system.hpp
Go to the documentation of this file.
1// Copyright 2023 Jij Inc.
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include "../utility/variable.hpp"
18#include "../utility/min_polynomial.hpp"
19#include "./sa_system.hpp"
20#include <cstdint>
21#include <random>
22#include <vector>
23
24namespace openjij {
25namespace system {
26
27template <typename RandType>
28class IntegerSASystem<graph::IntegerPolynomialModel, RandType> {
29
30public:
32 const typename RandType::result_type seed)
33 : model(model), seed(seed), random_number_engine(seed) {
34
35 const std::int64_t num_variables = model.GetNumVariables();
36
37 // Initialize the state with random values
38 this->state_.reserve(num_variables);
39 const auto &bounds = this->model.GetBounds();
40 for (std::int64_t i = 0; i < num_variables; ++i) {
41 auto x = utility::IntegerVariable(bounds[i].first, bounds[i].second);
42 x.SetRandomValue(this->random_number_engine);
43 this->state_.push_back(x);
44 }
45 this->energy_ = this->CalculateEnergy(this->state_);
46
47 // Set zero count and term product
48 const auto &key_value_list = this->model.GetKeyValueList();
49 const std::size_t num_terms = key_value_list.size();
50 this->zero_count_.resize(num_terms, 0);
51 this->term_prod_.resize(num_terms, 1.0);
52
53 for (std::size_t i = 0; i < num_terms; ++i) {
54 int count = 0;
55 double prod = 1.0;
56 for (const auto &[index, degree] : key_value_list[i].first) {
57 auto x = this->state_[index].value;
58 if (x == 0) {
59 count++;
60 } else {
61 prod *= std::pow(x, degree);
62 }
63 }
64 this->zero_count_[i] = count;
65 this->term_prod_[i] = prod;
66 }
67
68 // Initialize energy difference
69 this->base_energy_difference_.resize(num_variables);
70 for (std::size_t i = 0; i < num_terms; ++i) {
71 const double value = key_value_list[i].second;
72 for (const auto &[index, degree] : key_value_list[i].first) {
73 const auto x = this->state_[index].value;
74 if (x == 0) {
75 if (this->zero_count_[i] == 1) {
76 this->base_energy_difference_[index][degree] +=
77 value * this->term_prod_[i];
78 }
79 } else {
80 if (this->zero_count_[i] == 0) {
81 this->base_energy_difference_[index][degree] +=
82 value * this->term_prod_[i] / std::pow(x, degree);
83 }
84 }
85 }
86 }
87 }
88
89 double
90 CalculateEnergy(const std::vector<utility::IntegerVariable> &state) const {
91 double energy = this->model.GetConstant();
92 const auto &key_value_list = this->model.GetKeyValueList();
93 for (const auto &term : key_value_list) {
94 double prod = 1.0;
95 for (const auto &[index, degree] : term.first) {
96 prod *= std::pow(state[index].value, degree);
97 }
98 energy += term.second * prod;
99 }
100 return energy;
101 }
102
103 std::int64_t GenerateCandidateValue(std::int64_t index) {
104 return this->state_[index].GenerateCandidateValue(
105 this->random_number_engine);
106 }
107
108 double GetEnergyDifference(std::int64_t index, std::int64_t new_value) const {
109 double dE = 0.0;
110 const auto current_value = this->state_[index].value;
111 for (const auto &[degree, value] : this->base_energy_difference_[index]) {
112 dE += value *
113 (std::pow(new_value, degree) - std::pow(current_value, degree));
114 }
115 return dE;
116 }
117
118 void SetValue(std::int64_t index, std::int64_t new_value) {
119 const auto current_value = this->state_[index].value;
120 if (current_value == new_value) {
121 return;
122 }
123
124 this->energy_ += this->GetEnergyDifference(index, new_value);
125 this->state_[index].SetValue(new_value);
126
127 const auto &interactions = this->model.GetIndexToInteractions().at(index);
128 const auto &key_value_list = this->model.GetKeyValueList();
129
130 if (current_value != 0 && new_value != 0) {
131 for (const auto &[cons_ind, degree] : interactions) {
132 const double a =
133 std::pow(static_cast<double>(new_value) / current_value, degree);
134 const double ddE = (a - 1) * key_value_list[cons_ind].second *
135 this->term_prod_[cons_ind];
136 this->term_prod_[cons_ind] *= a;
137 const int total_count = this->zero_count_[cons_ind];
138 for (const auto &[i, d] : key_value_list[cons_ind].first) {
139 if (i != index) {
140 if (this->state_[i].value == 0 && total_count == 1) {
141 this->base_energy_difference_[i][d] += ddE;
142 } else if (this->state_[i].value != 0 && total_count == 0) {
143 this->base_energy_difference_[i][d] +=
144 ddE / std::pow(this->state_[i].value, d);
145 }
146 }
147 }
148 }
149 } else if (current_value == 0 && new_value != 0) {
150 for (const auto &[cons_ind, degree] : interactions) {
151 this->zero_count_[cons_ind]--;
152 const int total_count = this->zero_count_[cons_ind];
153 const double b = std::pow(new_value, degree);
154 const double ddE =
155 b * key_value_list[cons_ind].second * this->term_prod_[cons_ind];
156 this->term_prod_[cons_ind] *= b;
157 for (const auto &[i, d] : key_value_list[cons_ind].first) {
158 if (i != index) {
159 if (this->state_[i].value == 0 && total_count == 1) {
160 this->base_energy_difference_[i][d] += ddE;
161 } else if (this->state_[i].value != 0 && total_count == 0) {
162 this->base_energy_difference_[i][d] +=
163 ddE / std::pow(this->state_[i].value, d);
164 }
165 }
166 }
167 }
168 } else { // current_value != 0 && new_value == 0
169 for (const auto &[cons_ind, degree] : interactions) {
170 const int total_count = this->zero_count_[cons_ind];
171 this->zero_count_[cons_ind]++;
172 const double ddE =
173 -key_value_list[cons_ind].second * this->term_prod_[cons_ind];
174 this->term_prod_[cons_ind] /= std::pow(current_value, degree);
175 for (const auto &[i, d] : key_value_list[cons_ind].first) {
176 if (i != index) {
177 if (this->state_[i].value == 0 && total_count == 1) {
178 this->base_energy_difference_[i][d] += ddE;
179 } else if (this->state_[i].value != 0 && total_count == 0) {
180 this->base_energy_difference_[i][d] +=
181 ddE / std::pow(this->state_[i].value, d);
182 }
183 }
184 }
185 }
186 }
187 }
188
189 std::pair<int, double> GetMinEnergyDifference(std::int64_t index) {
190 const auto &x = this->state_[index];
191 const std::int64_t dxl = x.lower_bound - x.value;
192 const std::int64_t dxu = x.upper_bound - x.value;
193
194 if (this->UnderQuadraticCoeff(index)) {
195 auto it_a = this->base_energy_difference_[index].find(2);
196 const double a = (it_a != this->base_energy_difference_[index].end())
197 ? it_a->second
198 : 0.0;
199 auto it_b = this->base_energy_difference_[index].find(1);
200 const double b = (it_b != this->base_energy_difference_[index].end())
201 ? it_b->second
202 : 0.0;
203
204 const double aa = a;
205 const double bb = b + 2 * x.value * a;
206
207 return utility::FindMinimumIntegerQuadratic(aa, bb, dxl, dxu, x.value, this->random_number_engine);
208 }
209 else if (this->IsCubicCoeff(index)) {
210 auto it_a = this->base_energy_difference_[index].find(3);
211 const double a = (it_a != this->base_energy_difference_[index].end())
212 ? it_a->second
213 : 0.0;
214 auto it_b = this->base_energy_difference_[index].find(2);
215 const double b = (it_b != this->base_energy_difference_[index].end())
216 ? it_b->second
217 : 0.0;
218 auto it_c = this->base_energy_difference_[index].find(1);
219 const double c = (it_c != this->base_energy_difference_[index].end())
220 ? it_c->second
221 : 0.0;
222
223 const double aa = a;
224 const double bb = 3 * a * x.value + b;
225 const double cc = 3 * a * x.value * x.value + 2 * b * x.value + c;
226
227 return utility::FindMinimumIntegerCubic(aa, bb, cc, dxl, dxu, x.value, this->random_number_engine);
228 } else if (this->IsQuarticCoeff(index)) {
229 auto it_a = this->base_energy_difference_[index].find(4);
230 const double a = (it_a != this->base_energy_difference_[index].end())
231 ? it_a->second
232 : 0.0;
233 auto it_b = this->base_energy_difference_[index].find(3);
234 const double b = (it_b != this->base_energy_difference_[index].end())
235 ? it_b->second
236 : 0.0;
237 auto it_c = this->base_energy_difference_[index].find(2);
238 const double c = (it_c != this->base_energy_difference_[index].end())
239 ? it_c->second
240 : 0.0;
241 auto it_d = this->base_energy_difference_[index].find(1);
242 const double d = (it_d != this->base_energy_difference_[index].end())
243 ? it_d->second
244 : 0.0;
245
246 const double aa = a;
247 const double bb = 4 * a * x.value + b;
248 const double cc = 6 * a * x.value * x.value + 3 * b * x.value + c;
249 const double dd = 4 * a * x.value * x.value * x.value +
250 3 * b * x.value * x.value + 2 * c * x.value + d;
251
252 return utility::FindMinimumIntegerQuartic(aa, bb, cc, dd, dxl, dxu, x.value, this->random_number_engine);
253 } else {
254 double min_dE = std::numeric_limits<double>::infinity();
255 std::int64_t min_value = -1;
256
257 for (std::int64_t val = this->state_[index].lower_bound;
258 val <= this->state_[index].upper_bound; ++val) {
259 double dE = this->GetEnergyDifference(index, val);
260 if (dE < min_dE) {
261 min_dE = dE;
262 min_value = val;
263 }
264 }
265 if (min_value == -1) {
266 throw std::runtime_error("No valid state number found.");
267 }
268 return {min_value, min_dE};
269 }
270 }
271
272 double GetLinearCoeff(std::int64_t index) const {
273 auto it1 = this->base_energy_difference_[index].find(1);
274 double c1 =
275 (it1 != this->base_energy_difference_[index].end()) ? it1->second : 0.0;
276
277 auto it2 = this->base_energy_difference_[index].find(2);
278 double c2 =
279 (it2 != this->base_energy_difference_[index].end()) ? it2->second : 0.0;
280
281 return c1 + 2 * this->state_[index].value * c2;
282 }
283
284 const std::vector<utility::IntegerVariable> &GetState() const {
285 return this->state_;
286 }
287
288 double GetEnergy() const { return this->energy_; }
289
290 bool IsLinearCoeff(std::int64_t index) const {
291 return this->model.GetEachVariableDegreeAt(index) == 1;
292 }
293
294 bool UnderQuadraticCoeff(std::int64_t index) const {
295 return (this->model.GetEachVariableDegreeAt(index) == 2) || (this->model.GetEachVariableDegreeAt(index) == 1);
296 }
297
298 bool IsCubicCoeff(std::int64_t index) const {
299 return this->model.GetEachVariableDegreeAt(index) == 3;
300 }
301
302 bool IsQuarticCoeff(std::int64_t index) const {
303 return this->model.GetEachVariableDegreeAt(index) == 4;
304 }
305
306 bool CanOptMove(std::int64_t index) const {
307 return this->UnderQuadraticCoeff(index) || this->IsCubicCoeff(index) || this->IsQuarticCoeff(index);
308 }
309
310public:
312 const typename RandType::result_type seed;
314
315private:
316 std::vector<utility::IntegerVariable> state_;
317 double energy_;
318 std::vector<int> zero_count_;
319 std::vector<double> term_prod_;
320 std::vector<std::unordered_map<std::int64_t, double>> base_energy_difference_;
321};
322
323} // namespace system
324} // namespace openjij
Definition integer_polynomial_model.hpp:20
const std::vector< std::pair< std::int64_t, std::int64_t > > & GetBounds() const
Definition integer_polynomial_model.hpp:162
std::int64_t GetNumVariables() const
Definition integer_polynomial_model.hpp:161
const std::vector< std::pair< std::vector< std::pair< std::int64_t, std::int64_t > >, double > > & GetKeyValueList() const
Definition integer_polynomial_model.hpp:168
double CalculateEnergy(const std::vector< utility::IntegerVariable > &state) const
Definition integer_polynomial_sa_system.hpp:90
RandType random_number_engine
Definition integer_polynomial_sa_system.hpp:313
const RandType::result_type seed
Definition integer_polynomial_sa_system.hpp:312
double GetEnergy() const
Definition integer_polynomial_sa_system.hpp:288
bool UnderQuadraticCoeff(std::int64_t index) const
Definition integer_polynomial_sa_system.hpp:294
double GetEnergyDifference(std::int64_t index, std::int64_t new_value) const
Definition integer_polynomial_sa_system.hpp:108
IntegerSASystem(const graph::IntegerPolynomialModel &model, const typename RandType::result_type seed)
Definition integer_polynomial_sa_system.hpp:31
void SetValue(std::int64_t index, std::int64_t new_value)
Definition integer_polynomial_sa_system.hpp:118
bool IsCubicCoeff(std::int64_t index) const
Definition integer_polynomial_sa_system.hpp:298
const graph::IntegerPolynomialModel & model
Definition integer_polynomial_sa_system.hpp:311
std::vector< utility::IntegerVariable > state_
Definition integer_polynomial_sa_system.hpp:316
bool CanOptMove(std::int64_t index) const
Definition integer_polynomial_sa_system.hpp:306
std::vector< double > term_prod_
Definition integer_polynomial_sa_system.hpp:319
std::int64_t GenerateCandidateValue(std::int64_t index)
Definition integer_polynomial_sa_system.hpp:103
const std::vector< utility::IntegerVariable > & GetState() const
Definition integer_polynomial_sa_system.hpp:284
std::vector< int > zero_count_
Definition integer_polynomial_sa_system.hpp:318
std::pair< int, double > GetMinEnergyDifference(std::int64_t index)
Definition integer_polynomial_sa_system.hpp:189
std::vector< std::unordered_map< std::int64_t, double > > base_energy_difference_
Definition integer_polynomial_sa_system.hpp:320
double GetLinearCoeff(std::int64_t index) const
Definition integer_polynomial_sa_system.hpp:272
double energy_
Definition integer_polynomial_sa_system.hpp:317
bool IsLinearCoeff(std::int64_t index) const
Definition integer_polynomial_sa_system.hpp:290
bool IsQuarticCoeff(std::int64_t index) const
Definition integer_polynomial_sa_system.hpp:302
Definition sa_system.hpp:24
std::pair< std::int64_t, double > FindMinimumIntegerQuartic(const double a, const double b, const double c, const double d, const std::int64_t l, const std::int64_t u, const std::int64_t x, RandomNumberEngine &random_number_engine)
Definition min_polynomial.hpp:104
std::pair< std::int64_t, double > FindMinimumIntegerQuadratic(const double a, const double b, const std::int64_t l, const std::int64_t u, const std::int64_t x, RandomNumberEngine &random_number_engine)
Definition min_polynomial.hpp:15
std::pair< std::int64_t, double > FindMinimumIntegerCubic(const double a, const double b, const double c, const std::int64_t l, const std::int64_t u, const std::int64_t x, RandomNumberEngine &random_number_engine)
Definition min_polynomial.hpp:45
Definition algorithm.hpp:24
Definition variable.hpp:20