-
Notifications
You must be signed in to change notification settings - Fork 4
/
pixel_partition_genetic.py
135 lines (112 loc) · 5.08 KB
/
pixel_partition_genetic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from __future__ import division, print_function
import numpy as np
import math
def faster_std(a):
'''
3x times faster than np.std
'''
m = a.mean()
return math.sqrt((np.dot(a, a)/a.size) - m**2)
class Subsampler():
def __init__(self, ra, dec, labels, neighbors, weights=None, **kwargs):
self.pop_size = kwargs['pop_size']
self.surv_rate = kwargs['surv_rate']
self.mutate_rate = kwargs['mutate_rate']
self.equality_weight = kwargs['equality_weight']
self.tournament_k = kwargs['tournament_k']
self.npix = len(ra)
self.x = np.cos(dec/180.*np.pi) * np.cos(ra/180.*np.pi)
self.y = np.cos(dec/180.*np.pi) * np.sin(ra/180.*np.pi)
self.z = np.sin(dec/180.*np.pi)
self.neighbors = neighbors
if weights is None:
self.weights = np.ones(self.npix)
else:
self.weights = weights
ngroup = len(np.unique(labels))
# Check if labels satisfied the requirements:
if (labels.min()!=0) or (labels.max()!=ngroup-1):
raise ValueError('labels must be consecutive integers starting with 0')
self.ngroup = ngroup
self.average_count = np.sum(weights)/self.ngroup
# generate a population of solutions by repeatedly copying of the labels
self.labels_all = np.tile(labels, self.pop_size).reshape(self.pop_size, self.npix)
# if 'preserve_best' in kwargs:
# self.preserve_best = kwargs['preserve_best']
# else:
# self.preserve_best = False
def fitness(self):
# compactness score: lower the better
compactness = np.zeros(self.pop_size)
# equality score: lower the better
equality = np.zeros(self.pop_size)
# weighted counts of each group in each solution
counts = np.zeros((self.pop_size, self.ngroup))
# loop through each individual (solution) in the population
for idx_pop in range(self.pop_size):
labels = self.labels_all[idx_pop]
s = labels.argsort()
label_edges = np.where(np.ediff1d(labels[s]))[0] + 1
# solutions with missing labels are giving the lowest score:
if len(label_edges) < self.ngroup - 1:
equality[idx_pop] = np.inf
compactness[idx_pop] = np.inf
continue
# loop through each group
label_edges = np.append(np.insert(label_edges, 0, 0), self.npix)
for idx_grp in range(self.ngroup):
k1 = label_edges[idx_grp]
k2 = label_edges[idx_grp+1]
members = s[k1:k2]
counts[idx_pop, idx_grp] = np.sum(self.weights[members])
compactness[idx_pop] += math.sqrt(faster_std(self.x[members])**2 \
+ faster_std(self.y[members])**2 + faster_std(self.z[members])**2)
equality[idx_pop] = self.equality_weight * faster_std(counts[idx_pop])/(self.average_count)
# total score: higher the better
scores = -(compactness + equality)
self.scores = scores
self.counts = counts
self.compactness = compactness
self.equality = equality
pass
def selection(self):
'''
Select solutions for the next generation;
'''
# select top scorers as candidates
candidates = np.argsort(self.scores)[-int(self.pop_size*self.surv_rate):]
# Tournament Selection - obtain a list of survivers with the original size
survivers = np.zeros(self.pop_size, dtype=int)
scores = np.zeros(self.pop_size)
for idx_pop in range(self.pop_size):
t_candidates = np.random.choice(candidates, size=self.tournament_k, replace=False)
# select the highest scorer
idx_winner = np.argmax(self.scores[t_candidates])
survivers[idx_pop] = t_candidates[idx_winner]
scores[idx_pop] = self.scores[t_candidates[idx_winner]]
return survivers, scores
def mutate(self):
'''
Only pixels that are bordering a different group are mutated
'''
for idx_pop in range(self.pop_size):
labels = self.labels_all[idx_pop]
# find bordering pixels
mask1 = labels[self.neighbors] != labels[:, None]
mask2 = self.neighbors>=0
mask_border = np.any(mask1 & mask2, axis=1)
# id of border pixels
border_idx = np.where(mask_border)[0]
# mutate with a probablity
mutator = np.where(np.random.rand(len(border_idx)) < self.mutate_rate)[0]
for idx_pixel in border_idx[mutator]:
# pixel id of neighboring groups
neighbors_idx = self.neighbors[idx_pixel]
# not an empty neighbor
mask = neighbors_idx>=0
# # not the same group
# mask &= labels[idx_pixel]!=labels[neighbors_idx]
neighbors_idx = neighbors_idx[mask]
if len(neighbors_idx)>0:
labels[idx_pixel] = np.random.choice(labels[neighbors_idx])
pass