Completed
Push — master ( e45971...e8eda8 )
by Andrei
01:01
created

calculate_ellipse_description()   A

Complexity

Conditions 1

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 19
rs 9.4285
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
## The number \f$pi\f$ is a mathematical constant, the ratio of a circle's circumference to its diameter.
39
pi = 3.1415926535;
40
41
42
def read_sample(filename):
43
    """!
44
    @brief Returns data sample from simple text file.
45
    @details This function should be used for text file with following format:
46
    @code
47
    point_1_coord_1 point_1_coord_2 ... point_1_coord_n
48
    point_2_coord_1 point_2_coord_2 ... point_2_coord_n
49
    ... ...
50
    @endcode
51
    
52
    @param[in] filename (string): Path to file with data.
53
    
54
    @return (list) Points where each point represented by list of coordinates.
55
    
56
    """
57
    
58
    file = open(filename, 'r');
59
60
    sample = [[float(val) for val in line.split()] for line in file if len(line.strip()) > 0];
61
    
62
    file.close();
63
    return sample;
64
65
66
def read_image(filename):
67
    """!
68
    @brief Returns image as N-dimension (depends on the input image) matrix, where one element of list describes pixel.
69
    
70
    @param[in] filename (string): Path to image.
71
    
72
    @return (list) Pixels where each pixel described by list of RGB-values.
73
    
74
    """
75
    
76
    with Image.open(filename) as image_source:
77
        data = [pixel for pixel in image_source.getdata()];
78
        return data;
79
    
80
    return [];
81
82
83
def rgb2gray(image_rgb_array):
84
    """!
85
    @brief Returns image as 1-dimension (gray colored) matrix, where one element of list describes pixel.
86
    @details Luma coding is used for transformation and that is calculated directly from gamma-compressed primary intensities as a weighted sum:
87
    
88
    \f[Y = 0.2989R + 0.587G + 0.114B\f]
89
    
90
    @param[in] image_rgb_array (list): Image represented by RGB list.
91
    
92
    @return (list) Image as gray colored matrix, where one element of list describes pixel.
93
    
94
    @code
95
        colored_image = read_image(file_name);
96
        gray_image = rgb2gray(colored_image);
97
    @endcode
98
    
99
    @see read_image()
100
    
101
    """
102
    
103
    image_gray_array = [0.0] * len(image_rgb_array);
104
    for index in range(0, len(image_rgb_array), 1):
105
        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;
106
    
107
    return image_gray_array;
108
109
110
def stretch_pattern(image_source):
111
    """!
112
    @brief Returns stretched content as 1-dimension (gray colored) matrix with size of input image.
113
    
114
    @param[in] image_source (Image): PIL Image instance.
115
    
116
    @return (list, Image) Stretched image as gray colored matrix and source image.
117
    
118
    """
119
    wsize, hsize = image_source.size;
120
    
121
    # Crop digit exactly
122
    (ws, hs, we, he) = gray_pattern_borders(image_source);
123
    image_source = image_source.crop((ws, hs, we, he));
124
    
125
    # Stretch it to initial sizes
126
    image_source = image_source.resize((wsize, hsize), Image.ANTIALIAS);
127
    
128
    # Transform image to simple array
129
    data = [pixel for pixel in image_source.getdata()];
130
    image_pattern = rgb2gray(data);
131
    
132
    return (image_pattern, image_source);
133
134
135
def gray_pattern_borders(image):
136
    """!
137
    @brief Returns coordinates of gray image content on the input image.
138
    
139
    @param[in] image (Image): PIL Image instance that is processed.
140
    
141
    @return (tuple) Returns coordinates of gray image content as (width_start, height_start, width_end, height_end).
142
    
143
    """
144
    
145
    width, height = image.size;
146
    
147
    width_start = width;
148
    width_end = 0;
149
    height_start = height;
150
    height_end = 0;
151
    
152
    row, col = 0, 0;
153
    for pixel in image.getdata():
154
        value = float(pixel[0]) * 0.2989 + float(pixel[1]) * 0.5870 + float(pixel[2]) * 0.1140;
155
        
156
        if (value < 128):
157
            if (width_end < col): 
158
                width_end = col;
159
            
160
            if (height_end < row):
161
                height_end = row;
162
        
163
            if (width_start > col):
164
                width_start = col;
165
            
166
            if (height_start > row):
167
                height_start = row;
168
        
169
        col += 1;
170
        if (col >= width):
171
            col = 0;
172
            row += 1;
173
174
    return (width_start, height_start, width_end + 1, height_end + 1);
175
176
177
def knearest(points, k):
178
    """!
179
    @brief Calculates k-nearest graph.
180
    
181
    @param[in] points (list): Input data, list of points where each point represented by list.
182
    @param[in] k (uint): Minimum number of neighbors for each point.
183
    
184
    @return (list) k-nearest graph.
185
    
186
    """
187
    
188
    graph = [];
189
    for i in range(len(points)):
190
        candidates = [];
191
        for j in range(i + i, len(points), 1):
192
            distance = euclidean_distance_sqrt(points[i], points[j]);
193
            if (len(candidates) < k):
194
                candidates.append((j, distance));
195
            else:
196
                index_loser, value_loser = max(candidates, key = lambda value: value[1]);
197
                if (value_loser[1] > distance):
198
                    candidates[index_loser] = (j, distance);
199
    
200
        graph.append(candidates);
201
    
202
    return graph;
203
204
205
def average_neighbor_distance(points, num_neigh):
206
    """!
207
    @brief Returns average distance for establish links between specified number of nearest neighbors.
208
    
209
    @param[in] points (list): Input data, list of points where each point represented by list.
210
    @param[in] num_neigh (uint): Number of neighbors that should be used for distance calculation.
211
    
212
    @return (double) Average distance for establish links between 'num_neigh' in data set 'points'.
213
    
214
    """
215
    
216
    if (num_neigh > len(points) - 1):
217
        raise NameError('Impossible to calculate average distance to neighbors when number of object is less than number of neighbors.');
218
    
219
    dist_matrix = [ [ 0.0 for i in range(len(points)) ] for j in range(len(points)) ];
220
    for i in range(0, len(points), 1):
221
        for j in range(i + 1, len(points), 1):
222
            distance = euclidean_distance(points[i], points[j]);
223
            dist_matrix[i][j] = distance;
224
            dist_matrix[j][i] = distance;
225
            
226
        dist_matrix[i] = sorted(dist_matrix[i]);
227
228
    total_distance = 0;
229
    for i in range(0, len(points), 1):
230
        # start from 0 - first element is distance to itself.
231
        for j in range(0, num_neigh, 1):
232
            total_distance += dist_matrix[i][j + 1];
233
            
234
    return ( total_distance / (num_neigh * len(points)) );
235
236
237
def centroid(points, indexes = None):
238
    """!
239
    @brief Calculate centroid of input set of points. 
240
    
241
    @param[in] points (list): Set of points for centroid calculation.
242
    @param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during centroid calculation.
243
    
244
    @return (list) centroid on the set of points where each element of list is corresponding to value in its dimension.
245
    
246
    """
247
    
248
    dimension = len(points[0]);
249
    centroid_value = [0.0] * dimension;
250
    
251
    range_points = None;
252
    if (indexes is None):
253
        range_points = range(len(points));
254
    else:
255
        range_points = indexes;
256
    
257
    for index_point in range_points:
258
        centroid_value = list_math_addition(centroid_value, points[index_point]);
259
    
260
    centroid_value = list_math_division_number(centroid_value, len(range_points));
261
    return centroid_value;
262
263
264
def median(points, indexes = None):
265
    """!
266
    @brief Calculate geometric median of input set of points using Euclidian distance. 
267
    
268
    @param[in] points (list): Set of points for median calculation.
269
    @param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during median calculation.
270
    
271
    @return (uint) index of point in input set that corresponds to median.
272
    
273
    """
274
    
275
    index_median = None;
276
    distance = float('Inf');
277
    
278
    range_points = None;
279
    if (indexes is None):
280
        range_points = range(len(points));
281
    else:
282
        range_points = indexes;
283
    
284
    for index_candidate in range_points:
285
        distance_candidate = 0.0;
286
        for index in range_points:
287
            distance_candidate += euclidean_distance_sqrt(points[index_candidate], points[index]);
288
        
289
        if (distance_candidate < distance):
290
            distance = distance_candidate;
291
            index_median = index_candidate;
292
    
293
    return index_median;
294
295
296
def euclidean_distance(a, b):
297
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
298
    @brief Calculate Euclidean distance between vector a and b. 
299
    @details The Euclidean between vectors (points) a and b is calculated by following formula:
300
    
301
    \f[
302
    dist(a, b) = \sqrt{ \sum_{i=0}^{N}(b_{i} - a_{i})^{2}) };
303
    \f]
304
    
305
    Where N is a length of each vector.
306
    
307
    @param[in] a (list): The first vector.
308
    @param[in] b (list): The second vector.
309
    
310
    @return (double) Euclidian distance between two vectors.
311
    
312
    @note This function for calculation is faster then standard function in ~100 times!
313
    
314
    """
315
    
316
    distance = euclidean_distance_sqrt(a, b);
317
    return distance**(0.5);
318
319
320
def euclidean_distance_sqrt(a, b):
321
    """!
322
    @brief Calculate square Euclidian distance between vector a and b.
323
    
324
    @param[in] a (list): The first vector.
325
    @param[in] b (list): The second vector.
326
    
327
    @return (double) Square Euclidian distance between two vectors.
328
    
329
    """  
330
    
331
    if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
332
        return (a - b)**2.0;
333
    
334
    distance = 0.0;
335
    for i in range(0, len(a)):
336
        distance += (a[i] - b[i])**2.0;
337
        
338
    return distance;
339
340
341
def manhattan_distance(a, b):
342
    """!
343
    @brief Calculate Manhattan distance between vector a and b.
344
    
345
    @param[in] a (list): The first cluster.
346
    @param[in] b (list): The second cluster.
347
    
348
    @return (double) Manhattan distance between two vectors.
349
    
350
    """
351
    
352
    if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
353
        return abs(a - b);
354
    
355
    distance = 0.0;
356
    dimension = len(a);
357
    
358
    for i in range(0, dimension):
359
        distance += abs(a[i] - b[i]);
360
    
361
    return distance;
362
363
364
def average_inter_cluster_distance(cluster1, cluster2, data = None):
365
    """!
366
    @brief Calculates average inter-cluster distance between two clusters.
367
    @details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
368
             or by list of indexes of points from the data (represented by list of points), in this case 
369
             data should be specified.
370
             
371
    @param[in] cluster1 (list): The first cluster where each element can represent index from the data or object itself.
372
    @param[in] cluster2 (list): The second cluster where each element can represent index from the data or object itself.
373
    @param[in] data (list): If specified than elements of clusters will be used as indexes,
374
               otherwise elements of cluster will be considered as points.
375
    
376
    @return (double) Average inter-cluster distance between two clusters.
377
    
378
    """
379
    
380
    distance = 0.0;
381
    
382
    if (data is None):
383
        for i in range(len(cluster1)):
384
            for j in range(len(cluster2)):
385
                distance += euclidean_distance_sqrt(cluster1[i], cluster2[j]);
386
    else:
387
        for i in range(len(cluster1)):
388
            for j in range(len(cluster2)):
389
                distance += euclidean_distance_sqrt(data[ cluster1[i] ], data[ cluster2[j] ]);
390
    
391
    distance /= float(len(cluster1) * len(cluster2));
392
    return distance ** 0.5;
393
394
395
def average_intra_cluster_distance(cluster1, cluster2, data = None):
396
    """!
397
    @brief Calculates average intra-cluster distance between two clusters.
398
    @details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
399
             or by list of indexes of points from the data (represented by list of points), in this case 
400
             data should be specified.
401
    
402
    @param[in] cluster1 (list): The first cluster.
403
    @param[in] cluster2 (list): The second cluster.
404
    @param[in] data (list): If specified than elements of clusters will be used as indexes,
405
               otherwise elements of cluster will be considered as points.
406
    
407
    @return (double) Average intra-cluster distance between two clusters.
408
    
409
    """
410
        
411
    distance = 0.0;
412
    
413
    for i in range(len(cluster1) + len(cluster2)):
414
        for j in range(len(cluster1) + len(cluster2)):
415
            first_point = None;
416
            second_point = None;
417
            
418
            if (data is None):
419
                # the first point
420
                if (i < len(cluster1)): first_point = cluster1[i];
421
                else: first_point = cluster2[i - len(cluster1)];
422
                
423
                # the second point
424
                if (j < len(cluster1)): second_point = cluster1[j];
425
                else: second_point = cluster2[j - len(cluster1)];
426
                
427
            else:
428
                # the first point
429
                if (i < len(cluster1)): first_point = data[ cluster1[i] ];
430
                else: first_point = data[ cluster2[i - len(cluster1)] ];
431
            
432
                if (j < len(cluster1)): second_point = data[ cluster1[j] ];
433
                else: second_point = data[ cluster2[j - len(cluster1)] ];    
434
            
435
436
            
437
            distance += euclidean_distance_sqrt(first_point, second_point);
438
    
439
    distance /= float( (len(cluster1) + len(cluster2)) * (len(cluster1) + len(cluster2) - 1.0) );
440
    return distance ** 0.5;
441
442
443
def variance_increase_distance(cluster1, cluster2, data = None):
444
    """!
445
    @brief Calculates variance increase distance between two clusters.
446
    @details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
447
             or by list of indexes of points from the data (represented by list of points), in this case 
448
             data should be specified.
449
    
450
    @param[in] cluster1 (list): The first cluster.
451
    @param[in] cluster2 (list): The second cluster.
452
    @param[in] data (list): If specified than elements of clusters will be used as indexes,
453
               otherwise elements of cluster will be considered as points.
454
    
455
    @return (double) Average variance increase distance between two clusters.
456
    
457
    """
458
    
459
    # calculate local sum
460
    member_cluster1 = None;
461
    member_cluster2 = None;
462
    
463
    if (data is None):
464
        member_cluster1 = [0.0] * len(cluster1[0]);
465
        member_cluster2 = [0.0] * len(cluster2[0]);
466
        
467
    else:
468
        member_cluster1 = [0.0] * len(data[0]);
469
        member_cluster2 = [0.0] * len(data[0]);
470
    
471
    for i in range(len(cluster1)):
472
        if (data is None):
473
            member_cluster1 = list_math_addition(member_cluster1, cluster1[i]);
474
        else:
475
            member_cluster1 = list_math_addition(member_cluster1, data[ cluster1[i] ]);
476
    
477
    
478
    for j in range(len(cluster2)):
479
        if (data is None):
480
            member_cluster2 = list_math_addition(member_cluster2, cluster2[j]);
481
        else:
482
            member_cluster2 = list_math_addition(member_cluster2, data[ cluster2[j] ]);
483
    
484
    member_cluster_general = list_math_addition(member_cluster1, member_cluster2);
485
    member_cluster_general = list_math_division_number(member_cluster_general, len(cluster1) + len(cluster2));
486
    
487
    member_cluster1 = list_math_division_number(member_cluster1, len(cluster1));
488
    member_cluster2 = list_math_division_number(member_cluster2, len(cluster2));
489
    
490
    # calculate global sum
491
    distance_general = 0.0;
492
    distance_cluster1 = 0.0;
493
    distance_cluster2 = 0.0;
494
    
495
    for i in range(len(cluster1)):
496
        if (data is None):
497
            distance_cluster1 += euclidean_distance_sqrt(cluster1[i], member_cluster1);
498
            distance_general += euclidean_distance_sqrt(cluster1[i], member_cluster_general);
499
            
500
        else:
501
            distance_cluster1 += euclidean_distance_sqrt(data[ cluster1[i] ], member_cluster1);
502
            distance_general += euclidean_distance_sqrt(data[ cluster1[i] ], member_cluster_general);
503
    
504
    for j in range(len(cluster2)):
505
        if (data is None):
506
            distance_cluster2 += euclidean_distance_sqrt(cluster2[j], member_cluster2);
507
            distance_general += euclidean_distance_sqrt(cluster2[j], member_cluster_general);
508
            
509
        else:
510
            distance_cluster2 += euclidean_distance_sqrt(data[ cluster2[j] ], member_cluster2);
511
            distance_general += euclidean_distance_sqrt(data[ cluster2[j] ], member_cluster_general);
512
    
513
    return distance_general - distance_cluster1 - distance_cluster2;
514
515
516
def calculate_ellipse_description(covariance, scale = 2.0):
517
    """!
518
    @brief Calculates description of ellipse using covariance matrix.
519
    
520
    @param[in] covariance (numpy.array): Covariance matrix for which ellipse area should be calculated.
521
    @param[in] scale (float): Scale of the ellipse.
522
    
523
    @return (float, float, float) Return ellipse description: angle, width, height.
524
    
525
    """
526
    
527
    eigh_values, eigh_vectors = numpy.linalg.eigh(covariance);
528
    order = eigh_values.argsort()[::-1];
529
    
530
    values, vectors = eigh_values[order], eigh_vectors[order];
531
    angle = numpy.degrees(numpy.arctan2(*vectors[:,0][::-1]));
532
    
533
    width, height = 2.0 * scale * numpy.sqrt(values);
534
    return angle, width, height;
535
536
537
def data_corners(data, data_filter = None):
538
    """!
539
    @brief Finds maximum and minimum corner in each dimension of the specified data.
540
    
541
    @param[in] data (list): List of points that should be analysed.
542
    @param[in] data_filter (list): List of indexes of the data that should be analysed,
543
                if it is 'None' then whole 'data' is analysed to obtain corners.
544
    
545
    @return (list) Tuple of two points that corresponds to minimum and maximum corner (min_corner, max_corner).
546
    
