signature:
def _phase_kuramoto ( self , teta , t , argv ) :
    """@ brief Returns result of phase calculation for oscillator in the network .
 @ param [ in ] teta ( double ) : Value of phase of the oscillator with index argv in the network .
 @ param [ in ] t ( double ) : Unused , can be ignored .
 @ param [ in ] argv ( uint ) : Index of the oscillator in the network .
 @ return ( double ) New value of phase for oscillator with index argv ."""

implementation:
index = argv ;
phase = 0 ;
for k in range ( 0 , self . _num_osc ) :
    if ( self . has_connection ( index , k ) == True ) :
        phase += self . _negative_weight * math . sin ( self . _phases [ k ] - teta ) ;
    else :
        phase += self . _positive_weight * math . sin ( self . _phases [ k ] - teta ) ;
return ( phase / self . _reduction ) ;


signature:
def process ( self , order = 0.998 , solution = solve_type . FAST , collect_dynamic = False ) :
    """@ brief Performs simulation of the network ( performs solving of graph coloring problem ) .
 @ param [ in ] order ( double ) : Defines when process of synchronization in the network is over , range from 0 to 1.
 @ param [ in ] solution ( solve _ type ) : defines type ( method ) of solving diff . equation .
 @ param [ in ] collect _ dynamic ( bool ) : If True - return full dynamic of the network , otherwise - last state of phases .
 @ return ( syncnet _ analyser ) Returns analyser of results of coloring ."""

implementation:
analyser = self . simulate_dynamic ( order , solution , collect_dynamic ) ;
return syncgcolor_analyser ( analyser . output , analyser . time , None ) ;


signature:
def allocate_clusters ( self , eps = 0.01 , indexes = None , iteration = None ) :
    """@ brief Returns list of clusters in line with state of ocillators ( phases ) .
 @ param [ in ] eps ( double ) : Tolerance level that define maximal difference between phases of oscillators in one cluster .
 @ param [ in ] indexes ( list ) : List of real object indexes and it should be equal to amount of oscillators ( in case of ' None ' - indexes are in range [ 0 ; amount _ oscillators ] ) .
 @ param [ in ] iteration ( uint ) : Iteration of simulation that should be used for allocation .
 @ return ( list ) List of clusters , for example [ [ cluster1 ] , [ cluster2 ] , . . . ] . )"""

implementation: return self . allocate_sync_ensembles ( eps , indexes , iteration ) ;


signature:
def animate_cluster_allocation ( dataset , analyser , animation_velocity = 75 , tolerance = 0.1 , save_movie = None , title = None ) :
    """@ brief Shows animation of output dynamic ( output of each oscillator ) during simulation on a circle from [ 0 ; 2pi ] .
 @ param [ in ] dataset ( list ) : Input data that was used for processing by the network .
 @ param [ in ] analyser ( syncnet _ analyser ) : Output dynamic analyser of the Sync network .
 @ param [ in ] animation _ velocity ( uint ) : Interval between frames in milliseconds .
 @ param [ in ] tolerance ( double ) : Tolerance level that define maximal difference between phases of oscillators in one cluster .
 @ param [ in ] save _ movie ( string ) : If it is specified then animation will be stored to file that is specified in this parameter .
 @ param [ in ] title ( string ) : If it is specified then title will be displayed on the animation plot ."""

implementation:
figure = plt . figure ( ) ;
def init_frame ( ) :
    return frame_generation ( 0 ) ;
def frame_generation ( index_dynamic ) :
    figure . clf ( ) ;
    if ( title is not None ) :
        figure . suptitle ( title , fontsize = 26 , fontweight = 'bold' ) ;
    ax1 = figure . add_subplot ( 121 , projection = 'polar' ) ;
    clusters = analyser . allocate_clusters ( eps = tolerance , iteration = index_dynamic ) ;
    dynamic = analyser . output [ index_dynamic ] ;
    visualizer = cluster_visualizer ( size_row = 2 ) ;
    visualizer . append_clusters ( clusters , dataset ) ;
    artist1 , = ax1 . plot ( dynamic , [ 1.0 ] * len ( dynamic ) , marker = 'o' , color = 'blue' , ls = '' ) ;
    visualizer . show ( figure , display = False ) ;
    artist2 = figure . gca ( ) ;
    return [ artist1 , artist2 ] ;
cluster_animation = animation . FuncAnimation ( figure , frame_generation , len ( analyser ) , interval = animation_velocity , init_func = init_frame , repeat_delay = 5000 ) ;
if ( save_movie is not None ) : # plt . rcParams [ ' animation . ffmpeg _ path ' ] = ' D : \ \ Program Files \ \ ffmpeg - 3.3.1 - win64 - static \ \ bin \ \ ffmpeg . exe ' ;
# ffmpeg _ writer = animation . FFMpegWriter ( fps = 15 ) ;
# cluster _ animation . save ( save _ movie , writer = ffmpeg _ writer ) ;
    cluster_animation . save ( save_movie , writer = 'ffmpeg' , fps = 15 , bitrate = 1500 ) ;
else :
    plt . show ( ) ;


signature:
def _create_connections ( self , radius ) :
    """@ brief Create connections between oscillators in line with input radius of connectivity .
 @ param [ in ] radius ( double ) : Connectivity radius between oscillators ."""

implementation:
if ( self . _ena_conn_weight is True ) :
    self . _conn_weight = [ [ 0 ] * self . _num_osc for _ in range ( 0 , self . _num_osc , 1 ) ] ;
maximum_distance = 0 ;
minimum_distance = float ( 'inf' ) ;
# Create connections
for i in range ( 0 , self . _num_osc , 1 ) :
    for j in range ( i + 1 , self . _num_osc , 1 ) :
        dist = euclidean_distance ( self . _osc_loc [ i ] , self . _osc_loc [ j ] ) ;
        if ( self . _ena_conn_weight is True ) :
            self . _conn_weight [ i ] [ j ] = dist ;
            self . _conn_weight [ j ] [ i ] = dist ;
            if ( dist > maximum_distance ) :
                maximum_distance = dist ;
            if ( dist < minimum_distance ) :
                minimum_distance = dist ;
        if ( dist <= radius ) :
            self . set_connection ( i , j ) ;
if ( self . _ena_conn_weight is True ) :
    multiplier = 1 ;
    subtractor = 0 ;
    if ( maximum_distance != minimum_distance ) :
        multiplier = ( maximum_distance - minimum_distance ) ;
        subtractor = minimum_distance ;
    for i in range ( 0 , self . _num_osc , 1 ) :
        for j in range ( i + 1 , self . _num_osc , 1 ) :
            value_conn_weight = ( self . _conn_weight [ i ] [ j ] - subtractor ) / multiplier ;
            self . _conn_weight [ i ] [ j ] = value_conn_weight ;
            self . _conn_weight [ j ] [ i ] = value_conn_weight ;


signature:
def process ( self , order = 0.998 , solution = solve_type . FAST , collect_dynamic = True ) :
    """@ brief Peforms cluster analysis using simulation of the oscillatory network .
 @ param [ in ] order ( double ) : Order of synchronization that is used as indication for stopping processing .
 @ param [ in ] solution ( solve _ type ) : Specified type of solving diff . equation .
 @ param [ in ] collect _ dynamic ( bool ) : Specified requirement to collect whole dynamic of the network .
 @ return ( syncnet _ analyser ) Returns analyser of results of clustering ."""

implementation:
if ( self . _ccore_network_pointer is not None ) :
    pointer_output_dynamic = syncnet_process ( self . _ccore_network_pointer , order , solution , collect_dynamic ) ;
    return syncnet_analyser ( None , None , pointer_output_dynamic ) ;
else :
    output_sync_dynamic = self . simulate_dynamic ( order , solution , collect_dynamic ) ;
    return syncnet_analyser ( output_sync_dynamic . output , output_sync_dynamic . time , None ) ;


signature:
def _phase_kuramoto ( self , teta , t , argv ) :
    """@ brief Overrided method for calculation of oscillator phase .
 @ param [ in ] teta ( double ) : Current value of phase .
 @ param [ in ] t ( double ) : Time ( can be ignored ) .
 @ param [ in ] argv ( uint ) : Index of oscillator whose phase represented by argument teta .
 @ return ( double ) New value of phase of oscillator with index ' argv ' ."""

implementation:
index = argv ;
# index of oscillator
phase = 0.0 ;
# phase of a specified oscillator that will calculated in line with current env . states .
neighbors = self . get_neighbors ( index ) ;
for k in neighbors :
    conn_weight = 1.0 ;
    if ( self . _ena_conn_weight is True ) :
        conn_weight = self . _conn_weight [ index ] [ k ] ;
    phase += conn_weight * self . _weight * math . sin ( self . _phases [ k ] - teta ) ;
divider = len ( neighbors ) ;
if ( divider == 0 ) :
    divider = 1.0 ;
return ( self . _freq [ index ] + ( phase / divider ) ) ;


signature:
def show_network ( self ) :
    """@ brief Shows connections in the network . It supports only 2 - d and 3 - d representation ."""

implementation:
if ( ( self . _ccore_network_pointer is not None ) and ( self . _osc_conn is None ) ) :
    self . _osc_conn = sync_connectivity_matrix ( self . _ccore_network_pointer ) ;
dimension = len ( self . _osc_loc [ 0 ] ) ;
if ( ( dimension != 3 ) and ( dimension != 2 ) ) :
    raise NameError ( 'Network that is located in different from 2-d and 3-d dimensions can not be represented' ) ;
from matplotlib . font_manager import FontProperties ;
from matplotlib import rcParams ;
rcParams [ 'font.sans-serif' ] = [ 'Arial' ] ;
rcParams [ 'font.size' ] = 12 ;
fig = plt . figure ( ) ;
axes = None ;
if ( dimension == 2 ) :
    axes = fig . add_subplot ( 111 ) ;
elif ( dimension == 3 ) :
    axes = fig . gca ( projection = '3d' ) ;
surface_font = FontProperties ( ) ;
surface_font . set_name ( 'Arial' ) ;
surface_font . set_size ( '12' ) ;
for i in range ( 0 , self . _num_osc , 1 ) :
    if ( dimension == 2 ) :
        axes . plot ( self . _osc_loc [ i ] [ 0 ] , self . _osc_loc [ i ] [ 1 ] , 'bo' ) ;
        if ( self . _conn_represent == conn_represent . MATRIX ) :
            for j in range ( i , self . _num_osc , 1 ) : # draw connection between two points only one time
                if ( self . has_connection ( i , j ) == True ) :
                    axes . plot ( [ self . _osc_loc [ i ] [ 0 ] , self . _osc_loc [ j ] [ 0 ] ] , [ self . _osc_loc [ i ] [ 1 ] , self . _osc_loc [ j ] [ 1 ] ] , 'b-' , linewidth = 0.5 ) ;
        else :
            for j in self . get_neighbors ( i ) :
                if ( ( self . has_connection ( i , j ) == True ) and ( i > j ) ) : # draw connection between two points only one time
                    axes . plot ( [ self . _osc_loc [ i ] [ 0 ] , self . _osc_loc [ j ] [ 0 ] ] , [ self . _osc_loc [ i ] [ 1 ] , self . _osc_loc [ j ] [ 1 ] ] , 'b-' , linewidth = 0.5 ) ;
    elif ( dimension == 3 ) :
        axes . scatter ( self . _osc_loc [ i ] [ 0 ] , self . _osc_loc [ i ] [ 1 ] , self . _osc_loc [ i ] [ 2 ] , c = 'b' , marker = 'o' ) ;
        if ( self . _conn_represent == conn_represent . MATRIX ) :
            for j in range ( i , self . _num_osc , 1 ) : # draw connection between two points only one time
                if ( self . has_connection ( i , j ) == True ) :
                    axes . plot ( [ self . _osc_loc [ i ] [ 0 ] , self . _osc_loc [ j ] [ 0 ] ] , [ self . _osc_loc [ i ] [ 1 ] , self . _osc_loc [ j ] [ 1 ] ] , [ self . _osc_loc [ i ] [ 2 ] , self . _osc_loc [ j ] [ 2 ] ] , 'b-' , linewidth = 0.5 ) ;
        else :
            for j in self . get_neighbors ( i ) :
                if ( ( self . has_connection ( i , j ) == True ) and ( i > j ) ) : # draw connection between two points only one time
                    axes . plot ( [ self . _osc_loc [ i ] [ 0 ] , self . _osc_loc [ j ] [ 0 ] ] , [ self . _osc_loc [ i ] [ 1 ] , self . _osc_loc [ j ] [ 1 ] ] , [ self . _osc_loc [ i ] [ 2 ] , self . _osc_loc [ j ] [ 2 ] ] , 'b-' , linewidth = 0.5 ) ;
