66from ._soft_segment import soft_segment_unbounded
77
88import numpy as np
9- import numbers
109
1110
1211def activate_formation_block (exported_fields : ExportedFields , ids : np .ndarray ,
@@ -30,24 +29,6 @@ def activate_formation_block(exported_fields: ExportedFields, ids: np.ndarray,
3029 ids = ids ,
3130 sigmoid_slope = sigmoid_slope
3231 )
33- return sigm
34-
35- match BackendTensor .engine_backend :
36- case AvailableBackends .PYTORCH :
37- sigm = soft_segment_unbounded_torch (
38- Z = Z_x ,
39- edges = scalar_value_at_sp ,
40- ids = ids ,
41- sigmoid_slope = sigmoid_slope
42- )
43- case AvailableBackends .numpy :
44- sigm = soft_segment_unbounded_np (
45- Z = Z_x ,
46- edges = scalar_value_at_sp ,
47- ids = ids ,
48- sigmoid_slope = sigmoid_slope
49- )
50-
5132 return sigm
5233
5334
@@ -89,140 +70,3 @@ def _compute_sigmoid(Z_x, scale_0, scale_1, drift_0, drift_1, drift_id, sigmoid_
8970 for i in range (len (ids )):
9071 sigm += _compute_sigmoid (Z_x , scalar_0_v [i ], scalar_1_v [i ], drift_0_v [i ], drift_1_v [i ], ids [i ], sigmoid_slope )
9172 return sigm
92-
93-
94- import torch
95-
96-
97- def soft_segment_unbounded_torch (Z , edges , ids , sigmoid_slope ):
98- """
99- Z: (...,) tensor of scalar values
100- edges: (K-1,) tensor of finite split points [e1, e2, ..., e_{K-1}]
101- ids: (K,) tensor of the id for each of the K bins
102- tau: scalar target peak slope m > 0
103- returns: (...,) tensor of the soft‐assigned id
104- """
105- # --- ensure torch tensors on the same device/dtype ---
106- if not isinstance (Z , torch .Tensor ):
107- Z = torch .tensor (Z , dtype = torch .float32 , device = edges .device )
108- if not isinstance (edges , torch .Tensor ):
109- edges = torch .tensor (edges , dtype = Z .dtype , device = Z .device )
110- if not isinstance (ids , torch .Tensor ):
111- ids = torch .tensor (ids , dtype = Z .dtype , device = Z .device )
112-
113- # --- 1) per-edge temp: tau_k = jump_k / (4 * m) ---
114- # jumps = ids[1:] - ids[:-1] # shape (K-1,)
115-
116- jumps = torch .abs (ids [1 :] - ids [:- 1 ]) # shape (K-1,)
117- tau_k = jumps / (4 * sigmoid_slope ) # shape (K-1,)
118-
119- # --- 2) first bin (-∞, e1) ---
120- first = torch .sigmoid ((edges [0 ] - Z ) / tau_k [0 ])[..., None ] # (...,1)
121-
122- # --- 3) last bin [e_{K-1}, ∞) ---
123- last = torch .sigmoid ((Z - edges [- 1 ]) / tau_k [- 1 ])[..., None ] # (...,1)
124-
125- # --- 4) middle bins [e_i, e_{i+1}) ---
126- left = torch .sigmoid ((Z [..., None ] - edges [:- 1 ]) / tau_k [:- 1 ]) # (...,K-2)
127- right = torch .sigmoid ((Z [..., None ] - edges [1 :]) / tau_k [1 :]) # (...,K-2)
128- middle = left - right # (...,K-2)
129-
130- # --- 5) assemble memberships and weight by ids ---
131- membership = torch .cat ([first , middle , last ], dim = - 1 ) # (...,K)
132- # return (membership * ids).sum(dim=-1) # (...,)
133-
134- # weighted sum by the ids
135- ids__sum = (membership * ids ).sum (dim = - 1 )
136-
137- # make it at least 2d
138- ids__sum = ids__sum [None , :]
139-
140- return ids__sum
141-
142-
143- import numpy as np
144-
145-
146- def soft_segment_unbounded_np (Z , edges , ids , sigmoid_slope ):
147- """
148- Z: array of shape (...,) of scalar values
149- edges: array of shape (K-1,) of finite split points [e1, e2, ..., e_{K-1}]
150- ids: array of shape (K,) of the id for each of the K bins
151- sigmoid_slope: scalar target peak slope m > 0
152- returns: array of shape (...,) of the soft-assigned id
153- """
154- Z = np .asarray (Z )
155- edges = np .asarray (edges )
156- ids = np .asarray (ids [::- 1 ])
157-
158- # Check if sigmoid function is num or array
159- match sigmoid_slope :
160- case np .ndarray ():
161- membership = _final_faults_segmentation (Z , edges , sigmoid_slope )
162- case numbers .Number ():
163- membership = _lith_segmentation (Z , edges , ids , sigmoid_slope )
164- case _:
165- raise ValueError ("sigmoid_slope must be a float or an array" )
166-
167- ids__sum = np .sum (membership * ids , axis = - 1 )
168- return np .atleast_2d (ids__sum )
169-
170-
171- def _final_faults_segmentation (Z , edges , sigmoid_slope ):
172- first = _sigmoid (
173- scalar_field = Z ,
174- edges = edges [0 ],
175- tau_k = 1 / sigmoid_slope
176- ) # shape (...,)
177- last = _sigmoid (
178- scalar_field = Z ,
179- edges = edges [- 1 ],
180- tau_k = 1 / sigmoid_slope
181- )
182- membership = np .concatenate (
183- [first [..., None ], last [..., None ]],
184- axis = - 1
185- ) # shape (...,K)
186- return membership
187-
188-
189- def _lith_segmentation (Z , edges , ids , sigmoid_slope ):
190- # 1) per-edge temperatures τ_k = |Δ_k|/(4·m)
191- jumps = np .abs (ids [1 :] - ids [:- 1 ]) # shape (K-1,)
192- tau_k = jumps / float (sigmoid_slope ) # shape (K-1,)
193- # 2) first bin (-∞, e1) via σ((e1 - Z)/τ₁)
194- first = _sigmoid (
195- scalar_field = - Z ,
196- edges = - edges [0 ],
197- tau_k = tau_k [0 ]
198- ) # shape (...,)
199- # 3) last bin [e_{K-1}, ∞) via σ((Z - e_{K-1})/τ_{K-1})
200- # last = 1.0 / (1.0 + np.exp(-(Z - edges[-1]) / tau_k[-1])) # shape (...,)
201- last = _sigmoid (
202- scalar_field = Z ,
203- edges = edges [- 1 ],
204- tau_k = tau_k [- 1 ]
205- )
206- # 4) middle bins [e_i, e_{i+1}): σ((Z - e_i)/τ_i) - σ((Z - e_{i+1})/τ_{i+1})
207- # shape (...,1)
208- left = _sigmoid (
209- scalar_field = (Z [..., None ]),
210- edges = edges [:- 1 ],
211- tau_k = tau_k [:- 1 ]
212- )
213- right = _sigmoid (
214- scalar_field = (Z [..., None ]),
215- edges = edges [1 :],
216- tau_k = tau_k [1 :]
217- )
218- middle = left - right # (...,K-2)
219- # 5) assemble memberships and weight by ids
220- membership = np .concatenate (
221- [first [..., None ], middle , last [..., None ]],
222- axis = - 1
223- ) # shape (...,K)
224- return membership
225-
226-
227- def _sigmoid (scalar_field , edges , tau_k ):
228- return 1.0 / (1.0 + np .exp (- (scalar_field - edges ) / tau_k ))
0 commit comments