547
    """
548
    
549
    dimensions = len(data[0]);
550
    
551
    bypass = data_filter;
552
    if (bypass is None):
553
        bypass = range(len(data));
554
    
555
    maximum_corner = data[bypass[0]][:];
556
    minimum_corner = data[bypass[0]][:];
557
    
558
    for index_point in bypass:
559
        for index_dimension in range(dimensions):
560
            if (data[index_point][index_dimension] > maximum_corner[index_dimension]):
561
                maximum_corner[index_dimension] = data[index_point][index_dimension];
562
            
563
            if (data[index_point][index_dimension] < minimum_corner[index_dimension]):
564
                minimum_corner[index_dimension] = data[index_point][index_dimension];
565
    
566
    return (minimum_corner, maximum_corner);
567
568
569
def norm_vector(vector):
570
    """!
571
    @brief Calculates norm of an input vector that is known as a vector length.
572
    
573
    @param[in] vector (list): The input vector whose length is calculated.
574
    
575
    @return (double) vector norm known as vector length.
576
    
577
    """
578
    
579
    length = 0.0;
580
    for component in vector:
581
        length += component * component;
582
    
583
    length = length ** 0.5;
584
    
585
    return length;
586
587
588
def unit_vector(vector):
589
    """!
590
    @brief Calculates unit vector.
591
    @details Unit vector calculates of an input vector involves two steps. 
592
              The first, calculate vector length. The second,
593
              divide each vector component by the obtained length.
594
    
595
    @param[in] vector (list): The input vector that is used for unit vector calculation.
596
    
597
    """
598
    
599
    length = norm_vector(vector);
600
    unit_vector_instance = [];
601
    
602
    for component in vector:
603
        unit_vector_instance.append(component / length);
604
    
605
    return unit_vector_instance;
606
607
608
def heaviside(value):
609
    """!
610
    @brief Calculates Heaviside function that represents step function.
611
    @details If input value is greater than 0 then returns 1, otherwise returns 0.
612
    
613
    @param[in] value (double): Argument of Heaviside function.
614
    
615
    @return (double) Value of Heaviside function.
616
    
617
    """
618
    if (value > 0.0): 
619
        return 1.0;
620
    
621
    return 0.0;
622
623
624
def timedcall(executable_function, *args):
625
    """!
626
    @brief Executes specified method or function with measuring of execution time.
627
    
628
    @param[in] executable_function (pointer): Pointer to function or method.
629
    @param[in] args (*): Arguments of called function or method.
630
    
631
    @return (tuple) Execution time and result of execution of function or method (execution_time, result_execution).
632
    
633
    """
634
    
635
    time_start = time.clock();
636
    result = executable_function(*args);
637
    time_end = time.clock();
638
    
639
    return (time_end - time_start, result);
640
641
642
def extract_number_oscillations(osc_dyn, index = 0, amplitude_threshold = 1.0):
643
    """!
644
    @brief Extracts number of oscillations of specified oscillator.
645
    
646
    @param[in] osc_dyn (list): Dynamic of oscillators.
647
    @param[in] index (uint): Index of oscillator in dynamic.
648
    @param[in] amplitude_threshold (double): Amplitude threshold when oscillation is taken into account, for example,
649
                when oscillator amplitude is greater than threshold then oscillation is incremented.
650
    
651
    @return (uint) Number of oscillations of specified oscillator.
652
    
653
    """
654
    
655
    number_oscillations = 0;
656 View Code Duplication
    waiting_differential = False;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
657
    threshold_passed = False;
658
    high_level_trigger = True if (osc_dyn[0][index] > amplitude_threshold) else False;
659
    
660
    for values in osc_dyn:
661
        if ( (values[index] >= amplitude_threshold) and (high_level_trigger is False) ):
662
            high_level_trigger = True;
663
            threshold_passed = True;
664
        
665
        elif ( (values[index] < amplitude_threshold) and (high_level_trigger is True) ):
666
            high_level_trigger = False;
667
            threshold_passed = True;
668
        
669
        if (threshold_passed is True):
670
            threshold_passed = False;
671
            if (waiting_differential is True and high_level_trigger is False):
672
                number_oscillations += 1;
673
                waiting_differential = False;
674
675
            else:
676
                waiting_differential = True;
677 View Code Duplication
        
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
678
    return number_oscillations;
679
680
681
def allocate_sync_ensembles(dynamic, tolerance = 0.1, threshold = 1.0, ignore = None):
682
    """!
683
    @brief Allocate clusters in line with ensembles of synchronous oscillators where each
684
           synchronous ensemble corresponds to only one cluster.
685
    
686
    @param[in] dynamic (dynamic): Dynamic of each oscillator.
687
    @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
688
    @param[in] threshold (double): Amlitude trigger when spike is taken into account.
689
    @param[in] ignore (bool): Set of indexes that shouldn't be taken into account.
690
    
691
    @return (list) Grours (lists) of indexes of synchronous oscillators, for example, 