plt . grid ( ) ;
plt . show ( ) ;


signature:
def simulate_static ( self , steps , time , solution = solve_type . RK4 ) :
    """@ brief Performs static simulation of oscillatory network based on Hodgkin - Huxley neuron model .
 @ details Output dynamic is sensible to amount of steps of simulation and solver of differential equation .
 Python implementation uses ' odeint ' from ' scipy ' , CCORE uses classical RK4 and RFK45 methods ,
 therefore in case of CCORE HHN ( Hodgkin - Huxley network ) amount of steps should be greater than in
 case of Python HHN .
 @ param [ in ] steps ( uint ) : Number steps of simulations during simulation .
 @ param [ in ] time ( double ) : Time of simulation .
 @ param [ in ] solution ( solve _ type ) : Type of solver for differential equations .
 @ return ( tuple ) Dynamic of oscillatory network represented by ( time , peripheral neurons dynamic , central elements
 dynamic ) , where types are ( list , list , list ) ."""

implementation:
# Check solver before simulation
if ( solution == solve_type . FAST ) :
    raise NameError ( "Solver FAST is not support due to low accuracy that leads to huge error." ) ;
self . _membrane_dynamic_pointer = None ;
if ( self . __ccore_hhn_pointer is not None ) :
    self . __ccore_hhn_dynamic_pointer = wrapper . hhn_dynamic_create ( True , False , False , False ) ;
    wrapper . hhn_simulate ( self . __ccore_hhn_pointer , steps , time , solution , self . _stimulus , self . __ccore_hhn_dynamic_pointer ) ;
    peripheral_membrane_potential = wrapper . hhn_dynamic_get_peripheral_evolution ( self . __ccore_hhn_dynamic_pointer , 0 ) ;
    central_membrane_potential = wrapper . hhn_dynamic_get_central_evolution ( self . __ccore_hhn_dynamic_pointer , 0 ) ;
    dynamic_time = wrapper . hhn_dynamic_get_time ( self . __ccore_hhn_dynamic_pointer ) ;
    self . _membrane_dynamic_pointer = peripheral_membrane_potential ;
    wrapper . hhn_dynamic_destroy ( self . __ccore_hhn_dynamic_pointer ) ;
    return ( dynamic_time , peripheral_membrane_potential , central_membrane_potential ) ;
if ( solution == solve_type . RKF45 ) :
    raise NameError ( "Solver RKF45 is not support in python version." ) ;
dyn_peripheral = [ self . _membrane_potential [ : ] ] ;
dyn_central = [ [ 0.0 , 0.0 ] ] ;
dyn_time = [ 0.0 ] ;
step = time / steps ;
int_step = step / 10.0 ;
for t in numpy . arange ( step , time + step , step ) : # update states of oscillators
    ( memb_peripheral , memb_central ) = self . _calculate_states ( solution , t , step , int_step ) ;
    # update states of oscillators
    dyn_peripheral . append ( memb_peripheral ) ;
    dyn_central . append ( memb_central ) ;
    dyn_time . append ( t ) ;
self . _membrane_dynamic_pointer = dyn_peripheral ;
return ( dyn_time , dyn_peripheral , dyn_central ) ;


signature:
def _calculate_states ( self , solution , t , step , int_step ) :
    """@ brief Caclculates new state of each oscillator in the network . Returns only excitatory state of oscillators .
 @ param [ in ] solution ( solve _ type ) : Type solver of the differential equations .
 @ param [ in ] t ( double ) : Current time of simulation .
 @ param [ in ] step ( uint ) : Step of solution at the end of which states of oscillators should be calculated .
 @ param [ in ] int _ step ( double ) : Differentiation step that is used for solving differential equation .
 @ return ( list ) New states of membrance potentials for peripheral oscillators and for cental elements as a list where
 the last two values correspond to central element 1 and 2."""

implementation:
next_membrane = [ 0.0 ] * self . _num_osc ;
next_active_sodium = [ 0.0 ] * self . _num_osc ;
next_inactive_sodium = [ 0.0 ] * self . _num_osc ;
next_active_potassium = [ 0.0 ] * self . _num_osc ;
# Update states of oscillators
for index in range ( 0 , self . _num_osc , 1 ) :
    result = odeint ( self . hnn_state , [ self . _membrane_potential [ index ] , self . _active_cond_sodium [ index ] , self . _inactive_cond_sodium [ index ] , self . _active_cond_potassium [ index ] ] , numpy . arange ( t - step , t , int_step ) , ( index , ) ) ;
    [ next_membrane [ index ] , next_active_sodium [ index ] , next_inactive_sodium [ index ] , next_active_potassium [ index ] ] = result [ len ( result ) - 1 ] [ 0 : 4 ] ;
next_cn_membrane = [ 0.0 , 0.0 ] ;
next_cn_active_sodium = [ 0.0 , 0.0 ] ;
next_cn_inactive_sodium = [ 0.0 , 0.0 ] ;
next_cn_active_potassium = [ 0.0 , 0.0 ] ;
# Update states of central elements
for index in range ( 0 , len ( self . _central_element ) ) :
    result = odeint ( self . hnn_state , [ self . _central_element [ index ] . membrane_potential , self . _central_element [ index ] . active_cond_sodium , self . _central_element [ index ] . inactive_cond_sodium , self . _central_element [ index ] . active_cond_potassium ] , numpy . arange ( t - step , t , int_step ) , ( self . _num_osc + index , ) ) ;
    [ next_cn_membrane [ index ] , next_cn_active_sodium [ index ] , next_cn_inactive_sodium [ index ] , next_cn_active_potassium [ index ] ] = result [ len ( result ) - 1 ] [ 0 : 4 ] ;
# Noise generation
self . _noise = [ 1.0 + 0.01 * ( random . random ( ) * 2.0 - 1.0 ) for i in range ( self . _num_osc ) ] ;
# Updating states of PNs
self . __update_peripheral_neurons ( t , step , next_membrane , next_active_sodium , next_inactive_sodium , next_active_potassium ) ;
# Updation states of CN
self . __update_central_neurons ( t , next_cn_membrane , next_cn_active_sodium , next_cn_inactive_sodium , next_cn_active_potassium ) ;
return ( next_membrane , next_cn_membrane ) ;


signature:
def __update_peripheral_neurons ( self , t , step , next_membrane , next_active_sodium , next_inactive_sodium , next_active_potassium ) :
    """@ brief Update peripheral neurons in line with new values of current in channels .
 @ param [ in ] t ( doubles ) : Current time of simulation .
 @ param [ in ] step ( uint ) : Step ( time duration ) during simulation when states of oscillators should be calculated .
 @ param [ in ] next _ membrane ( list ) : New values of membrane potentials for peripheral neurons .
 @ Param [ in ] next _ active _ sodium ( list ) : New values of activation conductances of the sodium channels for peripheral neurons .
 @ param [ in ] next _ inactive _ sodium ( list ) : New values of inactivaton conductances of the sodium channels for peripheral neurons .
 @ param [ in ] next _ active _ potassium ( list ) : New values of activation conductances of the potassium channel for peripheral neurons ."""

implementation:
self . _membrane_potential = next_membrane [ : ] ;
self . _active_cond_sodium = next_active_sodium [ : ] ;
self . _inactive_cond_sodium = next_inactive_sodium [ : ] ;
self . _active_cond_potassium = next_active_potassium [ : ] ;
for index in range ( 0 , self . _num_osc ) :
    if ( self . _pulse_generation [ index ] is False ) :
        if ( self . _membrane_potential [ index ] >= 0.0 ) :
            self . _pulse_generation [ index ] = True ;
            self . _pulse_generation_time [ index ] . append ( t ) ;
    elif ( self . _membrane_potential [ index ] < 0.0 ) :
        self . _pulse_generation [ index ] = False ;
    # Update connection from CN2 to PN
    if ( self . _link_weight3 [ index ] == 0.0 ) :
        if ( self . _membrane_potential [ index ] > self . _params . threshold ) :
            self . _link_pulse_counter [ index ] += step ;
            if ( self . _link_pulse_counter [ index ] >= 1 / self . _params . eps ) :
                self . _link_weight3 [ index ] = self . _params . w3 ;
                self . _link_activation_time [ index ] = t ;
    elif ( not ( ( self . _link_activation_time [ index ] < t ) and ( t < self . _link_activation_time [ index ] + self . _params . deltah ) ) ) :
        self . _link_weight3 [ index ] = 0.0 ;
        self . _link_pulse_counter [ index ] = 0.0 ;


signature:
def __update_central_neurons ( self , t , next_cn_membrane , next_cn_active_sodium , next_cn_inactive_sodium , next_cn_active_potassium ) :
    """@ brief Update of central neurons in line with new values of current in channels .
 @ param [ in ] t ( doubles ) : Current time of simulation .
 @ param [ in ] next _ membrane ( list ) : New values of membrane potentials for central neurons .
 @ Param [ in ] next _ active _ sodium ( list ) : New values of activation conductances of the sodium channels for central neurons .
 @ param [ in ] next _ inactive _ sodium ( list ) : New values of inactivaton conductances of the sodium channels for central neurons .
 @ param [ in ] next _ active _ potassium ( list ) : New values of activation conductances of the potassium channel for central neurons ."""

implementation:
for index in range ( 0 , len ( self . _central_element ) ) :
    self . _central_element [ index ] . membrane_potential = next_cn_membrane [ index ] ;
    self . _central_element [ index ] . active_cond_sodium = next_cn_active_sodium [ index ] ;
    self . _central_element [ index ] . inactive_cond_sodium = next_cn_inactive_sodium [ index ] ;
    self . _central_element [ index ] . active_cond_potassium = next_cn_active_potassium [ index ] ;
    if ( self . _central_element [ index ] . pulse_generation is False ) :
        if ( self . _central_element [ index ] . membrane_potential >= 0.0 ) :
            self . _central_element [ index ] . pulse_generation = True ;
            self . _central_element [ index ] . pulse_generation_time . append ( t ) ;
    elif ( self . _central_element [ index ] . membrane_potential < 0.0 ) :
        self . _central_element [ index ] . pulse_generation = False ;


signature:
def hnn_state ( self , inputs , t , argv ) :
    """@ brief Returns new values of excitatory and inhibitory parts of oscillator and potential of oscillator .
 @ param [ in ] inputs ( list ) : States of oscillator for integration [ v , m , h , n ] ( see description below ) .
 @ param [ in ] t ( double ) : Current time of simulation .
 @ param [ in ] argv ( tuple ) : Extra arguments that are not used for integration - index of oscillator .
 @ return ( list ) new values of oscillator [ v , m , h , n ] , where :
 v - membrane potantial of oscillator ,
 m - activation conductance of the sodium channel ,
 h - inactication conductance of the sodium channel ,
 n - activation conductance of the potassium channel ."""

implementation:
index = argv ;
v = inputs [ 0 ] ;
# membrane potential ( v ) .
m = inputs [ 1 ] ;
# activation conductance of the sodium channel ( m ) .
h = inputs [ 2 ] ;
# inactivaton conductance of the sodium channel ( h ) .
n = inputs [ 3 ] ;
# activation conductance of the potassium channel ( n ) .
# Calculate ion current
# gNa * m [ i ] ^ 3 * h * ( v [ i ] - vNa ) + gK * n [ i ] ^ 4 * ( v [ i ] - vK ) + gL ( v [ i ] - vL )
active_sodium_part = self . _params . gNa * ( m ** 3 ) * h * ( v - self . _params . vNa ) ;
inactive_sodium_part = self . _params . gK * ( n ** 4 ) * ( v - self . _params . vK ) ;
active_potassium_part = self . _params . gL * ( v - self . _params . vL ) ;
Iion = active_sodium_part + inactive_sodium_part + active_potassium_part ;
Iext = 0.0 ;
Isyn = 0.0 ;
if ( index < self . _num_osc ) : # PN - peripheral neuron - calculation of external current and synaptic current .
    Iext = self . _stimulus [ index ] * self . _noise [ index ] ;
    # probably noise can be pre - defined for reducting compexity
    memory_impact1 = 0.0 ;
    for i in range ( 0 , len ( self . _central_element [ 0 ] . pulse_generation_time ) ) :
        memory_impact1 += self . __alfa_function ( t - self . _central_element [ 0 ] . pulse_generation_time [ i ] , self . _params . alfa_inhibitory , self . _params . betta_inhibitory ) ;
    memory_impact2 = 0.0 ;
    for i in range ( 0 , len ( self . _central_element [ 1 ] . pulse_generation_time ) ) :
        memory_impact2 += self . __alfa_function ( t - self . _central_element [ 1 ] . pulse_generation_time [ i ] , self . _params . alfa_inhibitory , self . _params . betta_inhibitory ) ;
    Isyn = self . _params . w2 * ( v - self . _params . Vsyninh ) * memory_impact1 + self . _link_weight3 [ index ] * ( v - self . _params . Vsyninh ) * memory_impact2 ;
