Arcane  4.2.1.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 po::options_description desc("Options");
51
52 desc.add_options()("help,h", "Show this help.")("prm-file,P",
53 po::value<string>(),
54 "Parameter file in json format. ")(
55 "prm,p",
56 po::value<vector<string>>()->multitoken(),
57 "Parameters specified as name=value pairs. "
58 "May be provided multiple times. Examples:\n"
59 " -p solver.tol=1e-3\n"
60 " -p precond.coarse_enough=300")("matrix,A",
61 po::value<string>()->required(),
62 "System matrix in the MatrixMarket format.")(
63 "rhs,f",
64 po::value<string>(),
65 "The RHS vector in the MatrixMarket format. "
66 "When omitted, a vector of ones is used by default. "
67 "Should only be provided together with a system matrix. ")(
68 "scale,s",
69 po::bool_switch()->default_value(false),
70 "Scale the matrix so that the diagonal is unit. ")(
71 "null,N",
72 po::value<string>(),
73 "Starting null-vectors in the MatrixMarket format. ")(
74 "numvec,n",
75 po::value<int>()->default_value(3),
76 "The number of near nullspace vectors to search for. ")(
77 "binary,B",
78 po::bool_switch()->default_value(false),
79 "When specified, treat input files as binary instead of as MatrixMarket. "
80 "It is assumed the files were converted to binary format with mm2bin utility. ")(
81 "output,o",
82 po::value<string>(),
83 "Output the computed nullspace to the MatrixMarket file.");
84
86 p.add("prm", -1);
87
89 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
90 po::notify(vm);
91
92 if (vm.count("help")) {
93 std::cout << desc << std::endl;
94 return 0;
95 }
96
97 for (int i = 0; i < argc; ++i) {
98 if (i)
99 std::cout << " ";
100 std::cout << argv[i];
101 }
102 std::cout << std::endl;
103
105 if (vm.count("prm-file")) {
106 prm.read_json(vm["prm-file"].as<string>());
107 }
108
109 if (vm.count("prm")) {
110 for (const string& v : vm["prm"].as<vector<string>>()) {
111 prm.putKeyValue(v);
112 }
113 }
114
115 ptrdiff_t rows, nv = 0, numvec = vm["numvec"].as<int>();
116 vector<ptrdiff_t> ptr, col;
117 vector<double> val, rhs;
118 std::list<std::vector<double>> Z;
119
120 {
121 auto t = prof.scoped_tic("read");
122
123 string Afile = vm["matrix"].as<string>();
124 bool binary = vm["binary"].as<bool>();
125
126 if (binary) {
127 io::read_crs(Afile, rows, ptr, col, val);
128 }
129 else {
130 ptrdiff_t cols;
131 std::tie(rows, cols) = io::mm_reader(Afile)(ptr, col, val);
132 precondition(rows == cols, "Non-square system matrix");
133 }
134
135 if (vm.count("rhs")) {
136 string bfile = vm["rhs"].as<string>();
137
138 ptrdiff_t n, m;
139
140 if (binary) {
141 io::read_dense(bfile, n, m, rhs);
142 }
143 else {
144 std::tie(n, m) = io::mm_reader(bfile)(rhs);
145 }
146
147 precondition(n == rows && m == 1, "The RHS vector has wrong size");
148 }
149 else {
150 rhs.resize(rows, 1.0);
151 }
152
153 if (vm.count("null")) {
154 string nfile = vm["null"].as<string>();
155
156 std::vector<double> null;
157 ptrdiff_t m;
158
159 if (binary) {
160 io::read_dense(nfile, m, nv, null);
161 }
162 else {
163 std::tie(m, nv) = io::mm_reader(nfile)(null);
164 }
165
166 precondition(m == rows, "Near null-space vectors have wrong size");
167
168 for (ptrdiff_t i = 0; i < nv; ++i) {
169 Z.emplace_back(rows);
170 for (ptrdiff_t j = 0; j < rows; ++j) {
171 Z.back()[j] = null[j * nv + i];
172 }
173 }
174 }
175 }
176
177 if (vm["scale"].as<bool>()) {
178 auto t = prof.scoped_tic("scaling");
179 std::vector<double> dia(rows, 1.0);
180
181 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
182 double d = 1.0;
183 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
184 if (col[j] == i) {
185 d = 1 / sqrt(val[j]);
186 }
187 }
188 if (!std::isnan(d))
189 dia[i] = d;
190 }
191
192 for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(rows); ++i) {
193 rhs[i] *= dia[i];
194 for (ptrdiff_t j = ptr[i], e = ptr[i + 1]; j < e; ++j) {
195 val[j] *= dia[i] * dia[col[j]];
196 }
197 }
198 }
199
200 using Backend = Alina::BuiltinBackend<double>;
203
204 std::mt19937 rng;
205 std::uniform_real_distribution<double> rnd(-1, 1);
206 std::vector<double> x(rows), zero(rows, 0.0);
207
208 auto A = std::tie(rows, ptr, col, val);
209
210 prm.put("solver.ns_search", true);
211
212 prof.tic("search");
213 for (int k = nv; k < numvec; ++k) {
214 auto t = prof.scoped_tic(std::string("vector ") + std::to_string(k));
215 std::vector<double> N;
216
217 if (k) {
218 N.resize(k * rows);
219 int j = 0;
220 for (const auto& z : Z) {
221 for (ptrdiff_t i = 0; i < rows; ++i) {
222 N[i * k + j] = z[i];
223 }
224 ++j;
225 }
226
227 prm.put("precond.coarsening.nullspace.rows", rows);
228 prm.put("precond.coarsening.nullspace.cols", k);
229 prm.put("precond.coarsening.nullspace.B", N.data());
230 }
231
232 prof.tic("setup");
233 Solver S(A, prm);
234 prof.toc("setup");
235
236 std::cout << std::endl
237 << "-------------------------" << std::endl
238 << "-- Searching for vector " << k << std::endl
239 << "-------------------------" << std::endl
240 << S << std::endl;
241
242 for (auto& v : x)
243 v = rnd(rng);
244
245 prof.tic("solve");
246 Alina::SolverResult r = S(zero, x);
247 prof.toc("solve");
248
249 std::cout << "Iterations: " << r.nbIteration() << std::endl
250 << "Error: " << r.residual() << std::endl;
251
252 // Orthonormalize the new vector
253 for (const auto& z : Z) {
254 double c = Alina::backend::inner_product(x, z) / Alina::backend::inner_product(z, z);
255 Alina::backend::axpby(-c, z, 1, x);
256 }
257
258 double nx = sqrt(Alina::backend::inner_product(x, x));
259 for (auto& v : x)
260 v /= nx;
261 Z.push_back(x);
262 }
263 prof.toc("search");
264
265 // Solve the system using the near nullspace vectors:
266 std::vector<double> N(numvec * rows);
267 {
268 auto t = prof.scoped_tic("apply");
269
270 int j = 0;
271 for (const auto& z : Z) {
272 for (ptrdiff_t i = 0; i < rows; ++i) {
273 N[i * numvec + j] = z[i];
274 }
275 ++j;
276 }
277
278 prm.put("precond.coarsening.nullspace.rows", rows);
279 prm.put("precond.coarsening.nullspace.cols", numvec);
280 prm.put("precond.coarsening.nullspace.B", N.data());
281
282 prof.tic("setup");
283 Solver S(A, prm);
284 prof.toc("setup");
285
286 std::cout << std::endl
287 << "-------------------------" << std::endl
288 << "-- Solving the system " << std::endl
289 << "-------------------------" << std::endl
290 << S << std::endl;
291
292 Alina::backend::clear(x);
293
294 prof.tic("solve");
295 Alina::SolverResult r = S(rhs, x);
296 prof.toc("solve");
297
298 std::cout << "Iterations: " << r.nbIteration() << std::endl
299 << "Error: " << r.residual() << std::endl;
300 }
301
302 if (vm.count("output")) {
303 auto t = prof.scoped_tic("write");
304 Alina::IO::mm_write(vm["output"].as<string>(), N.data(), rows, numvec);
305 }
306
307 std::cout << prof << std::endl;
308}
Convenience class that bundles together a preconditioner and an iterative solver.
Result of a solution.
Definition AlinaUtils.h:53
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.