Skip to content

Commit eba176c

Browse files
authored
Merge pull request opencv#10398 from alalek:ml_simplify_simulated_annealing
2 parents 9148a37 + 00e43a9 commit eba176c

File tree

4 files changed

+100
-209
lines changed

4 files changed

+100
-209
lines changed

modules/ml/include/opencv2/ml.hpp

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,75 +1912,49 @@ class CV_EXPORTS_W ANN_MLP_ANNEAL : public ANN_MLP
19121912
* Simulated annealing solver *
19131913
\****************************************************************************************/
19141914

1915-
/** @brief The class defines interface for system state used in simulated annealing optimization algorithm.
1915+
#ifdef CV_DOXYGEN
1916+
/** @brief This class declares example interface for system state used in simulated annealing optimization algorithm.
19161917
1917-
@cite Kirkpatrick83 for details
1918+
@note This class is not defined in C++ code and can't be use directly - you need your own implementation with the same methods.
19181919
*/
1919-
class CV_EXPORTS SimulatedAnnealingSolverSystem
1920+
struct SimulatedAnnealingSolverSystem
19201921
{
1921-
protected:
1922-
inline SimulatedAnnealingSolverSystem() {}
1923-
public:
1924-
virtual ~SimulatedAnnealingSolverSystem() {}
1925-
19261922
/** Give energy value for a state of system.*/
1927-
virtual double energy() const = 0;
1923+
double energy() const;
19281924
/** Function which change the state of system (random pertubation).*/
1929-
virtual void changeState() = 0;
1925+
void changeState();
19301926
/** Function to reverse to the previous state. Can be called once only after changeState(). */
1931-
virtual void reverseState() = 0;
1927+
void reverseState();
19321928
};
1929+
#endif // CV_DOXYGEN
19331930

19341931
/** @brief The class implements simulated annealing for optimization.
1935-
*
1936-
@cite Kirkpatrick83 for details
1937-
*/
1938-
class CV_EXPORTS SimulatedAnnealingSolver : public Algorithm
1939-
{
1940-
public:
1941-
SimulatedAnnealingSolver(const Ptr<SimulatedAnnealingSolverSystem>& system);
1942-
inline ~SimulatedAnnealingSolver() { release(); }
1943-
1944-
/** Simulated annealing procedure. */
1945-
int run();
1946-
/** Set/initialize RNG (energy).
1947-
@param rng new RNG
1948-
*/
1949-
void setEnergyRNG(const RNG& rng);
1950-
/** Set initial temperature of simulated annealing procedure.
1951-
@param x new initial temperature. x\>0
1952-
*/
1953-
void setInitialTemperature(double x);
1954-
/** Set final temperature of simulated annealing procedure.
1955-
@param x new final temperature value. 0\<x\<initial temperature
1956-
*/
1957-
void setFinalTemperature(double x);
1958-
/** Get final temperature of simulated annealing procedure. */
1959-
double getFinalTemperature();
1960-
/** Set setCoolingRatio of simulated annealing procedure : T(t) = coolingRatio * T(t-1).
1961-
@param x new cooling ratio value. 0\<x\<1
1962-
*/
1963-
void setCoolingRatio(double x);
1964-
/** Set number iteration per temperature step.
1965-
@param ite number of iteration per temperature step ite \> 0
1966-
*/
1967-
void setIterPerStep(int ite);
1968-
1969-
void release();
1970-
SimulatedAnnealingSolver(const SimulatedAnnealingSolver&);
1971-
SimulatedAnnealingSolver& operator=(const SimulatedAnnealingSolver&);
19721932
1973-
struct Impl; friend struct Impl;
1974-
protected:
1975-
Impl* impl;
1976-
};
1933+
@cite Kirkpatrick83 for details
19771934
1935+
@param solverSystem optimization system (see SimulatedAnnealingSolverSystem)
1936+
@param initialTemperature initial temperature
1937+
@param finalTemperature final temperature
1938+
@param coolingRatio temperature step multiplies
1939+
@param iterationsPerStep number of iterations per temperature changing step
1940+
@param lastTemperature optional output for last used temperature
1941+
@param rngEnergy specify custom random numbers generator (cv::theRNG() by default)
1942+
*/
1943+
template<class SimulatedAnnealingSolverSystem>
1944+
int simulatedAnnealingSolver(SimulatedAnnealingSolverSystem& solverSystem,
1945+
double initialTemperature, double finalTemperature, double coolingRatio,
1946+
size_t iterationsPerStep,
1947+
CV_OUT double* lastTemperature = NULL,
1948+
cv::RNG& rngEnergy = cv::theRNG()
1949+
);
19781950

