Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
openfpm_pdata
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Sbalzarini Lab
Software
Parallel Computing
OpenFPM
openfpm_pdata
Commits
4fc64232
Commit
4fc64232
authored
7 years ago
by
Pietro Incardona
Browse files
Options
Downloads
Patches
Plain Diff
Fixing PS-CMA-ES and high dimension structures blow-up
parent
9f384106
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
example/Numerics/PS-CMA-ES/main.cpp
+136
-50
136 additions, 50 deletions
example/Numerics/PS-CMA-ES/main.cpp
with
136 additions
and
50 deletions
example/Numerics/PS-CMA-ES/main.cpp
+
136
−
50
View file @
4fc64232
...
...
@@ -27,7 +27,7 @@
#include
<boost/math/special_functions/sign.hpp>
constexpr
int
dim
=
3
;
constexpr
int
dim
=
10
;
// set this to 4+std::floor(3*log(dim))
constexpr
int
lambda
=
7
;
constexpr
int
mu
=
lambda
/
2
;
...
...
@@ -48,6 +48,8 @@ constexpr int validfit = 12;
constexpr
int
xold
=
13
;
constexpr
int
last_restart
=
14
;
constexpr
int
iniphase
=
15
;
constexpr
int
xmean_st
=
16
;
constexpr
int
meanz_st
=
17
;
const
double
c_m
=
1.0
;
...
...
@@ -64,8 +66,10 @@ double b = 0.1;
double
psoWeight
=
0.7
;
// number of cma-step before pso step
int
N_pso
=
200
;
double
stopTolX
=
1
e-1
2
;
double
stopTolX
=
2
e-1
1
;
double
stopTolUpX
=
2000.0
;
int
restart_cma
=
1
;
size_t
max_fun_eval
=
1000000
;
typedef
vector_dist
<
dim
,
double
,
aggregate
<
double
,
Eigen
::
VectorXd
[
lambda
],
...
...
@@ -82,7 +86,9 @@ typedef vector_dist<dim,double, aggregate<double,
double
,
Eigen
::
VectorXd
,
int
,
bool
>
>
particle_type
;
bool
,
Eigen
::
VectorXd
,
Eigen
::
VectorXd
>
>
particle_type
;
double
generateGaussianNoise
(
double
mu
,
double
sigma
)
...
...
@@ -154,7 +160,7 @@ double wm[mu];
void
init_weight
()
{
for
(
size_t
i
=
0
;
i
<
mu
;
i
++
)
{
wm
[
i
]
=
log
(
mu
+
1.0
)
-
log
(
i
+
1
);}
{
wm
[
i
]
=
log
(
double
(
mu
)
+
1.0
)
-
log
(
double
(
i
)
+
1.0
);}
double
tot
=
0.0
;
...
...
@@ -271,6 +277,7 @@ void create_rotmat(Eigen::VectorXd & S,Eigen::VectorXd & T, Eigen::MatrixXd & R)
void
updatePso
(
openfpm
::
vector
<
double
>
&
best_sol
,
double
sigma
,
Eigen
::
VectorXd
&
xmean
,
Eigen
::
VectorXd
&
xold
,
Eigen
::
MatrixXd
&
B
,
Eigen
::
DiagonalMatrix
<
double
,
Eigen
::
Dynamic
>
&
D
,
Eigen
::
MatrixXd
&
C_pso
)
...
...
@@ -308,7 +315,8 @@ void updatePso(openfpm::vector<double> & best_sol,
Eigen
::
MatrixXd
B_rot
(
dim
,
dim
);
Eigen
::
DiagonalMatrix
<
double
,
Eigen
::
Dynamic
>
D_square
(
dim
);
create_rotmat
(
b_main
,
gb_vec
,
R
);
Eigen
::
VectorXd
gb_vec_old
=
best_sol_ei
-
xold
;
create_rotmat
(
b_main
,
gb_vec_old
,
R
);
for
(
size_t
i
=
0
;
i
<
dim
;
i
++
)
{
B_rot
.
col
(
i
)
=
R
*
B
.
col
(
i
);}
...
...
@@ -322,7 +330,6 @@ void updatePso(openfpm::vector<double> & best_sol,
}
}
void
broadcast_best_solution
(
particle_type
&
vd
,
openfpm
::
vector
<
double
>
&
best_sol
,
double
&
best
,
...
...
@@ -546,7 +553,7 @@ void cmaes_handlebounds(openfpm::vector<fun_index> & f_obj,
{
if
(
idx_any
)
{
if
(
maxI
==
1
)
if
(
maxI
==
0
)
{
value
=
fithist
[
0
];}
else
{
...
...
@@ -597,7 +604,7 @@ void cmaes_handlebounds(openfpm::vector<fun_index> & f_obj,
double
arpenalty
[
lambda
];
for
(
size_t
i
=
0
;
i
<
lambda
;
i
++
)
{
arpenal
i
ty
[
i
]
=
0.0
;
arpenalty
[
i
]
=
0.0
;
for
(
size_t
j
=
0
;
j
<
dim
;
j
++
)
{
arpenalty
[
i
]
+=
weight
[
j
]
*
(
arxvalid
[
i
](
j
)
-
arx
[
i
](
j
))
*
(
arxvalid
[
i
](
j
)
-
arx
[
i
](
j
));
...
...
@@ -607,10 +614,25 @@ void cmaes_handlebounds(openfpm::vector<fun_index> & f_obj,
// fitness%sel = fitness%raw + bnd%arpenalty;
}
double
adjust_sigma
(
double
sigma
,
Eigen
::
MatrixXd
&
C
)
{
for
(
size_t
i
=
0
;
i
<
dim
;
i
++
)
{
if
(
sigma
*
sqrt
(
C
(
i
,
i
))
>
5.0
)
{
sigma
=
5.0
/
sqrt
(
C
(
i
,
i
));}
}
//if(any(vd.getProp<sigma>(p)*sqrt(diag) > maxdx)) sigma = minval(maxdx/sqrt(diag));
return
sigma
;
}
void
cma_step
(
particle_type
&
vd
,
int
step
,
double
&
best
,
int
&
best_i
,
openfpm
::
vector
<
double
>
&
best_sol
,
int
&
stop_cond
)
size_t
&
fun_eval
)
{
size_t
fe
=
0
;
Eigen
::
VectorXd
xmean
(
dim
);
Eigen
::
VectorXd
mean_z
(
dim
);
Eigen
::
VectorXd
arxvalid
[
lambda
];
...
...
@@ -637,6 +659,8 @@ void cma_step(particle_type & vd, int step, double & best,
if
(
vd
.
getProp
<
stop
>
(
p
)
==
true
)
{
++
it
;
continue
;}
Eigen
::
VectorXd
(
&
arz
)[
lambda
]
=
vd
.
getProp
<
Zeta
>
(
p
);
// fill the mean vector;
fill_vector
(
vd
.
getPos
(
p
),
xmean
);
...
...
@@ -666,11 +690,12 @@ void cma_step(particle_type & vd, int step, double & best,
f_obj
.
get
(
j
).
f
=
hybrid_composition
<
dim
>
(
arxvalid
[
j
]);
f_obj
.
get
(
j
).
id
=
j
;
fe
++
;
// Get the best ever
if
(
f_obj
.
get
(
0
).
f
<
best_sample
)
if
(
f_obj
.
get
(
j
).
f
<
best_sample
)
{
best_sample
=
f_obj
.
get
(
0
).
f
;
best_sample
=
f_obj
.
get
(
j
).
f
;
// Copy the new mean as position of the particle
for
(
size_t
i
=
0
;
i
<
dim
;
i
++
)
...
...
@@ -692,14 +717,9 @@ void cma_step(particle_type & vd, int step, double & best,
for
(
size_t
j
=
0
;
j
<
lambda
;
j
++
)
{
vd
.
getProp
<
ord
>
(
p
)[
j
]
=
f_obj
.
get
(
j
).
id
;}
// Calculate weighted mean
if
(
step
==
5
)
{
Eigen
::
VectorXd
(
&
debug4
)[
lambda
]
=
vd
.
getProp
<
Zeta
>
(
p
);
vd
.
getProp
<
xold
>
(
p
)
=
xmean
;
int
debug
=
0
;
}
// Calculate weighted mean
xmean
.
setZero
();
mean_z
.
setZero
();
...
...
@@ -709,7 +729,8 @@ void cma_step(particle_type & vd, int step, double & best,
mean_z
+=
weight_sample
(
j
)
*
vd
.
getProp
<
Zeta
>
(
p
)[
vd
.
getProp
<
ord
>
(
p
)[
j
]];
}
vd
.
getProp
<
xold
>
(
p
)
=
xmean
;
vd
.
getProp
<
xmean_st
>
(
p
)
=
xmean
;
vd
.
getProp
<
meanz_st
>
(
p
)
=
mean_z
;
++
it
;
}
...
...
@@ -717,9 +738,10 @@ void cma_step(particle_type & vd, int step, double & best,
// Find the best point across processors
broadcast_best_solution
(
vd
,
best_sol
,
best
,
best_sample
,
best_sample_sol
);
stop_cond
=
1
;
// Particle swarm update
// bool calculate B and D
bool
calc_bd
=
counteval
-
eigeneval
>
lambda
/
(
ccov
)
/
dim
/
10
;
if
(
calc_bd
==
true
)
{
eigeneval
=
counteval
;}
auto
it2
=
vd
.
getDomainIterator
();
while
(
it2
.
isNext
())
...
...
@@ -729,19 +751,19 @@ void cma_step(particle_type & vd, int step, double & best,
if
(
vd
.
getProp
<
stop
>
(
p
)
==
true
)
{
++
it2
;
continue
;}
// There are still particles to process
stop_cond
=
0
;
xmean
=
vd
.
getProp
<
xmean_st
>
(
p
);
mean_z
=
vd
.
getProp
<
meanz_st
>
(
p
)
;
vd
.
getProp
<
path_s
>
(
p
)
=
vd
.
getProp
<
path_s
>
(
p
)
*
(
1.0
-
cs
)
+
sqrt
(
cs
*
(
2.0
-
cs
)
*
mu_eff
)
*
vd
.
getProp
<
B
>
(
p
)
*
mean_z
;
double
hsig
=
vd
.
getProp
<
path_s
>
(
p
).
norm
()
/
(
1
-
pow
(
1
-
cs
,
2
*
counteval
/
lambda
))
/
dim
<
2.0
+
4
.0
/
(
dim
+
1
);
double
hsig
=
vd
.
getProp
<
path_s
>
(
p
).
norm
()
/
sqrt
(
1.0
-
pow
((
1.0
-
cs
),(
2.0
*
double
((
step
-
vd
.
getProp
<
last_restart
>
(
p
))))))
/
chiN
<
1.4
+
2
.0
/
(
dim
+
1
);
vd
.
getProp
<
path_c
>
(
p
)
=
(
1
-
cc
)
*
vd
.
getProp
<
path_c
>
(
p
)
+
hsig
*
sqrt
(
cc
*
(
2
-
cc
)
*
mu_eff
)
*
(
vd
.
getProp
<
B
>
(
p
)
*
vd
.
getProp
<
D
>
(
p
)
*
mean_z
);
if
(
step
%
N_pso
==
0
)
{
Eigen
::
MatrixXd
C_pso
(
dim
,
dim
);
updatePso
(
best_sol
,
vd
.
getProp
<
sigma
>
(
p
),
xmean
,
vd
.
getProp
<
B
>
(
p
),
vd
.
getProp
<
D
>
(
p
),
C_pso
);
updatePso
(
best_sol
,
vd
.
getProp
<
sigma
>
(
p
),
xmean
,
vd
.
getProp
<
xold
>
(
p
),
vd
.
getProp
<
B
>
(
p
),
vd
.
getProp
<
D
>
(
p
),
C_pso
);
// Adapt covariance matrix C
vd
.
getProp
<
Cov_m
>
(
p
)
=
(
1.0
-
ccov
+
(
1.0
-
hsig
)
*
ccov
*
cc
*
(
2.0
-
cc
)
/
mu_eff
)
*
vd
.
getProp
<
Cov_m
>
(
p
)
+
...
...
@@ -781,17 +803,16 @@ void cma_step(particle_type & vd, int step, double & best,
{
vd
.
getProp
<
sigma
>
(
p
)
=
smaller
;}
//Adapt step-size sigma
Eigen
::
VectorXd
&
debug_ps
=
vd
.
getProp
<
path_s
>
(
p
);
vd
.
getProp
<
sigma
>
(
p
)
=
vd
.
getProp
<
sigma
>
(
p
)
*
exp
((
cs
/
d_amps
)
*
(
vd
.
getProp
<
path_s
>
(
p
).
norm
()
/
chiN
-
1
));
auto
&
v_cl
=
create_vcluster
();
std
::
cout
<<
vd
.
getProp
<
sigma
>
(
p
)
<<
" "
<<
v_cl
.
rank
()
<<
std
::
endl
;
//
auto & v_cl = create_vcluster();
//
std::cout << vd.getProp<sigma>(p) << " " << v_cl.rank() << std::endl;
// Update B and D from C
if
(
c
ounteval
-
eigeneval
>
lambda
/
(
ccov
)
/
dim
/
10
)
if
(
c
alc_bd
)
{
eigeneval
=
counteval
;
Eigen
::
MatrixXd
trUp
=
vd
.
getProp
<
Cov_m
>
(
p
).
triangularView
<
Eigen
::
Upper
>
();
Eigen
::
MatrixXd
trDw
=
vd
.
getProp
<
Cov_m
>
(
p
).
triangularView
<
Eigen
::
StrictlyUpper
>
();
vd
.
getProp
<
Cov_m
>
(
p
)
=
trUp
+
trDw
.
transpose
();
...
...
@@ -814,6 +835,16 @@ void cma_step(particle_type & vd, int step, double & best,
Eigen
::
MatrixXd
tmp
=
vd
.
getProp
<
B
>
(
p
).
transpose
();
Eigen
::
MatrixXd
&
debug3
=
vd
.
getProp
<
Cov_m
>
(
p
);
double
debug_sigma
=
vd
.
getProp
<
sigma
>
(
p
);
if
(
step
%
161
==
0
)
{
//Adapt step-size sigma
Eigen
::
VectorXd
&
debug_ps
=
vd
.
getProp
<
path_s
>
(
p
);
Eigen
::
MatrixXd
&
debug4
=
vd
.
getProp
<
Cov_m
>
(
p
);
int
debug
=
0
;
}
int
debug
=
0
;
}
...
...
@@ -821,13 +852,6 @@ void cma_step(particle_type & vd, int step, double & best,
Eigen
::
MatrixXd
&
debug1
=
vd
.
getProp
<
B
>
(
p
);
Eigen
::
DiagonalMatrix
<
double
,
Eigen
::
Dynamic
>
&
debug2
=
vd
.
getProp
<
D
>
(
p
);
// Escape flat fitness, or better terminate?
if
(
f_obj
.
get
(
0
).
f
==
f_obj
.
get
(
std
::
ceil
(
0.7
*
lambda
)).
f
)
{
vd
.
getProp
<
sigma
>
(
p
)
=
vd
.
getProp
<
sigma
>
(
p
)
*
exp
(
0.2
+
cs
/
d_amps
);
std
::
cout
<<
"warning: flat fitness, consider reformulating the objective"
;
}
// Copy the new mean as position of the particle
for
(
size_t
i
=
0
;
i
<
dim
;
i
++
)
{
...
...
@@ -841,28 +865,84 @@ void cma_step(particle_type & vd, int step, double & best,
double
debug_sigma
=
vd
.
getProp
<
sigma
>
(
p
);
Eigen
::
DiagonalMatrix
<
double
,
Eigen
::
Dynamic
>
&
debug_d
=
vd
.
getProp
<
D
>
(
p
);
vd
.
getProp
<
sigma
>
(
p
)
=
adjust_sigma
(
vd
.
getProp
<
sigma
>
(
p
),
vd
.
getProp
<
Cov_m
>
(
p
));
// Stop conditions
bool
stop_tol
=
true
;
bool
stop_tolX
=
true
;
for
(
size_t
i
=
0
;
i
<
dim
;
i
++
)
{
stop_tol
&=
vd
.
getProp
<
sigma
>
(
p
)
*
std
::
max
(
fabs
(
vd
.
getProp
<
path_c
>
(
p
)(
i
)),
sqrt
(
vd
.
getProp
<
D
>
(
p
).
diagonal
()[
i
]))
<
stopTolX
;
double
debug1
=
std
::
max
(
fabs
(
vd
.
getProp
<
path_c
>
(
p
)(
i
)),
sqrt
(
vd
.
getProp
<
Cov_m
>
(
p
)(
i
,
i
)));
stop_tol
&=
(
vd
.
getProp
<
sigma
>
(
p
)
*
std
::
max
(
fabs
(
vd
.
getProp
<
path_c
>
(
p
)(
i
)),
sqrt
(
vd
.
getProp
<
Cov_m
>
(
p
)(
i
,
i
))))
<
stopTolX
;
stop_tolX
&=
vd
.
getProp
<
sigma
>
(
p
)
*
sqrt
(
vd
.
getProp
<
D
>
(
p
).
diagonal
()[
i
])
>
stopTolUpX
;
}
vd
.
getProp
<
stop
>
(
p
)
=
stop_tol
|
stop_tolX
;
// Escape flat fitness, or better terminate?
if
(
f_obj
.
get
(
0
).
f
==
f_obj
.
get
(
std
::
ceil
(
0.7
*
lambda
)).
f
)
{
vd
.
getProp
<
sigma
>
(
p
)
=
vd
.
getProp
<
sigma
>
(
p
)
*
exp
(
0.2
+
cs
/
d_amps
);
std
::
cout
<<
"warning: flat fitness, consider reformulating the objective"
;
// Stop it
vd
.
getProp
<
stop
>
(
p
)
=
true
;
}
if
(
vd
.
getProp
<
stop
>
(
p
)
==
true
)
{
std
::
cout
<<
"Stopped"
<<
std
::
endl
;
}
if
(
restart_cma
&&
vd
.
getProp
<
stop
>
(
p
)
==
true
)
{
std
::
cout
<<
"------- Restart #"
<<
std
::
endl
;
std
::
cout
<<
"---------------------------------"
<<
std
::
endl
;
std
::
cout
<<
"Best: "
<<
best
<<
" "
<<
fun_eval
<<
std
::
endl
;
std
::
cout
<<
"---------------------------------"
<<
std
::
endl
;
vd
.
getProp
<
last_restart
>
(
p
)
=
step
;
vd
.
getProp
<
xold
>
(
p
).
setZero
();
for
(
size_t
i
=
0
;
i
<
vd
.
getProp
<
D
>
(
p
).
diagonal
().
size
()
;
i
++
)
{
vd
.
getProp
<
D
>
(
p
).
diagonal
()[
i
]
=
1.0
;}
vd
.
getProp
<
B
>
(
p
).
resize
(
dim
,
dim
);
vd
.
getProp
<
B
>
(
p
).
setIdentity
();
vd
.
getProp
<
Cov_m
>
(
p
)
=
vd
.
getProp
<
B
>
(
p
)
*
vd
.
getProp
<
D
>
(
p
)
*
vd
.
getProp
<
D
>
(
p
)
*
vd
.
getProp
<
B
>
(
p
);
vd
.
getProp
<
path_s
>
(
p
).
resize
(
dim
);
vd
.
getProp
<
path_s
>
(
p
).
setZero
(
dim
);
vd
.
getProp
<
path_c
>
(
p
).
resize
(
dim
);
vd
.
getProp
<
path_c
>
(
p
).
setZero
(
dim
);
vd
.
getProp
<
stop
>
(
p
)
=
false
;
vd
.
getProp
<
iniphase
>
(
p
)
=
true
;
vd
.
getProp
<
last_restart
>
(
p
)
=
0
;
vd
.
getProp
<
sigma
>
(
p
)
=
2.0
;
// a different point in space
for
(
size_t
i
=
0
;
i
<
dim
;
i
++
)
{
// we define x, assign a random position between 0.0 and 1.0
vd
.
getPos
(
p
)[
i
]
=
10.0
*
(
double
)
rand
()
/
RAND_MAX
-
5.0
;
}
// Initialize the bound history
for
(
size_t
i
=
0
;
i
<
hist_size
;
i
++
)
{
vd
.
getProp
<
fithist
>
(
p
)[
i
]
=
-
1.0
;}
vd
.
getProp
<
fithist
>
(
p
)[
0
]
=
1.0
;
vd
.
getProp
<
validfit
>
(
p
)
=
0.0
;
}
++
it2
;
}
auto
&
v_cl
=
create_vcluster
();
v_cl
.
min
(
stop_cond
);
v_cl
.
sum
(
fe
);
v_cl
.
execute
();
fun_eval
+=
fe
;
}
...
...
@@ -893,7 +973,7 @@ int main(int argc, char* argv[])
// extended boundary around the domain, and the processor domain
Ghost
<
dim
,
double
>
g
(
0.0
);
particle_type
vd
(
v_cl
.
size
()
,
domain
,
bc
,
g
);
particle_type
vd
(
16
,
domain
,
bc
,
g
);
// Initialize constants
...
...
@@ -905,7 +985,7 @@ int main(int argc, char* argv[])
// Strategy parameter setting: Adaptation
cc
=
4.0
/
(
dim
+
4.0
);
cs
=
(
mu_eff
+
2.0
)
/
(
dim
+
mu_eff
+
3.0
);
cs
=
(
mu_eff
+
2.0
)
/
(
double
(
dim
)
+
mu_eff
+
3.0
);
ccov
=
(
1.0
/
mu_eff
)
*
2.0
/
((
dim
+
1.41
)
*
(
dim
+
1.41
))
+
(
1.0
-
1.0
/
mu_eff
)
*
std
::
min
(
1.0
,(
2.0
*
mu_eff
-
1.0
)
/
((
dim
+
2.0
)
*
(
dim
+
2.0
)
+
mu_eff
));
d_amps
=
1
+
2
*
std
::
max
(
0.0
,
sqrt
((
mu_eff
-
1.0
)
/
(
dim
+
1
))
-
1
)
+
cs
;
...
...
@@ -916,13 +996,10 @@ int main(int argc, char* argv[])
// initialize the srand
int
seed
=
24756
*
v_cl
.
rank
()
*
v_cl
.
rank
();
int
seed
=
24756
*
v_cl
.
rank
()
*
v_cl
.
rank
()
+
time
(
NULL
)
;
srand
(
seed
);
for
(
size_t
k
=
0
;
k
<
100
;
k
++
)
{
auto
it
=
vd
.
getDomainIterator
();
auto
it
=
vd
.
getDomainIterator
();
while
(
it
.
isNext
())
{
...
...
@@ -971,16 +1048,25 @@ int main(int argc, char* argv[])
// now do several iteration
int
stop_cond
=
0
;
size_t
fun_eval
=
0
;
int
i
=
0
;
while
(
stop_cond
==
0
)
while
(
fun_eval
<
max_fun_eval
&&
best
>
120.000001
)
{
// sample offspring
cma_step
(
vd
,
i
+
1
,
best
,
best_i
,
best_sol
,
stop_cond
);
cma_step
(
vd
,
i
+
1
,
best
,
best_i
,
best_sol
,
fun_eval
);
i
++
;
}
std
::
cout
<<
"Best solution: "
<<
best
<<
std
::
endl
;
if
(
v_cl
.
rank
()
==
0
)
{
std
::
cout
<<
"Best solution: "
<<
best
<<
" with "
<<
fun_eval
<<
std
::
endl
;
std
::
cout
<<
"at: "
<<
std
::
endl
;
for
(
size_t
i
=
0
;
i
<
best_sol
.
size
()
;
i
++
)
{
std
::
cout
<<
best_sol
.
get
(
i
)
<<
" "
;
}
}
openfpm_finalize
();
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment