.. _program_listing_file_src_permutation.hpp: Program Listing for File permutation.hpp ======================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/permutation.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* This file is part of brille. Copyright © 2019,2020 Greg Tucker brille is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. brille is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with brille. If not, see . */ /* This file holds implementations of Linear Assignment Problem solvers which can be used to find the sorting permutations at grid points. */ #ifndef BRILLE_PERMUTATION_H #define BRILLE_PERMUTATION_H #include #include #include "array.hpp" #include "array_latvec.hpp" // defines bArray #include "sorting_status.hpp" #include "munkres.hpp" #include "lapjv.hpp" #include "smp.hpp" namespace brille { template struct CostTraits{ using type = T; constexpr static T max = (std::numeric_limits::max)(); }; #ifndef DOXYGEN_SHOULD_SKIP_THIS template struct CostTraits>{ using type = T; constexpr static T max = (std::numeric_limits::max)(); }; #endif template::type, R>::value>::type > bool munkres_permutation(const T* centre, const T* neighbour, const std::array& Nel, const R Wscl, const R Wvec, const R Wmat, const size_t span, const size_t Nobj, bArray& permutations, const size_t centre_idx, const size_t neighbour_idx, const int vec_cost_func = 0 ){ /* An earlier version of this function took `centre` and `neighbour` arrays which were effectively [span, Nobj] 2D arrays. This has the unfortunate property that individual objects were not contiguous in memory but instead had a stride equal to Nobj. Now this function requires arrays which are [Nobj, span], placing each object in contiguous memory and eliminating the need to copy sub-object vector/matrices into contiguous memory before calling subroutines. */ // initialize variables R s_cost{0}, v_cost{0}, m_cost{0}; brille::assignment::Munkres munkres(Nobj); size_t *assignment = new size_t[Nobj](); const T *c_i, *n_j; // calculate costs and fill the Munkres cost matrix for (size_t i=0; i = (Dᵢⱼaⱼ)† bᵢ = Dᵢⱼ* aⱼ* bᵢ = aⱼ* Dᵢⱼ* bᵢ = < ⃗a, D† ⃗b > and, since D ⃗a = α ⃗a and D† ⃗b = D ⃗b = β ⃗b, < α ⃗a, ⃗b > = < ⃗a, β ⃗b > → (α-β)< ⃗a, ⃗b > = 0. ∴ < ⃗a, ⃗b > = 0 for non-degenerate solutions to D ϵ = ω² ϵ. For degenerate solutions, eigenvalue solvers still tend to return an arbitrary linear combination c₀ ⃗a + s₁ ⃗b and s₀ ⃗a + c₁ ⃗b which are still orthogonal. It stands to reason that at nearby grid points the orthogonality will only be approximate, and we can instead try to identify the any least-orthogonal modes as being equivalent. */ switch(vec_cost_func){ case 0: v_cost = std::abs(std::sin(brille::utils::hermitian_angle(Nel[1], c_i, n_j))); break; case 1: v_cost = brille::utils::vector_distance(Nel[1], c_i, n_j); break; case 2: v_cost = 1-brille::utils::vector_product(Nel[1], c_i, n_j); break; case 3: v_cost = brille::utils::vector_angle(Nel[1], c_i, n_j); break; } c_i += Nel[1]; n_j += Nel[1]; } if (Nel[2]){ I nel2 = std::sqrt(Nel[2]); if (nel2*nel2 != Nel[2]) throw std::runtime_error("Non-square matrix in munkres_permutation"); m_cost = brille::utils::frobenius_distance(nel2, c_i, n_j); } // for each i we want to determine the cheapest j munkres.get_cost()[i*Nobj+j] = Wscl*s_cost + Wvec*v_cost + Wmat*m_cost; } } // use the Munkres' algorithm to determine the optimal assignment munkres.run_assignment(); if (!munkres.get_assignment(assignment)){ delete[] assignment; return false; } /* use the fact that the neighbour objects have already had their global permutation saved into `permutations` to determine the global permuation for the centre objects too; storing the result into `permutations` as well. */ brille::ind_t nind = static_cast(neighbour_idx); brille::ind_t cind = static_cast(centre_idx); for (brille::ind_t i=0; i::type, R>::value>::type > bool jv_permutation(const T* centre, const T* neighbour, const std::array& Nel, const R Wscl, const R Wvec, const R Wmat, const size_t span, const size_t Nobj, bArray& permutations, const size_t centre_idx, const size_t neighbour_idx, const int vec_cost_func = 0 ){ /* An earlier version of this function took `centre` and `neighbour` arrays which were effectively [span, Nobj] 2D arrays. This has the unfortunate property that individual objects were not contiguous in memory but instead had a stride equal to Nobj. Now this function requires arrays which are [Nobj, span], placing each object in contiguous memory and eliminating the need to copy sub-object vector/matrices into contiguous memory before calling subroutines. */ // initialize variables R s_cost{0}, v_cost{0}, m_cost{0}; R *cost=nullptr, *usol=nullptr, *vsol=nullptr; cost = new R[Nobj*Nobj]; usol = new R[Nobj]; vsol = new R[Nobj]; int *rowsol=nullptr, *colsol=nullptr; rowsol = new int[Nobj]; colsol = new int[Nobj]; const T *c_i, *n_j; // calculate costs and fill the Munkres cost matrix for (size_t i=0; i