else : # CN - central element .
    central_index = index - self . _num_osc ;
    if ( central_index == 0 ) :
        Iext = self . _params . Icn1 ;
        # CN1
        memory_impact = 0.0 ;
        for index_oscillator in range ( 0 , self . _num_osc ) :
            for index_generation in range ( 0 , len ( self . _pulse_generation_time [ index_oscillator ] ) ) :
                memory_impact += self . __alfa_function ( t - self . _pulse_generation_time [ index_oscillator ] [ index_generation ] , self . _params . alfa_excitatory , self . _params . betta_excitatory ) ;
        Isyn = self . _params . w1 * ( v - self . _params . Vsynexc ) * memory_impact ;
    elif ( central_index == 1 ) :
        Iext = self . _params . Icn2 ;
        # CN2
        Isyn = 0.0 ;
    else :
        assert 0 ;
# Membrane potential
dv = - Iion + Iext - Isyn ;
# Calculate variables
potential = v - self . _params . vRest ;
am = ( 2.5 - 0.1 * potential ) / ( math . exp ( 2.5 - 0.1 * potential ) - 1.0 ) ;
ah = 0.07 * math . exp ( - potential / 20.0 ) ;
an = ( 0.1 - 0.01 * potential ) / ( math . exp ( 1.0 - 0.1 * potential ) - 1.0 ) ;
bm = 4.0 * math . exp ( - potential / 18.0 ) ;
bh = 1.0 / ( math . exp ( 3.0 - 0.1 * potential ) + 1.0 ) ;
bn = 0.125 * math . exp ( - potential / 80.0 ) ;
dm = am * ( 1.0 - m ) - bm * m ;
dh = ah * ( 1.0 - h ) - bh * h ;
dn = an * ( 1.0 - n ) - bn * n ;
return [ dv , dm , dh , dn ] ;


signature:
def __alfa_function ( self , time , alfa , betta ) :
    """@ brief Calculates value of alfa - function for difference between spike generation time and current simulation time .
 @ param [ in ] time ( double ) : Difference between last spike generation time and current time .
 @ param [ in ] alfa ( double ) : Alfa parameter for alfa - function .
 @ param [ in ] betta ( double ) : Betta parameter for alfa - function .
 @ return ( double ) Value of alfa - function ."""

implementation: return alfa * time * math . exp ( - betta * time ) ;


signature:
def show_grid ( cells , data ) :
    """@ brief Show CLIQUE blocks as a grid in data space .
 @ details Each block contains points and according to this density is displayed . CLIQUE grid helps to visualize
 grid that was used for clustering process .
 @ param [ in ] cells ( list ) : List of cells that is produced by CLIQUE algorithm .
 @ param [ in ] data ( array _ like ) : Input data that was used for clustering process ."""

implementation:
dimension = cells [ 0 ] . dimensions
amount_canvases = 1
if dimension > 1 :
    amount_canvases = int ( dimension * ( dimension - 1 ) / 2 )
figure = plt . figure ( )
grid_spec = gridspec . GridSpec ( 1 , amount_canvases )
pairs = list ( itertools . combinations ( range ( dimension ) , 2 ) )
if len ( pairs ) == 0 :
    pairs = [ ( 0 , 0 ) ]
for index in range ( amount_canvases ) :
    ax = figure . add_subplot ( grid_spec [ index ] )
    clique_visualizer . __draw_cells ( ax , cells , pairs [ index ] )
    clique_visualizer . __draw_two_dimension_data ( ax , data , pairs [ index ] )
plt . show ( )


signature:
def show_clusters ( data , clusters , noise = None ) :
    """@ brief Display CLIQUE clustering results .
 @ param [ in ] data ( list ) : Data that was used for clustering .
 @ param [ in ] clusters ( array _ like ) : Clusters that were allocated by the algorithm .
 @ param [ in ] noise ( array _ like ) : Noise that were allocated by the algorithm ."""

implementation:
visualizer = cluster_visualizer ( )
visualizer . append_clusters ( clusters , data )
visualizer . append_cluster ( noise or [ ] , data , marker = 'x' )
visualizer . show ( )


signature:
def __draw_two_dimension_data ( ax , data , pair ) :
    """@ brief Display data in two - dimensional canvas .
 @ param [ in ] ax ( Axis ) : Canvas where data should be displayed .
 @ param [ in ] data ( list ) : Data points that should be displayed .
 @ param [ in ] pair ( tuple ) : Pair of dimension indexes ."""

implementation:
ax . set_xlabel ( "x%d" % pair [ 0 ] )
ax . set_ylabel ( "x%d" % pair [ 1 ] )
for point in data :
    if len ( data [ 0 ] ) > 1 :
        ax . plot ( point [ pair [ 0 ] ] , point [ pair [ 1 ] ] , color = 'red' , marker = '.' )
    else :
        ax . plot ( point [ pair [ 0 ] ] , 0 , color = 'red' , marker = '.' )
        ax . yaxis . set_ticklabels ( [ ] )


signature:
def capture_points ( self , data , point_availability ) :
    """@ brief Finds points that belong to this block using availability map to reduce computational complexity by
 checking whether the point belongs to the block .
 @ details Algorithm complexity of this method is O ( n ) .
 @ param [ in ] data ( array _ like ) : Data where points are represented as coordinates .
 @ param [ in ] point _ availability ( array _ like ) : Contains boolean values that denote whether point is already belong
 to another CLIQUE block ."""

implementation:
for index_point in range ( len ( data ) ) :
    if ( point_availability [ index_point ] is True ) and ( data [ index_point ] in self . __spatial_location ) :
        self . __points . append ( index_point )
        point_availability [ index_point ] = False


signature:
def get_location_neighbors ( self , edge ) :
    """@ brief Forms list of logical location of each neighbor for this particular CLIQUE block .
 @ param [ in ] edge ( uint ) : Amount of intervals in each dimension that is used for clustering process .
 @ return ( list ) Logical location of each neighbor for this particular CLIQUE block ."""

implementation:
neighbors = [ ]
for index_dimension in range ( len ( self . __logical_location ) ) :
    if self . __logical_location [ index_dimension ] + 1 < edge :
        position = self . __logical_location [ : ]
        position [ index_dimension ] += 1
        neighbors . append ( position )
    if self . __logical_location [ index_dimension ] - 1 >= 0 :
        position = self . __logical_location [ : ]
        position [ index_dimension ] -= 1
        neighbors . append ( position )
return neighbors


signature:
def increment ( self ) :
    """@ brief Forms logical location for next block ."""

implementation:
for index_dimension in range ( self . __dimension ) :
    if self . __coordiate [ index_dimension ] + 1 < self . __intervals :
        self . __coordiate [ index_dimension ] += 1
        return
    else :
        self . __coordiate [ index_dimension ] = 0
self . __coordiate = None


signature:
def __process_by_ccore ( self ) :
    """@ brief Performs cluster analysis using C + + implementation of CLIQUE algorithm that is used by default if
 user ' s target platform is supported ."""

implementation:
( self . __clusters , self . __noise , block_logical_locations , block_max_corners , block_min_corners , block_points ) = wrapper . clique ( self . __data , self . __amount_intervals , self . __density_threshold )
amount_cells = len ( block_logical_locations )
for i in range ( amount_cells ) :
    self . __cells . append ( clique_block ( block_logical_locations [ i ] , spatial_block ( block_max_corners [ i ] , block_min_corners [ i ] ) , block_points [ i ] , True ) )


signature:
def __validate_arguments ( self ) :
    """@ brief Check input arguments of CLIQUE algorithm and if one of them is not correct then appropriate exception
 is thrown ."""

implementation:
if len ( self . __data ) == 0 :
    raise ValueError ( "Empty input data. Data should contain at least one point." )
if self . __amount_intervals <= 0 :
    raise ValueError ( "Incorrect amount of intervals '%d'. Amount of intervals value should be greater than 0." % self . __amount_intervals )
if self . __density_threshold < 0 :
    raise ValueError ( "Incorrect density threshold '%f'. Density threshold should not be negative." % self . __density_threshold )


signature:
def __allocate_clusters ( self ) :
    """@ brief Performs cluster analysis using formed CLIQUE blocks ."""

implementation:
for cell in self . __cells :
    if cell . visited is False :
        self . __expand_cluster ( cell )


signature:
def __expand_cluster ( self , cell ) :
    """@ brief Tries to expand cluster from specified cell .
 @ details During expanding points are marked as noise or append to new cluster .
 @ param [ in ] cell ( clique _ block ) : CLIQUE block from that cluster should be expanded ."""

implementation:
cell . visited = True
if len ( cell . points ) <= self . __density_threshold :
    if len ( cell . points ) > 0 :
        self . __noise . extend ( cell . points )
    return
cluster = cell . points [ : ]
neighbors = self . __get_neighbors ( cell )
for neighbor in neighbors :
    if len ( neighbor . points ) > self . __density_threshold :
        cluster . extend ( neighbor . points )
        neighbors += self . __get_neighbors ( neighbor )
    elif len ( neighbor . points ) > 0 :
        self . __noise . extend ( neighbor . points )
self . __clusters . append ( cluster )


signature:
def __get_neighbors ( self , cell ) :
    """@ brief Returns neighbors for specified CLIQUE block as clique _ block objects .
 @ return ( list ) Neighbors as clique _ block objects ."""

implementation:
neighbors = [ ]
location_neighbors = cell . get_location_neighbors ( self . __amount_intervals )
for i in range ( len ( location_neighbors ) ) :
    key = self . __location_to_key ( location_neighbors [ i ] )
    candidate_neighbor = self . __cell_map [ key ]
    if not candidate_neighbor . visited :
        candidate_neighbor . visited = True
        neighbors . append ( candidate_neighbor )
return neighbors


signature:
def __create_grid ( self ) :
    """@ brief Creates CLIQUE grid that consists of CLIQUE blocks for clustering process ."""

implementation:
data_sizes , min_corner , max_corner = self . __get_data_size_derscription ( )
dimension = len ( self . __data [ 0 ] )
cell_sizes = [ dimension_length / self . __amount_intervals for dimension_length in data_sizes ]
self . __cells = [ clique_block ( ) for _ in range ( pow ( self . __amount_intervals , dimension ) ) ]
iterator = coordinate_iterator ( dimension , self . __amount_intervals )
point_availability = [ True ] * len ( self . __data )
self . __cell_map = { }
for index_cell in range ( len ( self . __cells ) ) :
    logical_location = iterator . get_coordinate ( )
    iterator . increment ( )
    self . __cells [ index_cell ] . logical_location = logical_location [ : ]
    cur_max_corner , cur_min_corner = self . __get_spatial_location ( logical_location , min_corner , max_corner , cell_sizes )
    self . __cells [ index_cell ] . spatial_location = spatial_block ( cur_max_corner , cur_min_corner )
    self . __cells [ index_cell ] . capture_points ( self . __data , point_availability )
    self . __cell_map [ self . __location_to_key ( logical_location ) ] = self . __cells [ index_cell ]


signature:
def __get_spatial_location ( self , logical_location , min_corner , max_corner , cell_sizes ) :
    """@ brief Calculates spatial location for CLIQUE block with logical coordinates defined by logical _ location .
 @ param [ in ] logical _ location ( list ) : Logical location of CLIQUE block for that spatial location should be calculated .
 @ param [ in ] min _ corner ( list ) : Minimum corner of an input data .
 @ param [ in ] max _ corner ( list ) : Maximum corner of an input data .
 @ param [ in ] cell _ sizes ( list ) : Size of CLIQUE block in each dimension .
 @ return ( list , list ) : Maximum and minimum corners for the specified CLIQUE block ."""

implementation:
cur_min_corner = min_corner [ : ]
cur_max_corner = min_corner [ : ]
dimension = len ( self . __data [ 0 ] )
for index_dimension in range ( dimension ) :
    cur_min_corner [ index_dimension ] += cell_sizes [ index_dimension ] * logical_location [ index_dimension ]
    if logical_location [ index_dimension ] == self . __amount_intervals - 1 :
        cur_max_corner [ index_dimension ] = max_corner [ index_dimension ]
    else :
        cur_max_corner [ index_dimension ] = cur_min_corner [ index_dimension ] + cell_sizes [ index_dimension ]
