Completed
Push — 0.7.dev ( 2e222e...d9bef7 )
by Andrei
01:11
created

draw_dynamics_set()   C

Complexity

Conditions 7

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
dl 0
loc 43
rs 5.5
c 0
b 0
f 0
1
"""!
2
3
@brief Utils that are used by modules of pyclustering.
4
5
@authors Andrei Novikov ([email protected])
6
@date 2014-2017
7
@copyright GNU Public License
8
9
@cond GNU_PUBLIC_LICENSE
10
    PyClustering is free software: you can redistribute it and/or modify
11
    it under the terms of the GNU General Public License as published by
12
    the Free Software Foundation, either version 3 of the License, or
13
    (at your option) any later version.
14
    
15
    PyClustering is distributed in the hope that it will be useful,
16
    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
    GNU General Public License for more details.
19
    
20
    You should have received a copy of the GNU General Public License
21
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
@endcond
23
24
"""
25
26
import time;
27
import numpy;
28
29
from PIL import Image;
30
from numpy import array;
31
32
import matplotlib.pyplot as plt;
33
from mpl_toolkits.mplot3d import Axes3D;
34
35
from sys import platform as _platform;
36
37
38
"""!
39
@brief The number \f$pi$ is a mathematical constant, the ratio of a circle's circumference to its diameter.
40
41
"""
42
pi = 3.1415926535;
43
44
45
def read_sample(filename):
46
    """!
47
    @brief Returns sample for cluster analysis.
48
    
49
    @param[in] filename (string): Path to file with data for cluster analysis.
50
    
51
    @return (list) Points where each point represented by list of coordinates.
52
    
53
    """
54
    
55
    file = open(filename, 'r');
56
57
    sample = [[float(val) for val in line.split()] for line in file];
58
    
59
    file.close();
60
    return sample;
61
62
63
def read_image(filename):
64
    """!
65
    @brief Returns image as N-dimension (depends on the input image) matrix, where one element of list describes pixel.
66
    
67
    @param[in] filename (string): Path to image.
68
    
69
    @return (list) Pixels where each pixel described by list of RGB-values.
70
    
71
    """
72
    
73
    image_source = Image.open(filename);
74
    data = [pixel for pixel in image_source.getdata()];
75
    
76
    del image_source;
77
    image_source = None;
78
    return data;
79
80
81
def rgb2gray(image_rgb_array):
82
    """!
83
    @brief Returns image as 1-dimension (gray colored) matrix, where one element of list describes pixel.
84
    @details Luma coding is used for transformation and that is calculated directly from gamma-compressed primary intensities as a weighted sum:
85
    
86
    \f[Y = 0.2989R + 0.587G + 0.114B\f]
87
    
88
    @param[in] image_rgb_array (list): Image represented by RGB list.
89
    
90
    @return (list) Image as gray colored matrix, where one element of list describes pixel.
91
    
92
    @code
93
        colored_image = read_image(file_name);
94
        gray_image = rgb2gray(colored_image);
95
    @endcode
96
    
97
    @see read_image()
98
    
99
    """
100
    
101
    image_gray_array = [0.0] * len(image_rgb_array);
102
    for index in range(0, len(image_rgb_array), 1):
103
        image_gray_array[index] = float(image_rgb_array[index][0]) * 0.2989 + float(image_rgb_array[index][1]) * 0.5870 + float(image_rgb_array[index][2]) * 0.1140;
104
    
105
    return image_gray_array;
106
107
108
def stretch_pattern(image_source):
109
    """!
110
    @brief Returns stretched content as 1-dimension (gray colored) matrix with size of input image.
111
    
112
    @param[in] image_source (Image): PIL Image instance.
113
    
114
    @return (list, Image) Stretched image as gray colored matrix and source image.
115
    
116
    """
117
    wsize, hsize = image_source.size;
118
    
119
    # Crop digit exactly
120
    (ws, hs, we, he) = gray_pattern_borders(image_source);
121
    image_source = image_source.crop((ws, hs, we, he));
122
    
123
    # Stretch it to initial sizes
124
    image_source = image_source.resize((wsize, hsize), Image.ANTIALIAS);
125
    
126
    # Transform image to simple array
127
    data = [pixel for pixel in image_source.getdata()];
128
    image_pattern = rgb2gray(data);
129
    
130
    return (image_pattern, image_source);
131
132
133
def gray_pattern_borders(image):
134
    """!
135
    @brief Returns coordinates of gray image content on the input image.
136
    
137
    @param[in] image (Image): PIL Image instance that is processed.
138
    
139
    @return (tuple) Returns coordinates of gray image content as (width_start, height_start, width_end, height_end).
140
    
141
    """
142
    
143
    width, height = image.size;
144
    
145
    width_start = width;
146
    width_end = 0;
147
    height_start = height;
148
    height_end = 0;
149
    
150
    row, col = 0, 0;
151
    for pixel in image.getdata():
152
        value = float(pixel[0]) * 0.2989 + float(pixel[1]) * 0.5870 + float(pixel[2]) * 0.1140;
153
        
154
        if (value < 128):
155
            if (width_end < col): 
156
                width_end = col;
157
            
158
            if (height_end < row):
159
                height_end = row;
160
        
161
            if (width_start > col):
162
                width_start = col;
163
            
164
            if (height_start > row):
165
                height_start = row;
166
        
167
        col += 1;
168
        if (col >= width):
169
            col = 0;
170
            row += 1;
171
172
    return (width_start, height_start, width_end + 1, height_end + 1);
173
174
175
def knearest(points, k):
176
    """!
177
    @brief Calculates k-nearest graph.
178
    
179
    @param[in] points (list): Input data, list of points where each point represented by list.
180
    @param[in] k (uint): Minimum number of neighbors for each point.
181
    
182
    @return (list) k-nearest graph.
183
    
184
    """
185
    
186
    graph = [];
187
    for i in range(len(points)):
188
        candidates = [];
189
        for j in range(i + i, len(points), 1):
190
            distance = euclidean_distance_sqrt(points[i], points[j]);
191
            if (len(candidates) < k):
192
                candidates.append((j, distance));
193
            else:
