3
3
from gempy_engine .config import DEBUG_MODE , AvailableBackends
4
4
from gempy_engine .core .backend_tensor import BackendTensor as bt , BackendTensor
5
5
import numpy as np
6
+ import numbers
6
7
7
8
from gempy_engine .core .data .exported_fields import ExportedFields
8
9
@@ -16,9 +17,9 @@ def activate_formation_block(exported_fields: ExportedFields, ids: np.ndarray,
16
17
17
18
if LEGACY := False and not sigmoid_slope_negative : # * Here we branch to the experimental activation function with hard sigmoid
18
19
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 ,
22
23
sigmoid_slope = sigmoid_slope
23
24
)
24
25
else :
@@ -35,7 +36,7 @@ def activate_formation_block(exported_fields: ExportedFields, ids: np.ndarray,
35
36
Z = Z_x ,
36
37
edges = scalar_value_at_sp ,
37
38
ids = ids ,
38
- sigmoid_slope = sigmoid_slope
39
+ sigmoid_slope = sigmoid_slope
39
40
)
40
41
41
42
return sigm
@@ -103,7 +104,7 @@ def soft_segment_unbounded(Z, edges, ids, sigmoid_slope):
103
104
# --- 1) per-edge temp: tau_k = jump_k / (4 * m) ---
104
105
# jumps = ids[1:] - ids[:-1] # shape (K-1,)
105
106
106
- jumps = torch .abs (ids [1 :] - ids [:- 1 ]) # shape (K-1,)
107
+ jumps = torch .abs (ids [1 :] - ids [:- 1 ]) # shape (K-1,)
107
108
tau_k = jumps / (4 * sigmoid_slope ) # shape (K-1,)
108
109
109
110
# --- 2) first bin (-∞, e1) ---
@@ -128,6 +129,7 @@ def soft_segment_unbounded(Z, edges, ids, sigmoid_slope):
128
129
129
130
import numpy as np
130
131
132
+
131
133
def soft_segment_unbounded_np (Z , edges , ids , sigmoid_slope ):
132
134
"""
133
135
Z: array of shape (...,) of scalar values
@@ -136,31 +138,79 @@ def soft_segment_unbounded_np(Z, edges, ids, sigmoid_slope):
136
138
sigmoid_slope: scalar target peak slope m > 0
137
139
returns: array of shape (...,) of the soft-assigned id
138
140
"""
139
- Z = np .asarray (Z )
141
+ Z = np .asarray (Z )
140
142
edges = np .asarray (edges )
141
- ids = np .asarray (ids )
143
+ ids = np .asarray (ids )
142
144
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" )
146
153
147
- # 2) first bin (-∞, e1) via σ((e1 - Z)/τ₁)
148
- first = 1.0 / (1.0 + np .exp ((Z - edges [0 ]) / tau_k [0 ])) # shape (...,)
149
154
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
152
175
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)
158
176
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)
159
207
# 5) assemble memberships and weight by ids
160
208
membership = np .concatenate (
161
- [ first [..., None ], middle , last [..., None ] ],
209
+ [first [..., None ], middle , last [..., None ]],
162
210
axis = - 1
163
211
) # shape (...,K)
212
+ return membership
164
213
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