19791951
//! @} ml
19801952

19811953
}
19821954
}
19831955

1956+
#include <opencv2/ml/ml.inl.hpp>
1957+
19841958
#endif // __cplusplus
19851959
#endif // OPENCV_ML_HPP
19861960

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef OPENCV_ML_INL_HPP
6+
#define OPENCV_ML_INL_HPP
7+
8+
namespace cv { namespace ml {
9+
10+
// declared in ml.hpp
11+
template<class SimulatedAnnealingSolverSystem>
12+
int simulatedAnnealingSolver(SimulatedAnnealingSolverSystem& solverSystem,
13+
double initialTemperature, double finalTemperature, double coolingRatio,
14+
size_t iterationsPerStep,
15+
CV_OUT double* lastTemperature,
16+
cv::RNG& rngEnergy
17+
)
18+
{
19+
CV_Assert(finalTemperature > 0);
20+
CV_Assert(initialTemperature > finalTemperature);
21+
CV_Assert(iterationsPerStep > 0);
22+
CV_Assert(coolingRatio < 1.0f);
23+
double Ti = initialTemperature;
24+
double previousEnergy = solverSystem.energy();
25+
int exchange = 0;
26+
while (Ti > finalTemperature)
27+
{
28+
for (size_t i = 0; i < iterationsPerStep; i++)
29+
{
30+
solverSystem.changeState();
31+
double newEnergy = solverSystem.energy();
32+
if (newEnergy < previousEnergy)
33+
{
34+
previousEnergy = newEnergy;
35+
exchange++;
36+
}
37+
else
38+
{
39+
double r = rngEnergy.uniform(0.0, 1.0);
40+
if (r < std::exp(-(newEnergy - previousEnergy) / Ti))
41+
{
42+
previousEnergy = newEnergy;
43+
exchange++;
44+
}
45+
else
46+
{
47+
solverSystem.reverseState();
48+
}
49+
}
50+
}
51+
Ti *= coolingRatio;
52+
}
53+
if (lastTemperature)
54+
*lastTemperature = Ti;
55+
return exchange;
56+
}
57+
58+
}} //namespace
59+
60+
#endif // OPENCV_ML_INL_HPP

modules/ml/src/ann_mlp.cpp

Lines changed: 5 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -42,41 +42,6 @@
4242