194
                index_loser, value_loser = max(candidates, key = lambda value: value[1]);
195
                if (value_loser[1] > distance):
196
                    candidates[index_loser] = (j, distance);
197
    
198
        graph.append(candidates);
199
    
200
    return graph;
201
202
203
def average_neighbor_distance(points, num_neigh):
204
    """!
205
    @brief Returns average distance for establish links between specified number of nearest neighbors.
206
    
207
    @param[in] points (list): Input data, list of points where each point represented by list.
208
    @param[in] num_neigh (uint): Number of neighbors that should be used for distance calculation.
209
    
210
    @return (double) Average distance for establish links between 'num_neigh' in data set 'points'.
211
    
212
    """
213
    
214
    if (num_neigh > len(points) - 1):
215
        raise NameError('Impossible to calculate average distance to neighbors when number of object is less than number of neighbors.');
216
    
217
    dist_matrix = [ [ 0.0 for i in range(len(points)) ] for j in range(len(points)) ];
218
    for i in range(0, len(points), 1):
219
        for j in range(i + 1, len(points), 1):
220
            distance = euclidean_distance(points[i], points[j]);
221
            dist_matrix[i][j] = distance;
222
            dist_matrix[j][i] = distance;
223
            
224
        dist_matrix[i] = sorted(dist_matrix[i]);
225
226
    total_distance = 0;
227
    for i in range(0, len(points), 1):
228
        # start from 0 - first element is distance to itself.
229
        for j in range(0, num_neigh, 1):
230
            total_distance += dist_matrix[i][j + 1];
231
            
232
    return ( total_distance / (num_neigh * len(points)) );
233
234
235
def centroid(points, indexes = None):
236
    """!
237
    @brief Calculate centroid of input set of points. 
238
    
239
    @param[in] points (list): Set of points for centroid calculation.
240
    @param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during centroid calculation.
241
    
242
    @return (list) centroid on the set of points where each element of list is corresponding to value in its dimension.
243
    
244
    """
245
    
246
    dimension = len(points[0]);
247
    centroid_value = [0.0] * dimension;
248
    
249
    range_points = None;
250
    if (indexes is None):
251
        range_points = range(len(points));
252
    else:
253
        range_points = indexes;
254
    
255
    for index_point in range_points:
256
        centroid_value = list_math_addition(centroid_value, points[index_point]);
257
    
258
    centroid_value = list_math_division_number(centroid_value, len(range_points));
259
    return centroid_value;
260
261
262
def median(points, indexes = None):
263
    """!
264
    @brief Calculate geometric median of input set of points using Euclidian distance. 
265
    
266
    @param[in] points (list): Set of points for median calculation.
267
    @param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during median calculation.
268
    
269
    @return (uint) index of point in input set that corresponds to median.
270
    
271
    """
272
    
273
    index_median = None;
274
    distance = float('Inf');
275
    
276
    range_points = None;
277
    if (indexes is None):
278
        range_points = range(len(points));
279
    else:
280
        range_points = indexes;
281
    
282
    for index_candidate in range_points:
283
        distance_candidate = 0.0;
284
        for index in range_points:
285
            distance_candidate += euclidean_distance_sqrt(points[index_candidate], points[index]);
286
        
287
        if (distance_candidate < distance):
288
            distance = distance_candidate;
289
            index_median = index_candidate;
290
    
291
    return index_median;
292
293
294
def euclidean_distance(a, b):
295
    """!
296
    @brief Calculate Euclidian distance between vector a and b. 
297
    
298
    @param[in] a (list): The first vector.
299
    @param[in] b (list): The second vector.
300
    
301
    @return (double) Euclidian distance between two vectors.
302
    
303
    @note This function for calculation is faster then standard function in ~100 times!
304
    
305
    """
306
    
307
    distance = euclidean_distance_sqrt(a, b);
308
    return distance**(0.5);
309
310
311
def euclidean_distance_sqrt(a, b):
312
    """!
313
    @brief Calculate square Euclidian distance between vector a and b.
314
    
315
    @param[in] a (list): The first vector.
316
    @param[in] b (list): The second vector.
317
    
318
    @return (double) Square Euclidian distance between two vectors.
319
    
320
    """  
321
    
322
    if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
323
        return (a - b)**2.0;
324
        
325
    dimension = len(a);
326
    # assert len(a) == len(b);
327
    
328
    distance = 0.0;
329
    for i in range(0, dimension):
330
        distance += (a[i] - b[i])**2.0;
331
        
332
    return distance;
333
334
335
def manhattan_distance(a, b):
336
    """!
337
    @brief Calculate Manhattan distance between vector a and b.
338
    
339
    @param[in] a (list): The first cluster.
340
    @param[in] b (list): The second cluster.
341
    
342
    @return (double) Manhattan distance between two vectors.
343
    
344
    """
345
    
346
    if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
347
        return abs(a - b);
348
    
349
    distance = 0.0;
350
    dimension = len(a);
351
    
352
    for i in range(0, dimension):
353
        distance += abs(a[i] - b[i]);
354
    
355
    return distance;
356
357
358
def average_inter_cluster_distance(cluster1, cluster2, data = None):
359
    """!
360
    @brief Calculates average inter-cluster distance between two clusters.
361
    @details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
362
             or by list of indexes of points from the data (represented by list of points), in this case 
363
             data should be specified.
364
             
365
    @param[in] cluster1 (list): The first cluster where each element can represent index from the data or object itself.
366
    @param[in] cluster2 (list): The second cluster where each element can represent index from the data or object itself.
367
    @param[in] data (list): If specified than elements of clusters will be used as indexes,
368
               otherwise elements of cluster will be considered as points.
369
    
370
    @return (double) Average inter-cluster distance between two clusters.
371
    
372
    """
373
    
374
    distance = 0.0;
375
    
376
    if (data is None):
377
        for i in range(len(cluster1)):
378
            for j in range(len(cluster2)):
379
                distance += euclidean_distance_sqrt(cluster1[i], cluster2[j]);
380
    else:
381
        for i in range(len(cluster1)):
382
            for j in range(len(cluster2)):
383
                distance += euclidean_distance_sqrt(data[ cluster1[i] ], data[ cluster2[j] ]);
