1 /* -*- mode: C -*- */
2 /*
3 IGraph library.
4 Copyright (C) 2006-2012 Gabor Csardi <csardi.gabor@gmail.com>
5 334 Harvard street, Cambridge, MA 02139 USA
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA
21
22 */
23
24 /* The original version of this file was written by Joerg Reichardt
25 The original copyright notice follows here */
26
27 /***************************************************************************
28 main.cpp - description
29 -------------------
30 begin : Tue Jul 13 11:26:47 CEST 2004
31 copyright : (C) 2004 by
32 email :
33 ***************************************************************************/
34
35 /***************************************************************************
36 * *
37 * This program is free software; you can redistribute it and/or modify *
38 * it under the terms of the GNU General Public License as published by *
39 * the Free Software Foundation; either version 2 of the License, or *
40 * (at your option) any later version. *
41 * *
42 ***************************************************************************/
43
44 #ifdef HAVE_CONFIG_H
45 #include <config.h>
46 #endif
47
48 #include "NetDataTypes.h"
49 #include "NetRoutines.h"
50 #include "pottsmodel_2.h"
51
52 #include "igraph_community.h"
53 #include "igraph_error.h"
54 #include "igraph_random.h"
55 #include "igraph_math.h"
56 #include "igraph_interface.h"
57 #include "igraph_components.h"
58 #include "igraph_interrupt_internal.h"
59 #include "igraph_handle_exceptions.h"
60
61 static int igraph_i_community_spinglass_orig(
62 const igraph_t *graph,
63 const igraph_vector_t *weights,
64 igraph_real_t *modularity,
65 igraph_real_t *temperature,
66 igraph_vector_t *membership,
67 igraph_vector_t *csize,
68 igraph_integer_t spins,
69 igraph_bool_t parupdate,
70 igraph_real_t starttemp,
71 igraph_real_t stoptemp,
72 igraph_real_t coolfact,
73 igraph_spincomm_update_t update_rule,
74 igraph_real_t gamma);
75
76 static int igraph_i_community_spinglass_negative(
77 const igraph_t *graph,
78 const igraph_vector_t *weights,
79 igraph_real_t *modularity,
80 igraph_real_t *temperature,
81 igraph_vector_t *membership,
82 igraph_vector_t *csize,
83 igraph_integer_t spins,
84 igraph_bool_t parupdate,
85 igraph_real_t starttemp,
86 igraph_real_t stoptemp,
87 igraph_real_t coolfact,
88 igraph_spincomm_update_t update_rule,
89 igraph_real_t gamma,
90 /* igraph_matrix_t *adhesion, */
91 /* igraph_matrix_t *normalised_adhesion, */
92 /* igraph_real_t *polarization, */
93 igraph_real_t gamma_minus);
94
95 /**
96 * \function igraph_community_spinglass
97 * \brief Community detection based on statistical mechanics
98 *
99 * This function implements the community structure detection
100 * algorithm proposed by Joerg Reichardt and Stefan Bornholdt.
101 * The algorithm is described in their paper: Statistical Mechanics of
102 * Community Detection, http://arxiv.org/abs/cond-mat/0603718 .
103 *
104 * </para><para> From version 0.6 igraph also supports an extension to
105 * the algorithm that allows negative edge weights. This is described
106 * in V.A. Traag and Jeroen Bruggeman: Community detection in networks
107 * with positive and negative links, http://arxiv.org/abs/0811.2329 .
108 * \param graph The input graph, it may be directed but the direction
109 * of the edge is not used in the algorithm.
110 * \param weights The vector giving the edge weights, it may be \c NULL,
111 * in which case all edges are weighted equally. Edge weights
112 * should be positive, altough this is not tested.
113 * \param modularity Pointer to a real number, if not \c NULL then the
114 * modularity score of the solution will be stored here. This is the
115 * gereralized modularity that simplifies to the one defined in
116 * M. E. J. Newman and M. Girvan, Phys. Rev. E 69, 026113 (2004),
117 * if the gamma parameter is one.
118 * \param temperature Pointer to a real number, if not \c NULL then
119 * the temperature at the end of the algorithm will be stored
120 * here.
121 * \param membership Pointer to an initialized vector or \c NULL. If
122 * not \c NULL then the result of the clustering will be stored
123 * here, for each vertex the number of its cluster is given, the
124 * first cluster is numbered zero. The vector will be resized as
125 * needed.
126 * \param csize Pointer to an initialized vector or \c NULL. If not \c
127 * NULL then the sizes of the clusters will stored here in cluster
128 * number order. The vector will be resized as needed.
129 * \param spins Integer giving the number of spins, ie. the maximum
130 * number of clusters. Usually it is not a program to give a high
131 * number here, the default was 25 in the original code. Even if
132 * the number of spins is high the number of clusters in the
133 * result might small.
134 * \param parupdate A logical constant, whether to update all spins in
135 * parallel. The default for this argument was \c FALSE (ie. 0) in
136 * the original code. It is not implemented in the \c
137 * IGRAPH_SPINCOMM_INP_NEG implementation.
138 * \param starttemp Real number, the temperature at the start. The
139 * value of this argument was 1.0 in the original code.
140 * \param stoptemp Real number, the algorithm stops at this
141 * temperature. The default was 0.01 in the original code.
142 * \param coolfact Real number, the coolinf factor for the simulated
143 * annealing. The default was 0.99 in the original code.
144 * \param update_rule The type of the update rule. Possible values: \c
145 * IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c
146 * IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined
147 * the null model based on which the actual clustering is done. If
148 * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph
149 * (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the
150 * configuration model is used. The configuration means that the
151 * baseline for the clustering is a random graph with the same
152 * degree distribution as the input graph.
153 * \param gamma Real number. The gamma parameter of the
154 * algorithm. This defined the weight of the missing and existing
155 * links in the quality function for the clustering. The default
156 * value in the original code was 1.0, which is equal weight to
157 * missing and existing edges. Smaller values make the existing
158 * links contibute more to the energy function which is minimized
159 * in the algorithm. Bigger values make the missing links more
160 * important. (If my understanding is correct.)
161 * \param implementation Constant, chooses between the two
162 * implementations of the spin-glass algorithm that are included
163 * in igraph. \c IGRAPH_SPINCOMM_IMP_ORIG selects the original
164 * implementation, this is faster, \c IGRAPH_SPINCOMM_INP_NEG selects
165 * a new implementation by Vincent Traag that allows negative edge
166 * weights.
167 * \param gamma_minus Real number. Parameter for the \c
168 * IGRAPH_SPINCOMM_IMP_NEG implementation. This
169 * specifies the balance between the importance of present and
170 * non-present negative weighted edges in a community. Smaller values of
171 * \p gamma_minus lead to communities with lesser
172 * negative intra-connectivity.
173 * If this argument is set to zero, the algorithm reduces to a graph
174 * coloring algorithm, using the number of spins as the number of
175 * colors.
176 * \return Error code.
177 *
178 * \sa igraph_community_spinglass_single() for calculating the community
179 * of a single vertex.
180 *
181 * Time complexity: TODO.
182 *
183 * \example examples/simple/spinglass.c
184 */
185
igraph_community_spinglass(const igraph_t * graph,const igraph_vector_t * weights,igraph_real_t * modularity,igraph_real_t * temperature,igraph_vector_t * membership,igraph_vector_t * csize,igraph_integer_t spins,igraph_bool_t parupdate,igraph_real_t starttemp,igraph_real_t stoptemp,igraph_real_t coolfact,igraph_spincomm_update_t update_rule,igraph_real_t gamma,igraph_spinglass_implementation_t implementation,igraph_real_t gamma_minus)186 int igraph_community_spinglass(const igraph_t *graph,
187 const igraph_vector_t *weights,
188 igraph_real_t *modularity,
189 igraph_real_t *temperature,
190 igraph_vector_t *membership,
191 igraph_vector_t *csize,
192 igraph_integer_t spins,
193 igraph_bool_t parupdate,
194 igraph_real_t starttemp,
195 igraph_real_t stoptemp,
196 igraph_real_t coolfact,
197 igraph_spincomm_update_t update_rule,
198 igraph_real_t gamma,
199 /* the rest is for the NegSpin implementation */
200 igraph_spinglass_implementation_t implementation,
201 /* igraph_matrix_t *adhesion, */
202 /* igraph_matrix_t *normalised_adhesion, */
203 /* igraph_real_t *polarization, */
204 igraph_real_t gamma_minus) {
205
206 IGRAPH_HANDLE_EXCEPTIONS(
207 switch (implementation) {
208 case IGRAPH_SPINCOMM_IMP_ORIG:
209 return igraph_i_community_spinglass_orig(graph, weights, modularity,
210 temperature, membership, csize,
211 spins, parupdate, starttemp,
212 stoptemp, coolfact, update_rule,
213 gamma);
214 break;
215 case IGRAPH_SPINCOMM_IMP_NEG:
216 return igraph_i_community_spinglass_negative(graph, weights, modularity,
217 temperature, membership, csize,
218 spins, parupdate, starttemp,
219 stoptemp, coolfact,
220 update_rule, gamma,
221 /* adhesion, normalised_adhesion, */
222 /* polarization, */
223 gamma_minus);
224 break;
225 default:
226 IGRAPH_ERROR("Unknown `implementation' in spinglass community finding",
227 IGRAPH_EINVAL);
228 }
229 );
230 return 0;
231 }
232
igraph_i_community_spinglass_orig(const igraph_t * graph,const igraph_vector_t * weights,igraph_real_t * modularity,igraph_real_t * temperature,igraph_vector_t * membership,igraph_vector_t * csize,igraph_integer_t spins,igraph_bool_t parupdate,igraph_real_t starttemp,igraph_real_t stoptemp,igraph_real_t coolfact,igraph_spincomm_update_t update_rule,igraph_real_t gamma)233 static int igraph_i_community_spinglass_orig(
234 const igraph_t *graph,
235 const igraph_vector_t *weights,
236 igraph_real_t *modularity,
237 igraph_real_t *temperature,
238 igraph_vector_t *membership,
239 igraph_vector_t *csize,
240 igraph_integer_t spins,
241 igraph_bool_t parupdate,
242 igraph_real_t starttemp,
243 igraph_real_t stoptemp,
244 igraph_real_t coolfact,
245 igraph_spincomm_update_t update_rule,
246 igraph_real_t gamma) {
247
248 unsigned long changes, runs;
249 igraph_bool_t use_weights = 0;
250 bool zeroT;
251 double kT, acc, prob;
252 ClusterList<NNode*> *cl_cur;
253 network *net;
254 PottsModel *pm;
255
256 /* Check arguments */
257
258 if (spins < 2 || spins > 500) {
259 IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL);
260 }
261 if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE &&
262 update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) {
263 IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL);
264 }
265 if (weights) {
266 if (igraph_vector_size(weights) != igraph_ecount(graph)) {
267 IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
268 }
269 use_weights = 1;
270 }
271 if (coolfact < 0 || coolfact >= 1.0) {
272 IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL);
273 }
274 if (gamma < 0.0) {
275 IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL);
276 }
277 if (starttemp / stoptemp < 1.0) {
278 IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp",
279 IGRAPH_EINVAL);
280 }
281
282 /* Check whether we have a single component */
283 igraph_bool_t conn;
284 IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
285 if (!conn) {
286 IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL);
287 }
288
289 net = new network;
290 net->node_list = new DL_Indexed_List<NNode*>();
291 net->link_list = new DL_Indexed_List<NLink*>();
292 net->cluster_list = new DL_Indexed_List<ClusterList<NNode*>*>();
293
294 /* Transform the igraph_t */
295 IGRAPH_CHECK(igraph_i_read_network(graph, weights,
296 net, use_weights, 0));
297
298 prob = 2.0 * net->sum_weights / double(net->node_list->Size())
299 / double(net->node_list->Size() - 1);
300
301 pm = new PottsModel(net, (unsigned int)spins, update_rule);
302
303 /* initialize the random number generator */
304 RNG_BEGIN();
305
306 if ((stoptemp == 0.0) && (starttemp == 0.0)) {
307 zeroT = true;
308 } else {
309 zeroT = false;
310 }
311 if (!zeroT) {
312 kT = pm->FindStartTemp(gamma, prob, starttemp);
313 } else {
314 kT = stoptemp;
315 }
316 /* assign random initial configuration */
317 pm->assign_initial_conf(-1);
318 runs = 0;
319 changes = 1;
320
321 while (changes > 0 && (kT / stoptemp > 1.0 || (zeroT && runs < 150))) {
322
323 IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
324
325 runs++;
326 if (!zeroT) {
327 kT *= coolfact;
328 if (parupdate) {
329 changes = pm->HeatBathParallelLookup(gamma, prob, kT, 50);
330 } else {
331 acc = pm->HeatBathLookup(gamma, prob, kT, 50);
332 if (acc < (1.0 - 1.0 / double(spins)) * 0.01) {
333 changes = 0;
334 } else {
335 changes = 1;
336 }
337 }
338 } else {
339 if (parupdate) {
340 changes = pm->HeatBathParallelLookupZeroTemp(gamma, prob, 50);
341 } else {
342 acc = pm->HeatBathLookupZeroTemp(gamma, prob, 50);
343 /* less than 1 percent acceptance ratio */
344 if (acc < (1.0 - 1.0 / double(spins)) * 0.01) {
345 changes = 0;
346 } else {
347 changes = 1;
348 }
349 }
350 }
351 } /* while loop */
352
353 pm->WriteClusters(modularity, temperature, csize, membership, kT, gamma);
354
355 while (net->link_list->Size()) {
356 delete net->link_list->Pop();
357 }
358 while (net->node_list->Size()) {
359 delete net->node_list->Pop();
360 }
361 while (net->cluster_list->Size()) {
362 cl_cur = net->cluster_list->Pop();
363 while (cl_cur->Size()) {
364 cl_cur->Pop();
365 }
366 delete cl_cur;
367 }
368 delete net->link_list;
369 delete net->node_list;
370 delete net->cluster_list;
371
372 RNG_END();
373
374 delete net;
375 delete pm;
376
377 return 0;
378 }
379
380 /**
381 * \function igraph_community_spinglass_single
382 * \brief Community of a single node based on statistical mechanics
383 *
384 * This function implements the community structure detection
385 * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. It is
386 * described in their paper: Statistical Mechanics of
387 * Community Detection, http://arxiv.org/abs/cond-mat/0603718 .
388 *
389 * </para><para>
390 * This function calculates the community of a single vertex without
391 * calculating all the communities in the graph.
392 *
393 * \param graph The input graph, it may be directed but the direction
394 * of the edges is not used in the algorithm.
395 * \param weights Pointer to a vector with the weights of the edges.
396 * Alternatively \c NULL can be supplied to have the same weight
397 * for every edge.
398 * \param vertex The vertex id of the vertex of which ths community is
399 * calculated.
400 * \param community Pointer to an initialized vector, the result, the
401 * ids of the vertices in the community of the input vertex will be
402 * stored here. The vector will be resized as needed.
403 * \param cohesion Pointer to a real variable, if not \c NULL the
404 * cohesion index of the community will be stored here.
405 * \param adhesion Pointer to a real variable, if not \c NULL the
406 * adhesion index of the community will be stored here.
407 * \param inner_links Pointer to an integer, if not \c NULL the
408 * number of edges within the community is stored here.
409 * \param outer_links Pointer to an integer, if not \c NULL the
410 * number of edges between the community and the rest of the graph
411 * will be stored here.
412 * \param spins The number of spins to use, this can be higher than
413 * the actual number of clusters in the network, in which case some
414 * clusters will contain zero vertices.
415 * \param update_rule The type of the update rule. Possible values: \c
416 * IGRAPH_SPINCOMM_UPDATE_SIMPLE and \c
417 * IGRAPH_SPINCOMM_UPDATE_CONFIG. Basically this parameter defined
418 * the null model based on which the actual clustering is done. If
419 * this is \c IGRAPH_SPINCOMM_UPDATE_SIMPLE then the random graph
420 * (ie. G(n,p)), if it is \c IGRAPH_SPINCOMM_UPDATE then the
421 * configuration model is used. The configuration means that the
422 * baseline for the clustering is a random graph with the same
423 * degree distribution as the input graph.
424 * \param gamma Real number. The gamma parameter of the
425 * algorithm. This defined the weight of the missing and existing
426 * links in the quality function for the clustering. The default
427 * value in the original code was 1.0, which is equal weight to
428 * missing and existing edges. Smaller values make the existing
429 * links contibute more to the energy function which is minimized
430 * in the algorithm. Bigger values make the missing links more
431 * important. (If my understanding is correct.)
432 * \return Error code.
433 *
434 * \sa igraph_community_spinglass() for the traditional version of the
435 * algorithm.
436 *
437 * Time complexity: TODO.
438 */
439
igraph_community_spinglass_single(const igraph_t * graph,const igraph_vector_t * weights,igraph_integer_t vertex,igraph_vector_t * community,igraph_real_t * cohesion,igraph_real_t * adhesion,igraph_integer_t * inner_links,igraph_integer_t * outer_links,igraph_integer_t spins,igraph_spincomm_update_t update_rule,igraph_real_t gamma)440 int igraph_community_spinglass_single(const igraph_t *graph,
441 const igraph_vector_t *weights,
442 igraph_integer_t vertex,
443 igraph_vector_t *community,
444 igraph_real_t *cohesion,
445 igraph_real_t *adhesion,
446 igraph_integer_t *inner_links,
447 igraph_integer_t *outer_links,
448 igraph_integer_t spins,
449 igraph_spincomm_update_t update_rule,
450 igraph_real_t gamma) {
451 IGRAPH_HANDLE_EXCEPTIONS(
452 igraph_bool_t use_weights = 0;
453 double prob;
454 ClusterList<NNode*> *cl_cur;
455 network *net;
456 PottsModel *pm;
457 char startnode[255];
458
459 /* Check arguments */
460
461 if (spins < 2 || spins > 500) {
462 IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL);
463 }
464 if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE &&
465 update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) {
466 IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL);
467 }
468 if (weights) {
469 if (igraph_vector_size(weights) != igraph_ecount(graph)) {
470 IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
471 }
472 use_weights = 1;
473 }
474 if (gamma < 0.0) {
475 IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL);
476 }
477 if (vertex < 0 || vertex > igraph_vcount(graph)) {
478 IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL);
479 }
480
481 /* Check whether we have a single component */
482 igraph_bool_t conn;
483 IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
484 if (!conn) {
485 IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL);
486 }
487
488 net = new network;
489 net->node_list = new DL_Indexed_List<NNode*>();
490 net->link_list = new DL_Indexed_List<NLink*>();
491 net->cluster_list = new DL_Indexed_List<ClusterList<NNode*>*>();
492
493 /* Transform the igraph_t */
494 IGRAPH_CHECK(igraph_i_read_network(graph, weights,
495 net, use_weights, 0));
496
497 prob = 2.0 * net->sum_weights / double(net->node_list->Size())
498 / double(net->node_list->Size() - 1);
499
500 pm = new PottsModel(net, (unsigned int)spins, update_rule);
501
502 /* initialize the random number generator */
503 RNG_BEGIN();
504
505 /* to be exected, if we want to find the community around a particular node*/
506 /* the initial conf is needed, because otherwise,
507 the degree of the nodes is not in the weight property, stupid!!! */
508 pm->assign_initial_conf(-1);
509 snprintf(startnode, 255, "%li", (long int)vertex + 1);
510 pm->FindCommunityFromStart(gamma, prob, startnode, community,
511 cohesion, adhesion, inner_links, outer_links);
512
513 while (net->link_list->Size()) {
514 delete net->link_list->Pop();
515 }
516 while (net->node_list->Size()) {
517 delete net->node_list->Pop();
518 }
519 while (net->cluster_list->Size()) {
520 cl_cur = net->cluster_list->Pop();
521 while (cl_cur->Size()) {
522 cl_cur->Pop();
523 }
524 delete cl_cur;
525 }
526 delete net->link_list;
527 delete net->node_list;
528 delete net->cluster_list;
529
530 RNG_END();
531
532 delete net;
533 delete pm;
534 );
535
536 return 0;
537 }
538
igraph_i_community_spinglass_negative(const igraph_t * graph,const igraph_vector_t * weights,igraph_real_t * modularity,igraph_real_t * temperature,igraph_vector_t * membership,igraph_vector_t * csize,igraph_integer_t spins,igraph_bool_t parupdate,igraph_real_t starttemp,igraph_real_t stoptemp,igraph_real_t coolfact,igraph_spincomm_update_t update_rule,igraph_real_t gamma,igraph_real_t gamma_minus)539 static int igraph_i_community_spinglass_negative(
540 const igraph_t *graph,
541 const igraph_vector_t *weights,
542 igraph_real_t *modularity,
543 igraph_real_t *temperature,
544 igraph_vector_t *membership,
545 igraph_vector_t *csize,
546 igraph_integer_t spins,
547 igraph_bool_t parupdate,
548 igraph_real_t starttemp,
549 igraph_real_t stoptemp,
550 igraph_real_t coolfact,
551 igraph_spincomm_update_t update_rule,
552 igraph_real_t gamma,
553 /* igraph_matrix_t *adhesion, */
554 /* igraph_matrix_t *normalised_adhesion, */
555 /* igraph_real_t *polarization, */
556 igraph_real_t gamma_minus) {
557
558 unsigned long changes, runs;
559 igraph_bool_t use_weights = 0;
560 bool zeroT;
561 double kT, acc;
562 ClusterList<NNode*> *cl_cur;
563 network *net;
564 PottsModelN *pm;
565 igraph_real_t d_n;
566 igraph_real_t d_p;
567
568 /* Check arguments */
569
570 if (parupdate) {
571 IGRAPH_ERROR("Parallel spin update not implemented with "
572 "negative gamma", IGRAPH_UNIMPLEMENTED);
573 }
574
575 if (spins < 2 || spins > 500) {
576 IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL);
577 }
578 if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE &&
579 update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) {
580 IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL);
581 }
582 if (weights) {
583 if (igraph_vector_size(weights) != igraph_ecount(graph)) {
584 IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
585 }
586 use_weights = 1;
587 }
588 if (coolfact < 0 || coolfact >= 1.0) {
589 IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL);
590 }
591 if (gamma < 0.0) {
592 IGRAPH_ERROR("Invalid gamma value", IGRAPH_EINVAL);
593 }
594 if (starttemp / stoptemp < 1.0) {
595 IGRAPH_ERROR("starttemp should be larger in absolute value than stoptemp",
596 IGRAPH_EINVAL);
597 }
598
599 /* Check whether we have a single component */
600 igraph_bool_t conn;
601 IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
602 if (!conn) {
603 IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL);
604 }
605
606 if (weights) {
607 igraph_vector_minmax(weights, &d_n, &d_p);
608 } else {
609 d_n = d_p = 1;
610 }
611
612 if (d_n > 0) {
613 d_n = 0;
614 }
615 if (d_p < 0) {
616 d_p = 0;
617 }
618 d_n = -d_n;
619
620 net = new network;
621 net->node_list = new DL_Indexed_List<NNode*>();
622 net->link_list = new DL_Indexed_List<NLink*>();
623 net->cluster_list = new DL_Indexed_List<ClusterList<NNode*>*>();
624
625 /* Transform the igraph_t */
626 IGRAPH_CHECK(igraph_i_read_network(graph, weights,
627 net, use_weights, 0));
628
629 bool directed = igraph_is_directed(graph);
630
631 pm = new PottsModelN(net, (unsigned int)spins, directed);
632
633 /* initialize the random number generator */
634 RNG_BEGIN();
635
636 if ((stoptemp == 0.0) && (starttemp == 0.0)) {
637 zeroT = true;
638 } else {
639 zeroT = false;
640 }
641
642 //Begin at a high enough temperature
643 kT = pm->FindStartTemp(gamma, gamma_minus, starttemp);
644
645 /* assign random initial configuration */
646 pm->assign_initial_conf(true);
647
648 runs = 0;
649 changes = 1;
650 acc = 0;
651 while (changes > 0 && (kT / stoptemp > 1.0 || (zeroT && runs < 150))) {
652
653 IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */
654
655 runs++;
656 kT = kT * coolfact;
657 acc = pm->HeatBathLookup(gamma, gamma_minus, kT, 50);
658 if (acc < (1.0 - 1.0 / double(spins)) * 0.001) {
659 changes = 0;
660 } else {
661 changes = 1;
662 }
663
664 } /* while loop */
665
666 /* These are needed, otherwise 'modularity' is not calculated */
667 igraph_matrix_t adhesion, normalized_adhesion;
668 igraph_real_t polarization;
669 IGRAPH_MATRIX_INIT_FINALLY(&adhesion, 0, 0);
670 IGRAPH_MATRIX_INIT_FINALLY(&normalized_adhesion, 0, 0);
671 pm->WriteClusters(modularity, temperature, csize, membership,
672 &adhesion, &normalized_adhesion, &polarization,
673 kT, d_p, d_n, gamma, gamma_minus);
674 igraph_matrix_destroy(&normalized_adhesion);
675 igraph_matrix_destroy(&adhesion);
676 IGRAPH_FINALLY_CLEAN(2);
677
678 while (net->link_list->Size()) {
679 delete net->link_list->Pop();
680 }
681 while (net->node_list->Size()) {
682 delete net->node_list->Pop();
683 }
684 while (net->cluster_list->Size()) {
685 cl_cur = net->cluster_list->Pop();
686 while (cl_cur->Size()) {
687 cl_cur->Pop();
688 }
689 delete cl_cur;
690 }
691
692 RNG_END();
693
694 return 0;
695 }
696