return cur_max_corner , cur_min_corner


signature:
def __get_data_size_derscription ( self ) :
    """@ brief Calculates input data description that is required to create CLIQUE grid .
 @ return ( list , list , list ) : Data size in each dimension , minimum and maximum corners ."""

implementation:
min_corner = self . __data [ 0 ] [ : ]
max_corner = self . __data [ 0 ] [ : ]
dimension = len ( self . __data [ 0 ] )
for index_point in range ( 1 , len ( self . __data ) ) :
    for index_dimension in range ( dimension ) :
        coordinate = self . __data [ index_point ] [ index_dimension ]
        if coordinate > max_corner [ index_dimension ] :
            max_corner [ index_dimension ] = coordinate
        if coordinate < min_corner [ index_dimension ] :
            min_corner [ index_dimension ] = coordinate
data_sizes = [ 0.0 ] * dimension
for index_dimension in range ( dimension ) :
    data_sizes [ index_dimension ] = max_corner [ index_dimension ] - min_corner [ index_dimension ]
return data_sizes , min_corner , max_corner


signature:
def process ( self ) :
    """@ brief Performs cluster analysis in line with rules of X - Means algorithm .
 @ remark Results of clustering can be obtained using corresponding gets methods .
 @ see get _ clusters ( )
 @ see get _ centers ( )"""

implementation:
if ( self . __ccore is True ) :
    self . __clusters , self . __centers = wrapper . xmeans ( self . __pointer_data , self . __centers , self . __kmax , self . __tolerance , self . __criterion )
else :
    self . __clusters = [ ]
    while len ( self . __centers ) <= self . __kmax :
        current_cluster_number = len ( self . __centers )
        self . __clusters , self . __centers = self . __improve_parameters ( self . __centers )
        allocated_centers = self . __improve_structure ( self . __clusters , self . __centers )
        if current_cluster_number == len ( allocated_centers ) : # if ( ( current _ cluster _ number = = len ( allocated _ centers ) ) or ( len ( allocated _ centers ) > self . _ _ kmax ) ) :
            break
        else :
            self . __centers = allocated_centers
    self . __clusters , self . __centers = self . __improve_parameters ( self . __centers )


signature:
def __improve_parameters ( self , centers , available_indexes = None ) :
    """@ brief Performs k - means clustering in the specified region .
 @ param [ in ] centers ( list ) : Centers of clusters .
 @ param [ in ] available _ indexes ( list ) : Indexes that defines which points can be used for k - means clustering , if None - then all points are used .
 @ return ( list ) List of allocated clusters , each cluster contains indexes of objects in list of data ."""

implementation:
if available_indexes and len ( available_indexes ) == 1 :
    index_center = available_indexes [ 0 ]
    return [ available_indexes ] , self . __pointer_data [ index_center ]
local_data = self . __pointer_data
if available_indexes :
    local_data = [ self . __pointer_data [ i ] for i in available_indexes ]
local_centers = centers
if centers is None :
    local_centers = kmeans_plusplus_initializer ( local_data , 2 , kmeans_plusplus_initializer . FARTHEST_CENTER_CANDIDATE ) . initialize ( )
kmeans_instance = kmeans ( local_data , local_centers , tolerance = self . __tolerance , ccore = False )
kmeans_instance . process ( )
local_centers = kmeans_instance . get_centers ( )
clusters = kmeans_instance . get_clusters ( )
if available_indexes :
    clusters = self . __local_to_global_clusters ( clusters , available_indexes )
return clusters , local_centers


signature:
def __local_to_global_clusters ( self , local_clusters , available_indexes ) :
    """@ brief Converts clusters in local region define by ' available _ indexes ' to global clusters .
 @ param [ in ] local _ clusters ( list ) : Local clusters in specific region .
 @ param [ in ] available _ indexes ( list ) : Map between local and global point ' s indexes .
 @ return Global clusters ."""

implementation:
clusters = [ ]
for local_cluster in local_clusters :
    current_cluster = [ ]
    for index_point in local_cluster :
        current_cluster . append ( available_indexes [ index_point ] )
    clusters . append ( current_cluster )
return clusters


signature:
def __improve_structure ( self , clusters , centers ) :
    """@ brief Check for best structure : divides each cluster into two and checks for best results using splitting criterion .
 @ param [ in ] clusters ( list ) : Clusters that have been allocated ( each cluster contains indexes of points from data ) .
 @ param [ in ] centers ( list ) : Centers of clusters .
 @ return ( list ) Allocated centers for clustering ."""

implementation:
allocated_centers = [ ]
amount_free_centers = self . __kmax - len ( centers )
for index_cluster in range ( len ( clusters ) ) : # solve k - means problem for children where data of parent are used .
    ( parent_child_clusters , parent_child_centers ) = self . __improve_parameters ( None , clusters [ index_cluster ] )
    # If it ' s possible to split current data
    if len ( parent_child_clusters ) > 1 : # Calculate splitting criterion
        parent_scores = self . __splitting_criterion ( [ clusters [ index_cluster ] ] , [ centers [ index_cluster ] ] )
        child_scores = self . __splitting_criterion ( [ parent_child_clusters [ 0 ] , parent_child_clusters [ 1 ] ] , parent_child_centers )
        split_require = False
        # Reallocate number of centers ( clusters ) in line with scores
        if self . __criterion == splitting_type . BAYESIAN_INFORMATION_CRITERION :
            if parent_scores < child_scores :
                split_require = True
        elif self . __criterion == splitting_type . MINIMUM_NOISELESS_DESCRIPTION_LENGTH : # If its score for the split structure with two children is smaller than that for the parent structure ,
        # then representing the data samples with two clusters is more accurate in comparison to a single parent cluster .
            if parent_scores > child_scores :
                split_require = True ;
        if ( split_require is True ) and ( amount_free_centers > 0 ) :
            allocated_centers . append ( parent_child_centers [ 0 ] )
            allocated_centers . append ( parent_child_centers [ 1 ] )
            amount_free_centers -= 1
        else :
            allocated_centers . append ( centers [ index_cluster ] )
    else :
        allocated_centers . append ( centers [ index_cluster ] )
return allocated_centers


signature:
def __splitting_criterion ( self , clusters , centers ) :
    """@ brief Calculates splitting criterion for input clusters .
 @ param [ in ] clusters ( list ) : Clusters for which splitting criterion should be calculated .
 @ param [ in ] centers ( list ) : Centers of the clusters .
 @ return ( double ) Returns splitting criterion . High value of splitting cretion means that current structure is much better .
 @ see _ _ bayesian _ information _ criterion ( clusters , centers )
 @ see _ _ minimum _ noiseless _ description _ length ( clusters , centers )"""

implementation:
if self . __criterion == splitting_type . BAYESIAN_INFORMATION_CRITERION :
    return self . __bayesian_information_criterion ( clusters , centers )
elif self . __criterion == splitting_type . MINIMUM_NOISELESS_DESCRIPTION_LENGTH :
    return self . __minimum_noiseless_description_length ( clusters , centers )
else :
    assert 0 ;


signature:
def __minimum_noiseless_description_length ( self , clusters , centers ) :
    """@ brief Calculates splitting criterion for input clusters using minimum noiseless description length criterion .
 @ param [ in ] clusters ( list ) : Clusters for which splitting criterion should be calculated .
 @ param [ in ] centers ( list ) : Centers of the clusters .
 @ return ( double ) Returns splitting criterion in line with bayesian information criterion .
 Low value of splitting cretion means that current structure is much better .
 @ see _ _ bayesian _ information _ criterion ( clusters , centers )"""

implementation:
scores = float ( 'inf' )
W = 0.0
K = len ( clusters )
N = 0.0
sigma_sqrt = 0.0
alpha = 0.9
betta = 0.9
for index_cluster in range ( 0 , len ( clusters ) , 1 ) :
    Ni = len ( clusters [ index_cluster ] )
    if Ni == 0 :
        return float ( 'inf' )
    Wi = 0.0
    for index_object in clusters [ index_cluster ] : # euclidean _ distance _ square should be used in line with paper , but in this case results are
    # very poor , therefore square root is used to improved .
        Wi += euclidean_distance ( self . __pointer_data [ index_object ] , centers [ index_cluster ] )
    sigma_sqrt += Wi
    W += Wi / Ni
    N += Ni
if N - K > 0 :
    sigma_sqrt /= ( N - K )
    sigma = sigma_sqrt ** 0.5
    Kw = ( 1.0 - K / N ) * sigma_sqrt
    Ks = ( 2.0 * alpha * sigma / ( N ** 0.5 ) ) * ( ( alpha ** 2.0 ) * sigma_sqrt / N + W - Kw / 2.0 ) ** 0.5
    scores = sigma_sqrt * ( 2 * K ) ** 0.5 * ( ( 2 * K ) ** 0.5 + betta ) / N + W - sigma_sqrt + Ks + 2 * alpha ** 0.5 * sigma_sqrt / N
return scores


signature:
def __bayesian_information_criterion ( self , clusters , centers ) :
    """@ brief Calculates splitting criterion for input clusters using bayesian information criterion .
 @ param [ in ] clusters ( list ) : Clusters for which splitting criterion should be calculated .
 @ param [ in ] centers ( list ) : Centers of the clusters .
 @ return ( double ) Splitting criterion in line with bayesian information criterion .
 High value of splitting criterion means that current structure is much better .
 @ see _ _ minimum _ noiseless _ description _ length ( clusters , centers )"""

implementation:
scores = [ float ( 'inf' ) ] * len ( clusters )
# splitting criterion
dimension = len ( self . __pointer_data [ 0 ] )
# estimation of the noise variance in the data set
sigma_sqrt = 0.0
K = len ( clusters )
N = 0.0
for index_cluster in range ( 0 , len ( clusters ) , 1 ) :
    for index_object in clusters [ index_cluster ] :
        sigma_sqrt += euclidean_distance_square ( self . __pointer_data [ index_object ] , centers [ index_cluster ] ) ;
    N += len ( clusters [ index_cluster ] )
if N - K > 0 :
    sigma_sqrt /= ( N - K )
    p = ( K - 1 ) + dimension * K + 1
    # in case of the same points , sigma _ sqrt can be zero ( issue : # 407)
    sigma_multiplier = 0.0
    if sigma_sqrt <= 0.0 :
        sigma_multiplier = float ( '-inf' )
    else :
        sigma_multiplier = dimension * 0.5 * log ( sigma_sqrt )
    # splitting criterion
    for index_cluster in range ( 0 , len ( clusters ) , 1 ) :
        n = len ( clusters [ index_cluster ] )
        L = n * log ( n ) - n * log ( N ) - n * 0.5 * log ( 2.0 * numpy . pi ) - n * sigma_multiplier - ( n - K ) * 0.5
        # BIC calculation
        scores [ index_cluster ] = L - p * 0.5 * log ( N )
return sum ( scores )


signature:
def segmentation_image_simple1 ( ) :
    "Perfect"

implementation:
parameters = legion_parameters ( ) ;
parameters . eps = 0.02 ;
parameters . alpha = 0.005 ;
parameters . betta = 0.1 ;
parameters . gamma = 7.0 ;
parameters . teta = 0.9 ;
parameters . lamda = 0.1 ;
parameters . teta_x = - 0.5 ;
parameters . teta_p = 7.0 ;
parameters . Wz = 0.7 ;
parameters . mu = 0.01 ;
parameters . fi = 3.0 ;
parameters . teta_xz = 0.1 ;
parameters . teta_zx = 0.1 ;
parameters . ENABLE_POTENTIONAL = False ;
template_segmentation_image ( IMAGE_SIMPLE_SAMPLES . IMAGE_SIMPLE12 , parameters , 2000 , 2000 , True ) ;


signature:
def show_clusters ( sample , clusters , representatives , ** kwargs ) :
    """@ brief Display BSAS clustering results .
 @ param [ in ] sample ( list ) : Dataset that was used for clustering .
 @ param [ in ] clusters ( array _ like ) : Clusters that were allocated by the algorithm .
 @ param [ in ] representatives ( array _ like ) : Allocated representatives correspond to clusters .
 @ param [ in ] * * kwargs : Arbitrary keyword arguments ( available arguments : ' figure ' , ' display ' , ' offset ' ) .
 < b > Keyword Args : < / b > < br >
 - figure ( figure ) : If ' None ' then new is figure is created , otherwise specified figure is used for visualization .
 - display ( bool ) : If ' True ' then figure will be shown by the method , otherwise it should be shown manually using matplotlib function ' plt . show ( ) ' .
 - offset ( uint ) : Specify axes index on the figure where results should be drawn ( only if argument ' figure ' is specified ) .
 @ return ( figure ) Figure where clusters were drawn ."""

