From 0ccf3fa22f72413809fd17aa48856c3a03d7d5d6 Mon Sep 17 00:00:00 2001 From: denehoffman Date: Wed, 30 Oct 2024 14:50:23 -0400 Subject: [PATCH 01/12] style: refactor data loading code into a shared function --- src/data.rs | 512 ++++++++++------------------------------------------ 1 file changed, 94 insertions(+), 418 deletions(-) diff --git a/src/data.rs b/src/data.rs index cfb5898..fdf9aaa 100644 --- a/src/data.rs +++ b/src/data.rs @@ -156,6 +156,93 @@ impl Dataset { } } +fn batch_to_event(batch: &RecordBatch, row: usize) -> Event { + let mut p4s = Vec::new(); + let mut eps = Vec::new(); + + let p4_count = batch + .schema() + .fields() + .iter() + .filter(|field| field.name().starts_with("p4_")) + .count() + / 4; + let eps_count = batch + .schema() + .fields() + .iter() + .filter(|field| field.name().starts_with("eps_")) + .count() + / 3; + + for i in 0..p4_count { + let e = batch + .column_by_name(&format!("p4_{}_E", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + let px = batch + .column_by_name(&format!("p4_{}_Px", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + let py = batch + .column_by_name(&format!("p4_{}_Py", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + let pz = batch + .column_by_name(&format!("p4_{}_Pz", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + p4s.push(Vector4::new(e, px, py, pz)); + } + + // TODO: insert empty vectors if not provided + for i in 0..eps_count { + let x = batch + .column_by_name(&format!("eps_{}_x", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + let y = batch + .column_by_name(&format!("eps_{}_y", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + let z = batch + .column_by_name(&format!("eps_{}_z", i)) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + eps.push(Vector3::new(x, y, z)); + } + + let weight = batch + .column(19) + .as_any() + .downcast_ref::() + .unwrap() + .value(row) as Float; + + Event { p4s, eps, weight } +} + /// Open a Parquet file and read the data into a [`Dataset`]. #[cfg(feature = "rayon")] pub fn open(file_path: &str) -> Result, LadduError> { @@ -173,90 +260,8 @@ pub fn open(file_path: &str) -> Result, LadduError> { // Process each row in the batch for row in 0..num_rows { - let mut p4s = Vec::new(); - let mut eps = Vec::new(); - - let p4_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("p4_")) - .count() - / 4; - let eps_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("eps_")) - .count() - / 3; - - for i in 0..p4_count { - let e = batch - .column_by_name(&format!("p4_{}_E", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let px = batch - .column_by_name(&format!("p4_{}_Px", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let py = batch - .column_by_name(&format!("p4_{}_Py", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let pz = batch - .column_by_name(&format!("p4_{}_Pz", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - p4s.push(Vector4::new(e, px, py, pz)); - } - - // TODO: insert empty vectors if not provided - for i in 0..eps_count { - let x = batch - .column_by_name(&format!("eps_{}_x", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let y = batch - .column_by_name(&format!("eps_{}_y", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let z = batch - .column_by_name(&format!("eps_{}_z", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - eps.push(Vector3::new(x, y, z)); - } - - let weight = batch - .column(19) - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - - local_events.push(Event { p4s, eps, weight }); + let event = batch_to_event(&batch, row); + local_events.push(event); } local_events }) @@ -281,89 +286,8 @@ pub fn open(file_path: &str) -> Result, LadduError> { // Process each row in the batch for row in 0..num_rows { - let mut p4s = Vec::new(); - let mut eps = Vec::new(); - - let p4_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("p4_")) - .count() - / 4; - let eps_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("eps_")) - .count() - / 3; - - for i in 0..p4_count { - let e = batch - .column_by_name(&format!("p4_{}_E", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let px = batch - .column_by_name(&format!("p4_{}_Px", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let py = batch - .column_by_name(&format!("p4_{}_Py", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let pz = batch - .column_by_name(&format!("p4_{}_Pz", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - p4s.push(Vector4::new(e, px, py, pz)); - } - - for i in 0..eps_count { - let x = batch - .column_by_name(&format!("eps_{}_x", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let y = batch - .column_by_name(&format!("eps_{}_y", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let z = batch - .column_by_name(&format!("eps_{}_z", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - eps.push(Vector3::new(x, y, z)); - } - - let weight = batch - .column(19) - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - - local_events.push(Event { p4s, eps, weight }); + let event = batch_to_event(&batch, row); + local_events.push(event); } local_events }) @@ -392,90 +316,7 @@ where // Process each row in the batch for row in 0..num_rows { - let mut p4s = Vec::new(); - let mut eps = Vec::new(); - - let p4_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("p4_")) - .count() - / 4; - let eps_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("eps_")) - .count() - / 3; - - for i in 0..p4_count { - let e = batch - .column_by_name(&format!("p4_{}_E", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let px = batch - .column_by_name(&format!("p4_{}_Px", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let py = batch - .column_by_name(&format!("p4_{}_Py", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let pz = batch - .column_by_name(&format!("p4_{}_Pz", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - p4s.push(Vector4::new(e, px, py, pz)); - } - - // TODO: insert empty vectors if not provided - for i in 0..eps_count { - let x = batch - .column_by_name(&format!("eps_{}_x", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let y = batch - .column_by_name(&format!("eps_{}_y", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let z = batch - .column_by_name(&format!("eps_{}_z", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - eps.push(Vector3::new(x, y, z)); - } - - let weight = batch - .column(19) - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - - let event = Event { p4s, eps, weight }; + let event = batch_to_event(&batch, row); if predicate(&event) { local_events.push(event); } @@ -507,89 +348,7 @@ where // Process each row in the batch for row in 0..num_rows { - let mut p4s = Vec::new(); - let mut eps = Vec::new(); - - let p4_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("p4_")) - .count() - / 4; - let eps_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("eps_")) - .count() - / 3; - - for i in 0..p4_count { - let e = batch - .column_by_name(&format!("p4_{}_E", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let px = batch - .column_by_name(&format!("p4_{}_Px", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let py = batch - .column_by_name(&format!("p4_{}_Py", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let pz = batch - .column_by_name(&format!("p4_{}_Pz", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - p4s.push(Vector4::new(e, px, py, pz)); - } - - for i in 0..eps_count { - let x = batch - .column_by_name(&format!("eps_{}_x", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let y = batch - .column_by_name(&format!("eps_{}_y", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let z = batch - .column_by_name(&format!("eps_{}_z", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - eps.push(Vector3::new(x, y, z)); - } - - let weight = batch - .column(19) - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - - let event = Event { p4s, eps, weight }; + let event = batch_to_event(&batch, row); if predicate(&event) { local_events.push(event); } @@ -691,90 +450,7 @@ pub fn open_binned( // Process each row in the batch for row in 0..num_rows { - let mut p4s = Vec::new(); - let mut eps = Vec::new(); - - let p4_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("p4_")) - .count() - / 4; - let eps_count = batch - .schema() - .fields() - .iter() - .filter(|field| field.name().starts_with("eps_")) - .count() - / 3; - - for i in 0..p4_count { - let e = batch - .column_by_name(&format!("p4_{}_E", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let px = batch - .column_by_name(&format!("p4_{}_Px", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let py = batch - .column_by_name(&format!("p4_{}_Py", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let pz = batch - .column_by_name(&format!("p4_{}_Pz", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - p4s.push(Vector4::new(e, px, py, pz)); - } - - // TODO: insert empty vectors if not provided - for i in 0..eps_count { - let x = batch - .column_by_name(&format!("eps_{}_x", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let y = batch - .column_by_name(&format!("eps_{}_y", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - let z = batch - .column_by_name(&format!("eps_{}_z", i)) - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - eps.push(Vector3::new(x, y, z)); - } - - let weight = batch - .column(19) - .as_any() - .downcast_ref::() - .unwrap() - .value(row) as Float; - - let event = Event { p4s, eps, weight }; + let event = batch_to_event(&batch, row); let value = variable.value(&event); if value >= range.0 && value < range.1 { let bin_index = ((value - range.0) / bin_width) as usize; From d23e457018aadebd7b356a0c45df956a1dc7c4a3 Mon Sep 17 00:00:00 2001 From: denehoffman Date: Wed, 30 Oct 2024 14:50:50 -0400 Subject: [PATCH 02/12] get bin edges from `laddu` rather than `matplotlib` --- python_examples/example_1.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python_examples/example_1.py b/python_examples/example_1.py index 89ddb5a..c8d3661 100755 --- a/python_examples/example_1.py +++ b/python_examples/example_1.py @@ -25,7 +25,7 @@ def main(bins: int): # noqa: PLR0915 data_file = str(script_dir / "data_1.parquet") mc_file = str(script_dir / "mc_1.parquet") start = perf_counter() - binned_tot_res, binned_s0p_res, binned_d2p_res = fit_binned(bins, data_file, mc_file) + binned_tot_res, binned_s0p_res, binned_d2p_res, bin_edges = fit_binned(bins, data_file, mc_file) tot_weights, s0p_weights, d2p_weights, status, parameters = fit_unbinned(data_file, mc_file) end = perf_counter() pprint(f"Total time: {end - start:.3f}s") @@ -95,7 +95,7 @@ def main(bins: int): # noqa: PLR0915 black = "#000000" _, ax = plt.subplots(ncols=2, sharey=True, figsize=(22, 12)) - _, edges, _ = ax[0].hist(m_data, bins=bins, range=(1, 2), color=black, histtype="step", label="Data") + ax[0].hist(m_data, bins=bins, range=(1, 2), color=black, histtype="step", label="Data") ax[1].hist(m_data, bins=bins, range=(1, 2), color=black, histtype="step", label="Data") ax[0].hist( m_accmc, @@ -133,7 +133,7 @@ def main(bins: int): # noqa: PLR0915 alpha=0.1, label="$D_2^+$ (unbinned)", ) - centers = (edges[1:] + edges[:-1]) / 2 + centers = (bin_edges[1:] + bin_edges[:-1]) / 2 ax[0].scatter(centers, binned_tot_res, color=black, label="Fit (binned)") ax[1].scatter(centers, binned_tot_res, color=black, label="Fit (binned)") ax[0].scatter(centers, binned_s0p_res, color=blue, label="$S_0^+$ (binned)") @@ -183,7 +183,7 @@ def fit_binned(bins: int, data_file: str, mc_file: str): s0p_res.append(nll.project(status.x).sum()) nll.isolate(["Z22+", "D2+"]) d2p_res.append(nll.project(status.x).sum()) - return (tot_res, s0p_res, d2p_res) + return (tot_res, s0p_res, d2p_res, data_ds_binned.edges) def fit_unbinned(data_file: str, mc_file: str): From 9645ee4c253902d4d7a7a1d60f7713ba4e54b71a Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 00:20:58 -0400 Subject: [PATCH 03/12] feat: add method to resample datasets (bootstrapping) --- Cargo.toml | 1 + python/laddu/data/__init__.pyi | 1 + src/data.rs | 21 ++++++++++++++++++++- src/python.rs | 3 +++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bfe3fee..e192fec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ parking_lot = "0.12.3" dyn-clone = "1.0.17" auto_ops = "0.3.0" rand = "0.8.5" +rand_chacha = "0.3.1" rayon = { version = "1.10.0", optional = true } pyo3 = { version = "0.22.5", optional = true, features = ["num-complex"] } numpy = { version = "0.22.0", optional = true, features = ["nalgebra"] } diff --git a/python/laddu/data/__init__.pyi b/python/laddu/data/__init__.pyi index 0e37a0c..a1ae8c7 100644 --- a/python/laddu/data/__init__.pyi +++ b/python/laddu/data/__init__.pyi @@ -18,6 +18,7 @@ class Dataset: def __getitem__(self, index: int) -> Event: ... def len(self) -> int: ... def weighted_len(self) -> float: ... + def bootstrap(self, seed: int) -> Dataset: ... class BinnedDataset: bins: int diff --git a/src/data.rs b/src/data.rs index fdf9aaa..da60633 100644 --- a/src/data.rs +++ b/src/data.rs @@ -2,6 +2,8 @@ use arrow::array::Float32Array; use arrow::record_batch::RecordBatch; use nalgebra::{vector, Vector3, Vector4}; use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use std::ops::{Deref, DerefMut, Index, IndexMut}; use std::path::Path; use std::sync::Arc; @@ -76,7 +78,7 @@ impl Event { } /// A collection of [`Event`]s. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct Dataset { pub(crate) events: Vec, } @@ -154,6 +156,23 @@ impl Dataset { pub fn weighted_len(&self) -> Float { self.par_iter().map(|e| e.weight).sum() } + + /// Generate a new dataset with the same length by resampling the events in the original datset + /// with replacement. This can be used to perform error analysis via the bootstrap method. + pub fn bootstrap(&self, seed: usize) -> Arc { + if self.is_empty() { + return Arc::new(Dataset::default()); + } + let mut rng = ChaCha8Rng::seed_from_u64(seed as u64); + let mut bootstrapped_events = Vec::with_capacity(self.len()); + for _ in 0..self.len() { + let idx = rng.gen_range(0..self.len()); + bootstrapped_events.push(self[idx].clone()); + } + Arc::new(Dataset { + events: bootstrapped_events, + }) + } } fn batch_to_event(batch: &RecordBatch, row: usize) -> Event { diff --git a/src/python.rs b/src/python.rs index ace8ba4..426a79d 100644 --- a/src/python.rs +++ b/src/python.rs @@ -258,6 +258,9 @@ pub(crate) mod laddu { .ok_or(PyIndexError::new_err("index out of range")) .map(|rust_event| Event(rust_event.clone())) } + fn bootstrap(&self, seed: usize) -> Dataset { + Dataset(self.0.bootstrap(seed)) + } } #[pyclass] From 3e3403cf47982531972d585964aaac736e01753f Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 00:21:57 -0400 Subject: [PATCH 04/12] feat: update example_1 to use bootstrapping for binned fit errors and to run multiple randomized iterations for each fit --- python_examples/example_1.py | 86 ++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/python_examples/example_1.py b/python_examples/example_1.py index c8d3661..8b9afe8 100755 --- a/python_examples/example_1.py +++ b/python_examples/example_1.py @@ -7,6 +7,7 @@ """ import os +import sys from pathlib import Path from time import perf_counter @@ -25,7 +26,9 @@ def main(bins: int): # noqa: PLR0915 data_file = str(script_dir / "data_1.parquet") mc_file = str(script_dir / "mc_1.parquet") start = perf_counter() - binned_tot_res, binned_s0p_res, binned_d2p_res, bin_edges = fit_binned(bins, data_file, mc_file) + binned_tot_res, binned_tot_err, binned_s0p_res, binned_s0p_err, binned_d2p_res, binned_d2p_err, bin_edges = ( + fit_binned(bins, data_file, mc_file) + ) tot_weights, s0p_weights, d2p_weights, status, parameters = fit_unbinned(data_file, mc_file) end = perf_counter() pprint(f"Total time: {end - start:.3f}s") @@ -134,10 +137,10 @@ def main(bins: int): # noqa: PLR0915 label="$D_2^+$ (unbinned)", ) centers = (bin_edges[1:] + bin_edges[:-1]) / 2 - ax[0].scatter(centers, binned_tot_res, color=black, label="Fit (binned)") - ax[1].scatter(centers, binned_tot_res, color=black, label="Fit (binned)") - ax[0].scatter(centers, binned_s0p_res, color=blue, label="$S_0^+$ (binned)") - ax[1].scatter(centers, binned_d2p_res, color=red, label="$D_2^+$ (binned)") + ax[0].errorbar(centers, binned_tot_res, yerr=binned_tot_err, fmt=".", color=black, label="Fit (binned)") + ax[1].errorbar(centers, binned_tot_res, yerr=binned_tot_err, fmt=".", color=black, label="Fit (binned)") + ax[0].errorbar(centers, binned_s0p_res, yerr=binned_s0p_err, fmt=".", color=blue, label="$S_0^+$ (binned)") + ax[1].errorbar(centers, binned_d2p_res, yerr=binned_d2p_err, fmt=".", color=red, label="$D_2^+$ (binned)") ax[0].legend() ax[1].legend() @@ -168,22 +171,56 @@ def fit_binned(bins: int, data_file: str, mc_file: str): model = pos_re + pos_im tot_res = [] + tot_res_err = [] s0p_res = [] + s0p_res_err = [] d2p_res = [] + d2p_res_err = [] rng = np.random.default_rng() for ibin in range(bins): + best_nll = np.inf + best_status = None nll = ld.NLL(manager, data_ds_binned[ibin], accmc_ds_binned[ibin], model) - p0 = rng.uniform(-1000.0, 1000.0, len(nll.parameters)) - status = nll.minimize(p0) + for _ in range(20): + p0 = rng.uniform(-1000.0, 1000.0, len(nll.parameters)) + status = nll.minimize(p0) + if status.fx < best_nll: + best_nll = status.fx + best_status = status + + if best_status is None: + print(f"All fits for bin {ibin} failed!") + sys.exit(1) - tot_res.append(nll.project(status.x).sum()) + tot_res.append(nll.project(best_status.x).sum()) nll.isolate(["Z00+", "S0+"]) - s0p_res.append(nll.project(status.x).sum()) + s0p_res.append(nll.project(best_status.x).sum()) nll.isolate(["Z22+", "D2+"]) - d2p_res.append(nll.project(status.x).sum()) - return (tot_res, s0p_res, d2p_res, data_ds_binned.edges) + d2p_res.append(nll.project(best_status.x).sum()) + nll.activate_all() + + tot_boot = [] + s0p_boot = [] + d2p_boot = [] + for iboot in range(20): + boot_nll = ld.NLL( + manager, data_ds_binned[ibin].bootstrap(iboot), accmc_ds_binned[ibin].bootstrap(iboot), model + ) + boot_status = boot_nll.minimize(best_status.x) + + tot_boot.append(boot_nll.project(boot_status.x).sum()) + boot_nll.isolate(["Z00+", "S0+"]) + s0p_boot.append(boot_nll.project(boot_status.x).sum()) + boot_nll.isolate(["Z22+", "D2+"]) + d2p_boot.append(boot_nll.project(boot_status.x).sum()) + boot_nll.activate_all() + tot_res_err.append(np.std(tot_boot)) + s0p_res_err.append(np.std(s0p_boot)) + d2p_res_err.append(np.std(d2p_boot)) + + return (tot_res, tot_res_err, s0p_res, s0p_res_err, d2p_res, d2p_res_err, data_ds_binned.edges) def fit_unbinned(data_file: str, mc_file: str): @@ -211,8 +248,10 @@ def fit_unbinned(data_file: str, mc_file: str): pos_im = (s0p * bw_f01500 * z00p.imag() + d2p * bw_f21525 * z22p.imag()).norm_sqr() model = pos_re + pos_im - nll = ld.NLL(manager, data_ds, accmc_ds, model) - p0 = [0.8, 0.5, 100, 50, 50] + rng = np.random.default_rng() + + best_nll = np.inf + best_status = None bounds = [ (0.001, 1.0), (0.001, 1.0), @@ -220,15 +259,26 @@ def fit_unbinned(data_file: str, mc_file: str): (-1000.0, 1000.0), (-1000.0, 1000.0), ] - status = nll.minimize(p0, bounds=bounds) + nll = ld.NLL(manager, data_ds, accmc_ds, model) + for _ in range(20): + p0 = rng.uniform(-1000.0, 1000.0, 3) + p0 = np.append([0.8, 0.5], p0) + status = nll.minimize(p0, bounds=bounds) + if status.fx < best_nll: + best_nll = status.fx + best_status = status + + if best_status is None: + print("All unbinned fits failed!") + sys.exit(1) - tot_weights = nll.project(status.x) + tot_weights = nll.project(best_status.x) nll.isolate(["S0+", "Z00+", "f0(1500)"]) - s0p_weights = nll.project(status.x) + s0p_weights = nll.project(best_status.x) nll.isolate(["D2+", "Z22+", "f2(1525)"]) - d2p_weights = nll.project(status.x) + d2p_weights = nll.project(best_status.x) - return (tot_weights, s0p_weights, d2p_weights, status, nll.parameters) + return (tot_weights, s0p_weights, d2p_weights, best_status, nll.parameters) if __name__ == "__main__": From 83d0df9a9a52ee4d427a56f5c16c6664f39c20bb Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 11:35:21 -0400 Subject: [PATCH 05/12] docs: update plot and add output txt file for example_1 and reorganize directory structure --- README.md | 2 +- python_examples/example_1.svg | 6356 ----------------- python_examples/{ => example_1}/amp_gen_1.cfg | 0 .../{ => example_1}/data_1.parquet | Bin python_examples/{ => example_1}/example_1.py | 0 python_examples/example_1/example_1.svg | 4805 +++++++++++++ python_examples/example_1/example_1.txt | 24 + .../example_1/example_1_amptools.svg | 4173 +++++++++++ python_examples/{ => example_1}/mc_1.parquet | Bin .../{ => example_1}/requirements_1.txt | 0 10 files changed, 9003 insertions(+), 6357 deletions(-) delete mode 100644 python_examples/example_1.svg rename python_examples/{ => example_1}/amp_gen_1.cfg (100%) rename python_examples/{ => example_1}/data_1.parquet (100%) rename python_examples/{ => example_1}/example_1.py (100%) create mode 100644 python_examples/example_1/example_1.svg create mode 100644 python_examples/example_1/example_1.txt create mode 100644 python_examples/example_1/example_1_amptools.svg rename python_examples/{ => example_1}/mc_1.parquet (100%) rename python_examples/{ => example_1}/requirements_1.txt (100%) diff --git a/README.md b/README.md index b5c7e4e..c673b42 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,7 @@ The first example script uses data generated with [gen_amp](https://github.com/J

diff --git a/python_examples/example_1.svg b/python_examples/example_1.svg deleted file mode 100644 index 0cebb7b..0000000 --- a/python_examples/example_1.svg +++ /dev/null @@ -1,6356 +0,0 @@ - - - - - - - - 2024-10-29T14:50:08.899415 - image/svg+xml - - - Matplotlib v3.9.2, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python_examples/amp_gen_1.cfg b/python_examples/example_1/amp_gen_1.cfg similarity index 100% rename from python_examples/amp_gen_1.cfg rename to python_examples/example_1/amp_gen_1.cfg diff --git a/python_examples/data_1.parquet b/python_examples/example_1/data_1.parquet similarity index 100% rename from python_examples/data_1.parquet rename to python_examples/example_1/data_1.parquet diff --git a/python_examples/example_1.py b/python_examples/example_1/example_1.py similarity index 100% rename from python_examples/example_1.py rename to python_examples/example_1/example_1.py diff --git a/python_examples/example_1/example_1.svg b/python_examples/example_1/example_1.svg new file mode 100644 index 0000000..e9a0321 --- /dev/null +++ b/python_examples/example_1/example_1.svg @@ -0,0 +1,4805 @@ + + + + + + + + 2024-10-31T00:40:54.682028 + image/svg+xml + + + Matplotlib v3.9.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python_examples/example_1/example_1.txt b/python_examples/example_1/example_1.txt new file mode 100644 index 0000000..f3e5d57 --- /dev/null +++ b/python_examples/example_1/example_1.txt @@ -0,0 +1,24 @@ +Total time: 2063.206s +╒══════════════════════════════════════════════════════════════════════════════════════════════╕ +│ FIT RESULTS │ +╞════════════════════════════════════════════╤════════════════════╤═════════════╤══════════════╡ +│ Status: Converged │ fval: -2.343E6 │ #fcn: 146 │ #grad: 146 │ +├────────────────────────────────────────────┴────────────────────┴─────────────┴──────────────┤ +│ Message: F_EVAL CONVERGED │ +├───────╥──────────────┬──────────────╥──────────────┬──────────────┬──────────────┬───────────┤ +│ Par # ║ Value │ Uncertainty ║ Initial │ -Bound │ +Bound │ At Limit? │ +├───────╫──────────────┼──────────────╫──────────────┼──────────────┼──────────────┼───────────┤ +│ 0 ║ +1.083E-1 │ +8.838E-4 ║ +8.000E-1 │ +1.000E-3 │ +1.000E0 │ │ +│ 1 ║ +8.575E-2 │ +3.527E-4 ║ +5.000E-1 │ +1.000E-3 │ +1.000E0 │ │ +│ 2 ║ +8.607E2 │ +4.070E0 ║ +7.725E2 │ -1.000E3 │ +1.000E3 │ │ +│ 3 ║ +3.481E2 │ +3.047E0 ║ -1.076E2 │ -1.000E3 │ +1.000E3 │ │ +│ 4 ║ +4.723E2 │ +1.820E0 ║ +1.747E2 │ -1.000E3 │ +1.000E3 │ │ +└───────╨──────────────┴──────────────╨──────────────┴──────────────┴──────────────┴───────────┘ +┏━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓ +┃ ┃ f0(1500) ┃ f2'(1525) ┃ ┃ ┃ +┃ ┃ Width ┃ Width ┃ S/D Ratio ┃ S-D Phase ┃ +┡━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩ +│ Truth │ 0.11200 │ 0.08600 │ 1.41421 │ 0.78540 │ +│ Fit │ 0.10828 ± │ 0.08575 ± │ 1.46706 ± │ 0.93561 ± │ +│ │ 0.00088 │ 0.00035 │ 0.00906 │ 0.00457 │ +└───────┴─────────────┴──────────────┴─────────────┴──────────────┘ diff --git a/python_examples/example_1/example_1_amptools.svg b/python_examples/example_1/example_1_amptools.svg new file mode 100644 index 0000000..c3b7360 --- /dev/null +++ b/python_examples/example_1/example_1_amptools.svg @@ -0,0 +1,4173 @@ + + + + + + + + 2024-10-30T23:58:19.433902 + image/svg+xml + + + Matplotlib v3.7.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python_examples/mc_1.parquet b/python_examples/example_1/mc_1.parquet similarity index 100% rename from python_examples/mc_1.parquet rename to python_examples/example_1/mc_1.parquet diff --git a/python_examples/requirements_1.txt b/python_examples/example_1/requirements_1.txt similarity index 100% rename from python_examples/requirements_1.txt rename to python_examples/example_1/requirements_1.txt From b29850267cf90e1595249894869c5d7c5644cfff Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 13:28:25 -0400 Subject: [PATCH 06/12] feat: add benchmark for opening datasets --- Cargo.toml | 4 ++++ benches/open_benchmark.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 benches/open_benchmark.rs diff --git a/Cargo.toml b/Cargo.toml index e192fec..1f8493f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,10 @@ criterion = { version = "0.5.1", features = ["html_reports"] } name = "kmatrix_benchmark" harness = false +[[bench]] +name = "open_benchmark" +harness = false + [features] default = ["rayon", "python"] extension-module = ["pyo3/extension-module"] diff --git a/benches/open_benchmark.rs b/benches/open_benchmark.rs new file mode 100644 index 0000000..0dd9378 --- /dev/null +++ b/benches/open_benchmark.rs @@ -0,0 +1,13 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use laddu::data::open; + +fn open_data_benchmark(c: &mut Criterion) { + c.bench_function("open benchmark", |b| { + b.iter(|| { + black_box(open("benches/bench.parquet").unwrap()); + }); + }); +} + +criterion_group!(benches, open_data_benchmark); +criterion_main!(benches); From 099973ba4d8ca09a714dfd36910c6c11f13e15fc Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 13:29:03 -0400 Subject: [PATCH 07/12] feat: wrap `Event`s inside `Dataset`s in `Arc` to reduce bootstrap copying --- src/data.rs | 40 +++++++++++++++++----------------------- src/python.rs | 18 +++--------------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/data.rs b/src/data.rs index da60633..671f4a5 100644 --- a/src/data.rs +++ b/src/data.rs @@ -80,7 +80,7 @@ impl Event { /// A collection of [`Event`]s. #[derive(Debug, Clone, Default)] pub struct Dataset { - pub(crate) events: Vec, + pub(crate) events: Vec>, } impl Index for Dataset { @@ -91,14 +91,8 @@ impl Index for Dataset { } } -impl IndexMut for Dataset { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.events[index] - } -} - impl Deref for Dataset { - type Target = Vec; + type Target = Vec>; fn deref(&self) -> &Self::Target { &self.events @@ -123,14 +117,14 @@ impl Dataset { } /// Produces an iterator over the [`Event`]s in the [`Dataset`]. - pub fn iter(&self) -> std::slice::Iter<'_, Event> { - self.events.iter() + pub fn iter(&self) -> impl Iterator { + self.events.iter().map(|a| a.as_ref()) } /// Produces an parallelized iterator over the [`Event`]s in the [`Dataset`]. #[cfg(feature = "rayon")] - pub fn par_iter(&self) -> rayon::slice::Iter<'_, Event> { - self.events.par_iter() + pub fn par_iter(&self) -> impl IndexedParallelIterator { + self.events.par_iter().map(|a| a.as_ref()) } /// Extract a list of weights over each [`Event`] in the [`Dataset`]. @@ -167,7 +161,7 @@ impl Dataset { let mut bootstrapped_events = Vec::with_capacity(self.len()); for _ in 0..self.len() { let idx = rng.gen_range(0..self.len()); - bootstrapped_events.push(self[idx].clone()); + bootstrapped_events.push(self.events[idx].clone()); } Arc::new(Dataset { events: bootstrapped_events, @@ -271,7 +265,7 @@ pub fn open(file_path: &str) -> Result, LadduError> { let reader = builder.build()?; let batches: Vec = reader.collect::, _>>()?; - let events: Vec = batches + let events: Vec> = batches .into_par_iter() .flat_map(|batch| { let num_rows = batch.num_rows(); @@ -280,7 +274,7 @@ pub fn open(file_path: &str) -> Result, LadduError> { // Process each row in the batch for row in 0..num_rows { let event = batch_to_event(&batch, row); - local_events.push(event); + local_events.push(Arc::new(event)); } local_events }) @@ -297,7 +291,7 @@ pub fn open(file_path: &str) -> Result, LadduError> { let reader = builder.build()?; let batches: Vec = reader.collect::, _>>()?; - let events: Vec = batches + let events: Vec> = batches .into_iter() .flat_map(|batch| { let num_rows = batch.num_rows(); @@ -306,7 +300,7 @@ pub fn open(file_path: &str) -> Result, LadduError> { // Process each row in the batch for row in 0..num_rows { let event = batch_to_event(&batch, row); - local_events.push(event); + local_events.push(Arc::new(event)); } local_events }) @@ -327,7 +321,7 @@ where let reader = builder.build()?; let batches: Vec = reader.collect::, _>>()?; - let events: Vec = batches + let events: Vec> = batches .into_par_iter() .flat_map(|batch| { let num_rows = batch.num_rows(); @@ -337,7 +331,7 @@ where for row in 0..num_rows { let event = batch_to_event(&batch, row); if predicate(&event) { - local_events.push(event); + local_events.push(Arc::new(event)); } } local_events @@ -359,7 +353,7 @@ where let reader = builder.build()?; let batches: Vec = reader.collect::, _>>()?; - let events: Vec = batches + let events: Vec> = batches .into_iter() .flat_map(|batch| { let num_rows = batch.num_rows(); @@ -369,7 +363,7 @@ where for row in 0..num_rows { let event = batch_to_event(&batch, row); if predicate(&event) { - local_events.push(event); + local_events.push(Arc::new(event)); } } local_events @@ -460,7 +454,7 @@ pub fn open_binned( let reader = builder.build()?; let batches: Vec = reader.collect::, _>>()?; - let mut binned_events: Vec> = vec![Vec::default(); bins]; + let mut binned_events: Vec>> = vec![Vec::default(); bins]; let bin_width = (range.1 - range.0) / bins as Float; let bin_edges = get_bin_edges(bins, range); @@ -473,7 +467,7 @@ pub fn open_binned( let value = variable.value(&event); if value >= range.0 && value < range.1 { let bin_index = ((value - range.0) / bin_width) as usize; - binned_events[bin_index].push(event); + binned_events[bin_index].push(Arc::new(event)); } } }); diff --git a/src/python.rs b/src/python.rs index 426a79d..e061385 100644 --- a/src/python.rs +++ b/src/python.rs @@ -178,17 +178,17 @@ pub(crate) mod laddu { #[pyclass] #[derive(Clone)] - struct Event(rust::data::Event); + struct Event(Arc); #[pymethods] impl Event { #[new] pub(crate) fn new(p4s: Vec, eps: Vec, weight: Float) -> Self { - Self(rust::data::Event { + Self(Arc::new(rust::data::Event { p4s: p4s.into_iter().map(|arr| arr.0).collect(), eps: eps.into_iter().map(|arr| arr.0).collect(), weight, - }) + })) } pub(crate) fn __str__(&self) -> String { self.0.to_string() @@ -197,26 +197,14 @@ pub(crate) mod laddu { pub(crate) fn get_p4s(&self) -> Vec { self.0.p4s.iter().map(|p4| Vector4(*p4)).collect() } - #[setter] - pub(crate) fn set_p4s(&mut self, value: Vec) { - self.0.p4s = value.iter().map(|p4| p4.0).collect(); - } #[getter] pub(crate) fn get_eps(&self) -> Vec { self.0.eps.iter().map(|eps_vec| Vector3(*eps_vec)).collect() } - #[setter] - pub(crate) fn set_eps(&mut self, value: Vec) { - self.0.eps = value.iter().map(|eps_vec| eps_vec.0).collect(); - } #[getter] pub(crate) fn get_weight(&self) -> Float { self.0.weight } - #[setter] - pub(crate) fn set_weight(&mut self, value: Float) { - self.0.weight = value; - } } #[pyclass] From 09ec6ccb4d90f95efa92358964158e0cfc930b07 Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 13:29:18 -0400 Subject: [PATCH 08/12] ci: switch to Codspeed for benchmarking --- .github/workflows/benchmark.yml | 38 +++++++++++++++++++++++++-------- Cargo.toml | 4 +++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 7c3953d..19b2704 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,13 +1,33 @@ -on: [pull_request] -name: benchmark pull requests +name: CodSpeed + +on: + push: + branches: + - "main" + pull_request: + # `workflow_dispatch` allows CodSpeed to trigger backtest + # performance analysis in order to generate initial data. + workflow_dispatch: + jobs: - runBenchmark: - name: run benchmark + benchmarks: + name: Run benchmarks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: boa-dev/criterion-compare-action@v3 + - uses: actions/checkout@v4 + + - name: Setup rust toolchain, cache and cargo-codspeed binary + uses: moonrepo/setup-rust@v1 with: - branchName: ${{ github.base_ref }} - benchName: "kmatrix_benchmark" - token: ${{ secrets.GITHUB_TOKEN }} + channel: stable + cache-target: release + bins: cargo-codspeed + + - name: Build the benchmark target(s) + run: cargo codspeed build --profile bench + + - name: Run the benchmarks + uses: CodSpeedHQ/action@v3 + with: + run: cargo codspeed run + token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index 1f8493f..856a97c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,9 @@ accurate = "0.4.1" [dev-dependencies] approx = "0.5.1" -criterion = { version = "0.5.1", features = ["html_reports"] } +criterion = { version = "2.7.2", package = "codspeed-criterion-compat", features = [ + "html_reports", +] } [[bench]] name = "kmatrix_benchmark" From b09c5266a40a3254974429dee0c919f4f57eeda5 Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 14:29:21 -0400 Subject: [PATCH 09/12] feat: remove methods to open data into bins or filtered and replace with method on `Dataset` Since `Event`s are now wrapped in `Arc`, there's no need to open a `Dataset` multiple times, we can just make new `Dataset`s by referencing the events in the original. We keep the `Arc` wrapper on `Dataset` to allow them to be quickly copied (rather than increasing the reference count for each `Event`). --- python/laddu/__init__.py | 3 +- python/laddu/__init__.pyi | 3 +- python/laddu/data/__init__.py | 4 +- python/laddu/data/__init__.pyi | 7 +- src/data.rs | 266 ++++++++++++++++++--------------- src/lib.rs | 2 +- src/python.rs | 34 ++--- 7 files changed, 165 insertions(+), 154 deletions(-) diff --git a/python/laddu/__init__.py b/python/laddu/__init__.py index 9f26ed3..f934b81 100644 --- a/python/laddu/__init__.py +++ b/python/laddu/__init__.py @@ -6,7 +6,7 @@ from laddu.amplitudes.ylm import Ylm from laddu.amplitudes.zlm import Zlm from laddu.convert import convert_from_amptools -from laddu.data import BinnedDataset, Dataset, open, open_binned +from laddu.data import BinnedDataset, Dataset, open from laddu.likelihoods import NLL, LikelihoodManager, Status from laddu.utils.variables import Angles, CosTheta, Mass, Phi, PolAngle, Polarization, PolMagnitude from laddu.utils.vectors import Vector3, Vector4 @@ -30,7 +30,6 @@ def callback(self, step: int, status: Status) -> tuple[Status, bool]: "Dataset", "open", "BinnedDataset", - "open_binned", "utils", "data", "amplitudes", diff --git a/python/laddu/__init__.pyi b/python/laddu/__init__.pyi index 96e73f3..ae0be46 100644 --- a/python/laddu/__init__.pyi +++ b/python/laddu/__init__.pyi @@ -6,7 +6,7 @@ from laddu.amplitudes.common import ComplexScalar, PolarComplexScalar, Scalar from laddu.amplitudes.ylm import Ylm from laddu.amplitudes.zlm import Zlm from laddu.convert import convert_from_amptools -from laddu.data import BinnedDataset, Dataset, open, open_binned +from laddu.data import BinnedDataset, Dataset, open from laddu.likelihoods import NLL, LikelihoodManager, Status from laddu.utils.variables import Angles, CosTheta, Mass, Phi, PolAngle, Polarization, PolMagnitude from laddu.utils.vectors import Vector3, Vector4 @@ -26,7 +26,6 @@ __all__ = [ "Dataset", "open", "BinnedDataset", - "open_binned", "utils", "data", "amplitudes", diff --git a/python/laddu/data/__init__.py b/python/laddu/data/__init__.py index 010c56d..2f8493b 100644 --- a/python/laddu/data/__init__.py +++ b/python/laddu/data/__init__.py @@ -1,3 +1,3 @@ -from laddu.laddu import BinnedDataset, Dataset, Event, open, open_binned +from laddu.laddu import BinnedDataset, Dataset, Event, open -__all__ = ["Event", "Dataset", "open", "BinnedDataset", "open_binned"] +__all__ = ["Event", "Dataset", "open", "BinnedDataset"] diff --git a/python/laddu/data/__init__.pyi b/python/laddu/data/__init__.pyi index a1ae8c7..8873f57 100644 --- a/python/laddu/data/__init__.pyi +++ b/python/laddu/data/__init__.pyi @@ -18,6 +18,7 @@ class Dataset: def __getitem__(self, index: int) -> Event: ... def len(self) -> int: ... def weighted_len(self) -> float: ... + def bin_by(self, variable: Mass, bins: int, range: tuple[float, float]) -> BinnedDataset: ... # noqa: A002 def bootstrap(self, seed: int) -> Dataset: ... class BinnedDataset: @@ -29,9 +30,3 @@ class BinnedDataset: def __getitem__(self, index: int) -> Dataset: ... def open(path: str) -> Dataset: ... # noqa: A001 -def open_binned( - path: str, - variable: Mass, - bins: int, - range: tuple[float, float], # noqa: A002 -) -> BinnedDataset: ... diff --git a/src/data.rs b/src/data.rs index 671f4a5..f3ddeb3 100644 --- a/src/data.rs +++ b/src/data.rs @@ -120,35 +120,108 @@ impl Dataset { pub fn iter(&self) -> impl Iterator { self.events.iter().map(|a| a.as_ref()) } +} +#[cfg(feature = "rayon")] +impl Dataset { /// Produces an parallelized iterator over the [`Event`]s in the [`Dataset`]. - #[cfg(feature = "rayon")] pub fn par_iter(&self) -> impl IndexedParallelIterator { self.events.par_iter().map(|a| a.as_ref()) } /// Extract a list of weights over each [`Event`] in the [`Dataset`]. - #[cfg(not(feature = "rayon"))] - pub fn weights(&self) -> Vec { - self.iter().map(|e| e.weight).collect() - } - - /// Extract a list of weights over each [`Event`] in the [`Dataset`]. - #[cfg(feature = "rayon")] pub fn weights(&self) -> Vec { self.par_iter().map(|e| e.weight).collect() } /// Returns the sum of the weights for each [`Event`] in the [`Dataset`]. - #[cfg(not(feature = "rayon"))] pub fn weighted_len(&self) -> Float { - self.iter().map(|e| e.weight).sum() + self.par_iter().map(|e| e.weight).sum() + } + + /// Generate a new dataset with the same length by resampling the events in the original datset + /// with replacement. This can be used to perform error analysis via the bootstrap method. + pub fn bootstrap(&self, seed: usize) -> Arc { + if self.is_empty() { + return Arc::new(Dataset::default()); + } + let mut rng = ChaCha8Rng::seed_from_u64(seed as u64); + let mut indices: Vec = (0..self.len()) + .map(|_| rng.gen_range(0..self.len())) + .collect::>(); + indices.sort(); + let bootstrapped_events: Vec> = indices + .into_par_iter() + .map(|idx| self.events[idx].clone()) + .collect(); + Arc::new(Dataset { + events: bootstrapped_events, + }) + } + + /// Filter the [`Dataset`] by a given `predicate`, selecting events for which the predicate + /// returns `true`. + pub fn filter

(&self, predicate: P) -> Arc + where + P: Fn(&Event) -> bool + Send + Sync, + { + let filtered_events = self + .events + .par_iter() + .filter(|e| predicate(e)) + .cloned() + .collect(); + Arc::new(Dataset { + events: filtered_events, + }) + } + + /// Bin a [`Dataset`] by the value of the given [`Variable`] into a number of `bins` within the + /// given `range`. + pub fn bin_by(&self, variable: V, bins: usize, range: (Float, Float)) -> BinnedDataset + where + V: Variable, + { + let bin_width = (range.1 - range.0) / bins as Float; + let bin_edges = get_bin_edges(bins, range); + let evaluated: Vec<(usize, &Arc)> = self + .events + .par_iter() + .filter_map(|event| { + let value = variable.value(event.as_ref()); + if value >= range.0 && value < range.1 { + let bin_index = ((value - range.0) / bin_width) as usize; + let bin_index = bin_index.min(bins - 1); + Some((bin_index, event)) + } else { + None + } + }) + .collect(); + let mut binned_events: Vec>> = vec![Vec::default(); bins]; + for (bin_index, event) in evaluated { + binned_events[bin_index].push(event.clone()); + } + BinnedDataset { + datasets: binned_events + .into_par_iter() + .map(|events| Arc::new(Dataset { events })) + .collect(), + edges: bin_edges, + } + } +} + +#[cfg(not(feature = "rayon"))] +impl Dataset { + /// Extract a list of weights over each [`Event`] in the [`Dataset`]. + pub fn weights(&self) -> Vec { + self.iter().map(|e| e.weight).collect() } /// Returns the sum of the weights for each [`Event`] in the [`Dataset`]. - #[cfg(feature = "rayon")] pub fn weighted_len(&self) -> Float { - self.par_iter().map(|e| e.weight).sum() + self.iter().map(|e| e.weight).sum() } /// Generate a new dataset with the same length by resampling the events in the original datset @@ -158,15 +231,70 @@ impl Dataset { return Arc::new(Dataset::default()); } let mut rng = ChaCha8Rng::seed_from_u64(seed as u64); - let mut bootstrapped_events = Vec::with_capacity(self.len()); - for _ in 0..self.len() { - let idx = rng.gen_range(0..self.len()); - bootstrapped_events.push(self.events[idx].clone()); - } + let mut indices: Vec = (0..self.len()) + .map(|_| rng.gen_range(0..self.len())) + .collect::>(); + indices.sort(); + let bootstrapped_events: Vec> = indices + .into_iter() + .map(|idx| self.events[idx].clone()) + .collect(); Arc::new(Dataset { events: bootstrapped_events, }) } + + /// Filter the [`Dataset`] by a given `predicate`, selecting events for which the predicate + /// returns `true`. + pub fn filter

(&self, predicate: P) -> Arc + where + P: Fn(&Event) -> bool + Send + Sync, + { + let filtered_events = self + .events + .iter() + .filter(|e| predicate(e)) + .cloned() + .collect(); + Arc::new(Dataset { + events: filtered_events, + }) + } + + /// Bin a [`Dataset`] by the value of the given [`Variable`] into a number of `bins` within the + /// given `range`. + pub fn bin_by(&self, variable: V, bins: usize, range: (Float, Float)) -> BinnedDataset + where + V: Variable, + { + let bin_width = (range.1 - range.0) / bins as Float; + let bin_edges = get_bin_edges(bins, range); + let evaluated: Vec<(usize, &Arc)> = self + .events + .iter() + .filter_map(|event| { + let value = variable.value(event.as_ref()); + if value >= range.0 && value < range.1 { + let bin_index = ((value - range.0) / bin_width) as usize; + let bin_index = bin_index.min(bins - 1); + Some((bin_index, event)) + } else { + None + } + }) + .collect(); + let mut binned_events: Vec>> = vec![Vec::default(); bins]; + for (bin_index, event) in evaluated { + binned_events[bin_index].push(event.clone()); + } + BinnedDataset { + datasets: binned_events + .into_iter() + .map(|events| Arc::new(Dataset { events })) + .collect(), + edges: bin_edges, + } + } } fn batch_to_event(batch: &RecordBatch, row: usize) -> Event { @@ -308,70 +436,6 @@ pub fn open(file_path: &str) -> Result, LadduError> { Ok(Arc::new(Dataset { events })) } -/// Open a Parquet file and read the data into a [`Dataset`]. Only returns events for which the -/// given predicate returns `true`. -#[cfg(feature = "rayon")] -pub fn open_filtered

(file_path: &str, predicate: P) -> Result, LadduError> -where - P: Fn(&Event) -> bool + Send + Sync, -{ - let file_path = Path::new(&*shellexpand::full(file_path)?).canonicalize()?; - let file = File::open(file_path)?; - let builder = ParquetRecordBatchReaderBuilder::try_new(file)?; - let reader = builder.build()?; - let batches: Vec = reader.collect::, _>>()?; - - let events: Vec> = batches - .into_par_iter() - .flat_map(|batch| { - let num_rows = batch.num_rows(); - let mut local_events = Vec::with_capacity(num_rows); - - // Process each row in the batch - for row in 0..num_rows { - let event = batch_to_event(&batch, row); - if predicate(&event) { - local_events.push(Arc::new(event)); - } - } - local_events - }) - .collect(); - Ok(Arc::new(Dataset { events })) -} - -/// Open a Parquet file and read the data into a [`Dataset`]. Only returns events for which the -/// given predicate returns `true`. -#[cfg(not(feature = "rayon"))] -pub fn open_filtered

(file_path: &str, predicate: P) -> Result, LadduError> -where - P: Fn(&Event) -> bool, -{ - let file_path = Path::new(&*shellexpand::full(file_path)?).canonicalize()?; - let file = File::open(file_path)?; - let builder = ParquetRecordBatchReaderBuilder::try_new(file)?; - let reader = builder.build()?; - let batches: Vec = reader.collect::, _>>()?; - - let events: Vec> = batches - .into_iter() - .flat_map(|batch| { - let num_rows = batch.num_rows(); - let mut local_events = Vec::with_capacity(num_rows); - - // Process each row in the batch - for row in 0..num_rows { - let event = batch_to_event(&batch, row); - if predicate(&event) { - local_events.push(Arc::new(event)); - } - } - local_events - }) - .collect(); - Ok(Arc::new(Dataset { events })) -} - fn get_bin_edges(bins: usize, range: (Float, Float)) -> Vec { let bin_width = (range.1 - range.0) / (bins as Float); (0..=bins) @@ -439,43 +503,3 @@ impl BinnedDataset { (self.edges[0], self.edges[self.len()]) } } - -/// Open a Parquet file and read the data into a [`BinnedDataset`] using the given [`Variable`], -/// number of `bins` and `range`. -pub fn open_binned( - file_path: &str, - variable: V, - bins: usize, - range: (Float, Float), -) -> Result { - let file_path = Path::new(&*shellexpand::full(file_path)?).canonicalize()?; - let file = File::open(file_path)?; - let builder = ParquetRecordBatchReaderBuilder::try_new(file)?; - let reader = builder.build()?; - let batches: Vec = reader.collect::, _>>()?; - - let mut binned_events: Vec>> = vec![Vec::default(); bins]; - let bin_width = (range.1 - range.0) / bins as Float; - let bin_edges = get_bin_edges(bins, range); - - batches.into_iter().for_each(|batch| { - let num_rows = batch.num_rows(); - - // Process each row in the batch - for row in 0..num_rows { - let event = batch_to_event(&batch, row); - let value = variable.value(&event); - if value >= range.0 && value < range.1 { - let bin_index = ((value - range.0) / bin_width) as usize; - binned_events[bin_index].push(Arc::new(event)); - } - } - }); - Ok(BinnedDataset { - datasets: binned_events - .into_iter() - .map(|events| Arc::new(Dataset { events })) - .collect(), - edges: bin_edges, - }) -} diff --git a/src/lib.rs b/src/lib.rs index 118c20b..c6ca0a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,7 +287,7 @@ pub mod prelude { zlm::Zlm, Amplitude, AmplitudeID, Evaluator, Expression, Manager, ParameterLike, }; - pub use crate::data::{open, open_binned, open_filtered, BinnedDataset, Dataset, Event}; + pub use crate::data::{open, BinnedDataset, Dataset, Event}; pub use crate::likelihoods::{ LikelihoodEvaluator, LikelihoodExpression, LikelihoodID, LikelihoodManager, LikelihoodTerm, MinimizerOptions, NLL, diff --git a/src/python.rs b/src/python.rs index e061385..c8c05a7 100644 --- a/src/python.rs +++ b/src/python.rs @@ -246,6 +246,20 @@ pub(crate) mod laddu { .ok_or(PyIndexError::new_err("index out of range")) .map(|rust_event| Event(rust_event.clone())) } + #[pyo3(signature = (variable, bins, range))] + fn bin_by( + &self, + variable: Bound<'_, PyAny>, + bins: usize, + range: (Float, Float), + ) -> PyResult { + let rust_variable = if let Ok(py_mass) = variable.extract::>() { + py_mass.0.clone() + } else { + return Err(PyTypeError::new_err("Unsupported variable!")); + }; + Ok(BinnedDataset(self.0.bin_by(rust_variable, bins, range))) + } fn bootstrap(&self, seed: usize) -> Dataset { Dataset(self.0.bootstrap(seed)) } @@ -286,26 +300,6 @@ pub(crate) mod laddu { fn open(path: &str) -> PyResult { Ok(Dataset(rust::data::open(path)?)) } - #[pyfunction] - #[pyo3(signature = (path, variable, bins, range))] - fn open_binned( - path: &str, - variable: Bound<'_, PyAny>, - bins: usize, - range: (Float, Float), - ) -> PyResult { - let rust_variable = if let Ok(py_mass) = variable.extract::>() { - py_mass.0.clone() - } else { - return Err(PyTypeError::new_err("Unsupported variable!")); - }; - Ok(BinnedDataset(rust::data::open_binned( - path, - rust_variable, - bins, - range, - )?)) - } #[pyclass] #[derive(Clone)] From 7cd54c696b52358d5f1ac46d38a5bb158482afc3 Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 14:30:06 -0400 Subject: [PATCH 10/12] feat: update example_1 with new binning code This also adds a bootstrap to the unbinned fit which should yield more accurate uncertainties --- python_examples/example_1/example_1.py | 65 ++++++++++++++------------ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/python_examples/example_1/example_1.py b/python_examples/example_1/example_1.py index 8b9afe8..11a25f9 100755 --- a/python_examples/example_1/example_1.py +++ b/python_examples/example_1/example_1.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 """ -Usage: example_1.py [-n ] +Usage: example_1.py [-n ] [-i ] [-b ] Options: --n Number of bins to use in binned fit and plot [default: 50] +-n Number of bins to use in binned fit and plot [default: 50] +-i Number of iterations with randomized starting parameters for each fit [default: 20] +-b Number of bootstrapped fits to perform for each fit [default: 20] """ import os @@ -21,15 +23,19 @@ from uncertainties import ufloat -def main(bins: int): # noqa: PLR0915 +def main(bins: int, niters: int, nboot: int): # noqa: PLR0915 script_dir = Path(os.path.realpath(__file__)).parent.resolve() data_file = str(script_dir / "data_1.parquet") mc_file = str(script_dir / "mc_1.parquet") start = perf_counter() + data_ds = ld.open(data_file) + accmc_ds = ld.open(mc_file) binned_tot_res, binned_tot_err, binned_s0p_res, binned_s0p_err, binned_d2p_res, binned_d2p_err, bin_edges = ( - fit_binned(bins, data_file, mc_file) + fit_binned(bins, niters, nboot, data_ds, accmc_ds) + ) + tot_weights, s0p_weights, d2p_weights, status, boot_statuses, parameters = fit_unbinned( + niters, nboot, data_ds, accmc_ds ) - tot_weights, s0p_weights, d2p_weights, status, parameters = fit_unbinned(data_file, mc_file) end = perf_counter() pprint(f"Total time: {end - start:.3f}s") print(status) # noqa: T201 @@ -39,18 +45,12 @@ def main(bins: int): # noqa: PLR0915 f0_re = status.x[parameters.index("S0+ re")] f2_re = status.x[parameters.index("D2+ re")] f2_im = status.x[parameters.index("D2+ im")] - if status.err is not None: - f0_width_err = status.err[parameters.index("f0_width")] - f2_width_err = status.err[parameters.index("f2_width")] - f0_re_err = status.err[parameters.index("S0+ re")] - f2_re_err = status.err[parameters.index("D2+ re")] - f2_im_err = status.err[parameters.index("D2+ im")] - else: - f0_width_err = np.inf - f2_width_err = np.inf - f0_re_err = np.inf - f2_re_err = np.inf - f2_im_err = np.inf + + f0_width_err = np.array([boot_status.x[parameters.index("f0_width")] for boot_status in boot_statuses]).std() + f2_width_err = np.array([boot_status.x[parameters.index("f2_width")] for boot_status in boot_statuses]).std() + f0_re_err = np.array([boot_status.x[parameters.index("S0+ re")] for boot_status in boot_statuses]).std() + f2_re_err = np.array([boot_status.x[parameters.index("D2+ re")] for boot_status in boot_statuses]).std() + f2_im_err = np.array([boot_status.x[parameters.index("D2+ im")] for boot_status in boot_statuses]).std() u_f0_re = ufloat(f0_re, f0_re_err) u_f2_re = ufloat(f2_re, f2_re_err) @@ -70,9 +70,6 @@ def main(bins: int): # noqa: PLR0915 pprint(table) res_mass = ld.Mass([2, 3]) - data_ds = ld.open("./data_1.parquet") - accmc_ds = ld.open("./mc_1.parquet") - m_data = res_mass.value_on(data_ds) m_accmc = res_mass.value_on(accmc_ds) @@ -155,12 +152,12 @@ def main(bins: int): # noqa: PLR0915 plt.savefig("example_1.svg") -def fit_binned(bins: int, data_file: str, mc_file: str): +def fit_binned(bins: int, niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds: ld.Dataset): res_mass = ld.Mass([2, 3]) angles = ld.Angles(0, [1], [2], [2, 3]) polarization = ld.Polarization(0, [1]) - data_ds_binned = ld.open_binned(data_file, res_mass, bins, (1.0, 2.0)) - accmc_ds_binned = ld.open_binned(mc_file, res_mass, bins, (1.0, 2.0)) + data_ds_binned = data_ds.bin_by(res_mass, bins, (1.0, 2.0)) + accmc_ds_binned = accmc_ds.bin_by(res_mass, bins, (1.0, 2.0)) manager = ld.Manager() z00p = manager.register(ld.Zlm("Z00+", 0, 0, "+", angles, polarization)) z22p = manager.register(ld.Zlm("Z22+", 2, 2, "+", angles, polarization)) @@ -183,7 +180,7 @@ def fit_binned(bins: int, data_file: str, mc_file: str): best_nll = np.inf best_status = None nll = ld.NLL(manager, data_ds_binned[ibin], accmc_ds_binned[ibin], model) - for _ in range(20): + for _ in range(niters): p0 = rng.uniform(-1000.0, 1000.0, len(nll.parameters)) status = nll.minimize(p0) if status.fx < best_nll: @@ -204,7 +201,7 @@ def fit_binned(bins: int, data_file: str, mc_file: str): tot_boot = [] s0p_boot = [] d2p_boot = [] - for iboot in range(20): + for iboot in range(nboot): boot_nll = ld.NLL( manager, data_ds_binned[ibin].bootstrap(iboot), accmc_ds_binned[ibin].bootstrap(iboot), model ) @@ -223,12 +220,12 @@ def fit_binned(bins: int, data_file: str, mc_file: str): return (tot_res, tot_res_err, s0p_res, s0p_res_err, d2p_res, d2p_res_err, data_ds_binned.edges) -def fit_unbinned(data_file: str, mc_file: str): +def fit_unbinned( + niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds: ld.Dataset +) -> tuple[np.ndarray, np.ndarray, np.ndarray, ld.Status, list[ld.Status], list[str]]: res_mass = ld.Mass([2, 3]) angles = ld.Angles(0, [1], [2], [2, 3]) polarization = ld.Polarization(0, [1]) - data_ds = ld.open(data_file) - accmc_ds = ld.open(mc_file) manager = ld.Manager() z00p = manager.register(ld.Zlm("Z00+", 0, 0, "+", angles, polarization)) z22p = manager.register(ld.Zlm("Z22+", 2, 2, "+", angles, polarization)) @@ -260,7 +257,7 @@ def fit_unbinned(data_file: str, mc_file: str): (-1000.0, 1000.0), ] nll = ld.NLL(manager, data_ds, accmc_ds, model) - for _ in range(20): + for _ in range(niters): p0 = rng.uniform(-1000.0, 1000.0, 3) p0 = np.append([0.8, 0.5], p0) status = nll.minimize(p0, bounds=bounds) @@ -277,10 +274,16 @@ def fit_unbinned(data_file: str, mc_file: str): s0p_weights = nll.project(best_status.x) nll.isolate(["D2+", "Z22+", "f2(1525)"]) d2p_weights = nll.project(best_status.x) + nll.activate_all() + + boot_statuses = [] + for iboot in range(nboot): + boot_nll = ld.NLL(manager, data_ds.bootstrap(iboot), accmc_ds.bootstrap(iboot), model) + boot_statuses.append(boot_nll.minimize(best_status.x)) - return (tot_weights, s0p_weights, d2p_weights, best_status, nll.parameters) + return (tot_weights, s0p_weights, d2p_weights, best_status, boot_statuses, nll.parameters) if __name__ == "__main__": args = docopt(__doc__) # pyright: ignore - main(int(args["-n"])) + main(int(args["-n"]), int(args["-i"]), int(args["-b"])) From 5fe6bbc140923e308ee95b26934ea6bd713414a4 Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 14:44:28 -0400 Subject: [PATCH 11/12] fix: remove AmpTools version of example_1 It's not wrong, it's just missing error estimation and the total unbinned fit. --- .../example_1/example_1_amptools.svg | 4173 ----------------- 1 file changed, 4173 deletions(-) delete mode 100644 python_examples/example_1/example_1_amptools.svg diff --git a/python_examples/example_1/example_1_amptools.svg b/python_examples/example_1/example_1_amptools.svg deleted file mode 100644 index c3b7360..0000000 --- a/python_examples/example_1/example_1_amptools.svg +++ /dev/null @@ -1,4173 +0,0 @@ - - - - - - - - 2024-10-30T23:58:19.433902 - image/svg+xml - - - Matplotlib v3.7.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From f075d8eb1344d72e36a81135c8c28c07edc4197a Mon Sep 17 00:00:00 2001 From: denehoffman Date: Thu, 31 Oct 2024 15:54:49 -0400 Subject: [PATCH 12/12] feat: add logging to example_1 --- python_examples/example_1/example_1.py | 22 +- python_examples/example_1/example_1.svg | 2224 +++++++++--------- python_examples/example_1/example_1.txt | 30 +- python_examples/example_1/requirements_1.txt | 3 +- 4 files changed, 1136 insertions(+), 1143 deletions(-) diff --git a/python_examples/example_1/example_1.py b/python_examples/example_1/example_1.py index 11a25f9..43788d6 100755 --- a/python_examples/example_1/example_1.py +++ b/python_examples/example_1/example_1.py @@ -18,6 +18,7 @@ import numpy as np import uncertainties.umath as unp from docopt import docopt +from loguru import logger from rich import print as pprint from rich.table import Table from uncertainties import ufloat @@ -28,7 +29,9 @@ def main(bins: int, niters: int, nboot: int): # noqa: PLR0915 data_file = str(script_dir / "data_1.parquet") mc_file = str(script_dir / "mc_1.parquet") start = perf_counter() + logger.info("Opening Data file...") data_ds = ld.open(data_file) + logger.info("Opening MC file...") accmc_ds = ld.open(mc_file) binned_tot_res, binned_tot_err, binned_s0p_res, binned_s0p_err, binned_d2p_res, binned_d2p_err, bin_edges = ( fit_binned(bins, niters, nboot, data_ds, accmc_ds) @@ -37,8 +40,8 @@ def main(bins: int, niters: int, nboot: int): # noqa: PLR0915 niters, nboot, data_ds, accmc_ds ) end = perf_counter() - pprint(f"Total time: {end - start:.3f}s") - print(status) # noqa: T201 + logger.info(f"Total time: {end - start:.3f}s") + logger.info(f"\n\n{status}") f0_width = status.x[parameters.index("f0_width")] f2_width = status.x[parameters.index("f2_width")] @@ -153,6 +156,7 @@ def main(bins: int, niters: int, nboot: int): # noqa: PLR0915 def fit_binned(bins: int, niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds: ld.Dataset): + logger.info("Starting Binned Fit") res_mass = ld.Mass([2, 3]) angles = ld.Angles(0, [1], [2], [2, 3]) polarization = ld.Polarization(0, [1]) @@ -177,10 +181,12 @@ def fit_binned(bins: int, niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds rng = np.random.default_rng() for ibin in range(bins): + logger.info(f"Fitting Bin #{ibin}") best_nll = np.inf best_status = None nll = ld.NLL(manager, data_ds_binned[ibin], accmc_ds_binned[ibin], model) - for _ in range(niters): + for iiter in range(niters): + logger.info(f"Fitting Iteration #{iiter}") p0 = rng.uniform(-1000.0, 1000.0, len(nll.parameters)) status = nll.minimize(p0) if status.fx < best_nll: @@ -188,7 +194,7 @@ def fit_binned(bins: int, niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds best_status = status if best_status is None: - print(f"All fits for bin {ibin} failed!") + logger.error(f"All fits for bin #{ibin} failed!") sys.exit(1) tot_res.append(nll.project(best_status.x).sum()) @@ -202,6 +208,7 @@ def fit_binned(bins: int, niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds s0p_boot = [] d2p_boot = [] for iboot in range(nboot): + logger.info(f"Running bootstrap fit #{iboot}") boot_nll = ld.NLL( manager, data_ds_binned[ibin].bootstrap(iboot), accmc_ds_binned[ibin].bootstrap(iboot), model ) @@ -223,6 +230,7 @@ def fit_binned(bins: int, niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds def fit_unbinned( niters: int, nboot: int, data_ds: ld.Dataset, accmc_ds: ld.Dataset ) -> tuple[np.ndarray, np.ndarray, np.ndarray, ld.Status, list[ld.Status], list[str]]: + logger.info("Starting Unbinned Fit") res_mass = ld.Mass([2, 3]) angles = ld.Angles(0, [1], [2], [2, 3]) polarization = ld.Polarization(0, [1]) @@ -257,7 +265,8 @@ def fit_unbinned( (-1000.0, 1000.0), ] nll = ld.NLL(manager, data_ds, accmc_ds, model) - for _ in range(niters): + for iiter in range(niters): + logger.info(f"Fitting Iteration #{iiter}") p0 = rng.uniform(-1000.0, 1000.0, 3) p0 = np.append([0.8, 0.5], p0) status = nll.minimize(p0, bounds=bounds) @@ -266,7 +275,7 @@ def fit_unbinned( best_status = status if best_status is None: - print("All unbinned fits failed!") + logger.error("All unbinned fits failed!") sys.exit(1) tot_weights = nll.project(best_status.x) @@ -278,6 +287,7 @@ def fit_unbinned( boot_statuses = [] for iboot in range(nboot): + logger.info(f"Running bootstrap fit #{iboot}") boot_nll = ld.NLL(manager, data_ds.bootstrap(iboot), accmc_ds.bootstrap(iboot), model) boot_statuses.append(boot_nll.minimize(best_status.x)) diff --git a/python_examples/example_1/example_1.svg b/python_examples/example_1/example_1.svg index e9a0321..18e684d 100644 --- a/python_examples/example_1/example_1.svg +++ b/python_examples/example_1/example_1.svg @@ -6,7 +6,7 @@ - 2024-10-31T00:40:54.682028 + 2024-10-31T15:42:10.046502 image/svg+xml @@ -43,7 +43,7 @@ L 170.490227 782.34 L 170.490227 779.871878 L 157.941591 779.871878 z -" clip-path="url(#p35bec1fa52)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> +" clip-path="url(#pf115053602)" style="fill: #007bc0; opacity: 0.1"/> - - + @@ -905,7 +905,7 @@ z - + @@ -946,7 +946,7 @@ z - + @@ -982,7 +982,7 @@ z - + @@ -1029,7 +1029,7 @@ z - + @@ -1085,7 +1085,7 @@ z - + @@ -1100,124 +1100,124 @@ z - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1542,12 +1542,12 @@ z - - + @@ -1560,7 +1560,7 @@ L 8 0 - + @@ -1576,12 +1576,12 @@ L 8 0 - + - + @@ -1592,12 +1592,12 @@ L 8 0 - + - + @@ -1608,12 +1608,12 @@ L 8 0 - + - + @@ -1624,12 +1624,12 @@ L 8 0 - + - + @@ -1641,12 +1641,12 @@ L 8 0 - + - + @@ -1658,12 +1658,12 @@ L 8 0 - + - + @@ -1675,173 +1675,173 @@ L 8 0 - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1980,8 +1980,8 @@ L 283.427955 769.163642 L 295.976591 769.163642 L 295.976591 768.113353 L 308.525227 768.113353 -L 308.525227 764.914745 -L 321.073864 764.914745 +L 308.525227 764.914744 +L 321.073864 764.914744 L 321.073864 760.188442 L 333.6225 760.188442 L 333.6225 756.942093 @@ -1998,34 +1998,34 @@ L 396.365682 697.266561 L 408.914318 697.266561 L 408.914318 666.378505 L 421.462955 666.378505 -L 421.462955 619.688369 -L 434.011591 619.688369 -L 434.011591 537.383875 -L 446.560227 537.383875 -L 446.560227 402.803614 -L 459.108864 402.803614 -L 459.108864 213.417344 -L 471.6575 213.417344 -L 471.6575 59.884135 -L 484.206136 59.884135 -L 484.206136 143.047956 -L 496.754773 143.047956 -L 496.754773 336.921826 -L 509.303409 336.921826 -L 509.303409 493.653644 -L 521.852045 493.653644 -L 521.852045 592.715028 -L 534.400682 592.715028 -L 534.400682 647.377816 -L 546.949318 647.377816 -L 546.949318 691.53771 -L 559.497955 691.53771 +L 421.462955 619.688368 +L 434.011591 619.688368 +L 434.011591 537.383873 +L 446.560227 537.383873 +L 446.560227 402.803612 +L 459.108864 402.803612 +L 459.108864 213.417341 +L 471.6575 213.417341 +L 471.6575 59.884131 +L 484.206136 59.884131 +L 484.206136 143.047953 +L 496.754773 143.047953 +L 496.754773 336.921824 +L 509.303409 336.921824 +L 509.303409 493.653643 +L 521.852045 493.653643 +L 521.852045 592.715027 +L 534.400682 592.715027 +L 534.400682 647.377815 +L 546.949318 647.377815 +L 546.949318 691.537709 +L 559.497955 691.537709 L 559.497955 708.962965 L 572.046591 708.962965 L 572.046591 728.250097 L 584.595227 728.250097 -L 584.595227 739.803281 -L 597.143864 739.803281 +L 584.595227 739.80328 +L 597.143864 739.80328 L 597.143864 746.295978 L 609.6925 746.295978 L 609.6925 754.459591 @@ -2050,322 +2050,322 @@ L 722.630227 774.46283 L 735.178864 774.46283 L 735.178864 775.704081 L 747.7275 775.704081 -L 747.7275 775.56086 -L 760.276136 775.56086 +L 747.7275 775.560859 +L 760.276136 775.560859 L 760.276136 776.993072 L 772.824773 776.993072 L 772.824773 778.186583 L 785.373409 778.186583 L 785.373409 782.34 -" clip-path="url(#p35bec1fa52)" style="fill: none; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-linejoin: miter"/> - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +L 352.445455 751.4042 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> + - - +L 440.285909 537.383868 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> + + - - - - - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> + + + + + - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +L 615.966818 754.459586 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - +L 314.799545 777.385253 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> + - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +L 390.091364 759.952975 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - - - - - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> + + + + + +L 477.931818 630.091069 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - - - - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> + + + + - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> + - + +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +L 666.161364 777.95592 +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> +" clip-path="url(#pf115053602)" style="fill: none; stroke: #007bc0; stroke-width: 1.5"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2729,7 +2729,7 @@ L 574.585 152.53125 - + @@ -2757,7 +2757,7 @@ L 574.585 189.4425 - + @@ -2794,7 +2794,7 @@ L 913.985227 782.34 L 913.985227 779.871878 L 901.436591 779.871878 z -" clip-path="url(#pc2ce734f90)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> +" clip-path="url(#pa8e3177db9)" style="fill: #ef3a47; opacity: 0.1"/> - + @@ -3607,7 +3607,7 @@ z - + @@ -3622,7 +3622,7 @@ z - + @@ -3637,7 +3637,7 @@ z - + @@ -3652,7 +3652,7 @@ z - + @@ -3667,7 +3667,7 @@ z - + @@ -3682,119 +3682,119 @@ z - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3831,224 +3831,224 @@ z - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4102,8 +4102,8 @@ L 1026.922955 769.163642 L 1039.471591 769.163642 L 1039.471591 768.113353 L 1052.020227 768.113353 -L 1052.020227 764.914745 -L 1064.568864 764.914745 +L 1052.020227 764.914744 +L 1064.568864 764.914744 L 1064.568864 760.188442 L 1077.1175 760.188442 L 1077.1175 756.942093 @@ -4120,34 +4120,34 @@ L 1139.860682 697.266561 L 1152.409318 697.266561 L 1152.409318 666.378505 L 1164.957955 666.378505 -L 1164.957955 619.688369 -L 1177.506591 619.688369 -L 1177.506591 537.383875 -L 1190.055227 537.383875 -L 1190.055227 402.803614 -L 1202.603864 402.803614 -L 1202.603864 213.417344 -L 1215.1525 213.417344 -L 1215.1525 59.884135 -L 1227.701136 59.884135 -L 1227.701136 143.047956 -L 1240.249773 143.047956 -L 1240.249773 336.921826 -L 1252.798409 336.921826 -L 1252.798409 493.653644 -L 1265.347045 493.653644 -L 1265.347045 592.715028 -L 1277.895682 592.715028 -L 1277.895682 647.377816 -L 1290.444318 647.377816 -L 1290.444318 691.53771 -L 1302.992955 691.53771 +L 1164.957955 619.688368 +L 1177.506591 619.688368 +L 1177.506591 537.383873 +L 1190.055227 537.383873 +L 1190.055227 402.803612 +L 1202.603864 402.803612 +L 1202.603864 213.417341 +L 1215.1525 213.417341 +L 1215.1525 59.884131 +L 1227.701136 59.884131 +L 1227.701136 143.047953 +L 1240.249773 143.047953 +L 1240.249773 336.921824 +L 1252.798409 336.921824 +L 1252.798409 493.653643 +L 1265.347045 493.653643 +L 1265.347045 592.715027 +L 1277.895682 592.715027 +L 1277.895682 647.377815 +L 1290.444318 647.377815 +L 1290.444318 691.537709 +L 1302.992955 691.537709 L 1302.992955 708.962965 L 1315.541591 708.962965 L 1315.541591 728.250097 L 1328.090227 728.250097 -L 1328.090227 739.803281 -L 1340.638864 739.803281 +L 1328.090227 739.80328 +L 1340.638864 739.80328 L 1340.638864 746.295978 L 1353.1875 746.295978 L 1353.1875 754.459591 @@ -4172,376 +4172,376 @@ L 1466.125227 774.46283 L 1478.673864 774.46283 L 1478.673864 775.704081 L 1491.2225 775.704081 -L 1491.2225 775.56086 -L 1503.771136 775.56086 +L 1491.2225 775.560859 +L 1503.771136 775.560859 L 1503.771136 776.993072 L 1516.319773 776.993072 L 1516.319773 778.186583 L 1528.868409 778.186583 L 1528.868409 782.34 -" clip-path="url(#pc2ce734f90)" style="fill: none; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-linejoin: miter"/> - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +L 1095.940455 751.4042 +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> + - - +L 1183.780909 537.383868 +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> + + - - - - - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> + + + + + - + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +L 1359.461818 754.459586 +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #000000; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> - + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> - +L 1083.391818 767.60777 +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> - + - - - - - - - + + + + + + + - - - - - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> + + + + + - +L 1296.718636 712.962644 +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> + - - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> + + - +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> + +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> +" clip-path="url(#pa8e3177db9)" style="fill: none; stroke: #ef3a47; stroke-width: 1.5"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4743,7 +4743,7 @@ L 1314.72 152.29125 - + @@ -4771,7 +4771,7 @@ L 1314.72 189.2025 - + @@ -4795,10 +4795,10 @@ L 1314.72 189.2025 - + - + diff --git a/python_examples/example_1/example_1.txt b/python_examples/example_1/example_1.txt index f3e5d57..6930ad9 100644 --- a/python_examples/example_1/example_1.txt +++ b/python_examples/example_1/example_1.txt @@ -1,24 +1,6 @@ -Total time: 2063.206s -╒══════════════════════════════════════════════════════════════════════════════════════════════╕ -│ FIT RESULTS │ -╞════════════════════════════════════════════╤════════════════════╤═════════════╤══════════════╡ -│ Status: Converged │ fval: -2.343E6 │ #fcn: 146 │ #grad: 146 │ -├────────────────────────────────────────────┴────────────────────┴─────────────┴──────────────┤ -│ Message: F_EVAL CONVERGED │ -├───────╥──────────────┬──────────────╥──────────────┬──────────────┬──────────────┬───────────┤ -│ Par # ║ Value │ Uncertainty ║ Initial │ -Bound │ +Bound │ At Limit? │ -├───────╫──────────────┼──────────────╫──────────────┼──────────────┼──────────────┼───────────┤ -│ 0 ║ +1.083E-1 │ +8.838E-4 ║ +8.000E-1 │ +1.000E-3 │ +1.000E0 │ │ -│ 1 ║ +8.575E-2 │ +3.527E-4 ║ +5.000E-1 │ +1.000E-3 │ +1.000E0 │ │ -│ 2 ║ +8.607E2 │ +4.070E0 ║ +7.725E2 │ -1.000E3 │ +1.000E3 │ │ -│ 3 ║ +3.481E2 │ +3.047E0 ║ -1.076E2 │ -1.000E3 │ +1.000E3 │ │ -│ 4 ║ +4.723E2 │ +1.820E0 ║ +1.747E2 │ -1.000E3 │ +1.000E3 │ │ -└───────╨──────────────┴──────────────╨──────────────┴──────────────┴──────────────┴───────────┘ -┏━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓ -┃ ┃ f0(1500) ┃ f2'(1525) ┃ ┃ ┃ -┃ ┃ Width ┃ Width ┃ S/D Ratio ┃ S-D Phase ┃ -┡━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩ -│ Truth │ 0.11200 │ 0.08600 │ 1.41421 │ 0.78540 │ -│ Fit │ 0.10828 ± │ 0.08575 ± │ 1.46706 ± │ 0.93561 ± │ -│ │ 0.00088 │ 0.00035 │ 0.00906 │ 0.00457 │ -└───────┴─────────────┴──────────────┴─────────────┴──────────────┘ +┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓ +┃ ┃ f0(1500) Width ┃ f2'(1525) Width ┃ S/D Ratio ┃ S-D Phase ┃ +┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩ +│ Truth │ 0.11200 │ 0.08600 │ 1.41421 │ 0.78540 │ +│ Fit │ 0.10828 ± 0.00079 │ 0.08575 ± 0.00039 │ 1.46706 ± 0.00479 │ 0.93561 ± 0.00314 │ +└───────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ diff --git a/python_examples/example_1/requirements_1.txt b/python_examples/example_1/requirements_1.txt index 6fdb7de..9059316 100644 --- a/python_examples/example_1/requirements_1.txt +++ b/python_examples/example_1/requirements_1.txt @@ -1,7 +1,8 @@ # Automatically generated by https://github.com/damnever/pigar. +laddu docopt-ng==0.9.0 -laddu==0.1.3 +loguru==0.7.2 matplotlib==3.9.2 numpy==2.1.2 rich==13.9.3