Skip to content

Commit f311a33

Browse files
committed
[BUG] Adapting new activator for finite faults
1 parent 82303b8 commit f311a33

File tree

1 file changed

+72
-22
lines changed

1 file changed

+72
-22
lines changed

gempy_engine/modules/activator/activator_interface.py

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from gempy_engine.config import DEBUG_MODE, AvailableBackends
44
from gempy_engine.core.backend_tensor import BackendTensor as bt, BackendTensor
55
import numpy as np
6+
import numbers
67

78
from gempy_engine.core.data.exported_fields import ExportedFields
89

@@ -16,9 +17,9 @@ def activate_formation_block(exported_fields: ExportedFields, ids: np.ndarray,
1617

1718
if LEGACY := False and not sigmoid_slope_negative: # * Here we branch to the experimental activation function with hard sigmoid
1819
sigm = activate_formation_block_from_args(
19-
Z_x=Z_x,
20-
ids=ids,
21-
scalar_value_at_sp=scalar_value_at_sp,
20+
Z_x=Z_x,
21+
ids=ids,
22+
scalar_value_at_sp=scalar_value_at_sp,
2223
sigmoid_slope=sigmoid_slope
2324
)
2425
else:
@@ -35,7 +36,7 @@ def activate_formation_block(exported_fields: ExportedFields, ids: np.ndarray,
3536
Z=Z_x,
3637
edges=scalar_value_at_sp,
3738
ids=ids,
38-
sigmoid_slope=sigmoid_slope
39+
sigmoid_slope=sigmoid_slope
3940
)
4041

4142
return sigm
@@ -103,7 +104,7 @@ def soft_segment_unbounded(Z, edges, ids, sigmoid_slope):
103104
# --- 1) per-edge temp: tau_k = jump_k / (4 * m) ---
104105
# jumps = ids[1:] - ids[:-1] # shape (K-1,)
105106

106-
jumps = torch.abs(ids[1:] - ids[:-1]) # shape (K-1,)
107+
jumps = torch.abs(ids[1:] - ids[:-1]) # shape (K-1,)
107108
tau_k = jumps / (4 * sigmoid_slope) # shape (K-1,)
108109

109110
# --- 2) first bin (-∞, e1) ---
@@ -128,6 +129,7 @@ def soft_segment_unbounded(Z, edges, ids, sigmoid_slope):
128129

129130
import numpy as np
130131

132+
131133
def soft_segment_unbounded_np(Z, edges, ids, sigmoid_slope):
132134
"""
133135
Z: array of shape (...,) of scalar values
@@ -136,31 +138,79 @@ def soft_segment_unbounded_np(Z, edges, ids, sigmoid_slope):
136138
sigmoid_slope: scalar target peak slope m > 0
137139
returns: array of shape (...,) of the soft-assigned id
138140
"""
139-
Z = np.asarray(Z)
141+
Z = np.asarray(Z)
140142
edges = np.asarray(edges)
141-
ids = np.asarray(ids)
143+
ids = np.asarray(ids)
142144

143-
# 1) per-edge temperatures τ_k = |Δ_k|/(4·m)
144-
jumps = np.abs(ids[1:] - ids[:-1]) # shape (K-1,)
145-
tau_k = jumps / (4 * sigmoid_slope) # shape (K-1,)
145+
# Check if sigmoid function is num or array
146+
match sigmoid_slope:
147+
case np.ndarray():
148+
membership = _final_faults_segmentation(Z, edges, sigmoid_slope)
149+
case numbers.Number():
150+
membership = _lith_segmentation(Z, edges, ids, sigmoid_slope)
151+
case _:
152+
raise ValueError("sigmoid_slope must be a float or an array")
146153

147-
# 2) first bin (-∞, e1) via σ((e1 - Z)/τ₁)
148-
first = 1.0 / (1.0 + np.exp((Z - edges[0]) / tau_k[0])) # shape (...,)
149154

150-
# 3) last bin [e_{K-1}, ∞) via σ((Z - e_{K-1})/τ_{K-1})
151-
last = 1.0 / (1.0 + np.exp(-(Z - edges[-1]) / tau_k[-1])) # shape (...,)
155+
ids__sum = np.sum(membership * ids, axis=-1)
156+
return np.atleast_2d(ids__sum)
157+
158+
159+
def _final_faults_segmentation(Z, edges, sigmoid_slope):
160+
first = _sigmoid(
161+
scalar_field=Z,
162+
edges=edges[0],
163+
tau_k=1 / sigmoid_slope
164+
) # shape (...,)
165+
last = _sigmoid(
166+
scalar_field=Z,
167+
edges=edges[-1],
168+
tau_k=1 / sigmoid_slope
169+
)
170+
membership = np.concatenate(
171+
[first[..., None], last[..., None]],
172+
axis=-1
173+
) # shape (...,K)
174+
return membership
152175

153-
# 4) middle bins [e_i, e_{i+1}): σ((Z - e_i)/τ_i) - σ((Z - e_{i+1})/τ_{i+1})
154-
Z_exp = Z[..., None] # shape (...,1)
155-
left = 1.0 / (1.0 + np.exp(-(Z_exp - edges[:-1]) / tau_k[:-1])) # (...,K-2)
156-
right = 1.0 / (1.0 + np.exp(-(Z_exp - edges[1: ]) / tau_k[1: ])) # (...,K-2)
157-
middle = left - right # (...,K-2)
158176

177+
def _lith_segmentation(Z, edges, ids, sigmoid_slope):
178+
# 1) per-edge temperatures τ_k = |Δ_k|/(4·m)
179+
jumps = np.abs(ids[1:] - ids[:-1]) # shape (K-1,)
180+
tau_k = jumps / (4 * sigmoid_slope) # shape (K-1,)
181+
# 2) first bin (-∞, e1) via σ((e1 - Z)/τ₁)
182+
first = _sigmoid(
183+
scalar_field=Z,
184+
edges=edges[0],
185+
tau_k=tau_k[0]
186+
) # shape (...,)
187+
# 3) last bin [e_{K-1}, ∞) via σ((Z - e_{K-1})/τ_{K-1})
188+
# last = 1.0 / (1.0 + np.exp(-(Z - edges[-1]) / tau_k[-1])) # shape (...,)
189+
last = _sigmoid(
190+
scalar_field=Z,
191+
edges=edges[-1],
192+
tau_k=tau_k[-1]
193+
)
194+
# 4) middle bins [e_i, e_{i+1}): σ((Z - e_i)/τ_i) - σ((Z - e_{i+1})/τ_{i+1})
195+
# shape (...,1)
196+
left = _sigmoid(
197+
scalar_field=(Z[..., None]),
198+
edges=edges[:-1],
199+
tau_k=tau_k[:-1]
200+
)
201+
right = _sigmoid(
202+
scalar_field=(Z[..., None]),
203+
edges=edges[1:],
204+
tau_k=tau_k[1:]
205+
)
206+
middle = left - right # (...,K-2)
159207
# 5) assemble memberships and weight by ids
160208
membership = np.concatenate(
161-
[ first[..., None], middle, last[..., None] ],
209+
[first[..., None], middle, last[..., None]],
162210
axis=-1
163211
) # shape (...,K)
212+
return membership
164213

165-
ids__sum = np.sum(membership * ids, axis=-1)
166-
return np.atleast_2d(ids__sum)
214+
215+
def _sigmoid(scalar_field, edges, tau_k):
216+
return 1.0 / (1.0 + np.exp((scalar_field - edges) / tau_k))

0 commit comments

Comments
 (0)