implementation:
figure = kwargs . get ( 'figure' , None )
display = kwargs . get ( 'display' , True )
offset = kwargs . get ( 'offset' , 0 )
visualizer = cluster_visualizer ( )
visualizer . append_clusters ( clusters , sample , canvas = offset )
for cluster_index in range ( len ( clusters ) ) :
    visualizer . append_cluster_attribute ( offset , cluster_index , [ representatives [ cluster_index ] ] , '*' , 10 )
return visualizer . show ( figure = figure , display = display )


signature:
def _find_nearest_cluster ( self , point ) :
    """@ brief Find nearest cluster to the specified point .
 @ param [ in ] point ( list ) : Point from dataset .
 @ return ( uint , double ) Index of nearest cluster and distance to it ."""

implementation:
index_cluster = - 1 ;
nearest_distance = float ( 'inf' ) ;
for index in range ( len ( self . _representatives ) ) :
    distance = self . _metric ( point , self . _representatives [ index ] ) ;
    if distance < nearest_distance :
        index_cluster = index ;
        nearest_distance = distance ;
return index_cluster , nearest_distance ;


signature:
def _update_representative ( self , index_cluster , point ) :
    """@ brief Update cluster representative in line with new cluster size and added point to it .
 @ param [ in ] index _ cluster ( uint ) : Index of cluster whose representative should be updated .
 @ param [ in ] point ( list ) : Point that was added to cluster ."""

implementation:
length = len ( self . _clusters [ index_cluster ] ) ;
rep = self . _representatives [ index_cluster ] ;
for dimension in range ( len ( rep ) ) :
    rep [ dimension ] = ( ( length - 1 ) * rep [ dimension ] + point [ dimension ] ) / length ;


signature:
def show_second_layer_dynamic ( analyser ) :
    """@ brief Shows output dynamic of the second layer .
 @ param [ in ] analyser ( syncsegm _ analyser ) : Analyser of output dynamic of the ' syncsegm ' oscillatory network ."""

implementation:
second_layer_analysers = analyser . get_second_layer_analysers ( ) ;
analysers_sequence = [ object_segment_analyser [ 'analyser' ] for object_segment_analyser in second_layer_analysers ]
sync_visualizer . show_output_dynamics ( analysers_sequence ) ;


signature:
def allocate_colors ( self , eps = 0.01 , noise_size = 1 ) :
    """@ brief Allocates color segments .
 @ param [ in ] eps ( double ) : Tolerance level that define maximal difference between phases of oscillators in one segment .
 @ param [ in ] noise _ size ( uint ) : Threshold that defines noise - segments size ( in pixels ) that is less then the threshold is considered as a noise .
 @ return ( list ) Color segments where each color segment consists of indexes of pixels that forms color segment ."""

implementation:
segments = self . __color_analyser . allocate_clusters ( eps ) ;
real_segments = [ cluster for cluster in segments if len ( cluster ) > noise_size ] ;
return real_segments ;


signature:
def allocate_objects ( self , eps = 0.01 , noise_size = 1 ) :
    """@ brief Allocates object segments .
 @ param [ in ] eps ( double ) : Tolerance level that define maximal difference between phases of oscillators in one segment .
 @ param [ in ] noise _ size ( uint ) : Threshold that defines noise - segments size ( in pixels ) that is less then the threshold is considered as a noise .
 @ return ( list ) Object segments where each object segment consists of indexes of pixels that forms object segment ."""

implementation:
if ( self . __object_segment_analysers is None ) :
    return [ ] ;
segments = [ ] ;
for object_segment_analyser in self . __object_segment_analysers :
    indexes = object_segment_analyser [ 'color_segment' ] ;
    analyser = object_segment_analyser [ 'analyser' ] ;
    segments += analyser . allocate_clusters ( eps , indexes ) ;
real_segments = [ segment for segment in segments if len ( segment ) > noise_size ] ;
return real_segments ;


signature:
def process ( self , image_source , collect_dynamic = False , order_color = 0.9995 , order_object = 0.999 ) :
    """@ brief Performs image segmentation .
 @ param [ in ] image _ source ( string ) : Path to image file that should be processed .
 @ param [ in ] collect _ dynamic ( bool ) : If ' True ' then whole dynamic of each layer of the network is collected .
 @ param [ in ] order _ color ( double ) : Local synchronization order for the first layer - coloring segmentation .
 @ param [ in ] order _ object ( double ) : Local synchronization order for the second layer - object segmentation .
 @ return ( syncsegm _ analyser ) Analyser of segmentation results by the network ."""

implementation:
self . __order_color = order_color
self . __order_object = order_object
data = read_image ( image_source )
color_analyser = self . __analyse_colors ( data , collect_dynamic )
if self . __object_radius is None :
    return syncsegm_analyser ( color_analyser , None )
object_segment_analysers = self . __analyse_objects ( image_source , color_analyser , collect_dynamic )
return syncsegm_analyser ( color_analyser , object_segment_analysers )


signature:
def __analyse_colors ( self , image_data , collect_dynamic ) :
    """@ brief Performs color segmentation by the first layer .
 @ param [ in ] image _ data ( array _ like ) : Image sample as a array - like structure .
 @ param [ in ] collect _ dynamic ( bool ) : If ' True ' then whole dynamic of the first layer of the network is collected .
 @ return ( syncnet _ analyser ) Analyser of color segmentation results of the first layer ."""

implementation:
network = syncnet ( image_data , self . __color_radius , initial_phases = initial_type . RANDOM_GAUSSIAN , ccore = self . __ccore ) ;
analyser = network . process ( self . __order_color , solve_type . FAST , collect_dynamic ) ;
return analyser ;


signature:
def __analyse_objects ( self , image_source , color_analyser , collect_dynamic ) :
    """@ brief Performs object segmentation by the second layer .
 @ param [ in ] image _ source ( string ) : Path to image file that should be processed .
 @ param [ in ] color _ analyser ( syncnet _ analyser ) : Analyser of color segmentation results .
 @ param [ in ] collect _ dynamic ( bool ) : If ' True ' then whole dynamic of the first layer of the network is collected .
 @ return ( map ) Analysers of object segments ."""

implementation:
# continue analysis
pointer_image = Image . open ( image_source ) ;
image_size = pointer_image . size ;
object_analysers = [ ] ;
color_segments = color_analyser . allocate_clusters ( ) ;
for segment in color_segments :
    object_analyser = self . __analyse_color_segment ( image_size , segment , collect_dynamic ) ;
    if ( object_analyser is not None ) :
        object_analysers . append ( { 'color_segment' : segment , 'analyser' : object_analyser } ) ;
pointer_image . close ( ) ;
return object_analysers ;


signature:
def __analyse_color_segment ( self , image_size , color_segment , collect_dynamic ) :
    """@ brief Performs object segmentation of separate segment .
 @ param [ in ] image _ size ( list ) : Image size presented as a [ width x height ] .
 @ param [ in ] color _ segment ( list ) : Image segment that should be processed .
 @ param [ in ] collect _ dynamic ( bool ) : If ' True ' then whole dynamic of the second layer of the network is collected .
 @ return ( syncnet _ analyser ) Analyser of object segmentation results of the second layer ."""

implementation:
coordinates = self . __extract_location_coordinates ( image_size , color_segment ) ;
if ( len ( coordinates ) < self . __noise_size ) :
    return None ;
network = syncnet ( coordinates , self . __object_radius , initial_phases = initial_type . EQUIPARTITION , ccore = True ) ;
analyser = network . process ( self . __order_object , solve_type . FAST , collect_dynamic ) ;
return analyser ;


signature:
def __extract_location_coordinates ( self , image_size , color_segment ) :
    """@ brief Extracts coordinates of specified image segment .
 @ param [ in ] image _ size ( list ) : Image size presented as a [ width x height ] .
 @ param [ in ] color _ segment ( list ) : Image segment whose coordinates should be extracted .
 @ return ( list ) Coordinates of each pixel ."""

implementation:
coordinates = [ ] ;
for index in color_segment :
    y = floor ( index / image_size [ 0 ] ) ;
    x = index - y * image_size [ 0 ] ;
    coordinates . append ( [ x , y ] ) ;
return coordinates ;


signature:
def process ( self ) :
    """@ brief Perform graph coloring using DSATUR algorithm .
 @ see get _ colors ( )"""

implementation:
color_counter = 1 ;
degrees = list ( ) ;
saturation_degrees = [ 0 ] * len ( self . __data_pointer ) ;
self . __coloring = [ 0 ] * len ( self . __data_pointer ) ;
uncolored_vertices = set ( range ( len ( self . __data_pointer ) ) ) ;
index_maximum_degree = 0 ;
maximum_degree = 0 ;
for index_node in range ( len ( self . __data_pointer ) ) : # Fill degree of nodes in the input graph
    degrees . append ( ( sum ( self . __data_pointer [ index_node ] ) , index_node ) ) ;
    # And find node with maximal degree at the same time .
    if ( degrees [ index_node ] [ 0 ] > maximum_degree ) :
        ( maximum_degree , node_index ) = degrees [ index_node ] ;
        index_maximum_degree = index_node ;
# Update saturation
neighbors = self . __get_neighbors ( index_maximum_degree ) ;
for index_neighbor in neighbors :
    saturation_degrees [ index_neighbor ] += 1 ;
# Coloring the first node
self . __coloring [ index_maximum_degree ] = color_counter ;
uncolored_vertices . remove ( index_maximum_degree ) ;
while ( len ( uncolored_vertices ) > 0 ) : # Get maximum saturation degree
    maximum_satur_degree = - 1 ;
    for index in uncolored_vertices :
        if ( saturation_degrees [ index ] > maximum_satur_degree ) :
            maximum_satur_degree = saturation_degrees [ index ] ;
    # Get list of indexes with maximum saturation degree
    indexes_maximum_satur_degree = [ index for index in uncolored_vertices if saturation_degrees [ index ] == maximum_satur_degree ] ;
    coloring_index = indexes_maximum_satur_degree [ 0 ] ;
    if ( len ( indexes_maximum_satur_degree ) > 1 ) : # There are more then one node with maximum saturation
    # Find node with maximum degree
        maximum_degree = - 1 ;
        for index in indexes_maximum_satur_degree :
            ( degree , node_index ) = degrees [ index ] ;
            if ( degree > maximum_degree ) :
                coloring_index = node_index ;
                maximum_degree = degree ;
    # Coloring
    node_index_neighbors = self . __get_neighbors ( coloring_index ) ;
    for number_color in range ( 1 , color_counter + 1 , 1 ) :
        if ( self . __get_amount_color ( node_index_neighbors , number_color ) == 0 ) :
            self . __coloring [ coloring_index ] = number_color ;
            break ;
    # If it has not been colored then
    if ( self . __coloring [ coloring_index ] == 0 ) :
        color_counter += 1 ;
        # Add new color
        self . __coloring [ coloring_index ] = color_counter ;
    # Remove node from uncolored set
    uncolored_vertices . remove ( coloring_index ) ;
    # Update degree of saturation
    for index_neighbor in node_index_neighbors :
        subneighbors = self . __get_neighbors ( index_neighbor ) ;
        if ( self . __get_amount_color ( subneighbors , self . __coloring [ coloring_index ] ) == 1 ) :
            saturation_degrees [ index_neighbor ] += 1 ;


signature:
def __get_amount_color ( self , node_indexes , color_number ) :
    """@ brief Countes how many nodes has color ' color _ number ' .
 @ param [ in ] node _ indexes ( list ) : Indexes of graph nodes for checking .
 @ param [ in ] color _ number ( uint ) : Number of color that is searched in nodes .
 @ return ( uint ) Number found nodes with the specified color ' color _ number ' ."""

implementation:
color_counter = 0 ;
for index in node_indexes :
    if ( self . __coloring [ index ] == color_number ) :
        color_counter += 1 ;
return color_counter ;


signature:
def __get_neighbors ( self , node_index ) :
    """@ brief Returns indexes of neighbors of the specified node .
 @ param [ in ] node _ index ( uint ) :
 @ return ( list ) Neighbors of the specified node ."""

implementation: return [ index for index in range ( len ( self . __data_pointer [ node_index ] ) ) if self . __data_pointer [ node_index ] [ index ] != 0 ] ;