384
    
385
    distance /= float(len(cluster1) * len(cluster2));
386
    return distance ** 0.5;
387
388
389
def average_intra_cluster_distance(cluster1, cluster2, data = None):
390
    """!
391
    @brief Calculates average intra-cluster distance between two clusters.
392
    @details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
393
             or by list of indexes of points from the data (represented by list of points), in this case 
394
             data should be specified.
395
    
396
    @param[in] cluster1 (list): The first cluster.
397
    @param[in] cluster2 (list): The second cluster.
398
    @param[in] data (list): If specified than elements of clusters will be used as indexes,
399
               otherwise elements of cluster will be considered as points.
400
    
401
    @return (double) Average intra-cluster distance between two clusters.
402
    
403
    """
404
        
405
    distance = 0.0;
406
    
407
    for i in range(len(cluster1) + len(cluster2)):
408
        for j in range(len(cluster1) + len(cluster2)):
409
            first_point = None;
410
            second_point = None;
411
            
412
            if (data is None):
413
                # the first point
414
                if (i < len(cluster1)): first_point = cluster1[i];
415
                else: first_point = cluster2[i - len(cluster1)];
416
                
417
                # the second point
418
                if (j < len(cluster1)): second_point = cluster1[j];
419
                else: second_point = cluster2[j - len(cluster1)];
420
                
421
            else:
422
                # the first point
423
                if (i < len(cluster1)): first_point = data[ cluster1[i] ];
424
                else: first_point = data[ cluster2[i - len(cluster1)] ];
425
            
426
                if (j < len(cluster1)): second_point = data[ cluster1[j] ];
427
                else: second_point = data[ cluster2[j - len(cluster1)] ];    
428
            
429
430
            
431
            distance += euclidean_distance_sqrt(first_point, second_point);
432
    
433
    distance /= float( (len(cluster1) + len(cluster2)) * (len(cluster1) + len(cluster2) - 1.0) );
434
    return distance ** 0.5;
435
436
437
def variance_increase_distance(cluster1, cluster2, data = None):
438
    """!
439
    @brief Calculates variance increase distance between two clusters.
440
    @details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
441
             or by list of indexes of points from the data (represented by list of points), in this case 
442
             data should be specified.
443
    
444
    @param[in] cluster1 (list): The first cluster.
445
    @param[in] cluster2 (list): The second cluster.
446
    @param[in] data (list): If specified than elements of clusters will be used as indexes,
447
               otherwise elements of cluster will be considered as points.
448
    
449
    @return (double) Average variance increase distance between two clusters.
450
    
451
    """
452
    
453
    # calculate local sum
454
    member_cluster1 = None;
455
    member_cluster2 = None;
456
    
457
    if (data is None):
458
        member_cluster1 = [0.0] * len(cluster1[0]);
459
        member_cluster2 = [0.0] * len(cluster2[0]);
460
        
461
    else:
462
        member_cluster1 = [0.0] * len(data[0]);
463
        member_cluster2 = [0.0] * len(data[0]);
464
    
465
    for i in range(len(cluster1)):
466
        if (data is None):
467
            member_cluster1 = list_math_addition(member_cluster1, cluster1[i]);
468
        else:
469
            member_cluster1 = list_math_addition(member_cluster1, data[ cluster1[i] ]);
470
    
471
    
472
    for j in range(len(cluster2)):
473
        if (data is None):
474
            member_cluster2 = list_math_addition(member_cluster2, cluster2[j]);
475
        else:
476
            member_cluster2 = list_math_addition(member_cluster2, data[ cluster2[j] ]);
477
    
478
    member_cluster_general = list_math_addition(member_cluster1, member_cluster2);
479
    member_cluster_general = list_math_division_number(member_cluster_general, len(cluster1) + len(cluster2));
480
    
481
    member_cluster1 = list_math_division_number(member_cluster1, len(cluster1));
482
    member_cluster2 = list_math_division_number(member_cluster2, len(cluster2));
483
    
484
    # calculate global sum
485
    distance_general = 0.0;
486
    distance_cluster1 = 0.0;
487
    distance_cluster2 = 0.0;
488
    
489
    for i in range(len(cluster1)):
490
        if (data is None):
491
            distance_cluster1 += euclidean_distance_sqrt(cluster1[i], member_cluster1);
492
            distance_general += euclidean_distance_sqrt(cluster1[i], member_cluster_general);
493
            
494
        else:
495
            distance_cluster1 += euclidean_distance_sqrt(data[ cluster1[i] ], member_cluster1);
496
            distance_general += euclidean_distance_sqrt(data[ cluster1[i] ], member_cluster_general);
497
    
498
    for j in range(len(cluster2)):
499
        if (data is None):
500
            distance_cluster2 += euclidean_distance_sqrt(cluster2[j], member_cluster2);
501
            distance_general += euclidean_distance_sqrt(cluster2[j], member_cluster_general);
502
            
503
        else:
504
            distance_cluster2 += euclidean_distance_sqrt(data[ cluster2[j] ], member_cluster2);
505
            distance_general += euclidean_distance_sqrt(data[ cluster2[j] ], member_cluster_general);
506
    
507
    return distance_general - distance_cluster1 - distance_cluster2;
508
509
510
def data_corners(data, data_filter = None):
511
    """!
512
    @brief Finds maximum and minimum corner in each dimension of the specified data.
513
    
514
    @param[in] data (list): List of points that should be analysed.
515
    @param[in] data_filter (list): List of indexes of the data that should be analysed,
516
                if it is 'None' then whole 'data' is analysed to obtain corners.
517
    
518
    @return (list) Tuple of two points that corresponds to minimum and maximum corner (min_corner, max_corner).
519
    
520
    """
521
    
522
    dimensions = len(data[0]);
523
    
524
    bypass = data_filter;
525
    if (bypass is None):
526
        bypass = range(len(data));
527
    
528
    maximum_corner = data[bypass[0]][:];
529
    minimum_corner = data[bypass[0]][:];
530
    
531
    for index_point in bypass:
532
        for index_dimension in range(dimensions):
