AlvLouWeh2017: Implement remaining features

master
Alinson S. Xavier 4 years ago
parent 0480461a7f
commit f2b710e9f9
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -329,6 +329,9 @@ class FeaturesExtractor:
c_sa_down: Optional[np.ndarray] = None, c_sa_down: Optional[np.ndarray] = None,
c_sa_up: Optional[np.ndarray] = None, c_sa_up: Optional[np.ndarray] = None,
values: Optional[np.ndarray] = None, values: Optional[np.ndarray] = None,
with_m1: bool = True,
with_m2: bool = True,
with_m3: bool = True,
) -> np.ndarray: ) -> np.ndarray:
""" """
Computes static variable features described in: Computes static variable features described in:
@ -339,12 +342,8 @@ class FeaturesExtractor:
assert b is not None assert b is not None
assert c is not None assert c is not None
nvars = len(c) nvars = len(c)
c_pos_sum = c[c > 0].sum()
c_neg_sum = -c[c < 0].sum()
curr = 0 curr = 0
max_n_features = 30 max_n_features = 40
features = np.zeros((nvars, max_n_features)) features = np.zeros((nvars, max_n_features))
def push(v: np.ndarray) -> None: def push(v: np.ndarray) -> None:
@ -356,17 +355,33 @@ class FeaturesExtractor:
push(np.sign(v)) push(np.sign(v))
push(np.abs(v)) push(np.abs(v))
def maxmin(M: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
# Compute max using regular numpy operations
M_max = np.ravel(M.max(axis=0).todense())
# Compute min by iterating through the sparse matrix data, so that
# we skip non-zero entries
M_min = np.array(
[
0.0 if len(M[:, j].data) == 0 else M[:, j].data.min()
for j in range(M.shape[1])
]
)
return M_max, M_min
with np.errstate(divide="ignore", invalid="ignore"): with np.errstate(divide="ignore", invalid="ignore"):
# Feature 1 # Feature 1
push(np.sign(c)) push(np.sign(c))
# Feature 2 # Feature 2
c_pos_sum = c[c > 0].sum()
push(np.abs(c) / c_pos_sum) push(np.abs(c) / c_pos_sum)
# Feature 3 # Feature 3
c_neg_sum = -c[c < 0].sum()
push(np.abs(c) / c_neg_sum) push(np.abs(c) / c_neg_sum)
if A is not None: if A is not None and with_m1:
# Compute A_ji / |b_j| # Compute A_ji / |b_j|
M1 = A.T.multiply(1.0 / np.abs(b)).T.tocsr() M1 = A.T.multiply(1.0 / np.abs(b)).T.tocsr()
@ -394,13 +409,12 @@ class FeaturesExtractor:
push_sign_abs(M1_neg_min) push_sign_abs(M1_neg_min)
push_sign_abs(M1_neg_max) push_sign_abs(M1_neg_max)
if A is not None: if A is not None and with_m2:
# Compute |c_i| / A_ij # Compute |c_i| / A_ij
M2 = A.power(-1).multiply(np.abs(c)).tocsc() M2 = A.power(-1).multiply(np.abs(c)).tocsc()
# Compute max/min # Compute max/min
M2_max = np.ravel(M2.max(axis=0).todense()) M2_max, M2_min = maxmin(M2)
M2_min = np.array([M2[:, j].data.min() for j in range(nvars)])
# Make copies of M2 and erase elements based on sign(c) # Make copies of M2 and erase elements based on sign(c)
M2_pos_max = M2_max.copy() M2_pos_max = M2_max.copy()
@ -418,6 +432,45 @@ class FeaturesExtractor:
push_sign_abs(M2_neg_min) push_sign_abs(M2_neg_min)
push_sign_abs(M2_neg_max) push_sign_abs(M2_neg_max)
if A is not None and with_m3:
# Compute row sums
S_pos = A.maximum(0).sum(axis=1)
S_neg = np.abs(A.minimum(0).sum(axis=1))
# Divide A by positive and negative row sums
M3_pos = A.multiply(1 / S_pos).tocsr()
M3_neg = A.multiply(1 / S_neg).tocsr()
# Remove +inf and -inf generated by division by zero
M3_pos.data[~np.isfinite(M3_pos.data)] = 0.0
M3_neg.data[~np.isfinite(M3_neg.data)] = 0.0
M3_pos.eliminate_zeros()
M3_neg.eliminate_zeros()
# Split each matrix into positive and negative parts
M3_pos_pos = M3_pos.maximum(0)
M3_pos_neg = -(M3_pos.minimum(0))
M3_neg_pos = M3_neg.maximum(0)
M3_neg_neg = -(M3_neg.minimum(0))
# Calculate max/min
M3_pos_pos_max, M3_pos_pos_min = maxmin(M3_pos_pos)
M3_pos_neg_max, M3_pos_neg_min = maxmin(M3_pos_neg)
M3_neg_pos_max, M3_neg_pos_min = maxmin(M3_neg_pos)
M3_neg_neg_max, M3_neg_neg_min = maxmin(M3_neg_neg)
# Features 20-35
push_sign_abs(M3_pos_pos_max)
push_sign_abs(M3_pos_pos_min)
push_sign_abs(M3_pos_neg_max)
push_sign_abs(M3_pos_neg_min)
push_sign_abs(M3_neg_pos_max)
push_sign_abs(M3_neg_pos_min)
push_sign_abs(M3_neg_neg_max)
push_sign_abs(M3_neg_neg_min)
# Feature 36: only available during B&B
# Feature 37 # Feature 37
if values is not None: if values is not None:
push( push(
@ -427,22 +480,24 @@ class FeaturesExtractor:
) )
) )
# Features 38-43: only available during B&B
# Feature 44 # Feature 44
if c_sa_up is not None: if c_sa_up is not None:
push(np.sign(c_sa_up)) assert c_sa_down is not None
# Feature 46 # Features 44 and 46
if c_sa_down is not None: push(np.sign(c_sa_up))
push(np.sign(c_sa_down)) push(np.sign(c_sa_down))
# Feature 47 # Feature 45 is duplicated
if c_sa_down is not None:
push(np.log(c - c_sa_down / np.sign(c)))
# Feature 48 # Feature 47-48
if c_sa_up is not None: push(np.log(c - c_sa_down / np.sign(c)))
push(np.log(c - c_sa_up / np.sign(c))) push(np.log(c - c_sa_up / np.sign(c)))
# Features 49-64: only available during B&B
features = features[:, 0:curr] features = features[:, 0:curr]
_fix_infinity(features) _fix_infinity(features)
return features return features