signature:
def show_blocks ( directory ) :
    """@ brief Show BANG - blocks ( leafs only ) in data space .
 @ details BANG - blocks represents grid that was used for clustering process .
 @ param [ in ] directory ( bang _ directory ) : Directory that was created by BANG algorithm during clustering process ."""

implementation:
dimension = len ( directory . get_data ( ) [ 0 ] )
amount_canvases = 1
if dimension > 1 :
    amount_canvases = int ( dimension * ( dimension - 1 ) / 2 )
figure = plt . figure ( )
grid_spec = gridspec . GridSpec ( 1 , amount_canvases )
pairs = list ( itertools . combinations ( range ( dimension ) , 2 ) )
if len ( pairs ) == 0 :
    pairs = [ ( 0 , 0 ) ]
for index in range ( amount_canvases ) :
    ax = figure . add_subplot ( grid_spec [ index ] )
    bang_visualizer . __draw_blocks ( ax , directory . get_leafs ( ) , pairs [ index ] )
    bang_visualizer . __draw_two_dimension_data ( ax , directory . get_data ( ) , pairs [ index ] )
plt . show ( )


signature:
def show_dendrogram ( dendrogram ) :
    """@ brief Display dendrogram of BANG - blocks .
 @ param [ in ] dendrogram ( list ) : List representation of dendrogram of BANG - blocks .
 @ see bang . get _ dendrogram ( )"""

implementation:
plt . figure ( )
axis = plt . subplot ( 1 , 1 , 1 )
current_position = 0
for index_cluster in range ( len ( dendrogram ) ) :
    densities = [ block . get_density ( ) for block in dendrogram [ index_cluster ] ]
    xrange = range ( current_position , current_position + len ( densities ) )
    axis . bar ( xrange , densities , 1.0 , linewidth = 0.0 , color = color_list . get_color ( index_cluster ) )
    current_position += len ( densities )
axis . set_ylabel ( "density" )
axis . set_xlabel ( "block" )
axis . xaxis . set_ticklabels ( [ ] )
plt . xlim ( [ - 0.5 , current_position - 0.5 ] )
plt . show ( )


signature:
def __draw_blocks ( ax , blocks , pair ) :
    """@ brief Display BANG - blocks on specified figure .
 @ param [ in ] ax ( Axis ) : Axis where bang - blocks should be displayed .
 @ param [ in ] blocks ( list ) : List of blocks that should be displyed .
 @ param [ in ] pair ( tuple ) : Pair of coordinate index that should be displayed ."""

implementation:
ax . grid ( False )
density_scale = blocks [ - 1 ] . get_density ( )
for block in blocks :
    bang_visualizer . __draw_block ( ax , pair , block , density_scale )


signature:
def __draw_block ( ax , pair , block , density_scale ) :
    """@ brief Display BANG - block on the specified ax .
 @ param [ in ] ax ( Axis ) : Axis where block should be displayed .
 @ param [ in ] pair ( tuple ) : Pair of coordinate index that should be displayed .
 @ param [ in ] block ( bang _ block ) : BANG - block that should be displayed .
 @ param [ in ] density _ scale ( double ) : Max density to display density of the block by appropriate tone ."""

implementation:
max_corner , min_corner = bang_visualizer . __get_rectangle_description ( block , pair )
belong_cluster = block . get_cluster ( ) is not None
if density_scale != 0.0 :
    density_scale = bang_visualizer . __maximum_density_alpha * block . get_density ( ) / density_scale
face_color = matplotlib . colors . to_rgba ( 'blue' , alpha = density_scale )
edge_color = matplotlib . colors . to_rgba ( 'black' , alpha = 1.0 )
rect = patches . Rectangle ( min_corner , max_corner [ 0 ] - min_corner [ 0 ] , max_corner [ 1 ] - min_corner [ 1 ] , fill = belong_cluster , facecolor = face_color , edgecolor = edge_color , linewidth = 0.5 )
ax . add_patch ( rect )


signature:
def __get_rectangle_description ( block , pair ) :
    """@ brief Create rectangle description for block in specific dimension .
 @ param [ in ] pair ( tuple ) : Pair of coordinate index that should be displayed .
 @ param [ in ] block ( bang _ block ) : BANG - block that should be displayed
 @ return ( tuple ) Pair of corners that describes rectangle ."""

implementation:
max_corner , min_corner = block . get_spatial_block ( ) . get_corners ( )
max_corner = [ max_corner [ pair [ 0 ] ] , max_corner [ pair [ 1 ] ] ]
min_corner = [ min_corner [ pair [ 0 ] ] , min_corner [ pair [ 1 ] ] ]
if pair == ( 0 , 0 ) :
    max_corner [ 1 ] , min_corner [ 1 ] = 1.0 , - 1.0
return max_corner , min_corner


signature:
def __increment_block ( self ) :
    """@ brief Increment BANG block safely by updating block index , level and level block ."""

implementation:
self . __current_block += 1
if self . __current_block >= len ( self . __level_blocks ) :
    self . __current_block = 0
    self . __current_level += 1
    if self . __current_level < self . __directory . get_height ( ) :
        self . __level_blocks = self . __directory . get_level ( self . __current_level )


signature:
def __draw_block ( self , block , block_alpha = 0.0 ) :
    """@ brief Display single BANG block on axis .
 @ param [ in ] block ( bang _ block ) : BANG block that should be displayed .
 @ param [ in ] block _ alpha ( double ) : Transparency level - value of alpha ."""

implementation:
max_corner , min_corner = block . get_spatial_block ( ) . get_corners ( )
face_color = matplotlib . colors . to_rgba ( 'blue' , alpha = block_alpha )
edge_color = matplotlib . colors . to_rgba ( 'black' , alpha = 1.0 )
rect = patches . Rectangle ( min_corner , max_corner [ 0 ] - min_corner [ 0 ] , max_corner [ 1 ] - min_corner [ 1 ] , fill = True , facecolor = face_color , edgecolor = edge_color , linewidth = 0.5 )
self . __ax . add_patch ( rect )


signature:
def __draw_leaf_density ( self ) :
    """@ brief Display densities by filling blocks by appropriate colors ."""

implementation:
leafs = self . __directory . get_leafs ( )
density_scale = leafs [ - 1 ] . get_density ( )
if density_scale == 0.0 :
    density_scale = 1.0
for block in leafs :
    alpha = 0.8 * block . get_density ( ) / density_scale
    self . __draw_block ( block , alpha )


signature:
def __draw_clusters ( self ) :
    """@ brief Display clusters and outliers using different colors ."""

implementation:
data = self . __directory . get_data ( )
for index_cluster in range ( len ( self . __clusters ) ) :
    color = color_list . get_color ( index_cluster )
    self . __draw_cluster ( data , self . __clusters [ index_cluster ] , color , '.' )
self . __draw_cluster ( self . __directory . get_data ( ) , self . __noise , 'gray' , 'x' )


signature:
def __draw_cluster ( self , data , cluster , color , marker ) :
    """@ brief Draw 2 - D single cluster on axis using specified color and marker ."""

implementation:
for item in cluster :
    self . __ax . plot ( data [ item ] [ 0 ] , data [ item ] [ 1 ] , color = color , marker = marker )


signature:
def animate ( self , animation_velocity = 75 , movie_fps = 25 , movie_filename = None ) :
    """@ brief Animates clustering process that is performed by BANG algorithm .
 @ param [ in ] animation _ velocity ( uint ) : Interval between frames in milliseconds ( for run - time animation only ) .
 @ param [ in ] movie _ fps ( uint ) : Defines frames per second ( for rendering movie only ) .
 @ param [ in ] movie _ filename ( string ) : If it is specified then animation will be stored to file that is specified in this parameter ."""

implementation:
def init_frame ( ) :
    self . __figure . clf ( )
    self . __ax = self . __figure . add_subplot ( 1 , 1 , 1 )
    self . __figure . suptitle ( "BANG algorithm" , fontsize = 18 , fontweight = 'bold' )
    for point in self . __directory . get_data ( ) :
        self . __ax . plot ( point [ 0 ] , point [ 1 ] , color = 'red' , marker = '.' )
    return frame_generation ( 0 )
def frame_generation ( index_iteration ) :
    if self . __current_level < self . __directory . get_height ( ) :
        block = self . __level_blocks [ self . __current_block ]
        self . __draw_block ( block )
        self . __increment_block ( )
    else :
        if self . __special_frame == 0 :
            self . __draw_leaf_density ( )
        elif self . __special_frame == 15 :
            self . __draw_clusters ( )
        elif self . __special_frame == 30 :
            self . __figure . clf ( )
            self . __ax = self . __figure . add_subplot ( 1 , 1 , 1 )
            self . __figure . suptitle ( "BANG algorithm" , fontsize = 18 , fontweight = 'bold' )
            self . __draw_clusters ( )
        self . __special_frame += 1
iterations = len ( self . __directory ) + 60
# print ( " Total number of iterations : % d " % iterations )
cluster_animation = animation . FuncAnimation ( self . __figure , frame_generation , iterations , interval = animation_velocity , init_func = init_frame , repeat_delay = 5000 )
if movie_filename is not None :
    cluster_animation . save ( movie_filename , writer = 'ffmpeg' , fps = movie_fps , bitrate = 3500 )
else :
    plt . show ( )


signature:
def __create_directory ( self ) :
    """@ brief Create BANG directory as a tree with separate storage for leafs ."""

implementation:
min_corner , max_corner = data_corners ( self . __data )
data_block = spatial_block ( max_corner , min_corner )
cache_require = ( self . __levels == 1 )
self . __root = bang_block ( self . __data , 0 , 0 , data_block , cache_require )
if cache_require :
    self . __leafs . append ( self . __root )
    self . __store_level_blocks ( [ self . __root ] )
else :
    self . __build_directory_levels ( )


signature:
def __store_level_blocks ( self , level_blocks ) :
    """@ brief Store level blocks if observing is enabled .
 @ param [ in ] level _ blocks ( list ) : Created blocks on a new level ."""

implementation:
self . __size += len ( level_blocks )
if self . __observe is True :
    self . __level_blocks . append ( level_blocks )


signature:
def __build_directory_levels ( self ) :
    """@ brief Build levels of direction if amount of level is greater than one ."""

implementation:
previous_level_blocks = [ self . __root ]
for level in range ( 1 , self . __levels ) :
    previous_level_blocks = self . __build_level ( previous_level_blocks , level )
    self . __store_level_blocks ( previous_level_blocks )
self . __leafs = sorted ( self . __leafs , key = lambda block : block . get_density ( ) )


signature:
def __build_level ( self , previous_level_blocks , level ) :
    """@ brief Build new level of directory .
 @ param [ in ] previous _ level _ blocks ( list ) : BANG - blocks on the previous level .
 @ param [ in ] level ( uint ) : Level number that should be built .
 @ return ( list ) New block on the specified level ."""

implementation:
current_level_blocks = [ ]
split_dimension = level % len ( self . __data [ 0 ] )
cache_require = ( level == self . __levels - 1 )
for block in previous_level_blocks :
    self . __split_block ( block , split_dimension , cache_require , current_level_blocks )
if cache_require :
    self . __leafs += current_level_blocks
return current_level_blocks


signature:
def __split_block ( self , block , split_dimension , cache_require , current_level_blocks ) :
    """@ brief Split specific block in specified dimension .
 @ details Split is not performed for block whose density is lower than threshold value , such blocks are putted to
 leafs .
 @ param [ in ] block ( bang _ block ) : BANG - block that should be split .
 @ param [ in ] split _ dimension ( uint ) : Dimension at which splitting should be performed .
 @ param [ in ] cache _ require ( bool ) : Defines when points in cache should be stored during density calculation .
 @ param [ in | out ] current _ level _ blocks ( list ) : Block storage at the current level where new blocks should be added ."""

implementation:
if block . get_density ( ) <= self . __density_threshold or len ( block ) <= self . __amount_density :
    self . __leafs . append ( block )
else :
    left , right = block . split ( split_dimension , cache_require )
    current_level_blocks . append ( left )
    current_level_blocks . append ( right )


signature:
def split ( self , dimension ) :
    """@ brief Split current block into two spatial blocks in specified dimension .
 @ param [ in ] dimension ( uint ) : Dimension where current block should be split .
 @ return ( tuple ) Pair of new split blocks from current block ."""

implementation:
first_max_corner = self . __max_corner [ : ]
second_min_corner = self . __min_corner [ : ]
split_border = ( self . __max_corner [ dimension ] + self . __min_corner [ dimension ] ) / 2.0
first_max_corner [ dimension ] = split_border
second_min_corner [ dimension ] = split_border
return spatial_block ( first_max_corner , self . __min_corner ) , spatial_block ( self . __max_corner , second_min_corner )