533
            if (data[index_point][index_dimension] > maximum_corner[index_dimension]):
534
                maximum_corner[index_dimension] = data[index_point][index_dimension];
535
            
536
            if (data[index_point][index_dimension] < minimum_corner[index_dimension]):
537
                minimum_corner[index_dimension] = data[index_point][index_dimension];
538
    
539
    return (minimum_corner, maximum_corner);
540
541
542
def norm_vector(vector):
543
    """!
544
    @brief Calculates norm of an input vector that is known as a vector length.
545
    
546
    @param[in] vector (list): The input vector whose length is calculated.
547
    
548
    @return (double) vector norm known as vector length.
549
    
550
    """
551
    
552
    length = 0.0;
553
    for component in vector:
554
        length += component * component;
555
    
556
    length = length ** 0.5;
557
    
558
    return length;
559
560
561
def unit_vector(vector):
562
    """!
563
    @brief Calculates unit vector.
564
    @details Unit vector calculates of an input vector involves two steps. 
565
              The first, calculate vector length. The second,
566
              divide each vector component by the obtained length.
567
    
568
    @param[in] vector (list): The input vector that is used for unit vector calculation.
569
    
570
    """
571
    
572
    length = norm_vector(vector);
573
    unit_vector_instance = [];
574
    
575
    for component in vector:
576
        unit_vector_instance.append(component / length);
577
    
578
    return unit_vector_instance;
579
580
581
def heaviside(value):
582
    """!
583
    @brief Calculates Heaviside function that represents step function.
584
    @details If input value is greater than 0 then returns 1, otherwise returns 0.
585
    
586
    @param[in] value (double): Argument of Heaviside function.
587
    
588
    @return (double) Value of Heaviside function.
589
    
590
    """
591
    if (value > 0.0): 
592
        return 1.0;
593
    
594
    return 0.0;
595
596
597
def timedcall(executable_function, *args):
598
    """!
599
    @brief Executes specified method or function with measuring of execution time.
600
    
601
    @param[in] executable_function (pointer): Pointer to function or method.
602
    @param[in] args (*): Arguments of called function or method.
603
    
604
    @return (tuple) Execution time and result of execution of function or method (execution_time, result_execution).
605
    
606
    """
607
    
608
    time_start = time.clock();
609
    result = executable_function(*args);
610
    time_end = time.clock();
611
    
612
    return (time_end - time_start, result);
613
614
615
def extract_number_oscillations(osc_dyn, index = 0, amplitude_threshold = 1.0):
616
    """!
617
    @brief Extracts number of oscillations of specified oscillator.
618
    
619
    @param[in] osc_dyn (list): Dynamic of oscillators.
620
    @param[in] index (uint): Index of oscillator in dynamic.
621
    @param[in] amplitude_threshold (double): Amplitude threshold when oscillation is taken into account, for example,
622
                when oscillator amplitude is greater than threshold then oscillation is incremented.
623
    
624
    @return (uint) Number of oscillations of specified oscillator.
625
    
626
    """
627
    
628
    number_oscillations = 0;
629
    waiting_differential = False;
630
    threshold_passed = False;
631
    high_level_trigger = True if (osc_dyn[0][index] > amplitude_threshold) else False;
632
    
633
    for values in osc_dyn:
634
        if ( (values[index] >= amplitude_threshold) and (high_level_trigger is False) ):
635
            high_level_trigger = True;
636
            threshold_passed = True;
637
        
638
        elif ( (values[index] < amplitude_threshold) and (high_level_trigger is True) ):
639
            high_level_trigger = False;
640
            threshold_passed = True;
641
        
642
        if (threshold_passed is True):
643
            threshold_passed = False;
644
            if (waiting_differential is True):
645
                number_oscillations += 1;
646
                waiting_differential = False;
647
648
            else:
649
                waiting_differential = True;
650
        
651
    return number_oscillations;
652
653
654
def allocate_sync_ensembles(dynamic, tolerance = 0.1, threshold = 1.0, ignore = None):
655
    """!
656 View Code Duplication
    @brief Allocate clusters in line with ensembles of synchronous oscillators where each
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
657
           synchronous ensemble corresponds to only one cluster.
658
    
659
    @param[in] dynamic (dynamic): Dynamic of each oscillator.
660
    @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
661
    @param[in] threshold (double): Amlitude trigger when spike is taken into account.
662
    @param[in] ignore (bool): Set of indexes that shouldn't be taken into account.
663
    
664
    @return (list) Grours (lists) of indexes of synchronous oscillators, for example, 
665
            [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
666
            
667
    """
668
    
669
    descriptors = [] * len(dynamic);
670
    
671
    # Check from the end for obtaining result
672
    for index_dyn in range(0, len(dynamic[0]), 1):
673
        if ((ignore is not None) and (index_dyn in ignore)):
674
            continue;
675
        
676
        time_stop_simulation = len(dynamic) - 1;
677 View Code Duplication
        active_state = False;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
678
        
679
        if (dynamic[time_stop_simulation][index_dyn] > threshold):
680
            active_state = True;
681
            
682
        # if active state is detected, it means we don't have whole oscillatory period for the considered oscillator, should be skipped.
683
        if (active_state is True):
684
            while ( (dynamic[time_stop_simulation][index_dyn] > threshold) and (time_stop_simulation > 0) ):
685
                time_stop_simulation -= 1;
686
            
687
            # if there are no any oscillation than let's consider it like noise
688
            if (time_stop_simulation == 0):
689
                continue;
690
            
691
            # reset
692
            active_state = False;
693
        
694
        desc = [0, 0, 0]; # end, start, average time of oscillation
695
        for t in range(time_stop_simulation, 0, -1):
696
            if ( (dynamic[t][index_dyn] > 0) and (active_state is False) ):
697
                desc[0] = t;
698
                active_state = True;
699
            elif ( (dynamic[t][index_dyn] < 0) and (active_state is True) ):
700
                desc[1] = t;
701
                active_state = False;
702
                
703
                break;
704
        
705
        if (desc == [0, 0, 0]):
706
            continue;
707
        
708
        desc[2] = desc[1] + (desc[0] - desc[1]) / 2.0;
709
        descriptors.append(desc);
