Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
NonScalarSearch.cc
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/*---------------------------------------------------------------------------*/
9/*
10 * This file is based on the work on AMGCL library (version march 2026)
11 * which can be found at https://github.com/ddemidov/amgcl.
12 *
13 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
14 * SPDX-License-Identifier: MIT
15 */
16/*---------------------------------------------------------------------------*/
17/*---------------------------------------------------------------------------*/
18
19#include "arccore/alina/BuiltinBackend.h"
20#include "arccore/alina/RelaxationRuntime.h"
21#include "arccore/alina/CoarseningRuntime.h"
22#include "arccore/alina/SolverRuntime.h"
23#include "arccore/alina/PreconditionerRuntime.h"
24#include "arccore/alina/DeflatedSolver.h"
25#include "arccore/alina/AMG.h"
26#include "arccore/alina/Adapters.h"
27#include "arccore/alina/IO.h"
28#include "arccore/alina/Profiler.h"
29
30#include "arccore/common/internal/ProgramOptions.h"
31
32#include <iostream>
33#include <string>
34
35using namespace Arcane;
36
37using Alina::precondition;
38
39//---------------------------------------------------------------------------
40int main(int argc, char* argv[])
41{
42 auto& prof = Alina::Profiler::globalProfiler();
43
44 namespace po = Arcane::ProgramOptions;
45 namespace io = Alina::IO;
46
47 using std::string;
48 using std::vector;
49
50 Alina::AlinaContext context;
51 po::options_description desc("Options");
52
53 desc.add_options()("help,h", "Show this help.");
54 desc.add_options()("prm-file,P",
55 po::value<string>(),
56 "Parameter file in json format. ");
57 desc.add_options()("prm,p",
58 po::value<vector<string>>()->multitoken(),
59 "Parameters specified as name=value pairs. "
60 "May be provided multiple times. Examples:\n"
61 " -p solver.tol=1e-3\n"
62 " -p precond.coarse_enough=300");
63 desc.add_options()("matrix,A",
64 po::value<string>()->required(),
65 "System matrix in the MatrixMarket format.");
66 desc.add_options()("rhs,f",
67 po::value<string>(),
68 "The RHS vector in the MatrixMarket format. "
69 "When omitted, a vector of ones is used by default. "
70 "Should only be provided together with a system matrix. ");
71 desc.add_options()("scale,s",
72 po::bool_switch()->default_value(false),
73 "Scale the matrix so that the diagonal is unit. ");
74 desc.add_options()("null,N",
75 po::value<string>(),
76 "Starting null-vectors in the MatrixMarket format. ");
77 desc.add_options()("numvec,n",
78 po::value<int>()->default_value(3),
79 "The number of near nullspace vectors to search for. ");
80 desc.add_options()("binary,B",
81 po::bool_switch()->default_value(false),
82 "When specified, treat input files as binary instead of as MatrixMarket. "
83 "It is assumed the files were converted to binary format with mm2bin utility. ");
84 desc.add_options()("output,o",
85 po::value<string>(),
86 "Output the computed nullspace to the MatrixMarket file.");
87
89 p.add("prm", -1);
90
92 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
93 po::notify(vm);
94
95 if (vm.count("help")) {
96 std::cout << desc << std::endl;
97 return 0;
98 }
99
100 for (int i = 0; i < argc; ++i) {
101 if (i)
102 std::cout << " ";
103 std::cout << argv[i];
104 }
105 std::cout << std::endl;
106
108 if (vm.count("prm-file")) {
109 prm.read_json(vm["prm-file"].as<string>());
110 }
111
112 if (vm.count("prm")) {
113 for (const string& v : vm["prm"].as<vector<string>>()) {
114 prm.putKeyValue(v);
115 }
116 }
117
118 ptrdiff_t rows, nv = 0, numvec = vm["numvec"].as<int>();
119 vector<ptrdiff_t> ptr, col;
120 vector<double> val, rhs;
121 std::list<std::vector<double>> Z;
122
123 {
124 auto t = prof.scoped_tic("read");
125
126 string Afile = vm["matrix"].as<string>();
127 bool binary = vm["binary"].as<bool>();
128
129 if (binary) {
130 io::read_crs(Afile, rows, ptr, col, val);
131 }
132 else {
133 ptrdiff_t cols;
134 std::tie(rows, cols) = io::mm_reader(Afile)(ptr, col, val);
135 precondition(rows == cols, "Non-square system matrix");
136 }
137
138 if (vm.count("rhs")) {
139 string bfile = vm["rhs"].as<string>();
140
141 ptrdiff_t n, m;
142
143 if (binary) {
144 io::read_dense(bfile, n, m, rhs);
145 }
146 else {
147 std::tie(n, m) = io::mm_reader(bfile)(rhs);
148 }
149
150 precondition(n == rows && m == 1, "The RHS vector has wrong size");
151 }
152 else {
153 rhs.resize(rows, 1.0);
154 }
155
156 if (vm.count("null")) {
157 string nfile = vm["null"].as<string>();
158
159 std::vector<double> null;
160 ptrdiff_t m;
161
162 if (binary) {
163 io::read_dense(nfile, m, nv, null);
164 }
165 else {
166 std::tie(m, nv) = io::mm_reader(nfile)(null);
167 }
168
169 precondition(m == rows, "Near null-space vectors have wrong size");
170
171 for (ptrdiff_t i = 0; i < nv; ++i) {
172 Z.emplace_back(rows);
173 for (ptrdiff_t j = 0; j < rows; ++j) {
174 Z.back()[j] = null[j * nv + i];
175 }
176 }
177 }
178 }
179
180 if (vm["scale"].as<bool>()) {
181 auto t = prof.scoped_tic("scaling");
182 std::vector<double> dia(rows, 1.0);
183
184 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
185 double d = 1.0;
186 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
187 if (col[j] == i) {
188 d = 1 / sqrt(val[j]);
189 }
190 }
191 if (!std::isnan(d))
192 dia[i] = d;
193 }
194
195 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
196 rhs[i] *= dia[i];
197 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
198 val[j] *= dia[i] * dia[col[j]];
199 }
200 }
201 }
202
203 using Backend = Alina::BuiltinBackend<double>;
206
207 std::mt19937 rng;
208 std::uniform_real_distribution<double> rnd(-1, 1);
209 std::vector<double> x(rows), zero(rows, 0.0);
210
211 auto A = std::tie(rows, ptr, col, val);
212
213 prm.put("solver.ns_search", true);
214
215 prof.tic("search");
216 for (int k = nv; k < numvec; ++k) {
217 auto t = prof.scoped_tic(std::string("vector ") + std::to_string(k));
218 std::vector<double> N;
219
220 if (k) {
221 N.resize(k * rows);
222 int j = 0;
223 for (const auto& z : Z) {
224 for (ptrdiff_t i = 0; i < rows; ++i) {
225 N[i * k + j] = z[i];
226 }
227 ++j;
228 }
229
230 prm.put("precond.coarsening.nullspace.rows", rows);
231 prm.put("precond.coarsening.nullspace.cols", k);
232 prm.put("precond.coarsening.nullspace.B", N.data());
233 }
234
235 prof.tic("setup");
236 Solver S(context, A, prm);
237 prof.toc("setup");
238
239 std::cout << std::endl
240 << "-------------------------" << std::endl
241 << "-- Searching for vector " << k << std::endl
242 << "-------------------------" << std::endl
243 << S << std::endl;
244
245 for (auto& v : x)
246 v = rnd(rng);
247
248 prof.tic("solve");
249 Alina::SolverResult r = S(zero, x);
250 prof.toc("solve");
251
252 std::cout << "Iterations: " << r.nbIteration() << std::endl
253 << "Error: " << r.residual() << std::endl;
254
255 // Orthonormalize the new vector
256 for (const auto& z : Z) {
257 double c = Alina::backend::inner_product(x, z) / Alina::backend::inner_product(z, z);
258 Alina::backend::axpby(-c, z, 1, x);
259 }
260
261 double nx = sqrt(Alina::backend::inner_product(x, x));
262 for (auto& v : x)
263 v /= nx;
264 Z.push_back(x);
265 }
266 prof.toc("search");
267
268 // Solve the system using the near nullspace vectors:
269 std::vector<double> N(numvec * rows);
270 {
271 auto t = prof.scoped_tic("apply");
272
273 int j = 0;
274 for (const auto& z : Z) {
275 for (ptrdiff_t i = 0; i < rows; ++i) {
276 N[i * numvec + j] = z[i];
277 }
278 ++j;
279 }
280
281 prm.put("precond.coarsening.nullspace.rows", rows);
282 prm.put("precond.coarsening.nullspace.cols", numvec);
283 prm.put("precond.coarsening.nullspace.B", N.data());
284
285 prof.tic("setup");
286 Solver S(context, A, prm);
287 prof.toc("setup");
288
289 std::cout << std::endl
290 << "-------------------------" << std::endl
291 << "-- Solving the system " << std::endl
292 << "-------------------------" << std::endl
293 << S << std::endl;
294
295 Alina::backend::clear(x);
296
297 prof.tic("solve");
298 Alina::SolverResult r = S(rhs, x);
299 prof.toc("solve");
300
301 std::cout << "Iterations: " << r.nbIteration() << std::endl
302 << "Error: " << r.residual() << std::endl;
303 }
304
305 if (vm.count("output")) {
306 auto t = prof.scoped_tic("write");
307 Alina::IO::mm_write(vm["output"].as<string>(), N.data(), rows, numvec);
308 }
309
310 std::cout << prof << std::endl;
311}
Convenience class that bundles together a preconditioner and an iterative solver.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Result of a solution.
Definition AlinaUtils.h:56
Fluent command-line parser builder.
Describes a set of command-line options.
Describes positional (non-option) arguments.
Stores parsed option values.
apfloat sqrt(apfloat v)
Square root of v.
Definition MathApfloat.h:69
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Runtime-configurable wrappers around iterative solvers.