signature:
def is_neighbor ( self , block ) :
    """@ brief Performs calculation to identify whether specified block is neighbor of current block .
 @ details It also considers diagonal blocks as neighbors .
 @ param [ in ] block ( spatial _ block ) : Another block that is check whether it is neighbor .
 @ return ( bool ) True is blocks are neighbors , False otherwise ."""

implementation:
if block is not self :
    block_max_corner , _ = block . get_corners ( )
    dimension = len ( block_max_corner )
    neighborhood_score = self . __calculate_neighborhood ( block_max_corner )
    if neighborhood_score == dimension :
        return True
return False


signature:
def __calculate_neighborhood ( self , block_max_corner ) :
    """@ brief Calculates neighborhood score that defined whether blocks are neighbors .
 @ param [ in ] block _ max _ corner ( list ) : Maximum coordinates of other block .
 @ return ( uint ) Neighborhood score ."""

implementation:
dimension = len ( block_max_corner )
length_edges = [ self . __max_corner [ i ] - self . __min_corner [ i ] for i in range ( dimension ) ]
neighborhood_score = 0
for i in range ( dimension ) :
    diff = abs ( block_max_corner [ i ] - self . __max_corner [ i ] )
    if diff <= length_edges [ i ] + length_edges [ i ] * 0.0001 :
        neighborhood_score += 1
return neighborhood_score


signature:
def __calculate_volume ( self ) :
    """@ brief Calculates volume of current spatial block .
 @ details If empty dimension is detected ( where all points has the same value ) then such dimension is ignored
 during calculation of volume .
 @ return ( double ) Volume of current spatial block ."""

implementation:
volume = 0.0
for i in range ( 0 , len ( self . __max_corner ) ) :
    side_length = self . __max_corner [ i ] - self . __min_corner [ i ]
    if side_length != 0.0 :
        if volume == 0.0 :
            volume = side_length
        else :
            volume *= side_length
return volume


signature:
def split ( self , split_dimension , cache_points ) :
    """@ brief Split BANG - block into two new blocks in specified dimension .
 @ param [ in ] split _ dimension ( uint ) : Dimension where block should be split .
 @ param [ in ] cache _ points ( bool ) : If True then covered points are cached . Used for leaf blocks .
 @ return ( tuple ) Pair of BANG - block that were formed from the current ."""

implementation:
left_region_number = self . __region_number
right_region_number = self . __region_number + 2 ** self . __level
first_spatial_block , second_spatial_block = self . __spatial_block . split ( split_dimension )
left = bang_block ( self . __data , left_region_number , self . __level + 1 , first_spatial_block , cache_points )
right = bang_block ( self . __data , right_region_number , self . __level + 1 , second_spatial_block , cache_points )
return left , right


signature:
def __calculate_density ( self , amount_points ) :
    """@ brief Calculates BANG - block density .
 @ param [ in ] amount _ points ( uint ) : Amount of points in block .
 @ return ( double ) BANG - block density ."""

implementation:
volume = self . __spatial_block . get_volume ( )
if volume != 0.0 :
    return amount_points / volume
return 0.0


signature:
def __get_amount_points ( self ) :
    """@ brief Count covered points by the BANG - block and if cache is enable then covered points are stored .
 @ return ( uint ) Amount of covered points ."""

implementation:
amount = 0
for index in range ( len ( self . __data ) ) :
    if self . __data [ index ] in self . __spatial_block :
        self . __cache_point ( index )
        amount += 1
return amount


signature:
def __cache_covered_data ( self ) :
    """@ brief Cache covered data ."""

implementation:
self . __cache_points = True
self . __points = [ ]
for index_point in range ( len ( self . __data ) ) :
    if self . __data [ index_point ] in self . __spatial_block :
        self . __cache_point ( index_point )


signature:
def __cache_point ( self , index ) :
    """@ brief Store index points .
 @ param [ in ] index ( uint ) : Index point that should be stored ."""

implementation:
if self . __cache_points :
    if self . __points is None :
        self . __points = [ ]
    self . __points . append ( index )


signature:
def process ( self ) :
    """@ brief Performs clustering process in line with rules of BANG clustering algorithm .
 @ return ( bang ) Returns itself ( BANG instance ) .
 @ see get _ clusters ( )
 @ see get _ noise ( )
 @ see get _ directory ( )
 @ see get _ dendrogram ( )"""

implementation:
self . __directory = bang_directory ( self . __data , self . __levels , density_threshold = self . __density_threshold , amount_threshold = self . __amount_threshold )
self . __allocate_clusters ( )
return self


signature:
def __validate_arguments ( self ) :
    """@ brief Check input arguments of BANG algorithm and if one of them is not correct then appropriate exception
 is thrown ."""

implementation:
if self . __levels <= 0 :
    raise ValueError ( "Incorrect amount of levels '%d'. Level value should be greater than 0." % self . __levels )
if len ( self . __data ) == 0 :
    raise ValueError ( "Empty input data. Data should contain at least one point." )
if self . __density_threshold < 0 :
    raise ValueError ( "Incorrect density threshold '%f'. Density threshold should not be negative." % self . __density_threshold )


signature:
def __allocate_clusters ( self ) :
    """@ brief Performs cluster allocation using leafs of tree in BANG directory ( the smallest cells ) ."""

implementation:
leaf_blocks = self . __directory . get_leafs ( )
unhandled_block_indexes = set ( [ i for i in range ( len ( leaf_blocks ) ) if leaf_blocks [ i ] . get_density ( ) > self . __density_threshold ] )
current_block = self . __find_block_center ( leaf_blocks , unhandled_block_indexes )
cluster_index = 0
while current_block is not None :
    if current_block . get_density ( ) <= self . __density_threshold or len ( current_block ) <= self . __amount_threshold :
        break
    self . __expand_cluster_block ( current_block , cluster_index , leaf_blocks , unhandled_block_indexes )
    current_block = self . __find_block_center ( leaf_blocks , unhandled_block_indexes )
    cluster_index += 1
self . __store_clustering_results ( cluster_index , leaf_blocks )


signature:
def __expand_cluster_block ( self , block , cluster_index , leaf_blocks , unhandled_block_indexes ) :
    """@ brief Expand cluster from specific block that is considered as a central block .
 @ param [ in ] block ( bang _ block ) : Block that is considered as a central block for cluster .
 @ param [ in ] cluster _ index ( uint ) : Index of cluster that is assigned to blocks that forms new cluster .
 @ param [ in ] leaf _ blocks ( list ) : Leaf BANG - blocks that are considered during cluster formation .
 @ param [ in ] unhandled _ block _ indexes ( set ) : Set of candidates ( BANG block indexes ) to become a cluster member . The
 parameter helps to reduce traversing among BANG - block providing only restricted set of block that
 should be considered ."""

implementation:
block . set_cluster ( cluster_index )
self . __update_cluster_dendrogram ( cluster_index , [ block ] )
neighbors = self . __find_block_neighbors ( block , leaf_blocks , unhandled_block_indexes )
self . __update_cluster_dendrogram ( cluster_index , neighbors )
for neighbor in neighbors :
    neighbor . set_cluster ( cluster_index )
    neighbor_neighbors = self . __find_block_neighbors ( neighbor , leaf_blocks , unhandled_block_indexes )
    self . __update_cluster_dendrogram ( cluster_index , neighbor_neighbors )
    neighbors += neighbor_neighbors


signature:
def __store_clustering_results ( self , amount_clusters , leaf_blocks ) :
    """@ brief Stores clustering results in a convenient way .
 @ param [ in ] amount _ clusters ( uint ) : Amount of cluster that was allocated during processing .
 @ param [ in ] leaf _ blocks ( list ) : Leaf BANG - blocks ( the smallest cells ) ."""

implementation:
self . __clusters = [ [ ] for _ in range ( amount_clusters ) ]
for block in leaf_blocks :
    index = block . get_cluster ( )
    if index is not None :
        self . __clusters [ index ] += block . get_points ( )
    else :
        self . __noise += block . get_points ( )
self . __clusters = [ list ( set ( cluster ) ) for cluster in self . __clusters ]
self . __noise = list ( set ( self . __noise ) )


signature:
def __find_block_center ( self , level_blocks , unhandled_block_indexes ) :
    """@ brief Search block that is cluster center for new cluster .
 @ return ( bang _ block ) Central block for new cluster , if cluster is not found then None value is returned ."""

implementation:
for i in reversed ( range ( len ( level_blocks ) ) ) :
    if level_blocks [ i ] . get_density ( ) <= self . __density_threshold :
        return None
    if level_blocks [ i ] . get_cluster ( ) is None :
        unhandled_block_indexes . remove ( i )
        return level_blocks [ i ]
return None


signature:
def __find_block_neighbors ( self , block , level_blocks , unhandled_block_indexes ) :
    """@ brief Search block neighbors that are parts of new clusters ( density is greater than threshold and that are
 not cluster members yet ) , other neighbors are ignored .
 @ param [ in ] block ( bang _ block ) : BANG - block for which neighbors should be found ( which can be part of cluster ) .
 @ param [ in ] level _ blocks ( list ) : BANG - blocks on specific level .
 @ param [ in ] unhandled _ block _ indexes ( set ) : Blocks that have not been processed yet .
 @ return ( list ) Block neighbors that can become part of cluster ."""

implementation:
neighbors = [ ]
handled_block_indexes = [ ]
for unhandled_index in unhandled_block_indexes :
    if block . is_neighbor ( level_blocks [ unhandled_index ] ) :
        handled_block_indexes . append ( unhandled_index )
        neighbors . append ( level_blocks [ unhandled_index ] )
        # Maximum number of neighbors is eight
        if len ( neighbors ) == 8 :
            break
for handled_index in handled_block_indexes :
    unhandled_block_indexes . remove ( handled_index )
return neighbors


signature:
def __update_cluster_dendrogram ( self , index_cluster , blocks ) :
    """@ brief Append clustered blocks to dendrogram .
 @ param [ in ] index _ cluster ( uint ) : Cluster index that was assigned to blocks .
 @ param [ in ] blocks ( list ) : Blocks that were clustered ."""

implementation:
if len ( self . __dendrogram ) <= index_cluster :
    self . __dendrogram . append ( [ ] )
blocks = sorted ( blocks , key = lambda block : block . get_density ( ) , reverse = True )
self . __dendrogram [ index_cluster ] += blocks


signature:
def allocate_map_coloring ( self , tolerance , threshold_steps = 10 ) :
    """@ brief Returns list of color indexes that are assigned to each object from input data space accordingly .
 @ param [ in ] tolerance ( double ) : Tolerance level that define maximal difference between outputs of oscillators in one synchronous ensemble .
 @ param [ in ] threshold _ steps ( uint ) : Number of steps from the end of simulation that should be analysed for ensemble allocation .
 If amount of simulation steps has been less than threshold steps than amount of steps will be reduced to amount
 of simulation steps .
 @ remark Results can be obtained only after network simulation ( graph processing by the network ) .
 @ return ( list ) Color indexes that are assigned to each object from input data space accordingly .
 @ see allocate _ clusters ( )"""

implementation:
clusters = self . allocate_clusters ( tolerance , threshold_steps )
coloring_map = [ 0 ] * len ( self . _dynamic [ 0 ] )
for color_index in range ( len ( clusters ) ) :
    for node_index in clusters [ color_index ] :
        coloring_map [ node_index ] = color_index
return coloring_map


signature:
def process ( self , steps , time , collect_dynamic = True ) :
    """@ brief Peforms graph coloring analysis using simulation of the oscillatory network .
 @ param [ in ] steps ( uint ) : Number steps of simulations during simulation .
 @ param [ in ] time ( double ) : Time of simulation .
 @ param [ in ] collect _ dynamic ( bool ) : Specified requirement to collect whole dynamic of the network .
 @ return ( hysteresis _ analyser ) Returns analyser of results of clustering ."""

implementation:
output_dynamic = super ( ) . simulate ( steps , time , collect_dynamic = collect_dynamic )
return hysteresis_analyser ( output_dynamic . output , output_dynamic . time )


signature:
def process ( self ) :
    """@ brief Performs cluster analysis in line with rules of ROCK algorithm .
 @ remark Results of clustering can be obtained using corresponding get methods .
 @ see get _ clusters ( )"""

implementation:
# TODO : ( Not related to specification , just idea ) First iteration should be investigated . Euclidean distance should be used for clustering between two
# points and rock algorithm between clusters because we consider non - categorical samples . But it is required more investigations .
if ( self . __ccore is True ) :
    self . __clusters = wrapper . rock ( self . __pointer_data , self . __eps , self . __number_clusters , self . __threshold ) ;
