Completed
Push — master ( 907a73...cb4252 )
by Andrei
56s
created

hsyncnet.__init__()   B

Complexity

Conditions 3

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
"""!
2
3
@brief Cluster analysis algorithm: Hierarchical Sync (HSyncNet)
4
@details Based on article description:
5
         - J.Shao, X.He, C.Bohm, Q.Yang, C.Plant. Synchronization-Inspired Partitioning and Hierarchical Clustering. 2013.
6
7
@authors Andrei Novikov ([email protected])
8
@date 2014-2017
9
@copyright GNU Public License
10
11
@cond GNU_PUBLIC_LICENSE
12
    PyClustering is free software: you can redistribute it and/or modify
13
    it under the terms of the GNU General Public License as published by
14
    the Free Software Foundation, either version 3 of the License, or
15
    (at your option) any later version.
16
    
17
    PyClustering is distributed in the hope that it will be useful,
18
    but WITHOUT ANY WARRANTY; without even the implied warranty of
19
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
    GNU General Public License for more details.
21
    
22
    You should have received a copy of the GNU General Public License
23
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
@endcond
25
26
"""
27
28
29
import pyclustering.core.wrapper as wrapper;
30
31
from pyclustering.nnet import initial_type, solve_type;
32
33
from pyclustering.cluster.syncnet import syncnet, syncnet_analyser;
34
35
from pyclustering.utils import average_neighbor_distance;
36
37
38
class hsyncnet(syncnet):
39
    """!
40
    @brief Class represents clustering algorithm HSyncNet. HSyncNet is bio-inspired algorithm that is based on oscillatory network that uses modified Kuramoto model.
41
    
42
    Example:
43
    @code
44
        # read list of points for cluster analysis
45
        sample = read_sample(file);
46
        
47
        # create network for allocation three clusters using CCORE (C++ implementation)
48
        network = hsyncnet(sample, 3, ccore = True);
49
        
50
        # run cluster analysis and output dynamic of the network
51
        (time, dynamic) = network.process(0.995, collect_dynamic = True);
52
        
53
        # get allocated clusters
54
        clusters = network.get_clusters();
55
        
56
        # show output dynamic of the network
57
        draw_dynamics(time, dynamic);
58
    @endcode
59
    """
60
    
61
    def __init__(self, source_data, number_clusters, osc_initial_phases = initial_type.RANDOM_GAUSSIAN, initial_neighbors = 3, increase_persent = 0.15, ccore = False):
62
        """!
63
        @brief Costructor of the oscillatory network hSyncNet for cluster analysis.
64
65
        @param[in] source_data (list): Input data set defines structure of the network.
66
        @param[in] number_clusters (uint): Number of clusters that should be allocated.
67
        @param[in] osc_initial_phases (initial_type): Type of initialization of initial values of phases of oscillators.
68
        @param[in] initial_neighbors (uint): Defines initial radius connectivity by calculation average distance to connect specify number of oscillators.
69
        @param[in] increase_persent (double): Percent of increasing of radius connectivity on each step (input values in range (0.0; 1.0) correspond to (0%; 100%)).
70
        @param[in] ccore (bool): If True than DLL CCORE (C++ solution) will be used for solving.
71
        
72
        """
73
        
74
        self.__ccore_network_pointer = None;
75
        
76
        if (initial_neighbors >= len(source_data)):
77
            initial_neighbors = len(source_data) - 1;
78
        
79
        if (ccore is True):
80
            self.__ccore_network_pointer = wrapper.hsyncnet_create_network(source_data, number_clusters, osc_initial_phases, initial_neighbors, increase_persent);
81
        else: 
82
            super().__init__(source_data, 0, initial_phases = osc_initial_phases);
83
            
84
            self.__initial_neighbors = initial_neighbors;
85
            self.__increase_persent = increase_persent;
86
            self._number_clusters = number_clusters;
87
    
88
    
89
    def __del__(self):
90
        """!
91
        @brief Destructor of oscillatory network HSyncNet.
92
        
93
        """
94
        
95
        if (self.__ccore_network_pointer is not None):
96
            wrapper.hsyncnet_destroy_network(self.__ccore_network_pointer);
97
            self.__ccore_network_pointer = None;
98
            
99
            
100
    def process(self, order = 0.998, solution = solve_type.FAST, collect_dynamic = False):
101
        """!
102
        @brief Performs clustering of input data set in line with input parameters.
103
        
104
        @param[in] order (double): Level of local synchronization between oscillator that defines end of synchronization process, range [0..1].
105
        @param[in] solution (solve_type) Type of solving differential equation.
106
        @param[in] collect_dynamic (bool): If True - returns whole history of process synchronization otherwise - only final state (when process of clustering is over).
107
        
108
        @return (tuple) Returns dynamic of the network as tuple of lists on each iteration (time, oscillator_phases) that depends on collect_dynamic parameter. 
109
        
110
        @see get_clusters()
111
        
112
        """
113
        
114
        if (self.__ccore_network_pointer is not None):
115
            analyser = wrapper.hsyncnet_process(self.__ccore_network_pointer, order, solution, collect_dynamic);
116
            return syncnet_analyser(None, None, analyser);
117
        
118
        number_neighbors = self.__initial_neighbors;
119
        current_number_clusters = float('inf');
120
        
121
        dyn_phase = [];
122
        dyn_time = [];
123
        
124
        radius = average_neighbor_distance(self._osc_loc, number_neighbors);
125
        
126
        increase_step = int(len(self._osc_loc) * self.__increase_persent);
127
        if (increase_step < 1):
128
            increase_step = 1;
129
        
130
        
131
        analyser = None;
132
        while(current_number_clusters > self._number_clusters):
133
            self._create_connections(radius);
134
        
135
            analyser = self.simulate_dynamic(order, solution, collect_dynamic);
136
            if (collect_dynamic == True):
137
                if (len(dyn_phase) == 0):
138
                    dyn_time.append(0);
139
                    dyn_phase.append(analyser.output[0]);
140
                
141
                dyn_phase.append(analyser.output[len(analyser.output) - 1]);
142
                dyn_time.append(len(dyn_time));
143
            
144
            clusters = analyser.allocate_sync_ensembles(0.05);
145
            
146
            # Get current number of allocated clusters
147
            current_number_clusters = len(clusters);
148
            
149
            # Increase number of neighbors that should be used
150
            number_neighbors += increase_step;
151
            
152
            # Update connectivity radius and check if average function can be used anymore
153
            if (number_neighbors >= len(self._osc_loc)):
154
                radius = radius * self.__increase_persent + radius;
155
            else:
156
                radius = average_neighbor_distance(self._osc_loc, number_neighbors);
157
        
158
        if (collect_dynamic != True):
159
            dyn_phase = analyser.output;
160
            dyn_time = analyser.time;
161
        
162
        return syncnet_analyser(dyn_phase, dyn_time, None);
163