Arcane  v4.1.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
ParmetisMatrixPartitioner.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* ParmetisMatrixPartitioner.h (C) 2000-2026 */
9/* */
10/* Matrix partitioning using ParMetis. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_PARMETISMATRIXPARTITIONER_H
13#define ARCCORE_ALINA_PARMETISMATRIXPARTITIONER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * This file is based on the work on AMGCL library (version march 2026)
18 * which can be found at https://github.com/ddemidov/amgcl.
19 *
20 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
21 * SPDX-License-Identifier: MIT
22 */
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25
26#include <memory>
27
28#include "arccore/alina/BackendInterface.h"
29#include "arccore/alina/ValueTypeInterface.h"
30#include "arccore/alina/MessagePassingUtils.h"
31#include "arccore/alina/DistributedMatrix.h"
32#include "arccore/alina/MatrixPartitionUtils.h"
33
34#include <parmetis.h>
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39namespace Arcane::Alina
40{
41
42/*---------------------------------------------------------------------------*/
43/*---------------------------------------------------------------------------*/
44
45template <class Backend>
46struct ParmetisMatrixPartitioner
47{
48 typedef typename Backend::value_type value_type;
49 typedef DistributedMatrix<Backend> matrix;
50 using col_type = Backend::col_type;
51 using ptr_type = Backend::ptr_type;
52
53 struct params
54 {
55 bool shrink;
56 int min_per_proc;
57 int shrink_ratio;
58
59 params()
60 : shrink(false)
61 , min_per_proc(10000)
62 , shrink_ratio(8)
63 {}
64
65 params(const PropertyTree& p)
66 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, shrink)
67 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, min_per_proc)
68 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, shrink_ratio)
69 {
70 p.check_params({ "shrink", "min_per_proc", "shrink_ratio" });
71 }
72
73 void get(PropertyTree& p, const std::string& path = "") const
74 {
75 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, shrink);
76 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, min_per_proc);
77 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, shrink_ratio);
78 }
79
80 } prm;
81
82 explicit ParmetisMatrixPartitioner(const params& prm = params())
83 : prm(prm)
84 {}
85
86 bool is_needed(const matrix& A) const
87 {
88 if (!prm.shrink)
89 return false;
90
91 mpi_communicator comm = A.comm();
92 ptrdiff_t n = A.loc_rows();
93 std::vector<ptrdiff_t> row_dom = comm.exclusive_sum(n);
94
95 int non_empty = 0;
96 ptrdiff_t min_n = std::numeric_limits<ptrdiff_t>::max();
97 for (int i = 0; i < comm.size; ++i) {
98 ptrdiff_t m = row_dom[i + 1] - row_dom[i];
99 if (m) {
100 min_n = std::min(min_n, m);
101 ++non_empty;
102 }
103 }
104
105 return (non_empty > 1) && (min_n <= prm.min_per_proc);
106 }
107
108 std::shared_ptr<matrix> operator()(const matrix& A, unsigned block_size = 1) const
109 {
110 mpi_communicator comm = A.comm();
111 idx_t n = A.loc_rows();
112 ptrdiff_t row_beg = A.loc_col_shift();
113
114 // Partition the graph.
115 int active = (n > 0);
116 int active_ranks = comm.reduceSum(active);
117 int shrink = prm.shrink ? prm.shrink_ratio : 1;
118
119 idx_t npart = std::max(1, active_ranks / shrink);
120
121 if (comm.rank == 0)
122 std::cout << "Partitioning[ParMETIS] " << active_ranks << " -> " << npart << std::endl;
123
124 std::vector<ptrdiff_t> perm(n);
125 ptrdiff_t col_beg, col_end;
126
127 if (npart == 1) {
128 col_beg = (comm.rank == 0) ? 0 : A.glob_rows();
129 col_end = A.glob_rows();
130
131 for (ptrdiff_t i = 0; i < n; ++i) {
132 perm[i] = row_beg + i;
133 }
134 }
135 else {
136 if (block_size == 1) {
137 std::tie(col_beg, col_end) = partition(A, npart, perm);
138 }
139 else {
140 typedef typename math::scalar_of<value_type>::type scalar;
141 using sbackend = BuiltinBackend<scalar,col_type,ptr_type>;
142 ptrdiff_t np = n / block_size;
143
144 DistributedMatrix<sbackend> A_pw(A.comm(),
145 pointwise_matrix(*A.local(), block_size),
146 pointwise_matrix(*A.remote(), block_size));
147
148 std::vector<ptrdiff_t> perm_pw(np);
149
150 std::tie(col_beg, col_end) = partition(A_pw, npart, perm_pw);
151
152 col_beg *= block_size;
153 col_end *= block_size;
154
155 for (ptrdiff_t ip = 0; ip < np; ++ip) {
156 ptrdiff_t i = ip * block_size;
157 ptrdiff_t j = perm_pw[ip] * block_size;
158
159 for (unsigned k = 0; k < block_size; ++k)
160 perm[i + k] = j + k;
161 }
162 }
163 }
164
165 return mpi_graph_perm_matrix<Backend>(comm, col_beg, col_end, perm);
166 }
167
168 template <class B>
169 std::tuple<ptrdiff_t, ptrdiff_t>
170 partition(const DistributedMatrix<B>& A, idx_t npart, std::vector<ptrdiff_t>& perm) const
171 {
172 mpi_communicator comm = A.comm();
173 idx_t n = A.loc_rows();
174 int active = (n > 0);
175
176 std::vector<idx_t> ptr;
177 std::vector<idx_t> col;
178
179 mpi_symm_graph(A, ptr, col);
180
181 idx_t wgtflag = 0;
182 idx_t numflag = 0;
183 idx_t options = 0;
184 idx_t edgecut = 0;
185 idx_t ncon = 1;
186
187 std::vector<real_t> tpwgts(npart, 1.0 / npart);
188 std::vector<real_t> ubvec(ncon, 1.05);
189 std::vector<idx_t> part(n);
190 if (!n)
191 part.reserve(1); // So that part.data() is not NULL
192
193 MPI_Comm scomm;
194 MPI_Comm_split(comm, active ? 0 : MPI_UNDEFINED, comm.rank, &scomm);
195
196 if (active) {
197 mpi_communicator sc(scomm);
198 std::vector<idx_t> vtxdist = sc.exclusive_sum(n);
199
200 sc.check(
201 METIS_OK == ParMETIS_V3_PartKway(&vtxdist[0], &ptr[0], &col[0], NULL, NULL, &wgtflag, &numflag, &ncon, &npart, &tpwgts[0], &ubvec[0], &options, &edgecut, &part[0], &scomm),
202 "Error in ParMETIS");
203
204 MPI_Comm_free(&scomm);
205 }
206
207 return mpi_graph_perm_index(comm, npart, part, perm);
208 }
209};
210
211/*---------------------------------------------------------------------------*/
212/*---------------------------------------------------------------------------*/
213
214} // namespace Arcane::Alina
215
216/*---------------------------------------------------------------------------*/
217/*---------------------------------------------------------------------------*/
218
219#endif
Distributed Matrix using message passing.
Convenience wrapper around MPI_Comm.
std::vector< T > exclusive_sum(T n) const
Exclusive sum over mpi communicator.