710
        
711
    
712
    # Cluster allocation
713
    sync_ensembles = [];
714
    desc_sync_ensembles = [];
715
    
716
    for index_desc in range(0, len(descriptors), 1):
717
        if (descriptors[index_desc] == []):
718
            continue;
719
        
720
        if (len(sync_ensembles) == 0):
721
            desc_ensemble = descriptors[index_desc];
722
            reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
723
            
724
            desc_ensemble[0] = desc_ensemble[2] + reducer;
725
            desc_ensemble[1] = desc_ensemble[2] - reducer;
726
            
727
            desc_sync_ensembles.append(desc_ensemble);
728
            sync_ensembles.append([ index_desc ]);
729
        else:
730
            oscillator_captured = False;
731
            for index_ensemble in range(0, len(sync_ensembles), 1):
732
                if ( (desc_sync_ensembles[index_ensemble][0] > descriptors[index_desc][2]) and (desc_sync_ensembles[index_ensemble][1] < descriptors[index_desc][2])):
733
                    sync_ensembles[index_ensemble].append(index_desc);
734
                    oscillator_captured = True;
735
                    break;
736
                
737
            if (oscillator_captured is False):
738
                desc_ensemble = descriptors[index_desc];
739
                reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
740
        
741
                desc_ensemble[0] = desc_ensemble[2] + reducer;
742
                desc_ensemble[1] = desc_ensemble[2] - reducer;
743
        
744
                desc_sync_ensembles.append(desc_ensemble);
745
                sync_ensembles.append([ index_desc ]);
746
    
747
    return sync_ensembles;
748
    
749
    
750
def draw_clusters(data, clusters, noise = [], marker_descr = '.', hide_axes = False, axes = None, display_result = True):
751
    """!
752
    @brief Displays clusters for data in 2D or 3D.
753
    
754
    @param[in] data (list): Points that are described by coordinates represented.
755
    @param[in] clusters (list): Clusters that are represented by lists of indexes where each index corresponds to point in data.
756
    @param[in] noise (list): Points that are regarded to noise.
757
    @param[in] marker_descr (string): Marker for displaying points.
758
    @param[in] hide_axes (bool): If True - axes is not displayed.
759
    @param[in] axes (ax) Matplotlib axes where clusters should be drawn, if it is not specified (None) then new plot will be created.
760
    @param[in] display_result (bool): If specified then matplotlib axes will be used for drawing and plot will not be shown.
761
    
762
    @return (ax) Matplotlib axes where drawn clusters are presented.
763
    
764
    """
765
    # Get dimension
766
    dimension = 0;
767
    if ( (data is not None) and (clusters is not None) ):
768
        dimension = len(data[0]);
769
    elif ( (data is None) and (clusters is not None) ):
770
        dimension = len(clusters[0][0]);
771
    else:
772
        raise NameError('Data or clusters should be specified exactly.');
773
    
774
    "Draw clusters"
775
    colors = [ 'red', 'blue', 'darkgreen', 'brown', 'violet', 
776
               'deepskyblue', 'darkgrey', 'lightsalmon', 'deeppink', 'yellow',
777
               'black', 'mediumspringgreen', 'orange', 'darkviolet', 'darkblue',
778
               'silver', 'lime', 'pink', 'gold', 'bisque' ];
779
               
780
    if (len(clusters) > len(colors)):
781
        raise NameError('Impossible to represent clusters due to number of specified colors.');
782
    
783
    fig = plt.figure();
784
    
785
    if (axes is None):
786
        # Check for dimensions
787
        if ((dimension) == 1 or (dimension == 2)):
788
            axes = fig.add_subplot(111);
789
        elif (dimension == 3):
790
            axes = fig.gca(projection='3d');
791 View Code Duplication
        else:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
792
            raise NameError('Drawer supports only 2d and 3d data representation');
793
    
794
    color_index = 0;
795
    for cluster in clusters:
796
        color = colors[color_index];
797
        for item in cluster:
798
            if (dimension == 1):
799
                if (data is None):
800
                    axes.plot(item[0], 0.0, color = color, marker = marker_descr);
801
                else:
802
                    axes.plot(data[item][0], 0.0, color = color, marker = marker_descr);
803
            
804
            if (dimension == 2):
805
                if (data is None):
806
                    axes.plot(item[0], item[1], color = color, marker = marker_descr);
807
                else:
808
                    axes.plot(data[item][0], data[item][1], color = color, marker = marker_descr);
809
                    
810
            elif (dimension == 3):
811
                if (data is None):
812 View Code Duplication
                    axes.scatter(item[0], item[1], item[2], c = color, marker = marker_descr);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
813
                else:
814
                    axes.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker_descr);
815
        
816
        color_index += 1;
817
    
818
    for item in noise:
819
        if (dimension == 1):
820
            if (data is None):
821
                axes.plot(item[0], 0.0, 'w' + marker_descr);
822
            else:
823
                axes.plot(data[item][0], 0.0, 'w' + marker_descr);
824
825
        if (dimension == 2):
826
            if (data is None):
827
                axes.plot(item[0], item[1], 'w' + marker_descr);
828
            else:
829
                axes.plot(data[item][0], data[item][1], 'w' + marker_descr);
830
                
831
        elif (dimension == 3):
832
            if (data is None):
833
                axes.scatter(item[0], item[1], item[2], c = 'w', marker = marker_descr);
834
            else:
835
                axes.scatter(data[item][0], data[item][1], data[item][2], c = 'w', marker = marker_descr);
836
    
837
    axes.grid(True);
838
    
839
    if (hide_axes is True):
840
        axes.xaxis.set_ticklabels([]);
841
        axes.yaxis.set_ticklabels([]);
842
        
843
        if (dimension == 3):
844
            axes.zaxis.set_ticklabels([]);
845
    
846
    if (display_result is True):
847
        plt.show();
848
849
    return axes;
