diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe08fc0..70850d39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.8.3] - 30/08/2024 + +### Fixed + +- fixed a problem in the evolution when an EKO with 'similar' Q2 slices was + used to evolve; this caused the Q2 slices of the grids to be evolved several + times, leading to wrong results + ## [0.8.2] - 22/07/2024 ### Changed @@ -645,7 +653,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - first release -[Unreleased]: https://github.com/NNPDF/pineappl/compare/v0.8.2...HEAD +[Unreleased]: https://github.com/NNPDF/pineappl/compare/v0.8.3...HEAD +[0.8.3]: https://github.com/NNPDF/pineappl/compare/v0.8.2...v0.8.3 [0.8.2]: https://github.com/NNPDF/pineappl/compare/v0.8.1...v0.8.2 [0.8.1]: https://github.com/NNPDF/pineappl/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/NNPDF/pineappl/compare/v0.7.4...v0.8.0 diff --git a/Cargo.lock b/Cargo.lock index 164f0ca6..65dfee87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1225,7 +1225,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pineappl" -version = "0.8.2" +version = "0.8.3" dependencies = [ "anyhow", "arrayvec", @@ -1250,7 +1250,7 @@ dependencies = [ [[package]] name = "pineappl_applgrid" -version = "0.8.2" +version = "0.8.3" dependencies = [ "cc", "cxx", @@ -1261,7 +1261,7 @@ dependencies = [ [[package]] name = "pineappl_capi" -version = "0.8.2" +version = "0.8.3" dependencies = [ "itertools", "pineappl", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "pineappl_cli" -version = "0.8.2" +version = "0.8.3" dependencies = [ "anyhow", "assert_cmd", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "pineappl_fastnlo" -version = "0.8.2" +version = "0.8.3" dependencies = [ "cxx", "cxx-build", @@ -1310,7 +1310,7 @@ dependencies = [ [[package]] name = "pineappl_py" -version = "0.8.2" +version = "0.8.3" dependencies = [ "itertools", "ndarray", @@ -2480,7 +2480,7 @@ dependencies = [ [[package]] name = "xtask" -version = "0.8.2" +version = "0.8.3" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index 5e3d2445..7a1580ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ keywords = ["high-energy-physics", "physics"] license = "GPL-3.0-or-later" repository = "https://github.com/NNPDF/pineappl" rust-version = "1.70.0" -version = "0.8.2" +version = "0.8.3" [workspace.lints.clippy] all = { level = "warn", priority = -1 } diff --git a/maintainer/make-release.sh b/maintainer/make-release.sh index 74ce4942..2ec56519 100755 --- a/maintainer/make-release.sh +++ b/maintainer/make-release.sh @@ -19,9 +19,6 @@ features=( fktable ) -main=master -this_branch=$(git rev-parse --abbrev-ref HEAD) - cd .. if [[ $# != 1 ]]; then @@ -38,13 +35,6 @@ if [[ $(echo ${version} | grep -oP '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?: exit 1 fi -# in branches that are not master we only allow prereleases -if [[ ${this_branch} != ${main} ]] && [[ ${prerelease} == "" ]]; then - echo "Ordinary releases are only allowed in the '${main}' branch." - echo "If you really want to make a release from '${this_branch}', consider making a prerelease." - exit 1 -fi - for crate in ${crates[@]}; do if [[ -n $(git status ${crate} --porcelain) ]]; then echo "This repository isn't clean. Make sure to add or delete the corresponding files." diff --git a/pineappl/src/evolution.rs b/pineappl/src/evolution.rs index cefcc209..83e57c3d 100644 --- a/pineappl/src/evolution.rs +++ b/pineappl/src/evolution.rs @@ -16,7 +16,7 @@ use ndarray::{s, Array1, Array2, Array3, ArrayView1, ArrayView4, Axis}; use std::iter; /// Number of ULPS used to de-duplicate grid values in [`Grid::evolve_info`]. -pub(crate) const EVOLVE_INFO_TOL_ULPS: i64 = 64; +pub(crate) const EVOLVE_INFO_TOL_ULPS: i64 = 256; /// Number of ULPS used to search for grid values in this module. This value must be a large-enough /// multiple of [`EVOLVE_INFO_TOL_ULPS`], because otherwise similar values are not found in diff --git a/pineappl/src/grid.rs b/pineappl/src/grid.rs index 86e744b9..623e7efb 100644 --- a/pineappl/src/grid.rs +++ b/pineappl/src/grid.rs @@ -12,7 +12,7 @@ use super::ntuple_subgrid::NtupleSubgridV1; use super::pids::{self, PidBasis}; use super::subgrid::{ExtraSubgridParams, Mu2, Subgrid, SubgridEnum, SubgridParams}; use bitflags::bitflags; -use float_cmp::approx_eq; +use float_cmp::{approx_eq, assert_approx_eq}; use git_version::git_version; use lz4_flex::frame::{FrameDecoder, FrameEncoder}; use ndarray::{s, Array3, ArrayView3, ArrayView5, ArrayViewMut3, Axis, CowArray, Dimension, Ix4}; @@ -1411,11 +1411,40 @@ impl Grid { use super::evolution::EVOLVE_INFO_TOL_ULPS; let mut lhs: Option = None; - let mut fac1 = Vec::new(); + // Q2 slices we use + let mut used_op_fac1 = Vec::new(); + // Q2 slices we encounter, but possibly don't use + let mut op_fac1 = Vec::new(); + // Q2 slices needed by the grid + let grid_fac1: Vec<_> = self + .evolve_info(order_mask) + .fac1 + .into_iter() + .map(|fac| xi.1 * xi.1 * fac) + .collect(); for result in slices { let (info, operator) = result.map_err(|err| GridError::Other(err.into()))?; + op_fac1.push(info.fac1); + + // it's possible that due to small numerical differences we get two slices which are + // almost the same. We have to skip those in order not to evolve the 'same' slice twice + if used_op_fac1 + .iter() + .any(|&fac| approx_eq!(f64, fac, info.fac1, ulps = EVOLVE_INFO_TOL_ULPS)) + { + continue; + } + + // skip slices that the grid doesn't use + if !grid_fac1 + .iter() + .any(|&fac| approx_eq!(f64, fac, info.fac1, ulps = EVOLVE_INFO_TOL_ULPS)) + { + continue; + } + let op_info_dim = ( info.pids1.len(), info.x1.len(), @@ -1459,26 +1488,20 @@ impl Grid { lhs = Some(rhs); } - fac1.push(info.fac1); + used_op_fac1.push(info.fac1); } // UNWRAP: if we can't compare two numbers there's a bug - fac1.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!())); + op_fac1.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!())); // make sure we've evolved all slices - if let Some(muf2) = self - .evolve_info(order_mask) - .fac1 - .into_iter() - .map(|mu2| xi.1 * xi.1 * mu2) - .find(|&grid_mu2| { - !fac1 - .iter() - .any(|&eko_mu2| approx_eq!(f64, grid_mu2, eko_mu2, ulps = EVOLVE_INFO_TOL_ULPS)) - }) - { + if let Some(muf2) = grid_fac1.into_iter().find(|&grid_mu2| { + !used_op_fac1 + .iter() + .any(|&eko_mu2| approx_eq!(f64, grid_mu2, eko_mu2, ulps = EVOLVE_INFO_TOL_ULPS)) + }) { return Err(GridError::EvolutionFailure(format!( - "no operator for muf2 = {muf2} found in {fac1:?}" + "no operator for muf2 = {muf2} found in {op_fac1:?}" ))); } @@ -1512,12 +1535,49 @@ impl Grid { use itertools::izip; let mut lhs: Option = None; - let mut fac1 = Vec::new(); + // Q2 slices we use + let mut used_op_fac1 = Vec::new(); + // Q2 slices we encounter, but possibly don't use + let mut op_fac1 = Vec::new(); + // Q2 slices needed by the grid + let grid_fac1: Vec<_> = self + .evolve_info(order_mask) + .fac1 + .into_iter() + .map(|fac| xi.1 * xi.1 * fac) + .collect(); // TODO: simplify the ugly repetition below by offloading some ops into fn for (result_a, result_b) in izip!(slices_a, slices_b) { // Operate on `slices_a` let (info_a, operator_a) = result_a.map_err(|err| GridError::Other(err.into()))?; + // Operate on `slices_b` + let (info_b, operator_b) = result_b.map_err(|err| GridError::Other(err.into()))?; + + // TODO: what if the scales of the EKOs don't agree? Is there an ordering problem? + assert_approx_eq!(f64, info_a.fac1, info_b.fac1, ulps = EVOLVE_INFO_TOL_ULPS); + + // also the PID bases must be the same + assert_eq!(info_a.pid_basis, info_b.pid_basis); + + op_fac1.push(info_a.fac1); + + // it's possible that due to small numerical differences we get two slices which are + // almost the same. We have to skip those in order not to evolve the 'same' slice twice + if used_op_fac1 + .iter() + .any(|&fac| approx_eq!(f64, fac, info_a.fac1, ulps = EVOLVE_INFO_TOL_ULPS)) + { + continue; + } + + // skip slices that the grid doesn't use + if !grid_fac1 + .iter() + .any(|&fac| approx_eq!(f64, fac, info_a.fac1, ulps = EVOLVE_INFO_TOL_ULPS)) + { + continue; + } let op_info_dim_a = ( info_a.pids1.len(), @@ -1534,9 +1594,6 @@ impl Grid { ))); } - // Operate on `slices_b` - let (info_b, operator_b) = result_b.map_err(|err| GridError::Other(err.into()))?; - let op_info_dim_b = ( info_b.pids1.len(), info_b.x1.len(), @@ -1586,8 +1643,6 @@ impl Grid { more_members: self.more_members.clone(), }; - assert_eq!(infos[0].pid_basis, infos[1].pid_basis); - // TODO: use a new constructor to set this information rhs.set_pid_basis(infos[0].pid_basis); @@ -1597,27 +1652,20 @@ impl Grid { lhs = Some(rhs); } - // NOTE: The following should be shared by the 2 EKOs(?) - fac1.push(infos[0].fac1); + used_op_fac1.push(infos[0].fac1); } // UNWRAP: if we can't compare two numbers there's a bug - fac1.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!())); + op_fac1.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!())); // make sure we've evolved all slices - if let Some(muf2) = self - .evolve_info(order_mask) - .fac1 - .into_iter() - .map(|mu2| xi.1 * xi.1 * mu2) - .find(|&grid_mu2| { - !fac1 - .iter() - .any(|&eko_mu2| approx_eq!(f64, grid_mu2, eko_mu2, ulps = EVOLVE_INFO_TOL_ULPS)) - }) - { + if let Some(muf2) = grid_fac1.into_iter().find(|&grid_mu2| { + !used_op_fac1 + .iter() + .any(|&eko_mu2| approx_eq!(f64, grid_mu2, eko_mu2, ulps = EVOLVE_INFO_TOL_ULPS)) + }) { return Err(GridError::EvolutionFailure(format!( - "no operator for muf2 = {muf2} found in {fac1:?}" + "no operator for muf2 = {muf2} found in {op_fac1:?}" ))); } @@ -1816,7 +1864,6 @@ impl Grid { mod tests { use super::*; use crate::channel; - use float_cmp::assert_approx_eq; use std::fs::File; #[test] diff --git a/pineappl_capi/Cargo.toml b/pineappl_capi/Cargo.toml index eeb0d044..45651640 100644 --- a/pineappl_capi/Cargo.toml +++ b/pineappl_capi/Cargo.toml @@ -16,7 +16,7 @@ version.workspace = true workspace = true [dependencies] -pineappl = { path = "../pineappl", version = "=0.8.2" } +pineappl = { path = "../pineappl", version = "=0.8.3" } itertools = "0.10.1" [features] diff --git a/pineappl_cli/Cargo.toml b/pineappl_cli/Cargo.toml index bc0bdadb..f9c62982 100644 --- a/pineappl_cli/Cargo.toml +++ b/pineappl_cli/Cargo.toml @@ -30,9 +30,9 @@ lhapdf = { package = "managed-lhapdf", version = "0.3.2" } lz4_flex = { optional = true, version = "0.9.2" } ndarray = "0.15.4" ndarray-npy = { optional = true, version = "0.8.1" } -pineappl = { path = "../pineappl", version = "=0.8.2" } -pineappl_applgrid = { optional = true, path = "../pineappl_applgrid", version = "=0.8.2" } -pineappl_fastnlo = { optional = true, path = "../pineappl_fastnlo", version = "=0.8.2" } +pineappl = { path = "../pineappl", version = "=0.8.3" } +pineappl_applgrid = { optional = true, path = "../pineappl_applgrid", version = "=0.8.3" } +pineappl_fastnlo = { optional = true, path = "../pineappl_fastnlo", version = "=0.8.3" } prettytable-rs = { default-features = false, features = ["win_crlf"], version = "0.10.0" } rayon = "1.5.1" serde = { features = ["derive"], optional = true, version = "1.0.130" } diff --git a/pineappl_py/Cargo.toml b/pineappl_py/Cargo.toml index 526e3dff..b5850f49 100644 --- a/pineappl_py/Cargo.toml +++ b/pineappl_py/Cargo.toml @@ -30,5 +30,5 @@ crate-type = ["cdylib"] itertools = "0.10.1" ndarray = "0.15.4" numpy = "0.21.0" -pineappl = { path = "../pineappl", version = "=0.8.2" } +pineappl = { path = "../pineappl", version = "=0.8.3" } pyo3 = { features = ["extension-module"], version = "0.21.2" } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 908d0fc6..13092834 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -21,4 +21,4 @@ clap_mangen = "0.2.18" enum_dispatch = "0.3.7" #git2 = "0.17.2" #semver = "1.0.17" -pineappl_cli = { path = "../pineappl_cli", version = "=0.8.2" } +pineappl_cli = { path = "../pineappl_cli", version = "=0.8.3" }