Skip to content

Commit 2db2a1a

Browse files
committed
Getdist style .minimum output
Added .minimum and .minimum.txt getdist style output for BOBE runs. Updated tests to check correct output files are saved.
1 parent 28b59bf commit 2db2a1a

3 files changed

Lines changed: 303 additions & 148 deletions

File tree

‎BOBE/bo.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,10 @@ def finalise_results(self):
850850
logz_dict=logz_dict,
851851
converged=self.converged,
852852
termination_reason=self.termination_reason,
853-
gp_info=gp_info
853+
gp_info=gp_info,
854+
best_point=self.best_pt,
855+
best_loglike=self.best_f,
856+
best_iteration=self.best_pt_iteration
854857
)
855858

856859
# Create final results dictionary with only the specified keys

‎BOBE/utils/results.py‎

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ def _initialize_fresh(self):
185185
self.converged = False
186186
self.termination_reason = "Unknown"
187187
self.gp_info = {} # Store GP and classifier information
188+
189+
# Best point information (for getdist minimum files)
190+
self.best_point = None
191+
self.best_loglike = None
192+
self.best_iteration = None
188193

189194
def _load_existing_results(self, output_file: str) -> Optional[Dict[str, Any]]:
190195
"""
@@ -518,18 +523,22 @@ def finalize(self,
518523
logz_dict: Optional[Dict[str, float]] = None,
519524
converged: bool = False,
520525
termination_reason: str = "Max iterations reached",
521-
gp_info: Optional[Dict[str, Any]] = None):
526+
gp_info: Optional[Dict[str, Any]] = None,
527+
best_point: Optional[np.ndarray] = None,
528+
best_loglike: Optional[float] = None,
529+
best_iteration: Optional[int] = None):
522530
"""
523531
Finalize the results with final samples and metadata.
524532
525533
Args:
526-
samples: Final parameter samples [n_samples, n_params]
527-
weights: Sample weights [n_samples]
528-
loglikes: Log-likelihood values [n_samples]
534+
samples_dict: Dictionary with 'x', 'weights', 'logl' keys for final samples
529535
logz_dict: Final evidence information
530536
converged: Whether the run converged
531537
termination_reason: Reason for termination
532538
gp_info: Dictionary containing GP and classifier information
539+
best_point: Best point found (physical parameter space)
540+
best_loglike: Best log-likelihood value
541+
best_iteration: Iteration where best point was found
533542
"""
534543
self.end_time = time.time()
535544

@@ -550,8 +559,15 @@ def finalize(self,
550559
self.converged = converged
551560
self.termination_reason = termination_reason
552561
self.gp_info = gp_info or {}
562+
563+
# Store best point information
564+
self.best_point = best_point
565+
self.best_loglike = best_loglike
566+
self.best_iteration = best_iteration
553567

554568
log.info(f"Finalized BOBE results: converged={converged}, reason={termination_reason}")
569+
if best_point is not None and best_loglike is not None:
570+
log.info(f"Best point: logL={best_loglike:.6f} at iteration {best_iteration}")
555571

556572
# Save all results
557573
self.save_all_formats()
@@ -668,6 +684,9 @@ def save_all_formats(self):
668684

669685
# Timing data
670686
self.save_timing_data()
687+
688+
# GetDist minimum files (best point)
689+
self.save_minimum_files()
671690

672691
def save_main_results(self):
673692
"""Save main comprehensive results file."""
@@ -702,6 +721,66 @@ def save_chain_files(self, samples_dict: Optional[Dict[str, np.ndarray]] = None,
702721
getdist_samples.saveAsText(root=output_file, make_dirs=True)
703722
log.info(f"Saved GetDist format files to {output_file}")
704723
log.info("Created: .txt (chain), .paramnames (parameter info), .ranges (parameter bounds)")
724+
725+
def save_minimum_files(self):
726+
"""
727+
Save best point in GetDist minimum format.
728+
729+
Creates two files:
730+
- .minimum.txt: Simple table with best point
731+
- .minimum: Formatted text with parameter details
732+
"""
733+
if self.best_point is None or self.best_loglike is None:
734+
log.debug("No best point data available, skipping minimum files")
735+
return
736+
737+
best_point = np.atleast_1d(self.best_point)
738+
739+
if len(best_point) != self.ndim:
740+
log.warning(f"Best point dimension {len(best_point)} != {self.ndim}, skipping minimum files")
741+
return
742+
743+
minuslogpost = -self.best_loglike
744+
chi_sq = 2.0 * minuslogpost
745+
746+
# Write .minimum.txt file (simple table format)
747+
minimum_txt_file = f"{self.save_path}.minimum.txt"
748+
try:
749+
with open(minimum_txt_file, 'w') as f:
750+
# Header line
751+
header = "# weight minuslogpost"
752+
for param_name in self.param_names:
753+
header += f" {param_name:>13s}"
754+
f.write(header + "\n")
755+
756+
# Data line (weight is always 1 for single best point)
757+
line = f" 1 {minuslogpost:13.7f}"
758+
for val in best_point:
759+
line += f" {val:13.8e}"
760+
f.write(line + "\n")
761+
762+
log.info(f"Saved minimum table to {minimum_txt_file}")
763+
except Exception as e:
764+
log.warning(f"Failed to save .minimum.txt file: {e}")
765+
766+
# Write .minimum file (formatted text with labels)
767+
minimum_file = f"{self.save_path}.minimum"
768+
try:
769+
with open(minimum_file, 'w') as f:
770+
# Header with likelihood info
771+
f.write(f" -log(Like) = {minuslogpost:.12f}\n")
772+
f.write(f" chi-sq = {chi_sq:.12f}\n")
773+
f.write("\n")
774+
775+
# Parameter list with index, value, name, and LaTeX label
776+
for i, (param_name, param_label, val) in enumerate(zip(
777+
self.param_names, self.param_labels, best_point), start=1):
778+
# Format: index (right-aligned, width 5), value (scientific), name, label
779+
f.write(f"{i:>5d} {val:.9e} {param_name:40s} {param_label}\n")
780+
781+
log.info(f"Saved minimum point details to {minimum_file}")
782+
except Exception as e:
783+
log.warning(f"Failed to save .minimum file: {e}")
705784

706785

707786
def save_summary_stats(self):

0 commit comments

Comments
 (0)