4343
namespace cv { namespace ml {
4444

45-
struct SimulatedAnnealingSolver::Impl
46-
{
47-
int refcount;
48-
49-
const Ptr<SimulatedAnnealingSolverSystem> systemPtr;
50-
SimulatedAnnealingSolverSystem& system;
51-
RNG rEnergy;
52-
double coolingRatio;
53-
double initialT;
54-
double finalT;
55-
int iterPerStep;
56-
57-
Impl(const Ptr<SimulatedAnnealingSolverSystem>& s) :
58-
refcount(1),
59-
systemPtr(s),
60-
system(*(s.get())),
61-
rEnergy(12345)
62-
{
63-
CV_Assert(!systemPtr.empty());
64-
initialT = 2;
65-
finalT = 0.1;
66-
coolingRatio = 0.95;
67-
iterPerStep = 100;
68-
}
69-
70-
inline double energy() { return system.energy(); }
71-
inline void changeState() { system.changeState(); }
72-
inline void reverseState() { system.reverseState(); }
73-
74-
void addref() { CV_XADD(&refcount, 1); }
75-
void release() { if (CV_XADD(&refcount, -1) == 1) delete this; }
76-
protected:
77-
virtual ~Impl() { CV_Assert(refcount==0); }
78-
};
79-
8045
struct AnnParams
8146
{
8247
AnnParams()
@@ -115,103 +80,7 @@ inline T inBounds(T val, T min_val, T max_val)
11580
return std::min(std::max(val, min_val), max_val);
11681
}
11782

118-
SimulatedAnnealingSolver::SimulatedAnnealingSolver(const Ptr<SimulatedAnnealingSolverSystem>& system)
119-
{
120-
impl = new Impl(system);
121-
}
122-
123-
SimulatedAnnealingSolver::SimulatedAnnealingSolver(const SimulatedAnnealingSolver& b)
124-
{
125-
if (b.impl) b.impl->addref();
126-
release();
127-
impl = b.impl;
128-
}
129-
130-
void SimulatedAnnealingSolver::release()
131-
{
132-
if (impl) { impl->release(); impl = NULL; }
133-
}
134-
135-
void SimulatedAnnealingSolver::setIterPerStep(int ite)
136-
{
137-
CV_Assert(impl);
138-
CV_Assert(ite>0);
139-
impl->iterPerStep = ite;
140-
}
141-
142-
int SimulatedAnnealingSolver::run()
143-
{
144-
CV_Assert(impl);
145-
CV_Assert(impl->initialT>impl->finalT);
146-
double Ti = impl->initialT;
147-
double previousEnergy = impl->energy();
148-
int exchange = 0;
149-
while (Ti > impl->finalT)
150-
{
151-
for (int i = 0; i < impl->iterPerStep; i++)
152-
{
153-
impl->changeState();
154-
double newEnergy = impl->energy();
155-
if (newEnergy < previousEnergy)
156-
{
157-
previousEnergy = newEnergy;
158-
exchange++;
159-
}
160-
else
161-
{
162-
double r = impl->rEnergy.uniform(0.0, 1.0);
163-
if (r < std::exp(-(newEnergy - previousEnergy) / Ti))
164-
{
165-
previousEnergy = newEnergy;
166-
exchange++;
167-
}
168-
else
169-
{
170-
impl->reverseState();
171-
}
172-
}
173-
174-
}
175-
Ti *= impl->coolingRatio;
176-
}
177-
impl->finalT = Ti;
178-
return exchange;
179-
}
180-
181-
void SimulatedAnnealingSolver::setEnergyRNG(const RNG& rng)
182-
{
183-
CV_Assert(impl);
184-
impl->rEnergy = rng;
185-
}
186-
187-
void SimulatedAnnealingSolver::setInitialTemperature(double x)
188-
{
189-
CV_Assert(impl);
190-
CV_Assert(x>0);
191-
impl->initialT = x;
192-
}
193-
194-
void SimulatedAnnealingSolver::setFinalTemperature(double x)
195-
{
196-
CV_Assert(impl);
197-
CV_Assert(x>0);
198-
impl->finalT = x;
199-
}
200-
201-
double SimulatedAnnealingSolver::getFinalTemperature()
202-
{
203-
CV_Assert(impl);
204-
return impl->finalT;
205-
}
206-
207-
void SimulatedAnnealingSolver::setCoolingRatio(double x)
208-
{
209-
CV_Assert(impl);
210-
CV_Assert(x>0 && x<1);
211-
impl->coolingRatio = x;
212-
}
213-
214-
class SimulatedAnnealingANN_MLP : public SimulatedAnnealingSolverSystem
83+
class SimulatedAnnealingANN_MLP
21584
{
21685
protected:
21786
ml::ANN_MLP& nn;
@@ -228,7 +97,7 @@ class SimulatedAnnealingANN_MLP : public SimulatedAnnealingSolverSystem
22897
initVarMap();
22998
}
23099
~SimulatedAnnealingANN_MLP() {}
231-
protected:
100+
232101
void changeState()
233102
{
234103
index = rIndex.uniform(0, nbVariables);
@@ -1075,16 +944,11 @@ class ANN_MLPImpl : public ANN_MLP_ANNEAL
1075944
}
1076945
int train_anneal(const Ptr<TrainData>& trainData)
1077946
{
1078-
SimulatedAnnealingSolver t(Ptr<SimulatedAnnealingANN_MLP>(new SimulatedAnnealingANN_MLP(*this, trainData)));
1079-
t.setEnergyRNG(params.rEnergy);
1080-
t.setFinalTemperature(params.finalT);
1081-
t.setInitialTemperature(params.initialT);
1082-
t.setCoolingRatio(params.coolingRatio);
1083-
t.setIterPerStep(params.itePerStep);
947+
SimulatedAnnealingANN_MLP s(*this, trainData);
1084948
trained = true; // Enable call to CalcError
1085-
int iter = t.run();
949+
int iter = simulatedAnnealingSolver(s, params.initialT, params.finalT, params.coolingRatio, params.itePerStep, NULL, params.rEnergy);
1086950
trained =false;
1087-
return iter;
951+
return iter + 1; // ensure that 'train()' call is always successful
1088952
}
1089953

1090954
int train_backprop( const Mat& inputs, const Mat& outputs, const Mat& _sw, TermCriteria termCrit )

samples/cpp/travelsalesman.cpp

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
using namespace cv;
44

5-
class TravelSalesman : public ml::SimulatedAnnealingSolverSystem
5+
class TravelSalesman
66
{
77
private :
88
const std::vector<Point>& posCity;
@@ -17,11 +17,11 @@ private :
1717
rng = theRNG();
1818
}
1919
/** Give energy value for a state of system.*/
20-
/*virtual*/ double energy() const;
20+
double energy() const;
2121
/** Function which change the state of system (random pertubation).*/
22-
/*virtual*/ void changeState();
22+
void changeState();
2323
/** Function to reverse to the previous state.*/
24-
/*virtual*/ void reverseState();
24+
void reverseState();
2525

2626
};
2727

@@ -81,31 +81,24 @@ int main(void)
8181
posCity[i].y = static_cast<int>(radius*sin(theta)) + center.y;
8282
next[i]=(i+1)%nbCity;
8383
}
84-
Ptr<TravelSalesman> ts_system(new TravelSalesman(posCity, next));
85-
ml::SimulatedAnnealingSolver ts(ts_system);
84+
TravelSalesman ts_system(posCity, next);
8685

87-
ts.setIterPerStep(10000*nbCity);
88-
ts.setCoolingRatio(0.99);
89-
ts.setInitialTemperature(100);
90-
ts.setFinalTemperature(100*0.97);
9186
DrawTravelMap(img,posCity,next);
9287
imshow("Map",img);
9388
waitKey(10);
89+
double currentTemperature = 100.0;
9490
for (int i = 0, zeroChanges = 0; zeroChanges < 10; i++)
9591
{
96-
int changesApplied = ts.run();
97-
img = Mat::zeros(img.size(),CV_8UC3);
92+
int changesApplied = ml::simulatedAnnealingSolver(ts_system, currentTemperature, currentTemperature*0.97, 0.99, 10000*nbCity, &currentTemperature, rng);
93+
img.setTo(Scalar::all(0));
9894
DrawTravelMap(img, posCity, next);
9995
imshow("Map", img);
10096
int k = waitKey(10);
101-
double ti=ts.getFinalTemperature();
102-
std::cout << "i=" << i << " changesApplied=" << changesApplied << " temp=" << ti << " result=" << ts_system->energy() << std::endl;
97+
std::cout << "i=" << i << " changesApplied=" << changesApplied << " temp=" << currentTemperature << " result=" << ts_system.energy() << std::endl;
10398
if (k == 27 || k == 'q' || k == 'Q')
10499
return 0;
105100
if (changesApplied == 0)
106101
zeroChanges++;
107-
ts.setInitialTemperature(ti);
108-
ts.setFinalTemperature(ti*0.97);
109102
}
110103
std::cout << "Done" << std::endl;
111104
waitKey(0);

0 commit comments

Comments
 (0)