deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
matrix_scaling.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2001 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
15
24#include <deal.II/lac/vector.h>
25
26#include <boost/serialization/utility.hpp>
27
29
30namespace PETScWrappers
31{
32 namespace MPI
33 {
34 class SparseMatrix;
35 class Vector;
36 } // namespace MPI
37} // namespace PETScWrappers
38
39#include "lac/matrix_scaling.inst"
40
41// Check if the matrix type is sequential
42template <typename Matrix>
43constexpr bool
45{
46 return std::is_same_v<Matrix, ::SparseMatrix<double>> ||
47 std::is_same_v<Matrix, ::SparseMatrix<float>> ||
48 std::is_same_v<Matrix, ::FullMatrix<double>> ||
49 std::is_same_v<Matrix, ::FullMatrix<float>> ||
50 std::is_same_v<Matrix, ::BlockSparseMatrix<double>> ||
51 std::is_same_v<Matrix, ::BlockSparseMatrix<float>> ||
52 std::is_same_v<Matrix, ::SparseMatrixEZ<double>> ||
53 std::is_same_v<Matrix, ::SparseMatrixEZ<float>>;
54}
55
56
57
59 const NormType norm_type,
60 const unsigned int max_iterations)
61 : max_iterations(max_iterations)
62 , norm_type(norm_type)
63{}
64
65
66
68 const unsigned int start_inf_norm_steps,
69 const unsigned int l1_norm_steps,
70 const unsigned int end_inf_norm_steps)
71 : start_inf_norm_steps(start_inf_norm_steps)
72 , l1_norm_steps(l1_norm_steps)
73 , end_inf_norm_steps(end_inf_norm_steps)
74{}
75
76
77
79 const double scaling_tolerance,
80 const ScalingAlgorithm alg,
81 const SKParameters sk_params,
82 const l1linfParameters l1linf_params)
84 , algorithm(alg)
85 , sinkhorn_knopp_parameters(sk_params)
86 , l1linf_parameters(l1linf_params)
87{}
88
89
90
96
97
98
99template <class Matrix>
100bool
102{
103 bool converged = false;
104
105 if constexpr (is_sequential_matrix<Matrix>())
106 {
107 const auto n_rows = matrix.m();
108 const auto n_cols = matrix.n();
109
110 row_scaling.reinit(n_rows);
111 column_scaling.reinit(n_cols);
112
113 row_scaling = 1.0;
114 column_scaling = 1.0;
115
116 switch (control.algorithm)
117 {
119 {
120 converged =
121 do_sk_scaling(matrix,
123
124 break;
125 }
126
129 {
131 !converged)
132 converged = do_linfty_scaling(
134 if (control.l1linf_parameters.l1_norm_steps > 0 && !converged)
135 converged =
136 do_l1_scaling(matrix,
139 !converged)
140 converged = do_linfty_scaling(
142
143 break;
144 }
145
146 default:
148 }
149 }
150 else if constexpr (
151#ifdef DEAL_II_WITH_TRILINOS
152 std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix> ||
153#endif
154#ifdef DEAL_II_WITH_PETSC
155 std::is_same_v<Matrix, PETScWrappers::MPI::SparseMatrix> ||
156#endif
157 false)
158 {
159 locally_owned_rows = matrix.locally_owned_range_indices();
160
161 // If the matrix is square then use the same partitioning for
162 // columns and rows to easily scale a linear system, otherwise create a
163 // new balanced partitioning for the columns. We do this because
164 // distributed matrices do not really distribute columns, but only rows:
165 // we want to avoid saving the whole column scaling on each MPI rank
166 if (matrix.n() == matrix.m())
168 else
171 matrix.get_mpi_communicator(), matrix.n());
172
174 ghost_columns.set_size(matrix.n());
175
178
179 row_scaling = 1.0;
180 column_scaling = 1.0;
181
182 // Identify ghost columns
183 for (const auto row : locally_owned_rows)
184 {
185 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
186 {
187 if (!locally_owned_cols.is_element(it->column()))
188 ghost_columns.add_index(it->column());
189 }
190 }
193 matrix.get_mpi_communicator());
194
198 matrix.get_mpi_communicator());
199
200 switch (control.algorithm)
201 {
203 {
204 converged =
205 do_sk_scaling(matrix,
207
208 break;
209 }
210
213 {
215 !converged)
216 converged = do_linfty_scaling(
218 if (control.l1linf_parameters.l1_norm_steps > 0 && !converged)
219 converged =
220 do_l1_scaling(matrix,
223 !converged)
224 converged = do_linfty_scaling(
226
227 break;
228 }
229
230 default:
232 }
233 }
234 else
235 {
236 (void)matrix;
237 Assert(false, ExcNotImplemented());
238 }
239
240 return converged;
241}
242
243
244
245template <class Matrix, class VectorType>
246bool
248 VectorType &rhs)
249{
250 AssertDimension(matrix.m(), rhs.size());
251 AssertDimension(matrix.m(), matrix.n());
252
253 bool converged = false;
254
255 converged = find_scaling_and_scale_matrix(matrix);
256
257 if constexpr (std::is_same_v<VectorType, ::Vector<double>> ||
258 std::is_same_v<VectorType, ::Vector<float>>)
259 rhs.scale(row_scaling);
260 else if constexpr (std::is_same_v<VectorType, ::BlockVector<double>> ||
261 std::is_same_v<VectorType, ::BlockVector<float>>)
262 {
263 for (unsigned int i = 0; i < rhs.size(); ++i)
264 rhs[i] *= row_scaling[i];
265 }
266 else if constexpr (
267#ifdef DEAL_II_WITH_TRILINOS
268 std::is_same_v<VectorType, TrilinosWrappers::MPI::Vector> ||
269#endif
270#ifdef DEAL_II_WITH_PETSC
271 std::is_same_v<VectorType, PETScWrappers::MPI::Vector> ||
272#endif
273 false)
274 {
275 Assert(matrix.locally_owned_range_indices() ==
276 rhs.locally_owned_elements(),
277 ExcMessage("Matrix and vector must have the same partitioning"));
278 using Number = typename VectorType::value_type;
279 std::vector<Number> local_updates(row_scaling.size());
280 for (auto i : locally_owned_rows)
281 {
282 auto local_idx = partitioner.global_to_local(i);
283 local_updates[local_idx] = Number(rhs[i]) * row_scaling[local_idx];
284 }
285 rhs.set(locally_owned_rows.get_index_vector(), local_updates);
287 }
288 else
289 Assert(false, ExcNotImplemented());
290
291 return converged;
292}
293
294
295
296template <class VectorType>
297void
299{
300 if constexpr (std::is_same_v<VectorType, ::Vector<double>> ||
301 std::is_same_v<VectorType, ::Vector<float>>)
302 {
303 AssertDimension(sol.size(), column_scaling.size());
304 sol.scale(column_scaling);
305 }
306 else if constexpr (std::is_same_v<VectorType, ::BlockVector<double>> ||
307 std::is_same_v<VectorType, ::BlockVector<float>>)
308 {
309 AssertDimension(sol.size(), column_scaling.size());
310 for (unsigned int i = 0; i < sol.size(); ++i)
311 sol[i] *= column_scaling[i];
312 }
313 else if constexpr (
314#ifdef DEAL_II_WITH_TRILINOS
315 std::is_same_v<VectorType, TrilinosWrappers::MPI::Vector> ||
316#endif
317#ifdef DEAL_II_WITH_PETSC
318 std::is_same_v<VectorType, PETScWrappers::MPI::Vector> ||
319#endif
320 false)
321 {
322 Assert(locally_owned_cols == sol.locally_owned_elements(),
323 ExcMessage("Matrix and vector must have the same partitioning"));
324 using Number = typename VectorType::value_type;
325 std::vector<Number> local_updates(column_scaling.size());
326 for (auto i : locally_owned_cols)
327 {
328 auto local_idx = partitioner.global_to_local(i);
329 local_updates[local_idx] = Number(sol[i]) * column_scaling[local_idx];
330 }
331 sol.set(locally_owned_cols.get_index_vector(), local_updates);
333 }
334 else
335 Assert(false, ExcNotImplemented());
336}
337
338
339
340const Vector<double> &
342{
343 return row_scaling;
344}
345
346
347
348const Vector<double> &
353
354
355
356template <typename Number>
357bool
359 const Vector<Number> &row_col_norm,
360 const MatrixScaling::ConvergenceNormType &norm_type) const
361{
362 Number convergence_norm = 0;
363 switch (norm_type)
364 {
366 {
367 for (const auto &val : row_col_norm)
368 convergence_norm += std::abs(val - Number(1.0));
369
370 return convergence_norm < control.scaling_tolerance;
371 }
372
374 {
375 for (const auto &val : row_col_norm)
376 convergence_norm =
377 std::max(convergence_norm, std::abs(val - Number(1.0)));
378
379 return convergence_norm < control.scaling_tolerance;
380 }
381
382 default:
383 {
385 return false;
386 }
387 }
388}
389
390
391
392template <typename Number>
393bool
395 const Vector<Number> &row_norm,
396 const Vector<Number> &col_norm,
397 const MatrixScaling::ConvergenceNormType &norm_type) const
398{
399 Number convergence_row_norm = 0;
400 Number convergence_col_norm = 0;
401 switch (norm_type)
402 {
404 {
405 for (const auto &val : row_norm)
406 convergence_row_norm += std::abs(val - Number(1.0));
407 for (const auto &val : col_norm)
408 convergence_col_norm += std::abs(val - Number(1.0));
409
410 return (convergence_row_norm < control.scaling_tolerance &&
411 convergence_col_norm < control.scaling_tolerance);
412 }
413
415 {
416 for (const auto &val : row_norm)
417 convergence_row_norm =
418 std::max(convergence_row_norm, std::abs(val - Number(1.0)));
419 for (const auto &val : col_norm)
420 convergence_col_norm =
421 std::max(convergence_col_norm, std::abs(val - Number(1.0)));
422
423 return (convergence_row_norm < control.scaling_tolerance &&
424 convergence_col_norm < control.scaling_tolerance);
425 }
426
427 default:
428 {
430 return false;
431 }
432 }
433}
434
435
436
437bool
439 const Vector<double> &local_row_col_norm,
440 const MatrixScaling::ConvergenceNormType &norm_type,
441 const MPI_Comm mpi_communicator) const
442{
443 double convergence_norm = 0;
444 bool local_not_converged;
445
446 switch (norm_type)
447 {
449 {
450 for (const auto &val : local_row_col_norm)
451 convergence_norm += std::abs(val - 1.0);
452
453 local_not_converged = !(convergence_norm < control.scaling_tolerance);
454 break;
455 }
456
458 {
459 for (const auto &val : local_row_col_norm)
460 convergence_norm = std::max(convergence_norm, std::abs(val - 1.0));
461
462 local_not_converged = !(convergence_norm < control.scaling_tolerance);
463 break;
464 }
465
466 default:
467 {
469 return false;
470 }
471 }
472
473 const bool any_not_converged =
474 Utilities::MPI::logical_or(local_not_converged, mpi_communicator);
475
476 return !any_not_converged;
477}
478
479
480
481bool
483 const Vector<double> &local_row_norm,
484 const Vector<double> &local_col_norm,
485 const MatrixScaling::ConvergenceNormType &norm_type,
486 const MPI_Comm mpi_communicator) const
487{
488 double convergence_row_norm = 0;
489 double convergence_col_norm = 0;
490 bool local_not_converged;
491
492 switch (norm_type)
493 {
495 {
496 for (const auto &val : local_row_norm)
497 convergence_row_norm += std::abs(val - 1.0);
498 for (const auto &val : local_col_norm)
499 convergence_col_norm += std::abs(val - 1.0);
500
501 local_not_converged =
502 !(convergence_row_norm < control.scaling_tolerance &&
503 convergence_col_norm < control.scaling_tolerance);
504 break;
505 }
506
508 {
509 for (const auto &val : local_row_norm)
510 convergence_row_norm =
511 std::max(convergence_row_norm, std::abs(val - 1.0));
512 for (const auto &val : local_col_norm)
513 convergence_col_norm =
514 std::max(convergence_col_norm, std::abs(val - 1.0));
515
516 local_not_converged =
517 !(convergence_row_norm < control.scaling_tolerance &&
518 convergence_col_norm < control.scaling_tolerance);
519 break;
520 }
521
522 default:
523 {
525 return false;
526 }
527 }
528
529 const bool any_not_converged =
530 Utilities::MPI::logical_or(local_not_converged, mpi_communicator);
531
532 return !any_not_converged;
533}
534
535
536
537void
539 const std::map<types::global_dof_index, double> &partial_column_norms,
540 std::map<unsigned int,
541 std::vector<std::pair<types::global_dof_index, double>>> &send_data,
542 Vector<double> &local_col_norms)
543{
544 for (const auto &[col_idx, norm_value] : partial_column_norms)
545 {
546 if (locally_owned_cols.is_element(col_idx))
547 {
548 unsigned int local_idx = partitioner.global_to_local(col_idx);
549 local_col_norms[local_idx] = norm_value;
550 }
551 else
552 {
553 auto ghost_pos = ghost_columns.index_within_set(col_idx);
554 unsigned int target_rank = ghost_column_owners[ghost_pos];
555
556 send_data[target_rank].emplace_back(col_idx, norm_value);
557 }
558 }
559}
560
561
562
563void
565 const std::map<unsigned int,
566 std::vector<std::pair<types::global_dof_index, double>>>
567 &received_data,
568 const Vector<double> &local_col_norms,
569 std::map<unsigned int,
570 std::vector<std::pair<types::global_dof_index, double>>>
571 &send_column_norms)
572{
573 // For each rank that requested column norm data from me,
574 // send them the corresponding column norms updated
575 for (const auto &[sender_rank, pairs] : received_data)
576 {
577 for (const auto &[global_col, norm_contribution] : pairs)
578 {
579 // I received norm data for global_col, so sender_rank needs
580 // my scaling value for global_col
581 if (locally_owned_cols.is_element(global_col))
582 {
583 unsigned int local_idx = partitioner.global_to_local(global_col);
584 send_column_norms[sender_rank].emplace_back(
585 global_col, local_col_norms[local_idx]);
586 }
587 }
588 }
589}
590
591
592
593template <class Matrix>
594bool
595MatrixScaling::do_l1_scaling(Matrix &matrix, const unsigned int nsteps)
596{
597 if constexpr (is_sequential_matrix<Matrix>())
598 {
599 using Number = typename Matrix::value_type;
600
601 Vector<Number> row_norms(matrix.m());
602 Vector<Number> col_norms(matrix.n());
603
604 for (unsigned int i = 0; i < nsteps; i++)
605 {
606 row_norms = 0;
607 col_norms = 0;
608
609 for (unsigned int row = 0; row < matrix.m(); ++row)
610 {
611 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
612 {
613 row_norms[row] += std::abs(it->value());
614 col_norms[it->column()] += std::abs(it->value());
615 }
616 }
617
618 if (check_convergence(row_norms,
619 col_norms,
621 return true;
622
623 for (unsigned int row = 0; row < matrix.m(); ++row)
624 {
625 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
626 {
627 matrix.set(row,
628 it->column(),
629 it->value() / std::sqrt(row_norms[row] *
630 col_norms[it->column()]));
631 }
632 row_scaling[row] /= std::sqrt(row_norms[row]);
633 }
634 for (unsigned int col = 0; col < matrix.n(); ++col)
635 {
636 column_scaling[col] /= std::sqrt(col_norms[col]);
637 }
638 }
639 }
640 else if constexpr (
641#ifdef DEAL_II_WITH_TRILINOS
642 std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix> ||
643#endif
644#ifdef DEAL_II_WITH_PETSC
645 std::is_same_v<Matrix, PETScWrappers::MPI::SparseMatrix> ||
646#endif
647 false)
648 {
649 using Number = typename Matrix::value_type;
650
653 std::map<types::global_dof_index, double>
654 partial_column_norms; // column -> local norm
655
656 for (unsigned int i = 0; i < nsteps; i++)
657 {
658 local_row_norms = 0;
659 local_col_norms = 0;
660 partial_column_norms.clear();
661
662 for (const auto row : locally_owned_rows)
663 {
664 auto local_row_idx = partitioner.global_to_local(row);
665 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
666 {
667 local_row_norms[local_row_idx] += std::abs(it->value());
668 partial_column_norms[it->column()] += std::abs(it->value());
669 }
670 }
671 // Communicate partial column norms
672 std::map<unsigned int,
673 std::vector<std::pair<types::global_dof_index, double>>>
674 send_data;
675
676 send_prepare_col_norms(partial_column_norms,
677 send_data,
678 local_col_norms);
679
680 auto received_data =
681 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
682 send_data);
683
684 // Process received data and fill local_col_norms
685 for (const auto &[sender_rank, pairs] : received_data)
686 {
687 for (const auto &[global_col, contribution] : pairs)
688 {
689 unsigned int local_idx =
690 partitioner.global_to_local(global_col);
691 local_col_norms[local_idx] += contribution;
692 }
693 }
694
695 if (check_convergence(local_row_norms,
696 local_col_norms,
698 matrix.get_mpi_communicator()))
699 return true;
700
701
702 for (unsigned int i = 0; i < local_row_norms.size(); ++i)
703 row_scaling[i] /= std::sqrt(local_row_norms[i]);
704 for (unsigned int i = 0; i < local_col_norms.size(); ++i)
705 column_scaling[i] /= std::sqrt(local_col_norms[i]);
706
707 // Communicate column norm values to all ranks that need them
708 std::map<unsigned int,
709 std::vector<std::pair<types::global_dof_index, double>>>
710 send_column_norms;
711
712 send_prepare_updated_col_norms(received_data,
713 local_col_norms,
714 send_column_norms);
715
716 auto received_column_norms =
717 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
718 send_column_norms);
719
720 std::map<types::global_dof_index, double> ghost_column_norms_lookup;
721 for (const auto &[sender_rank, pairs] : received_column_norms)
722 {
723 for (const auto &[col_id, norm_val] : pairs)
724 {
725 ghost_column_norms_lookup[col_id] = norm_val;
726 }
727 }
728
729 // scale the matrix
730 for (const auto row : locally_owned_rows)
731 {
732 auto local_row_idx = partitioner.global_to_local(row);
733 auto row_size = matrix.row_length(row);
734 std::vector<Number> values(row_size);
735 std::vector<types::global_dof_index> columns(row_size);
736 unsigned int idx = 0;
737 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
738 {
739 if (locally_owned_cols.is_element(it->column()))
740 {
741 unsigned int local_col_idx =
742 partitioner.global_to_local(it->column());
743 columns[idx] = it->column();
744 values[idx] =
745 it->value() / std::sqrt(local_col_norms[local_col_idx] *
746 local_row_norms[local_row_idx]);
747 ++idx;
748 }
749 else
750 {
751 double col_norms =
752 ghost_column_norms_lookup[it->column()];
753 columns[idx] = it->column();
754 values[idx] =
755 it->value() /
756 std::sqrt(col_norms * local_row_norms[local_row_idx]);
757 ++idx;
758 }
759 }
760 matrix.set(row, columns, values);
761 if constexpr (std::is_same_v<Matrix,
763 matrix.compress(VectorOperation::insert);
764 }
765#ifdef DEAL_II_WITH_TRILINOS
766 if constexpr (std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix>)
767 matrix.compress(VectorOperation::insert);
768#endif
769 }
770 }
771 return false;
772}
773
774
775
776template <class Matrix>
777bool
778MatrixScaling::do_linfty_scaling(Matrix &matrix, const unsigned int nsteps)
779{
780 if constexpr (is_sequential_matrix<Matrix>())
781 {
782 using Number = typename Matrix::value_type;
783
784 Vector<Number> row_norms(matrix.m());
785 Vector<Number> col_norms(matrix.n());
786
787 for (unsigned int i = 0; i < nsteps; i++)
788 {
789 row_norms = 0;
790 col_norms = 0;
791
792 for (unsigned int row = 0; row < matrix.m(); ++row)
793 {
794 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
795 {
796 row_norms[row] =
797 std::max(row_norms[row], std::abs(it->value()));
798 col_norms[it->column()] =
799 std::max(col_norms[it->column()], std::abs(it->value()));
800 }
801 }
802
803 if (check_convergence(row_norms,
804 col_norms,
806 return true;
807
808
809 for (unsigned int row = 0; row < matrix.m(); ++row)
810 {
811 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
812 {
813 matrix.set(row,
814 it->column(),
815 it->value() / std::sqrt(row_norms[row] *
816 col_norms[it->column()]));
817 }
818 row_scaling[row] /= std::sqrt(row_norms[row]);
819 }
820 for (unsigned int col = 0; col < matrix.n(); ++col)
821 {
822 column_scaling[col] /= std::sqrt(col_norms[col]);
823 }
824 }
825 }
826 else if constexpr (
827#ifdef DEAL_II_WITH_TRILINOS
828 std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix> ||
829#endif
830#ifdef DEAL_II_WITH_PETSC
831 std::is_same_v<Matrix, PETScWrappers::MPI::SparseMatrix> ||
832#endif
833 false)
834 {
835 using Number = typename Matrix::value_type;
836
839 std::map<types::global_dof_index, double>
840 partial_column_norms; // column -> local norm
841
842 for (unsigned int i = 0; i < nsteps; i++)
843 {
844 local_row_norms = 0;
845 local_col_norms = 0;
846 partial_column_norms.clear();
847
848 for (const auto row : locally_owned_rows)
849 {
850 auto local_row_idx = partitioner.global_to_local(row);
851 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
852 {
853 local_row_norms[local_row_idx] =
854 std::max(local_row_norms[local_row_idx],
855 std::abs(it->value()));
856 partial_column_norms[it->column()] =
857 std::max(partial_column_norms[it->column()],
858 std::abs(it->value()));
859 }
860 }
861 // Communicate partial column norms
862 std::map<unsigned int,
863 std::vector<std::pair<types::global_dof_index, double>>>
864 send_data;
865
866 send_prepare_col_norms(partial_column_norms,
867 send_data,
868 local_col_norms);
869
870 auto received_data =
871 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
872 send_data);
873
874 // Process received data and fill local_col_norms
875 for (const auto &[sender_rank, pairs] : received_data)
876 {
877 for (const auto &[global_col, contribution] : pairs)
878 {
879 unsigned int local_idx =
880 partitioner.global_to_local(global_col);
881 local_col_norms[local_idx] =
882 std::max(local_col_norms[local_idx], contribution);
883 }
884 }
885
886 if (check_convergence(local_row_norms,
887 local_col_norms,
889 matrix.get_mpi_communicator()))
890 return true;
891
892 for (unsigned int i = 0; i < local_row_norms.size(); ++i)
893 row_scaling[i] /= std::sqrt(local_row_norms[i]);
894 for (unsigned int i = 0; i < local_col_norms.size(); ++i)
895 column_scaling[i] /= std::sqrt(local_col_norms[i]);
896
897 // Communicate column norm values to all ranks that need them
898 std::map<unsigned int,
899 std::vector<std::pair<types::global_dof_index, double>>>
900 send_column_norms;
901
902 send_prepare_updated_col_norms(received_data,
903 local_col_norms,
904 send_column_norms);
905
906 auto received_column_norms =
907 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
908 send_column_norms);
909
910 std::map<types::global_dof_index, double> ghost_column_norms_lookup;
911 for (const auto &[sender_rank, pairs] : received_column_norms)
912 {
913 for (const auto &[col_id, norm_val] : pairs)
914 {
915 ghost_column_norms_lookup[col_id] = norm_val;
916 }
917 }
918
919 // scale the matrix
920 for (const auto row : locally_owned_rows)
921 {
922 auto local_row_idx = partitioner.global_to_local(row);
923 auto row_size = matrix.row_length(row);
924 std::vector<Number> values(row_size);
925 std::vector<types::global_dof_index> columns(row_size);
926 unsigned int idx = 0;
927 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
928 {
929 if (locally_owned_cols.is_element(it->column()))
930 {
931 unsigned int local_col_idx =
932 partitioner.global_to_local(it->column());
933 columns[idx] = it->column();
934 values[idx] =
935 it->value() / std::sqrt(local_col_norms[local_col_idx] *
936 local_row_norms[local_row_idx]);
937 ++idx;
938 }
939 else
940 {
941 double col_norms =
942 ghost_column_norms_lookup[it->column()];
943 columns[idx] = it->column();
944 values[idx] =
945 it->value() /
946 std::sqrt(col_norms * local_row_norms[local_row_idx]);
947 ++idx;
948 }
949 }
950 matrix.set(row, columns, values);
951 if constexpr (std::is_same_v<Matrix,
953 matrix.compress(VectorOperation::insert);
954 }
955#ifdef DEAL_II_WITH_TRILINOS
956 if constexpr (std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix>)
957 matrix.compress(VectorOperation::insert);
958#endif
959 }
960 }
961 return false;
962}
963
964
965
966template <class Matrix>
967bool
968MatrixScaling::do_sk_scaling(Matrix &matrix, const unsigned int nsteps)
969{
970 if constexpr (is_sequential_matrix<Matrix>())
971 {
972 using Number = typename Matrix::value_type;
973
974 Vector<Number> row_norms(matrix.m());
975 Vector<Number> col_norms(matrix.n());
976
978 {
980 {
981 // Row_norms_0 to start the procedure
982 row_norms = 0;
983 for (unsigned int row = 0; row < matrix.m(); ++row)
984 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
985 row_norms[row] += std::abs(it->value());
986
987 for (unsigned int i = 0; i < nsteps; i++)
988 {
989 // Row step
990 col_norms = 0;
991 for (unsigned int row = 0; row < matrix.m(); ++row)
992 {
993 for (auto it = matrix.begin(row); it != matrix.end(row);
994 ++it)
995 {
996 matrix.set(row,
997 it->column(),
998 it->value() / row_norms[row]);
999 col_norms[it->column()] += std::abs(it->value());
1000 }
1001 row_scaling[row] /= row_norms[row];
1002 }
1003
1004 if (check_convergence(col_norms,
1006 return true;
1007
1008 // Column step
1009 row_norms = 0;
1010 for (unsigned int row = 0; row < matrix.m(); ++row)
1011 {
1012 for (auto it = matrix.begin(row); it != matrix.end(row);
1013 ++it)
1014 {
1015 matrix.set(row,
1016 it->column(),
1017 it->value() / col_norms[it->column()]);
1018 row_norms[row] += std::abs(it->value());
1019 }
1020 }
1021 for (unsigned int col = 0; col < matrix.n(); ++col)
1022 {
1023 column_scaling[col] /= col_norms[col];
1024 }
1025
1026 if (check_convergence(row_norms,
1028 return true;
1029 }
1030 break;
1031 }
1032
1034 {
1035 // Row_norms_0 to start the procedure
1036 row_norms = 0;
1037 for (unsigned int row = 0; row < matrix.m(); ++row)
1038 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
1039 row_norms[row] +=
1040 std::max(row_norms[row], std::abs(it->value()));
1041
1042 for (unsigned int i = 0; i < nsteps; i++)
1043 {
1044 // Row step
1045 col_norms = 0;
1046 for (unsigned int row = 0; row < matrix.m(); ++row)
1047 {
1048 for (auto it = matrix.begin(row); it != matrix.end(row);
1049 ++it)
1050 {
1051 matrix.set(row,
1052 it->column(),
1053 it->value() / row_norms[row]);
1054 col_norms[it->column()] =
1055 std::max(col_norms[it->column()],
1056 std::abs(it->value()));
1057 }
1058 row_scaling[row] /= row_norms[row];
1059 }
1060
1063 return true;
1064
1065 // Column step
1066 row_norms = 0;
1067 for (unsigned int row = 0; row < matrix.m(); ++row)
1068 {
1069 for (auto it = matrix.begin(row); it != matrix.end(row);
1070 ++it)
1071 {
1072 matrix.set(row,
1073 it->column(),
1074 it->value() / col_norms[it->column()]);
1075 row_norms[row] +=
1076 std::max(row_norms[row], std::abs(it->value()));
1077 }
1078 }
1079 for (unsigned int col = 0; col < matrix.n(); ++col)
1080 {
1081 column_scaling[col] /= col_norms[col];
1082 }
1083
1086 return true;
1087 }
1088 break;
1089 }
1090
1091 default:
1093 }
1094 }
1095 else if constexpr (
1096#ifdef DEAL_II_WITH_TRILINOS
1097 std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix> ||
1098#endif
1099#ifdef DEAL_II_WITH_PETSC
1100 std::is_same_v<Matrix, PETScWrappers::MPI::SparseMatrix> ||
1101#endif
1102 false)
1103 {
1104 using Number = typename Matrix::value_type;
1105
1106 Vector<double> local_row_norms(locally_owned_rows.n_elements());
1107 Vector<double> local_col_norms(locally_owned_cols.n_elements());
1108 std::map<types::global_dof_index, double>
1109 partial_column_norms; // column -> local norm
1110
1112 {
1114 {
1115 // Row_norms_0 to start the procedure
1116 local_row_norms = 0;
1117 for (const auto row : locally_owned_rows)
1118 {
1119 auto local_row_idx = partitioner.global_to_local(row);
1120 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
1121 local_row_norms[local_row_idx] += std::abs(it->value());
1122 }
1123
1124 for (unsigned int i = 0; i < nsteps; i++)
1125 {
1126 // Row step
1127 local_col_norms = 0;
1128 partial_column_norms.clear();
1129
1130 for (const auto row : locally_owned_rows)
1131 {
1132 auto local_row_idx = partitioner.global_to_local(row);
1133 auto row_size = matrix.row_length(row);
1134 std::vector<Number> values(row_size);
1135 std::vector<types::global_dof_index> columns(row_size);
1136 unsigned int idx = 0;
1137 for (auto it = matrix.begin(row); it != matrix.end(row);
1138 ++it)
1139 {
1140 partial_column_norms[it->column()] += std::abs(
1141 it->value() / local_row_norms[local_row_idx]);
1142 columns[idx] = it->column();
1143 values[idx] =
1144 it->value() / local_row_norms[local_row_idx];
1145 ++idx;
1146 }
1147 matrix.set(row, columns, values);
1148 if constexpr (std::is_same_v<
1149 Matrix,
1151 matrix.compress(VectorOperation::insert);
1152
1153 row_scaling[local_row_idx] /=
1154 local_row_norms[local_row_idx];
1155 }
1156#ifdef DEAL_II_WITH_TRILINOS
1157 if constexpr (std::is_same_v<Matrix,
1159 matrix.compress(VectorOperation::insert);
1160#endif
1161
1162 // Communicate partial column norms
1163 std::map<
1164 unsigned int,
1165 std::vector<std::pair<types::global_dof_index, double>>>
1166 send_data;
1167
1168 send_prepare_col_norms(partial_column_norms,
1169 send_data,
1170 local_col_norms);
1171
1172 auto received_data =
1173 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
1174 send_data);
1175
1176 // Process received data and fill local_col_norms
1177 for (const auto &[sender_rank, pairs] : received_data)
1178 {
1179 for (const auto &[global_col, contribution] : pairs)
1180 {
1181 unsigned int local_idx =
1182 partitioner.global_to_local(global_col);
1183 local_col_norms[local_idx] += contribution;
1184 }
1185 }
1186
1187 // Convergence check only on columns
1188 if (check_convergence(local_col_norms,
1190 matrix.get_mpi_communicator()))
1191 return true;
1192
1193 // Column step
1194 local_row_norms = 0;
1195
1196 // Communicate column norm values to all ranks that need them
1197 std::map<
1198 unsigned int,
1199 std::vector<std::pair<types::global_dof_index, double>>>
1200 send_column_norms;
1201
1202 send_prepare_updated_col_norms(received_data,
1203 local_col_norms,
1204 send_column_norms);
1205
1206 auto received_column_norms =
1207 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
1208 send_column_norms);
1209
1210 std::map<types::global_dof_index, double>
1211 ghost_column_norms_lookup;
1212 for (const auto &[sender_rank, pairs] : received_column_norms)
1213 {
1214 for (const auto &[col_id, norm_val] : pairs)
1215 {
1216 ghost_column_norms_lookup[col_id] = norm_val;
1217 }
1218 }
1219
1220 for (const auto row : locally_owned_rows)
1221 {
1222 auto local_row_idx = partitioner.global_to_local(row);
1223 auto row_size = matrix.row_length(row);
1224 std::vector<Number> values(row_size);
1225 std::vector<types::global_dof_index> columns(row_size);
1226 unsigned int idx = 0;
1227 for (auto it = matrix.begin(row); it != matrix.end(row);
1228 ++it)
1229 {
1230 if (locally_owned_cols.is_element(it->column()))
1231 {
1232 unsigned int local_col_idx =
1233 partitioner.global_to_local(it->column());
1234
1235 local_row_norms[local_row_idx] += std::abs(
1236 it->value() / local_col_norms[local_col_idx]);
1237
1238 columns[idx] = it->column();
1239 values[idx] =
1240 it->value() / local_col_norms[local_col_idx];
1241 ++idx;
1242 }
1243 else
1244 {
1245 double col_norms =
1246 ghost_column_norms_lookup[it->column()];
1247
1248 local_row_norms[local_row_idx] +=
1249 std::abs(it->value() / col_norms);
1250
1251 columns[idx] = it->column();
1252 values[idx] = it->value() / col_norms;
1253 ++idx;
1254 }
1255 }
1256 matrix.set(row, columns, values);
1257 if constexpr (std::is_same_v<
1258 Matrix,
1260 matrix.compress(VectorOperation::insert);
1261 }
1262#ifdef DEAL_II_WITH_TRILINOS
1263 if constexpr (std::is_same_v<Matrix,
1265 matrix.compress(VectorOperation::insert);
1266#endif
1267 for (unsigned int i = 0; i < local_col_norms.size(); ++i)
1268 column_scaling[i] /= local_col_norms[i];
1269
1270 if (check_convergence(local_row_norms,
1272 matrix.get_mpi_communicator()))
1273 return true;
1274 }
1275 break;
1276 }
1277
1279 {
1280 // Row_norms_0 to start the procedure
1281 local_row_norms = 0;
1282 for (const auto row : locally_owned_rows)
1283 {
1284 auto local_row_idx = partitioner.global_to_local(row);
1285 for (auto it = matrix.begin(row); it != matrix.end(row); ++it)
1286 local_row_norms[local_row_idx] =
1287 std::max(local_row_norms[local_row_idx],
1288 std::abs(it->value()));
1289 }
1290
1291 for (unsigned int i = 0; i < nsteps; i++)
1292 {
1293 // Row step
1294 local_col_norms = 0;
1295 partial_column_norms.clear();
1296
1297 for (const auto row : locally_owned_rows)
1298 {
1299 auto local_row_idx = partitioner.global_to_local(row);
1300 auto row_size = matrix.row_length(row);
1301 std::vector<Number> values(row_size);
1302 std::vector<types::global_dof_index> columns(row_size);
1303 unsigned int idx = 0;
1304 for (auto it = matrix.begin(row); it != matrix.end(row);
1305 ++it)
1306 {
1307 partial_column_norms[it->column()] =
1308 std::max(partial_column_norms[it->column()],
1309 std::abs(it->value() /
1310 local_row_norms[local_row_idx]));
1311 columns[idx] = it->column();
1312 values[idx] =
1313 it->value() / local_row_norms[local_row_idx];
1314 ++idx;
1315 }
1316 matrix.set(row, columns, values);
1317 if constexpr (std::is_same_v<
1318 Matrix,
1320 matrix.compress(VectorOperation::insert);
1321
1322 row_scaling[local_row_idx] /=
1323 local_row_norms[local_row_idx];
1324 }
1325#ifdef DEAL_II_WITH_TRILINOS
1326 if constexpr (std::is_same_v<Matrix,
1328 matrix.compress(VectorOperation::insert);
1329#endif
1330
1331 // Communicate partial column norms
1332 std::map<
1333 unsigned int,
1334 std::vector<std::pair<types::global_dof_index, double>>>
1335 send_data;
1336
1337 send_prepare_col_norms(partial_column_norms,
1338 send_data,
1339 local_col_norms);
1340
1341 auto received_data =
1342 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
1343 send_data);
1344
1345 // Process received data and fill local_col_norms
1346 for (const auto &[sender_rank, pairs] : received_data)
1347 {
1348 for (const auto &[global_col, contribution] : pairs)
1349 {
1350 unsigned int local_idx =
1351 partitioner.global_to_local(global_col);
1352 local_col_norms[local_idx] =
1353 std::max(local_col_norms[local_idx], contribution);
1354 }
1355 }
1356
1358 local_col_norms,
1360 matrix.get_mpi_communicator()))
1361 return true;
1362
1363 // Column step
1364 local_row_norms = 0;
1365
1366 // Communicate column norm values to all ranks that need them
1367 std::map<
1368 unsigned int,
1369 std::vector<std::pair<types::global_dof_index, double>>>
1370 send_column_norms;
1371
1372 send_prepare_updated_col_norms(received_data,
1373 local_col_norms,
1374 send_column_norms);
1375
1376 auto received_column_norms =
1377 Utilities::MPI::some_to_some(matrix.get_mpi_communicator(),
1378 send_column_norms);
1379
1380 std::map<types::global_dof_index, double>
1381 ghost_column_norms_lookup;
1382 for (const auto &[sender_rank, pairs] : received_column_norms)
1383 {
1384 for (const auto &[col_id, norm_val] : pairs)
1385 {
1386 ghost_column_norms_lookup[col_id] = norm_val;
1387 }
1388 }
1389
1390 for (const auto row : locally_owned_rows)
1391 {
1392 auto local_row_idx = partitioner.global_to_local(row);
1393 auto row_size = matrix.row_length(row);
1394 std::vector<Number> values(row_size);
1395 std::vector<types::global_dof_index> columns(row_size);
1396 unsigned int idx = 0;
1397 for (auto it = matrix.begin(row); it != matrix.end(row);
1398 ++it)
1399 {
1400 if (locally_owned_cols.is_element(it->column()))
1401 {
1402 unsigned int local_col_idx =
1403 partitioner.global_to_local(it->column());
1404
1405 local_row_norms[local_row_idx] =
1406 std::max(local_row_norms[local_row_idx],
1407 std::abs(
1408 it->value() /
1409 local_col_norms[local_col_idx]));
1410
1411 columns[idx] = it->column();
1412 values[idx] =
1413 it->value() / local_col_norms[local_col_idx];
1414 ++idx;
1415 }
1416 else
1417 {
1418 double col_norms =
1419 ghost_column_norms_lookup[it->column()];
1420
1421 local_row_norms[local_row_idx] =
1422 std::max(local_row_norms[local_row_idx],
1423 std::abs(it->value() / col_norms));
1424
1425 columns[idx] = it->column();
1426 values[idx] = it->value() / col_norms;
1427 ++idx;
1428 }
1429 }
1430 matrix.set(row, columns, values);
1431 if constexpr (std::is_same_v<
1432 Matrix,
1434 matrix.compress(VectorOperation::insert);
1435 }
1436#ifdef DEAL_II_WITH_TRILINOS
1437 if constexpr (std::is_same_v<Matrix,
1439 matrix.compress(VectorOperation::insert);
1440#endif
1441 for (unsigned int i = 0; i < local_col_norms.size(); ++i)
1442 column_scaling[i] /= local_col_norms[i];
1443
1445 local_row_norms,
1447 matrix.get_mpi_communicator()))
1448 return true;
1449 }
1450 break;
1451 }
1452
1453 default:
1455 }
1456 }
1457 return false;
1458}
1459
1460
1461
1462#define InstantiateMatrixScaling(MATRIX, VECTOR) \
1463 template bool MatrixScaling::find_scaling_and_scale_matrix(MATRIX &); \
1464 template bool MatrixScaling::find_scaling_and_scale_linear_system(MATRIX &, \
1465 VECTOR &); \
1466 template void MatrixScaling::scale_system_solution(VECTOR &) const;
1467
1468#ifdef DEAL_II_TRILINOS_WITH_EPETRA
1471#endif
1472
1473#ifdef DEAL_II_WITH_PETSC
1476#endif
1477
1478
size_type index_within_set(const size_type global_index) const
Definition index_set.h:1977
size_type n_elements() const
Definition index_set.h:1917
bool is_element(const size_type index) const
Definition index_set.h:1877
void set_size(const size_type size)
Definition index_set.h:1747
void clear()
Definition index_set.h:1735
void add_index(const size_type index)
Definition index_set.h:1778
std::vector< size_type > get_index_vector() const
Definition index_set.cc:911
void compress(VectorOperation::values operation)
virtual size_type size() const override
void reinit(const size_type size, const bool omit_zeroing_entries=false)
Utilities::MPI::Partitioner partitioner
MatrixScaling(const AdditionalData &control=AdditionalData())
const Vector< double > & get_row_scaling() const
Vector< double > column_scaling
bool do_linfty_scaling(Matrix &matrix, const unsigned int nsteps)
bool do_sk_scaling(Matrix &matrix, const unsigned int nsteps)
IndexSet locally_owned_cols
AdditionalData control
Vector< double > row_scaling
bool find_scaling_and_scale_linear_system(Matrix &matrix, VectorType &rhs)
bool check_convergence(const Vector< Number > &row_col_norm, const ConvergenceNormType &norm_type) const
const Vector< double > & get_column_scaling() const
void send_prepare_updated_col_norms(const std::map< unsigned int, std::vector< std::pair< types::global_dof_index, double > > > &received_data, const Vector< double > &local_col_norms, std::map< unsigned int, std::vector< std::pair< types::global_dof_index, double > > > &send_column_norms)
@ l_infty
l_infinity vector norm
void send_prepare_col_norms(const std::map< types::global_dof_index, double > &partial_column_norms, std::map< unsigned int, std::vector< std::pair< types::global_dof_index, double > > > &send_data, Vector< double > &local_col_norms)
IndexSet ghost_columns
IndexSet locally_owned_rows
std::vector< unsigned int > ghost_column_owners
bool do_l1_scaling(Matrix &matrix, const unsigned int nsteps)
void scale_system_solution(VectorType &sol) const
bool find_scaling_and_scale_matrix(Matrix &matrix)
unsigned int global_to_local(const types::global_dof_index global_index) const
virtual void reinit(const IndexSet &locally_owned_indices, const IndexSet &ghost_indices, const MPI_Comm communicator) override
virtual size_type size() const override
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcMessage(std::string arg1)
constexpr bool is_sequential_matrix()
#define InstantiateMatrixScaling(MATRIX, VECTOR)
T logical_or(const T &t, const MPI_Comm mpi_communicator)
std::map< unsigned int, T > some_to_some(const MPI_Comm comm, const std::map< unsigned int, T > &objects_to_send)
std::vector< unsigned int > compute_index_owner(const IndexSet &owned_indices, const IndexSet &indices_to_look_up, const MPI_Comm comm)
Definition mpi.cc:1820
IndexSet create_evenly_distributed_partitioning(const MPI_Comm comm, const types::global_dof_index total_size)
Definition mpi.cc:204
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
SKParameters(const NormType norm_type=NormType::l1, const unsigned int max_iterations=20)
l1linfParameters(const unsigned int start_inf_norm_steps=1, const unsigned int l1_norm_steps=3, const unsigned int end_inf_norm_steps=1)
@ l1_linf_symmetry_preserving
Symmetry preserving scaling algorithm.
@ sinkhorn_knopp
Sinkhorn-Knopp scaling algorithm.
AdditionalData(const double scaling_tolerance=1e-5, const ScalingAlgorithm alg=ScalingAlgorithm::l1_linf_symmetry_preserving, const SKParameters sk_params=SKParameters(), const l1linfParameters l1linf_params=l1linfParameters())