850
851
852
def draw_dynamics(t, dyn, x_title = None, y_title = None, x_lim = None, y_lim = None, x_labels = True, y_labels = True, separate = False, axes = None):
853
    """!
854
    @brief Draw dynamics of neurons (oscillators) in the network.
855
    @details It draws if matplotlib is not specified (None), othewise it should be performed manually.
856
    
857
    @param[in] t (list): Values of time (used by x axis).
858
    @param[in] dyn (list): Values of output of oscillators (used by y axis).
859
    @param[in] x_title (string): Title for Y.
860
    @param[in] y_title (string): Title for X.
861
    @param[in] x_lim (double): X limit.
862
    @param[in] y_lim (double): Y limit.
863
    @param[in] x_labels (bool): If True - shows X labels.
864
    @param[in] y_labels (bool): If True - shows Y labels.
865
    @param[in] separate (list): Consists of lists of oscillators where each such list consists of oscillator indexes that will be shown on separated stage.
866
    @param[in] axes (ax): If specified then matplotlib axes will be used for drawing and plot will not be shown.
867
    
868
    @return (ax) Axes of matplotlib.
869
    
870
    """
871
         
872
    number_lines = 0;
873
    
874
    stage_xlim = None;
875
    if (x_lim is not None):
876
        stage_xlim = x_lim;
877
    elif (len(t) > 0):
878
        stage_xlim = [0, t[len(t) - 1]];
879
    
880
    if ( (isinstance(separate, bool) is True) and (separate is True) ):
881
        if (isinstance(dyn[0], list) is True):
882
            number_lines = len(dyn[0]);
883
        else:
884
            number_lines = 1;
885
            
886
    elif (isinstance(separate, list) is True):
887
        number_lines = len(separate);
888
        
889
    else:
890
        number_lines = 1;
891
    
892
    dysplay_result = False;
893
    if (axes is None):
894
        dysplay_result = True;
895
        (fig, axes) = plt.subplots(number_lines, 1);
896
    
897
    # Check if we have more than one dynamic
898
    if (isinstance(dyn[0], list) is True):
899
        num_items = len(dyn[0]);
900
        for index in range(0, num_items, 1):
901
            y = [item[index] for item in dyn];
902
            
903
            if (number_lines > 1):
904
                index_stage = -1;
905
                
906
                # Find required axes for the y
907
                if (isinstance(separate, bool) is True):
908
                    index_stage = index;
909
                    
910
                elif (isinstance(separate, list) is True):
911
                    for index_group in range(0, len(separate), 1):
912
                        if (index in separate[index_group]): 
913
                            index_stage = index_group;
914
                            break;
915
                
916
                if (index_stage != -1):
917
                    if (index_stage != number_lines - 1):
918
                        axes[index_stage].get_xaxis().set_visible(False);
919
                              
920
                    axes[index_stage].plot(t, y, 'b-', linewidth = 0.5); 