692
            [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
693
            
694
    """
695
    
696
    descriptors = [] * len(dynamic);
697
    
698
    # Check from the end for obtaining result
699
    for index_dyn in range(0, len(dynamic[0]), 1):
700
        if ((ignore is not None) and (index_dyn in ignore)):
701
            continue;
702
        
703
        time_stop_simulation = len(dynamic) - 1;
704
        active_state = False;
705
        
706
        if (dynamic[time_stop_simulation][index_dyn] > threshold):
707
            active_state = True;
708
            
709
        # if active state is detected, it means we don't have whole oscillatory period for the considered oscillator, should be skipped.
710
        if (active_state is True):
711
            while ( (dynamic[time_stop_simulation][index_dyn] > threshold) and (time_stop_simulation > 0) ):
712
                time_stop_simulation -= 1;
713
            
714
            # if there are no any oscillation than let's consider it like noise
715
            if (time_stop_simulation == 0):
716
                continue;
717
            
718
            # reset
719
            active_state = False;
720
        
721
        desc = [0, 0, 0]; # end, start, average time of oscillation
722
        for t in range(time_stop_simulation, 0, -1):
723
            if ( (dynamic[t][index_dyn] > 0) and (active_state is False) ):
724
                desc[0] = t;
725
                active_state = True;
726
            elif ( (dynamic[t][index_dyn] < 0) and (active_state is True) ):
727
                desc[1] = t;
728
                active_state = False;
729
                
730
                break;
731
        
732
        if (desc == [0, 0, 0]):
733
            continue;
734
        
735
        desc[2] = desc[1] + (desc[0] - desc[1]) / 2.0;
736
        descriptors.append(desc);
737
        
738
    
739
    # Cluster allocation
740
    sync_ensembles = [];
741
    desc_sync_ensembles = [];
742
    
743
    for index_desc in range(0, len(descriptors), 1):
744
        if (descriptors[index_desc] == []):
745
            continue;
746
        
747
        if (len(sync_ensembles) == 0):
748
            desc_ensemble = descriptors[index_desc];
749
            reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
750
            
751
            desc_ensemble[0] = desc_ensemble[2] + reducer;
752
            desc_ensemble[1] = desc_ensemble[2] - reducer;
753
            
754
            desc_sync_ensembles.append(desc_ensemble);
755
            sync_ensembles.append([ index_desc ]);
756
        else:
757
            oscillator_captured = False;
758
            for index_ensemble in range(0, len(sync_ensembles), 1):
759
                if ( (desc_sync_ensembles[index_ensemble][0] > descriptors[index_desc][2]) and (desc_sync_ensembles[index_ensemble][1] < descriptors[index_desc][2])):
760
                    sync_ensembles[index_ensemble].append(index_desc);
761
                    oscillator_captured = True;
762
                    break;
763
                
764
            if (oscillator_captured is False):
765
                desc_ensemble = descriptors[index_desc];
766
                reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
767
        
768
                desc_ensemble[0] = desc_ensemble[2] + reducer;
769
                desc_ensemble[1] = desc_ensemble[2] - reducer;
770
        
771
                desc_sync_ensembles.append(desc_ensemble);
772
                sync_ensembles.append([ index_desc ]);
773
    
774
    return sync_ensembles;
775
    
776
    
777
def draw_clusters(data, clusters, noise = [], marker_descr = '.', hide_axes = False, axes = None, display_result = True):
778
    """!
779
    @brief Displays clusters for data in 2D or 3D.
780
    
781
    @param[in] data (list): Points that are described by coordinates represented.
782
    @param[in] clusters (list): Clusters that are represented by lists of indexes where each index corresponds to point in data.
783
    @param[in] noise (list): Points that are regarded to noise.
784
    @param[in] marker_descr (string): Marker for displaying points.
785
    @param[in] hide_axes (bool): If True - axes is not displayed.
786
    @param[in] axes (ax) Matplotlib axes where clusters should be drawn, if it is not specified (None) then new plot will be created.
787
    @param[in] display_result (bool): If specified then matplotlib axes will be used for drawing and plot will not be shown.
788
    
789
    @return (ax) Matplotlib axes where drawn clusters are presented.
790
    
791 View Code Duplication
    """
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
792
    # Get dimension
793
    dimension = 0;
794
    if ( (data is not None) and (clusters is not None) ):
795
        dimension = len(data[0]);
796
    elif ( (data is None) and (clusters is not None) ):
797
        dimension = len(clusters[0][0]);
798
    else:
799
        raise NameError('Data or clusters should be specified exactly.');
800
    
801
    "Draw clusters"
802
    colors = [ 'red', 'blue', 'darkgreen', 'brown', 'violet', 
803
               'deepskyblue', 'darkgrey', 'lightsalmon', 'deeppink', 'yellow',
804
               'black', 'mediumspringgreen', 'orange', 'darkviolet', 'darkblue',
805
               'silver', 'lime', 'pink', 'gold', 'bisque' ];
806
               
807
    if (len(clusters) > len(colors)):
808
        raise NameError('Impossible to represent clusters due to number of specified colors.');
809
    
810
    fig = plt.figure();
811
    
812 View Code Duplication
    if (axes is None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
813
        # Check for dimensions
814
        if ((dimension) == 1 or (dimension == 2)):
815
            axes = fig.add_subplot(111);
816
        elif (dimension == 3):
817
            axes = fig.gca(projection='3d');
818
        else:
819
            raise NameError('Drawer supports only 2d and 3d data representation');
820
    
821
    color_index = 0;
822
    for cluster in clusters:
823
        color = colors[color_index];
824
        for item in cluster:
825
            if (dimension == 1):
826
                if (data is None):
827
                    axes.plot(item[0], 0.0, color = color, marker = marker_descr);
828
                else:
829
                    axes.plot(data[item][0], 0.0, color = color, marker = marker_descr);
830
            
831
            if (dimension == 2):
832
                if (data is None):
833
                    axes.plot(item[0], item[1], color = color, marker = marker_descr);
834
                else:
835
                    axes.plot(data[item][0], data[item][1], color = color, marker = marker_descr);
836
                    
837
            elif (dimension == 3):
838
                if (data is None):
839
                    axes.scatter(item[0], item[1], item[2], c = color, marker = marker_descr);
840
                else:
841
                    axes.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker_descr);
842
        
843
        color_index += 1;
844
    
845
    for item in noise:
846
        if (dimension == 1):
847
            if (data is None):
848
                axes.plot(item[0], 0.0, 'w' + marker_descr);
849
            else:
850
                axes.plot(data[item][0], 0.0, 'w' + marker_descr);
851
852
        if (dimension == 2):
853
            if (data is None):
854
                axes.plot(item[0], item[1], 'w' + marker_descr);
855
            else:
856
                axes.plot(data[item][0], data[item][1], 'w' + marker_descr);
857
                
858
        elif (dimension == 3):
859
            if (data is None):
860
                axes.scatter(item[0], item[1], item[2], c = 'w', marker = marker_descr);
861
            else:
862
                axes.scatter(data[item][0], data[item][1], data[item][2], c = 'w', marker = marker_descr);
863
    
864
    axes.grid(True);
865
    
866
    if (hide_axes is True):
867
        axes.xaxis.set_ticklabels([]);
868
        axes.yaxis.set_ticklabels([]);
869
        
870
        if (dimension == 3):
871
            axes.zaxis.set_ticklabels([]);
872
    
873
    if (display_result is True):
874
        plt.show();
875
876
    return axes;
877
878
879
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):
880
    """!
881
    @brief Draw dynamics of neurons (oscillators) in the network.
882
    @details It draws if matplotlib is not specified (None), othewise it should be performed manually.
883
    
884
    @param[in] t (list): Values of time (used by x axis).
885
    @param[in] dyn (list): Values of output of oscillators (used by y axis).
886
    @param[in] x_title (string): Title for Y.
887
    @param[in] y_title (string): Title for X.
888
    @param[in] x_lim (double): X limit.
889
    @param[in] y_lim (double): Y limit.
890
    @param[in] x_labels (bool): If True - shows X labels.
891
    @param[in] y_labels (bool): If True - shows Y labels.
892
    @param[in] separate (list): Consists of lists of oscillators where each such list consists of oscillator indexes that will be shown on separated stage.
893
    @param[in] axes (ax): If specified then matplotlib axes will be used for drawing and plot will not be shown.
894
    
895
    @return (ax) Axes of matplotlib.
896
    
897
    """
898
         
899
    number_lines = 0;
900
    
901
    stage_xlim = None;
902
    if (x_lim is not None):
903
        stage_xlim = x_lim;
904
    elif (len(t) > 0):
905
        stage_xlim = [0, t[len(t) - 1]];
906
    
907
    if ( (isinstance(separate, bool) is True) and (separate is True) ):
908
        if (isinstance(dyn[0], list) is True):
909
            number_lines = len(dyn[0]);
910
        else:
911
            number_lines = 1;
912
            
913
    elif (isinstance(separate, list) is True):
914
        number_lines = len(separate);
915
        
916
    else:
917
        number_lines = 1;
918
    
919
    dysplay_result = False;
920
    if (axes is None):
921
        dysplay_result = True;
922
        (fig, axes) = plt.subplots(number_lines, 1);
923
    
924
    # Check if we have more than one dynamic
925
    if (isinstance(dyn[0], list) is True):
926
        num_items = len(dyn[0]);
927
        for index in range(0, num_items, 1):
928
            y = [item[index] for item in dyn];
929
            
930
            if (number_lines > 1):
931
                index_stage = -1;
932
                
933
                # Find required axes for the y
934
                if (isinstance(separate, bool) is True):
935
                    index_stage = index;
936
                    
937
                elif (isinstance(separate, list) is True):
938
                    for index_group in range(0, len(separate), 1):
939
                        if (index in separate[index_group]): 
940
                            index_stage = index_group;
941
                            break;
942
                
943
                if (index_stage != -1):
944
                    if (index_stage != number_lines - 1):
945
                        axes[index_stage].get_xaxis().set_visible(False);
946
                              
947
                    axes[index_stage].plot(t, y, 'b-', linewidth = 0.5); 
948
                    set_ax_param(axes[index_stage], x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
949
                
950
            else:
951
                axes.plot(t, y, 'b-', linewidth = 0.5);
952
                set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
953
    else:
954
        axes.plot(t, dyn, 'b-', linewidth = 0.5);
955
        set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
956
    
957
    if (dysplay_result is True):
958
        plt.show();
959
    
960
    return axes;
961
962
963
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):
964
    """!
965
    @brief Sets parameters for matplotlib ax.
966
    
967
    @param[in] ax (Axes): Axes for which parameters should applied.
968
    @param[in] x_title (string): Title for Y.
969
    @param[in] y_title (string): Title for X.
970
    @param[in] x_lim (double): X limit.
971
    @param[in] y_lim (double): Y limit.
972
    @param[in] x_labels (bool): If True - shows X labels.
973
    @param[in] y_labels (bool): If True - shows Y labels.
974
    @param[in] grid (bool): If True - shows grid.
975
    
976
    """
977
    from matplotlib.font_manager import FontProperties;
978
    from matplotlib import rcParams;
979
    
980
    if (_platform == "linux") or (_platform == "linux2"):
981
        rcParams['font.sans-serif'] = ['Liberation Serif'];
982
    else:
983
        rcParams['font.sans-serif'] = ['Arial'];
984
        
985
    rcParams['font.size'] = 12;
986
        
987
    surface_font = FontProperties();
988
    if (_platform == "linux") or (_platform == "linux2"):
989
        surface_font.set_name('Liberation Serif');
990
    else:
991
        surface_font.set_name('Arial');
992
        
993
    surface_font.set_size('12');
994
    
995
    if (y_title is not None): ax.set_ylabel(y_title, fontproperties = surface_font);
996
    if (x_title is not None): ax.set_xlabel(x_title, fontproperties = surface_font);
997
    
998
    if (x_lim is not None): ax.set_xlim(x_lim[0], x_lim[1]);
999
    if (y_lim is not None): ax.set_ylim(y_lim[0], y_lim[1]);
1000
    
1001
    if (x_labels is False): ax.xaxis.set_ticklabels([]);
1002
    if (y_labels is False): ax.yaxis.set_ticklabels([]);
1003
    
1004
    ax.grid(grid);
1005
1006
1007
def draw_dynamics_set(dynamics, xtitle = None, ytitle = None, xlim = None, ylim = None, xlabels = False, ylabels = False):
1008
    """!
1009
    @brief Draw lists of dynamics of neurons (oscillators) in the network.
1010
    
1011
    @param[in] dynamics (list): List of network outputs that are represented by values of output of oscillators (used by y axis).
1012
    @param[in] xtitle (string): Title for Y.
1013
    @param[in] ytitle (string): Title for X.
1014
    @param[in] xlim (double): X limit.
1015
    @param[in] ylim (double): Y limit.
1016
    @param[in] xlabels (bool): If True - shows X labels.
1017
    @param[in] ylabels (bool): If True - shows Y labels.
1018
    
1019
    """
1020
    # Calculate edge for confortable representation.
1021
    number_dynamics = len(dynamics);
1022
    if (number_dynamics == 1):
1023
        draw_dynamics(dynamics[0][0], dynamics[0][1], xtitle, ytitle, xlim, ylim, xlabels, ylabels);
1024
        return;
1025
    
1026
    number_cols = int(numpy.ceil(number_dynamics ** 0.5));
1027
    number_rows = int(numpy.ceil(number_dynamics / number_cols));
1028
1029
    real_index = 0, 0;
1030
    double_indexer = True;
1031
    if ( (number_cols == 1) or (number_rows == 1) ):
1032
        real_index = 0;
1033
        double_indexer = False;
1034
    
1035
    (_, axarr) = plt.subplots(number_rows, number_cols);
1036
    #plt.setp([ax for ax in axarr], visible = False);
1037
    
1038
    for dynamic in dynamics:
1039
        axarr[real_index] = draw_dynamics(dynamic[0], dynamic[1], xtitle, ytitle, xlim, ylim, xlabels, ylabels, axes = axarr[real_index]);
1040
        #plt.setp(axarr[real_index], visible = True);
1041
        
1042
        if (double_indexer is True):
1043
            real_index = real_index[0], real_index[1] + 1;
1044
            if (real_index[1] >= number_cols):
1045
                real_index = real_index[0] + 1, 0; 
1046
        else:
1047
            real_index += 1;
1048
            
1049
    plt.show();
1050
1051
1052
def draw_image_color_segments(source, clusters, hide_axes = True):
1053
    """!
1054
    @brief Shows image segments using colored image.
1055
    @details Each color on result image represents allocated segment. The first image is initial and other is result of segmentation.
1056
    
1057
    @param[in] source (string): Path to image.
1058
    @param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
1059
                                consists of indexes of pixel from source image.
1060
    @param[in] hide_axes (bool): If True then axes will not be displayed.
1061
    
1062
    """
1063
        
1064
    image_source = Image.open(source);
1065
    image_size = image_source.size;
1066
    
1067
    (fig, axarr) = plt.subplots(1, 2);
1068
    
1069
    plt.setp([ax for ax in axarr], visible = False);
1070
    
1071
    available_colors = [ (0, 162, 232),   (34, 177, 76),   (237, 28, 36),
1072
                         (255, 242, 0),   (0, 0, 0),       (237, 28, 36),
1073
                         (255, 174, 201), (127, 127, 127), (185, 122, 87), 
1074
                         (200, 191, 231), (136, 0, 21),    (255, 127, 39),
1075
                         (63, 72, 204),   (195, 195, 195), (255, 201, 14),
1076
                         (239, 228, 176), (181, 230, 29),  (153, 217, 234),
1077
                         (112, 146, 180) ];
1078
    
1079
    image_color_segments = [(255, 255, 255)] * (image_size[0] * image_size[1]);
1080
    
1081
    for index_segment in range(len(clusters)):
1082
        for index_pixel in clusters[index_segment]:
1083
            image_color_segments[index_pixel] = available_colors[index_segment];
1084
    
1085
    stage = array(image_color_segments, numpy.uint8);
1086
    stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
1087
    image_cluster = Image.fromarray(stage, 'RGB');
1088
    
1089
    axarr[0].imshow(image_source, interpolation = 'none');
1090
    axarr[1].imshow(image_cluster, interpolation = 'none');
1091
    
1092
    for i in range(2):
1093
        plt.setp(axarr[i], visible = True);
1094
        
1095
        if (hide_axes is True):
1096
            axarr[i].xaxis.set_ticklabels([]);
1097
            axarr[i].yaxis.set_ticklabels([]);
1098
            axarr[i].xaxis.set_ticks_position('none');
1099
            axarr[i].yaxis.set_ticks_position('none');
1100
    
1101
    plt.show();
1102
1103
1104
def draw_image_mask_segments(source, clusters, hide_axes = True):
1105
    """!
1106
    @brief Shows image segments using black masks.
1107
    @details Each black mask of allocated segment is presented on separate plot.
1108
             The first image is initial and others are black masks of segments.
1109
    
1110
    @param[in] source (string): Path to image.
1111
    @param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
1112
                                consists of indexes of pixel from source image.
1113
    @param[in] hide_axes (bool): If True then axes will not be displayed.
1114
    
1115
    """
1116
    if (len(clusters) == 0):
1117
        print("Warning: Nothing to draw - list of clusters is empty.")
1118
        return;
1119
        
1120
    image_source = Image.open(source);
1121
    image_size = image_source.size;
1122
    
1123
    # Calculate edge for confortable representation.
1124
    number_clusters = len(clusters) + 1; # show with the source image
1125
    
1126
    number_cols = int(numpy.ceil(number_clusters ** 0.5));
1127
    number_rows = int(numpy.ceil(number_clusters / number_cols));
1128
    
1129
1130
    real_index = 0, 0;
1131
    double_indexer = True;
1132
    if ( (number_cols == 1) or (number_rows == 1) ):
1133
        real_index = 0;
1134
        double_indexer = False;
1135
    
1136
    (fig, axarr) = plt.subplots(number_rows, number_cols);
1137
    plt.setp([ax for ax in axarr], visible = False);
1138
    
1139
    axarr[real_index].imshow(image_source, interpolation = 'none');
1140
    plt.setp(axarr[real_index], visible = True);
1141
    
1142
    if (hide_axes is True):
1143
        axarr[real_index].xaxis.set_ticklabels([]);
1144
        axarr[real_index].yaxis.set_ticklabels([]);
1145
        axarr[real_index].xaxis.set_ticks_position('none');
1146
        axarr[real_index].yaxis.set_ticks_position('none');
1147
            
1148
    if (double_indexer is True):
1149
        real_index = 0, 1;
1150
    else:
1151
        real_index += 1;
1152
    
1153
    for cluster in clusters:
1154
        stage_cluster = [(255, 255, 255)] * (image_size[0] * image_size[1]);
1155
        for index in cluster:
1156
            stage_cluster[index] = (0, 0, 0);
1157
          
1158
        stage = array(stage_cluster, numpy.uint8);
1159
        stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
1160
        
1161
        image_cluster = Image.fromarray(stage, 'RGB');
1162
        
1163
        axarr[real_index].imshow(image_cluster, interpolation = 'none');
1164
        plt.setp(axarr[real_index], visible = True);
1165
        
1166
        if (hide_axes is True):
1167
            axarr[real_index].xaxis.set_ticklabels([]);
1168
            axarr[real_index].yaxis.set_ticklabels([]);
1169
            
1170
            axarr[real_index].xaxis.set_ticks_position('none');
1171
            axarr[real_index].yaxis.set_ticks_position('none');
1172
        
1173
        if (double_indexer is True):
1174
            real_index = real_index[0], real_index[1] + 1;
1175
            if (real_index[1] >= number_cols):
1176
                real_index = real_index[0] + 1, 0; 
1177
        else:
1178
            real_index += 1;
1179
1180
            
1181
    plt.show();
1182
1183
1184
def linear_sum(list_vector):
1185
    """!
1186
    @brief Calculates linear 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 (list|double) Linear sum of vector that can be represented by list in case of multidimensional elements.
1191
    
1192
    """
1193
    dimension = 1;
1194
    linear_sum = 0.0;
1195
    list_representation = (type(list_vector[0]) == list);
1196
    
1197
    if (list_representation is True):
1198
        dimension = len(list_vector[0]);
1199
        linear_sum = [0] * dimension;
1200
        
1201
    for index_element in range(0, len(list_vector)):
1202
        if (list_representation is True):
1203
            for index_dimension in range(0, dimension):
1204
                linear_sum[index_dimension] += list_vector[index_element][index_dimension];
1205
        else:
1206
            linear_sum += list_vector[index_element];
1207
1208
    return linear_sum;
1209
1210
1211
def square_sum(list_vector):
1212
    """!
1213
    @brief Calculates square sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
1214
    
1215
    @param[in] list_vector (list): Input vector.
1216
    
1217
    @return (double) Square sum of vector.
1218
    
1219
    """
1220
    
1221
    square_sum = 0.0;
1222
    list_representation = (type(list_vector[0]) == list);
1223
        
1224
    for index_element in range(0, len(list_vector)):
1225
        if (list_representation is True):
1226
            square_sum += sum(list_math_multiplication(list_vector[index_element], list_vector[index_element]));
1227
        else:
1228
            square_sum += list_vector[index_element] * list_vector[index_element];
1229
         
1230
    return square_sum;
1231
1232
    
1233
def list_math_subtraction(a, b):
1234
    """!
1235
    @brief Calculates subtraction of two lists.
1236
    @details Each element from list 'a' is subtracted by element from list 'b' accordingly.
1237
    
1238
    @param[in] a (list): List of elements that supports mathematical subtraction.
1239
    @param[in] b (list): List of elements that supports mathematical subtraction.
1240
    
1241
    @return (list) Results of subtraction of two lists.
1242
    
1243
    """
1244
    return [a[i] - b[i] for i in range(len(a))];
1245
1246
1247
def list_math_substraction_number(a, b):
1248
    """!
1249
    @brief Calculates subtraction between list and number.
1250
    @details Each element from list 'a' is subtracted by number 'b'.
1251
    
1252
    @param[in] a (list): List of elements that supports mathematical subtraction.
1253
    @param[in] b (list): Value that supports mathematical subtraction.
1254
    
1255
    @return (list) Results of subtraction between list and number.
1256
    
1257
    """        
1258
    return [a[i] - b for i in range(len(a))];  
1259
1260
1261
def list_math_addition(a, b):
1262
    """!
1263
    @brief Addition of two lists.
1264
    @details Each element from list 'a' is added to element from list 'b' accordingly.
1265
    
1266
    @param[in] a (list): List of elements that supports mathematic addition..
1267
    @param[in] b (list): List of elements that supports mathematic addition..
1268
    
1269
    @return (list) Results of addtion of two lists.
1270
    
1271
    """    
1272
    return [a[i] + b[i] for i in range(len(a))];
1273
1274
1275
def list_math_addition_number(a, b):
1276
    """!
1277
    @brief Addition between list and number.
1278
    @details Each element from list 'a' is added to number 'b'.
1279
    
1280
    @param[in] a (list): List of elements that supports mathematic addition.
1281
    @param[in] b (double): Value that supports mathematic addition.
1282
    
1283
    @return (list) Result of addtion of two lists.
1284
    
1285
    """    
1286
    return [a[i] + b for i in range(len(a))];
1287
1288
1289
def list_math_division_number(a, b):
1290
    """!
1291
    @brief Division between list and number.
1292
    @details Each element from list 'a' is divided by number 'b'.
1293
    
1294
    @param[in] a (list): List of elements that supports mathematic division.
1295
    @param[in] b (double): Value that supports mathematic division.
1296
    
1297
    @return (list) Result of division between list and number.
1298
    
1299
    """    
1300
    return [a[i] / b for i in range(len(a))];
1301
1302
1303
def list_math_division(a, b):
1304
    """!
1305
    @brief Division of two lists.
1306
    @details Each element from list 'a' is divided by element from list 'b' accordingly.
1307
    
1308
    @param[in] a (list): List of elements that supports mathematic division.
1309
    @param[in] b (list): List of elements that supports mathematic division.
1310
    
1311
    @return (list) Result of division of two lists.
1312
    
1313
    """    
1314
    return [a[i] / b[i] for i in range(len(a))];
1315
1316
1317
def list_math_multiplication_number(a, b):
1318
    """!
1319
    @brief Multiplication between list and number.
1320
    @details Each element from list 'a' is multiplied by number 'b'.
1321
    
1322
    @param[in] a (list): List of elements that supports mathematic division.
1323
    @param[in] b (double): Number that supports mathematic division.
1324
    
1325
    @return (list) Result of division between list and number.
1326
    
1327
    """    
1328
    return [a[i] * b for i in range(len(a))];
1329
1330
1331
def list_math_multiplication(a, b):
1332
    """!
1333
    @brief Multiplication of two lists.
1334
    @details Each element from list 'a' is multiplied by element from list 'b' accordingly.
1335
    
1336
    @param[in] a (list): List of elements that supports mathematic multiplication.
1337
    @param[in] b (list): List of elements that supports mathematic multiplication.
1338
    
1339
    @return (list) Result of multiplication of elements in two lists.
1340
    
1341
    """        
1342
    return [a[i] * b[i] for i in range(len(a))];
1343