@ -86,6 +86,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.264368,
1.0,
0.264368,
0.0,
0.0,
0.0,
0.0,
1.0,
23.0,
1.0,
23.0,
0.0,
0.0,
0.0,
0.0,
], ],
[ [
26.0, 26.0,
@ -109,6 +125,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.298851,
1.0,
0.298851,
0.0,
0.0,
0.0,
0.0,
1.0,
26.0,
1.0,
26.0,
0.0,
0.0,
0.0,
0.0,
], ],
[ [
20.0, 20.0,
@ -132,6 +164,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.229885,
1.0,
0.229885,
0.0,
0.0,
0.0,
0.0,
1.0,
20.0,
1.0,
20.0,
0.0,
0.0,
0.0,
0.0,
], ],
[ [
18.0, 18.0,
@ -155,6 +203,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.206897,
1.0,
0.206897,
0.0,
0.0,
0.0,
0.0,
1.0,
18.0,
1.0,
18.0,
0.0,
0.0,
0.0,
0.0,
], ],
[ [
0.0, 0.0,
@ -178,6 +242,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.011494,
1.0,
0.011494,
0.0,
0.0,
0.0,
0.0,
1.0,
1.0,
1.0,
1.0,
], ],
] ]
), ),
@ -282,6 +362,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.264368,
1.0,
0.264368,
0.0,
0.0,
0.0,
0.0,
1.0,
23.0,
1.0,
23.0,
0.0,
0.0,
0.0,
0.0,
0.0, 0.0,
1.0, 1.0,
1.0, 1.0,
@ -318,6 +414,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.298851,
1.0,
0.298851,
0.0,
0.0,
0.0,
0.0,
1.0,
26.0,
1.0,
26.0,
0.0,
0.0,
0.0,
0.0,
0.076923, 0.076923,
1.0, 1.0,
1.0, 1.0,
@ -354,6 +466,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.229885,
1.0,
0.229885,
0.0,
0.0,
0.0,
0.0,
1.0,
20.0,
1.0,
20.0,
0.0,
0.0,
0.0,
0.0,
0.0, 0.0,
1.0, 1.0,
1.0, 1.0,
@ -390,6 +518,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
1.0,
0.206897,
1.0,
0.206897,
0.0,
0.0,
0.0,
0.0,
1.0,
18.0,
1.0,
18.0,
0.0,
0.0,
0.0,
0.0,
0.0, 0.0,
1.0, 1.0,
-1.0, -1.0,
@ -427,6 +571,22 @@ def test_knapsack() -> None:
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.0,
0.0,
0.0,
1.0,
0.011494,
1.0,
0.011494,
0.0,
0.0,
0.0,
0.0,
1.0,
1.0,
1.0,
1.0,
0.0,
1.0, 1.0,
-1.0, -1.0,
5.265874, 5.265874,

Loading…
Cancel
Save