Skip to content

Commit db5c119

Browse files
committed
Bring back logging to file
1 parent 51f05b1 commit db5c119

1 file changed

Lines changed: 73 additions & 34 deletions

File tree

simpeg/directives/_directives.py

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,12 +2685,14 @@ def __init__(
26852685
path: pathlib.Path | None = None,
26862686
nesting: list[list] | None = None,
26872687
target_chi: float = 1.0,
2688+
headers: list[str] | None = None,
26882689
**kwargs,
26892690
):
26902691
self.last_beta = None
26912692
self.chi_factors = None
26922693
self.target_chi = target_chi
26932694
self.nesting = nesting
2695+
self.headers = headers
26942696

26952697
if path is None:
26962698
path = pathlib.Path("./")
@@ -2699,24 +2701,44 @@ def __init__(
26992701

27002702
super().__init__(**kwargs)
27012703

2704+
self._log_array: np.ndarray | None = None
2705+
2706+
@property
2707+
def log_array(self, headers: list[str] | None = None):
2708+
if self._log_array is None:
2709+
if self.headers is None:
2710+
2711+
def append_sub_indices(elements, header):
2712+
values = []
2713+
for ii, elem in enumerate(elements):
2714+
heads = header + f"[{ii}]"
2715+
if isinstance(elem, Iterable):
2716+
values += append_sub_indices(elem, heads)
2717+
else:
2718+
values += [heads]
2719+
return values
2720+
2721+
headers = []
2722+
for ii, elem in enumerate(self.misfit_tree_indices):
2723+
headers += append_sub_indices(elem, f"[{ii}]")
2724+
self.headers = headers
2725+
2726+
dtype = np.dtype(
2727+
[("Iterations", np.int32)] + [(h, np.float32) for h in self.headers]
2728+
)
2729+
self._log_array = np.rec.fromrecords((), dtype=dtype)
2730+
2731+
return self._log_array
2732+
27022733
def initialize(self):
27032734
self.last_beta = self.invProb.beta
27042735
self.multipliers = self.invProb.dmisfit.multipliers
27052736
self.scalings = np.ones_like(self.multipliers) # Everyone gets a fair chance
27062737
self.misfit_tree_indices = self.parse_by_nested_levels(self.nesting)
2707-
# def append_labels(_, depth):
2708-
# return f"[{depth}]"
2709-
#
2710-
# with open(self.filepath, "w", encoding="utf-8") as f:
2711-
# f.write("Logging of [scaling * chi factor] per misfit function.\n\n")
2712-
#
2713-
# header = "Iterations\t"
2714-
# for elem in recursions(self.nested_misfits, append_labels):
2715-
# header += "\t".join(f"Misfit [{elem}]")
2716-
#
2717-
# f.write("\n")
2718-
2719-
def scalings_by_level(
2738+
2739+
self.write_log()
2740+
2741+
def scale_by_level(
27202742
self, nested_values, nested_indices, ratio, scaling_vector: np.ndarray | None
27212743
):
27222744
"""
@@ -2725,6 +2747,20 @@ def scalings_by_level(
27252747
The maximum chi-factor at each level is used to determine scaling factors
27262748
for the misfit functions at that level. The scaling factors are then propagated
27272749
down to the next level of the nested structure.
2750+
2751+
Parameters
2752+
----------
2753+
nested_values : list
2754+
Nested list of misfit residuals.
2755+
2756+
nested_indices : list
2757+
Nested list of indices corresponding to the misfit residuals.
2758+
2759+
ratio : float
2760+
Ratio of current beta to last beta.
2761+
2762+
scaling_vector : np.ndarray, optional
2763+
Current scaling vector to be updated.
27282764
"""
27292765
if scaling_vector is None:
27302766
scaling_vector = np.ones(len(self.invProb.dmisfit.multipliers))
@@ -2733,7 +2769,7 @@ def scalings_by_level(
27332769
flat_indices = []
27342770
for elem, indices in zip(nested_values, nested_indices):
27352771

2736-
# Reach the outer most level
2772+
# Reach the outermost level
27372773
if not isinstance(indices, list) or (
27382774
len(indices) == 1 and not isinstance(indices[0], list)
27392775
):
@@ -2759,9 +2795,7 @@ def scalings_by_level(
27592795
scaling_vector[group_ind] = np.maximum(
27602796
ratio, scale * scaling_vector[group_ind]
27612797
)
2762-
scaling_vector = self.scalings_by_level(
2763-
elem, indices, ratio, scaling_vector
2764-
)
2798+
scaling_vector = self.scale_by_level(elem, indices, ratio, scaling_vector)
27652799

27662800
return scaling_vector
27672801

@@ -2771,7 +2805,7 @@ def endIter(self):
27712805
self.nesting, self.invProb.residuals
27722806
)
27732807

2774-
scalings = self.scalings_by_level(
2808+
scalings = self.scale_by_level(
27752809
nested_residuals, self.misfit_tree_indices, ratio, None
27762810
)
27772811

@@ -2781,24 +2815,26 @@ def endIter(self):
27812815
# Normalize total phi_d with scalings
27822816
self.invProb.dmisfit.multipliers = self.multipliers * self.scalings
27832817
self.last_beta = self.invProb.beta
2784-
# self.write_log()
2818+
2819+
# Log the scaling factors
2820+
self.write_log()
27852821

27862822
def parse_by_nested_levels(
27872823
self, nesting: list[Iterable], values: Iterable | None = None
27882824
) -> Iterable:
27892825
"""
2790-
Replace leaf elements of `nesting` with values from `flat` (in order).
2791-
Assumes the number of leaf positions equals len(flat).
2826+
Replace leaf elements of `nesting` with values from `values` (in order).
2827+
Assumes the number of leaf positions equals len(values).
27922828
27932829
Parameters:
27942830
- nesting: arbitrarily nested list structure; leaves are non-list values
2795-
- flat: flat iterable whose values will fill the leaves in order
2831+
- values: flat iterable whose values will fill the leaves in order
27962832
27972833
Returns:
2798-
- A new nested structure with leaves replaced by values from `flat`.
2834+
- A new nested structure with leaves replaced by values from `values`.
27992835
28002836
Raises:
2801-
- ValueError if `flat` has fewer or more elements than required by `nesting`.
2837+
- ValueError if `values` has fewer or more elements than required by `nesting`.
28022838
"""
28032839
indices = np.arange(len(self.invProb.dmisfit.objfcts))
28042840
if nesting is None:
@@ -2836,16 +2872,19 @@ def write_log(self):
28362872
"""
28372873
Write the scaling factors to the log file.
28382874
"""
2839-
with open(self.filepath, "a", encoding="utf-8") as f:
2840-
f.write(
2841-
f"{self.opt.iter}\t"
2842-
+ "\t".join(
2843-
f"{multi:.2e} * {chi:.2e}"
2844-
for multi, chi in zip(
2845-
self.invProb.dmisfit.multipliers, self.chi_factors
2846-
)
2847-
)
2848-
+ "\n"
2875+
self._log_array = np.append(
2876+
self.log_array,
2877+
np.rec.fromrecords(
2878+
tuple([getattr(self.opt, "iter", 0)] + self.scalings.tolist()),
2879+
dtype=self.log_array.dtype,
2880+
),
2881+
)
2882+
with open(self.filepath, "w", encoding="utf-8") as f:
2883+
np.savetxt(
2884+
f,
2885+
self.log_array,
2886+
header="Iterations - Scaling per misfit",
2887+
fmt=["%d"] + ["%0.2e"] * (len(self._log_array.dtype) - 1),
28492888
)
28502889

28512890

0 commit comments

Comments
 (0)