921
                    set_ax_param(axes[index_stage], x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
922
                
923
            else:
924
                axes.plot(t, y, 'b-', linewidth = 0.5);
925
                set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
926
    else:
927
        axes.plot(t, dyn, 'b-', linewidth = 0.5);
928
        set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
929
    
930
    if (dysplay_result is True):
931
        plt.show();
932
    
933
    return axes;
934
935
936
def set_ax_param(ax, x_title = None, y_title = None, x_lim = None, y_lim = None, x_labels = True, y_labels = True, grid = True):
937
    """!
938
    @brief Sets parameters for matplotlib ax.
939
    
940
    @param[in] ax (Axes): Axes for which parameters should applied.
941
    @param[in] x_title (string): Title for Y.
942
    @param[in] y_title (string): Title for X.
943
    @param[in] x_lim (double): X limit.
944
    @param[in] y_lim (double): Y limit.
945
    @param[in] x_labels (bool): If True - shows X labels.
946
    @param[in] y_labels (bool): If True - shows Y labels.
947
    @param[in] grid (bool): If True - shows grid.
948
    
949
    """
950
    from matplotlib.font_manager import FontProperties;
951
    from matplotlib import rcParams;
952
    
953
    if (_platform == "linux") or (_platform == "linux2"):
954
        rcParams['font.sans-serif'] = ['Liberation Serif'];
955
    else:
956
        rcParams['font.sans-serif'] = ['Arial'];
957
        
958
    rcParams['font.size'] = 12;
959
        
960
    surface_font = FontProperties();
961
    if (_platform == "linux") or (_platform == "linux2"):
962
        surface_font.set_name('Liberation Serif');
963
    else:
964
        surface_font.set_name('Arial');
965
        
966
    surface_font.set_size('12');
967
    
968
    if (y_title is not None): ax.set_ylabel(y_title, fontproperties = surface_font);
969
    if (x_title is not None): ax.set_xlabel(x_title, fontproperties = surface_font);
970
    
971
    if (x_lim is not None): ax.set_xlim(x_lim[0], x_lim[1]);
972
    if (y_lim is not None): ax.set_ylim(y_lim[0], y_lim[1]);
973
    
974
    if (x_labels is False): ax.xaxis.set_ticklabels([]);
975
    if (y_labels is False): ax.yaxis.set_ticklabels([]);
976
    
977
    ax.grid(grid);
978
979
980
def draw_dynamics_set(dynamics, xtitle = None, ytitle = None, xlim = None, ylim = None, xlabels = False, ylabels = False):
981
    """!
982
    @brief Draw lists of dynamics of neurons (oscillators) in the network.
983
    
984
    @param[in] dynamics (list): List of network outputs that are represented by values of output of oscillators (used by y axis).
985
    @param[in] xtitle (string): Title for Y.
986
    @param[in] ytitle (string): Title for X.
987
    @param[in] xlim (double): X limit.
988
    @param[in] ylim (double): Y limit.
989
    @param[in] xlabels (bool): If True - shows X labels.
990
    @param[in] ylabels (bool): If True - shows Y labels.
991
    
992
    """
993
    # Calculate edge for confortable representation.
994
    number_dynamics = len(dynamics);
995
    if (number_dynamics == 1):
996
        draw_dynamics(dynamics[0][0], dynamics[0][1], xtitle, ytitle, xlim, ylim, xlabels, ylabels);
997
        return;
998
    
999
    number_cols = int(numpy.ceil(number_dynamics ** 0.5));
1000
    number_rows = int(numpy.ceil(number_dynamics / number_cols));
1001
1002
    real_index = 0, 0;
1003
    double_indexer = True;
1004
    if ( (number_cols == 1) or (number_rows == 1) ):
1005
        real_index = 0;
1006
        double_indexer = False;
1007
    
1008
    (_, axarr) = plt.subplots(number_rows, number_cols);
1009
    #plt.setp([ax for ax in axarr], visible = False);
1010
    
1011
    for dynamic in dynamics:
1012
        axarr[real_index] = draw_dynamics(dynamic[0], dynamic[1], xtitle, ytitle, xlim, ylim, xlabels, ylabels, axes = axarr[real_index]);
1013
        #plt.setp(axarr[real_index], visible = True);
1014
        
1015
        if (double_indexer is True):
1016
            real_index = real_index[0], real_index[1] + 1;
1017
            if (real_index[1] >= number_cols):
1018
                real_index = real_index[0] + 1, 0; 
1019
        else:
1020
            real_index += 1;
1021
            
1022
    plt.show();
1023
1024
1025
def draw_image_color_segments(source, clusters, hide_axes = True):
1026
    """!
1027
    @brief Shows image segments using colored image.
1028
    @details Each color on result image represents allocated segment. The first image is initial and other is result of segmentation.
1029
    
1030
    @param[in] source (string): Path to image.
1031
    @param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
1032
                                consists of indexes of pixel from source image.
1033
    @param[in] hide_axes (bool): If True then axes will not be displayed.
1034
    
1035
    """
1036
        
1037
    image_source = Image.open(source);
1038
    image_size = image_source.size;
1039
    
1040
    (fig, axarr) = plt.subplots(1, 2);
1041
    
1042
    plt.setp([ax for ax in axarr], visible = False);
1043
    
1044
    available_colors = [ (0, 162, 232),   (34, 177, 76),   (237, 28, 36),
1045
                         (255, 242, 0),   (0, 0, 0),       (237, 28, 36),
1046
                         (255, 174, 201), (127, 127, 127), (185, 122, 87), 
1047
                         (200, 191, 231), (136, 0, 21),    (255, 127, 39),
1048
                         (63, 72, 204),   (195, 195, 195), (255, 201, 14),
1049
                         (239, 228, 176), (181, 230, 29),  (153, 217, 234),
1050
                         (112, 146, 180) ];
1051
    
1052
    image_color_segments = [(255, 255, 255)] * (image_size[0] * image_size[1]);
1053
    
1054
    for index_segment in range(len(clusters)):
1055
        for index_pixel in clusters[index_segment]:
1056
            image_color_segments[index_pixel] = available_colors[index_segment];
1057
    
1058
    stage = array(image_color_segments, numpy.uint8);
1059
    stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
1060
    image_cluster = Image.fromarray(stage, 'RGB');
1061
    
1062
    axarr[0].imshow(image_source, interpolation = 'none');
1063
    axarr[1].imshow(image_cluster, interpolation = 'none');
1064
    
1065
    for i in range(2):
1066
        plt.setp(axarr[i], visible = True);
1067
        
1068
        if (hide_axes is True):
1069
            axarr[i].xaxis.set_ticklabels([]);
1070
            axarr[i].yaxis.set_ticklabels([]);
1071
            axarr[i].xaxis.set_ticks_position('none');
1072
            axarr[i].yaxis.set_ticks_position('none');
1073
    
1074
    plt.show();
1075
1076
1077
def draw_image_mask_segments(source, clusters, hide_axes = True):
1078
    """!
1079
    @brief Shows image segments using black masks.
1080
    @details Each black mask of allocated segment is presented on separate plot.
1081
             The first image is initial and others are black masks of segments.
1082
    
1083
    @param[in] source (string): Path to image.
1084
    @param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
1085
                                consists of indexes of pixel from source image.
1086
    @param[in] hide_axes (bool): If True then axes will not be displayed.
1087
    
1088
    """
1089
    if (len(clusters) == 0):
1090
        print("Warning: Nothing to draw - list of clusters is empty.")
1091
        return;
1092
        
1093
    image_source = Image.open(source);
1094
    image_size = image_source.size;
1095
    
1096
    # Calculate edge for confortable representation.
1097
    number_clusters = len(clusters) + 1; # show with the source image
1098
    
1099
    number_cols = int(numpy.ceil(number_clusters ** 0.5));
1100
    number_rows = int(numpy.ceil(number_clusters / number_cols));
1101
    
1102
1103
    real_index = 0, 0;
1104
    double_indexer = True;
1105
    if ( (number_cols == 1) or (number_rows == 1) ):
1106
        real_index = 0;
1107
        double_indexer = False;
1108
    
1109
    (fig, axarr) = plt.subplots(number_rows, number_cols);
1110
    plt.setp([ax for ax in axarr], visible = False);
1111
    
1112
    axarr[real_index].imshow(image_source, interpolation = 'none');
1113
    plt.setp(axarr[real_index], visible = True);
1114
    
1115
    if (hide_axes is True):
1116
        axarr[real_index].xaxis.set_ticklabels([]);
1117
        axarr[real_index].yaxis.set_ticklabels([]);
1118
        axarr[real_index].xaxis.set_ticks_position('none');
1119
        axarr[real_index].yaxis.set_ticks_position('none');
1120
            
1121
    if (double_indexer is True):
1122
        real_index = 0, 1;
1123
    else:
1124
        real_index += 1;
1125
    
1126
    for cluster in clusters:
1127
        stage_cluster = [(255, 255, 255)] * (image_size[0] * image_size[1]);
1128
        for index in cluster:
1129
            stage_cluster[index] = (0, 0, 0);
1130
          
1131
        stage = array(stage_cluster, numpy.uint8);
1132
        stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
1133
        
1134
        image_cluster = Image.fromarray(stage, 'RGB');
1135
        
1136
        axarr[real_index].imshow(image_cluster, interpolation = 'none');
1137
        plt.setp(axarr[real_index], visible = True);
1138
        
1139
        if (hide_axes is True):
1140
            axarr[real_index].xaxis.set_ticklabels([]);
1141
            axarr[real_index].yaxis.set_ticklabels([]);
1142
            
1143
            axarr[real_index].xaxis.set_ticks_position('none');
1144
            axarr[real_index].yaxis.set_ticks_position('none');
1145
        
1146
        if (double_indexer is True):
1147
            real_index = real_index[0], real_index[1] + 1;
1148
            if (real_index[1] >= number_cols):
1149
                real_index = real_index[0] + 1, 0; 
1150
        else:
1151
            real_index += 1;
1152
1153
            
1154
    plt.show();
1155
1156
1157
def linear_sum(list_vector):
1158
    """!
1159
    @brief Calculates linear sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
1160
    
1161
    @param[in] list_vector (list): Input vector.
1162
    
1163
    @return (list|double) Linear sum of vector that can be represented by list in case of multidimensional elements.
1164
    
1165
    """
1166
    dimension = 1;
1167
    linear_sum = 0.0;
1168
    list_representation = (type(list_vector[0]) == list);
1169
    
1170
    if (list_representation is True):
1171
        dimension = len(list_vector[0]);
1172
        linear_sum = [0] * dimension;
1173
        
1174
    for index_element in range(0, len(list_vector)):
1175
        if (list_representation is True):
1176
            for index_dimension in range(0, dimension):
1177
                linear_sum[index_dimension] += list_vector[index_element][index_dimension];
1178
        else:
1179
            linear_sum += list_vector[index_element];
1180
1181
    return linear_sum;
1182
1183
1184
def square_sum(list_vector):
1185
    """!
1186
    @brief Calculates square sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
1187
    
1188
    @param[in] list_vector (list): Input vector.
1189
    
1190
    @return (double) Square sum of vector.
1191
    
1192
    """
1193
    
1194
    square_sum = 0.0;
1195
    list_representation = (type(list_vector[0]) == list);
1196
        
1197
    for index_element in range(0, len(list_vector)):
1198
        if (list_representation is True):
1199
            square_sum += sum(list_math_multiplication(list_vector[index_element], list_vector[index_element]));
1200
        else:
1201
            square_sum += list_vector[index_element] * list_vector[index_element];
1202
         
1203
    return square_sum;
1204
1205
    
1206
def list_math_subtraction(a, b):
1207
    """!
1208
    @brief Calculates subtraction of two lists.
1209
    @details Each element from list 'a' is subtracted by element from list 'b' accordingly.
1210
    
1211
    @param[in] a (list): List of elements that supports mathematical subtraction.
1212
    @param[in] b (list): List of elements that supports mathematical subtraction.
1213
    
1214
    @return (list) Results of subtraction of two lists.
1215
    
1216
    """
1217
    return [a[i] - b[i] for i in range(len(a))];
1218
1219
1220
def list_math_substraction_number(a, b):
1221
    """!
1222
    @brief Calculates subtraction between list and number.
1223
    @details Each element from list 'a' is subtracted by number 'b'.
1224
    
1225
    @param[in] a (list): List of elements that supports mathematical subtraction.
1226
    @param[in] b (list): Value that supports mathematical subtraction.
1227
    
1228
    @return (list) Results of subtraction between list and number.
1229
    
1230
    """        
1231
    return [a[i] - b for i in range(len(a))];  
1232
1233
1234
def list_math_addition(a, b):
1235
    """!
1236
    @brief Addition of two lists.
1237
    @details Each element from list 'a' is added to element from list 'b' accordingly.
1238
    
1239
    @param[in] a (list): List of elements that supports mathematic addition..
1240
    @param[in] b (list): List of elements that supports mathematic addition..
1241
    
1242
    @return (list) Results of addtion of two lists.
1243
    
1244
    """    
1245
    return [a[i] + b[i] for i in range(len(a))];
1246
1247
1248
def list_math_addition_number(a, b):
1249
    """!
1250
    @brief Addition between list and number.
1251
    @details Each element from list 'a' is added to number 'b'.
1252
    
1253
    @param[in] a (list): List of elements that supports mathematic addition.
1254
    @param[in] b (double): Value that supports mathematic addition.
1255
    
1256
    @return (list) Result of addtion of two lists.
1257
    
1258
    """    
1259
    return [a[i] + b for i in range(len(a))];
1260
1261
1262
def list_math_division_number(a, b):
1263
    """!
1264
    @brief Division between list and number.
1265
    @details Each element from list 'a' is divided by number 'b'.
1266
    
1267
    @param[in] a (list): List of elements that supports mathematic division.
1268
    @param[in] b (double): Value that supports mathematic division.
1269
    
1270
    @return (list) Result of division between list and number.
1271
    
1272
    """    
1273
    return [a[i] / b for i in range(len(a))];
1274
1275
1276
def list_math_division(a, b):
1277
    """!
1278
    @brief Division of two lists.
1279
    @details Each element from list 'a' is divided by element from list 'b' accordingly.
1280
    
1281
    @param[in] a (list): List of elements that supports mathematic division.
1282
    @param[in] b (list): List of elements that supports mathematic division.
1283
    
1284
    @return (list) Result of division of two lists.
1285
    
1286
    """    
1287
    return [a[i] / b[i] for i in range(len(a))];
1288
1289
1290
def list_math_multiplication_number(a, b):
1291
    """!
1292
    @brief Multiplication between list and number.
1293
    @details Each element from list 'a' is multiplied by number 'b'.
1294
    
1295
    @param[in] a (list): List of elements that supports mathematic division.
1296
    @param[in] b (double): Number that supports mathematic division.
1297
    
1298
    @return (list) Result of division between list and number.
1299
    
1300
    """    
1301
    return [a[i] * b for i in range(len(a))];
1302
1303
1304
def list_math_multiplication(a, b):
1305
    """!
1306
    @brief Multiplication of two lists.
1307
    @details Each element from list 'a' is multiplied by element from list 'b' accordingly.
1308
    
1309
    @param[in] a (list): List of elements that supports mathematic multiplication.
1310
    @param[in] b (list): List of elements that supports mathematic multiplication.
1311
    
1312
    @return (list) Result of multiplication of elements in two lists.
1313
    
1314
    """        
1315
    return [a[i] * b[i] for i in range(len(a))];
1316