else :
    self . __clusters = [ [ index ] for index in range ( len ( self . __pointer_data ) ) ] ;
    while ( len ( self . __clusters ) > self . __number_clusters ) :
        indexes = self . __find_pair_clusters ( self . __clusters ) ;
        if ( indexes != [ - 1 , - 1 ] ) :
            self . __clusters [ indexes [ 0 ] ] += self . __clusters [ indexes [ 1 ] ] ;
            self . __clusters . pop ( indexes [ 1 ] ) ;
            # remove merged cluster .
        else :
            break ;


signature:
def __find_pair_clusters ( self , clusters ) :
    """@ brief Returns pair of clusters that are best candidates for merging in line with goodness measure .
 The pair of clusters for which the above goodness measure is maximum is the best pair of clusters to be merged .
 @ param [ in ] clusters ( list ) : List of clusters that have been allocated during processing , each cluster is represented by list of indexes of points from the input data set .
 @ return ( list ) List that contains two indexes of clusters ( from list ' clusters ' ) that should be merged on this step .
 It can be equals to [ - 1 , - 1 ] when no links between clusters ."""

implementation:
maximum_goodness = 0.0 ;
cluster_indexes = [ - 1 , - 1 ] ;
for i in range ( 0 , len ( clusters ) ) :
    for j in range ( i + 1 , len ( clusters ) ) :
        goodness = self . __calculate_goodness ( clusters [ i ] , clusters [ j ] ) ;
        if ( goodness > maximum_goodness ) :
            maximum_goodness = goodness ;
            cluster_indexes = [ i , j ] ;
return cluster_indexes ;


signature:
def __calculate_links ( self , cluster1 , cluster2 ) :
    """@ brief Returns number of link between two clusters .
 @ details Link between objects ( points ) exists only if distance between them less than connectivity radius .
 @ param [ in ] cluster1 ( list ) : The first cluster .
 @ param [ in ] cluster2 ( list ) : The second cluster .
 @ return ( uint ) Number of links between two clusters ."""

implementation:
number_links = 0 ;
for index1 in cluster1 :
    for index2 in cluster2 :
        number_links += self . __adjacency_matrix [ index1 ] [ index2 ] ;
return number_links ;


signature:
def __create_adjacency_matrix ( self ) :
    """@ brief Creates 2D adjacency matrix ( list of lists ) where each element described existence of link between points ( means that points are neighbors ) ."""

implementation:
size_data = len ( self . __pointer_data ) ;
self . __adjacency_matrix = [ [ 0 for i in range ( size_data ) ] for j in range ( size_data ) ] ;
for i in range ( 0 , size_data ) :
    for j in range ( i + 1 , size_data ) :
        distance = euclidean_distance ( self . __pointer_data [ i ] , self . __pointer_data [ j ] ) ;
        if ( distance <= self . __eps ) :
            self . __adjacency_matrix [ i ] [ j ] = 1 ;
            self . __adjacency_matrix [ j ] [ i ] = 1 ;


signature:
def __calculate_goodness ( self , cluster1 , cluster2 ) :
    """@ brief Calculates coefficient ' goodness measurement ' between two clusters . The coefficient defines level of suitability of clusters for merging .
 @ param [ in ] cluster1 ( list ) : The first cluster .
 @ param [ in ] cluster2 ( list ) : The second cluster .
 @ return Goodness measure between two clusters ."""

implementation:
number_links = self . __calculate_links ( cluster1 , cluster2 ) ;
devider = ( len ( cluster1 ) + len ( cluster2 ) ) ** self . __degree_normalization - len ( cluster1 ) ** self . __degree_normalization - len ( cluster2 ) ** self . __degree_normalization ;
return ( number_links / devider ) ;


signature:
def __process_by_ccore ( self ) :
    """@ brief Performs processing using CCORE ( C / C + + part of pyclustering library ) ."""

implementation:
ccore_metric = metric_wrapper . create_instance ( self . __metric )
self . __score = wrapper . silhoeutte ( self . __data , self . __clusters , ccore_metric . get_pointer ( ) )


signature:
def __process_by_python ( self ) :
    """@ brief Performs processing using python code ."""

implementation:
for index_cluster in range ( len ( self . __clusters ) ) :
    for index_point in self . __clusters [ index_cluster ] :
        self . __score [ index_point ] = self . __calculate_score ( index_point , index_cluster )


signature:
def __calculate_score ( self , index_point , index_cluster ) :
    """@ brief Calculates Silhouette score for the specific object defined by index _ point .
 @ param [ in ] index _ point ( uint ) : Index point from input data for which Silhouette score should be calculated .
 @ param [ in ] index _ cluster ( uint ) : Index cluster to which the point belongs to .
 @ return ( float ) Silhouette score for the object ."""

implementation:
difference = self . __calculate_dataset_difference ( index_point )
a_score = self . __calculate_within_cluster_score ( index_cluster , difference )
b_score = self . __caclulate_optimal_neighbor_cluster_score ( index_cluster , difference )
return ( b_score - a_score ) / max ( a_score , b_score )


signature:
def __calculate_within_cluster_score ( self , index_cluster , difference ) :
    """@ brief Calculates ' A ' score for the specific object in cluster to which it belongs to .
 @ param [ in ] index _ point ( uint ) : Index point from input data for which ' A ' score should be calculated .
 @ param [ in ] index _ cluster ( uint ) : Index cluster to which the point is belong to .
 @ return ( float ) ' A ' score for the object ."""

implementation:
score = self . __calculate_cluster_difference ( index_cluster , difference )
if len ( self . __clusters [ index_cluster ] ) == 1 :
    return float ( 'nan' )
return score / ( len ( self . __clusters [ index_cluster ] ) - 1 )


signature:
def __calculate_cluster_score ( self , index_cluster , difference ) :
    """@ brief Calculates ' B * ' score for the specific object for specific cluster .
 @ param [ in ] index _ point ( uint ) : Index point from input data for which ' B * ' score should be calculated .
 @ param [ in ] index _ cluster ( uint ) : Index cluster to which the point is belong to .
 @ return ( float ) ' B * ' score for the object for specific cluster ."""

implementation:
score = self . __calculate_cluster_difference ( index_cluster , difference )
return score / len ( self . __clusters [ index_cluster ] )


signature:
def __caclulate_optimal_neighbor_cluster_score ( self , index_cluster , difference ) :
    """@ brief Calculates ' B ' score for the specific object for the nearest cluster .
 @ param [ in ] index _ point ( uint ) : Index point from input data for which ' B ' score should be calculated .
 @ param [ in ] index _ cluster ( uint ) : Index cluster to which the point is belong to .
 @ return ( float ) ' B ' score for the object ."""

implementation:
optimal_score = float ( 'inf' )
for index_neighbor_cluster in range ( len ( self . __clusters ) ) :
    if index_cluster != index_neighbor_cluster :
        candidate_score = self . __calculate_cluster_score ( index_neighbor_cluster , difference )
        if candidate_score < optimal_score :
            optimal_score = candidate_score
if optimal_score == float ( 'inf' ) :
    optimal_score = - 1.0
return optimal_score


signature:
def __calculate_cluster_difference ( self , index_cluster , difference ) :
    """@ brief Calculates distance from each object in specified cluster to specified object .
 @ param [ in ] index _ point ( uint ) : Index point for which difference is calculated .
 @ return ( list ) Distance from specified object to each object from input data in specified cluster ."""

implementation:
cluster_difference = 0.0
for index_point in self . __clusters [ index_cluster ] :
    cluster_difference += difference [ index_point ]
return cluster_difference


signature:
def __calculate_dataset_difference ( self , index_point ) :
    """@ brief Calculate distance from each object to specified object .
 @ param [ in ] index _ point ( uint ) : Index point for which difference with other points is calculated .
 @ return ( list ) Distance to each object from input data from the specified ."""

implementation:
if self . __metric . get_type ( ) != type_metric . USER_DEFINED :
    dataset_differences = self . __metric ( self . __data , self . __data [ index_point ] )
else :
    dataset_differences = [ self . __metric ( point , self . __data [ index_point ] ) for point in self . __data ]
return dataset_differences


signature:
def get_type ( self ) :
    """@ brief Returns algorithm type that corresponds to specified enumeration value .
 @ return ( type ) Algorithm type for cluster analysis ."""

implementation:
if self == silhouette_ksearch_type . KMEANS :
    return kmeans
elif self == silhouette_ksearch_type . KMEDIANS :
    return kmedians
elif self == silhouette_ksearch_type . KMEDOIDS :
    return kmedoids
else :
    return None


implementation:
results = wrapper . silhoeutte_ksearch ( self . __data , self . __kmin , self . __kmax , self . __algorithm )
self . __amount = results [ 0 ]
self . __score = results [ 1 ]
self . __scores = results [ 2 ]


implementation:
self . __scores = { }
for k in range ( self . __kmin , self . __kmax ) :
    clusters = self . __calculate_clusters ( k )
    if len ( clusters ) != k :
        self . __scores [ k ] = float ( 'nan' )
        continue
    score = silhouette ( self . __data , clusters ) . process ( ) . get_score ( )
    self . __scores [ k ] = sum ( score ) / len ( score )
    if self . __scores [ k ] > self . __score :
        self . __score = self . __scores [ k ]
        self . __amount = k


signature:
def __calculate_clusters ( self , k ) :
    """@ brief Performs cluster analysis using specified K value .
 @ param [ in ] k ( uint ) : Amount of clusters that should be allocated .
 @ return ( array _ like ) Allocated clusters ."""

implementation:
initial_values = kmeans_plusplus_initializer ( self . __data , k ) . initialize ( return_index = self . __return_index )
algorithm_type = self . __algorithm . get_type ( )
return algorithm_type ( self . __data , initial_values ) . process ( ) . get_clusters ( )


signature:
def __verify_arguments ( self ) :
    """@ brief Checks algorithm ' s arguments and if some of them is incorrect then exception is thrown ."""

implementation:
if self . __kmax > len ( self . __data ) :
    raise ValueError ( "K max value '" + str ( self . __kmax ) + "' is bigger than amount of objects '" + str ( len ( self . __data ) ) + "' in input data." )
if self . __kmin <= 1 :
    raise ValueError ( "K min value '" + str ( self . __kmin ) + "' should be greater than 1 (impossible to provide " "silhouette score for only one cluster)." )


signature:
def notify ( self , clusters , centers ) :
    """@ brief This method is called by K - Means algorithm to notify about changes .
 @ param [ in ] clusters ( array _ like ) : Allocated clusters by K - Means algorithm .
 @ param [ in ] centers ( array _ like ) : Allocated centers by K - Means algorithm ."""

implementation:
self . __evolution_clusters . append ( clusters )
self . __evolution_centers . append ( centers )


signature:
def show_clusters ( sample , clusters , centers , initial_centers = None , ** kwargs ) :
    """@ brief Display K - Means clustering results .
 @ param [ in ] sample ( list ) : Dataset that was used for clustering .
 @ param [ in ] clusters ( array _ like ) : Clusters that were allocated by the algorithm .
 @ param [ in ] centers ( array _ like ) : Centers that were allocated by the algorithm .
 @ param [ in ] initial _ centers ( array _ like ) : Initial centers that were used by the algorithm , if ' None ' then initial centers are not displyed .
 @ param [ in ] * * kwargs : Arbitrary keyword arguments ( available arguments : ' figure ' , ' display ' , ' offset ' ) .
 < b > Keyword Args : < / b > < br >
 - figure ( figure ) : If ' None ' then new is figure is created , otherwise specified figure is used for visualization .
 - display ( bool ) : If ' True ' then figure will be shown by the method , otherwise it should be shown manually using matplotlib function ' plt . show ( ) ' .
 - offset ( uint ) : Specify axes index on the figure where results should be drawn ( only if argument ' figure ' is specified ) .
 @ return ( figure ) Figure where clusters were drawn ."""

implementation:
visualizer = cluster_visualizer ( )
visualizer . append_clusters ( clusters , sample )
offset = kwargs . get ( 'offset' , 0 )
figure = kwargs . get ( 'figure' , None )
display = kwargs . get ( 'display' , True )
if figure is None :
    figure = visualizer . show ( display = False )
else :
    visualizer . show ( figure = figure , display = False )
kmeans_visualizer . __draw_centers ( figure , offset , visualizer , centers , initial_centers )
kmeans_visualizer . __draw_rays ( figure , offset , visualizer , sample , clusters , centers )
if display is True :
    plt . show ( )
return figure